<commit_before>//! LR(1) state construction algorithm.

use collections::{map, Multimap};
use kernel_set;
use grammar::repr::*;
use lr1::core::*;
use lr1::first;
use lr1::lane_table::*;
use lr1::lookahead::*;
use std::rc::Rc;
use tls::Tls;

#[cfg(test)]
mod test;

pub fn build_lr1_states<'grammar>(grammar: &'grammar Grammar,
                                  start: NonterminalString)
                                  -> LR1Result<'grammar>
{
    // this is just here to squash dead code warnings while work on
    // lane-table proceeds
    if false {
        let _ = build_lane_table_states(grammar, start);
    }

    profile! {
        &Tls::session(),
        "LR(1) state construction",
        {
            let eof = TokenSet::eof();
            let mut lr1: LR<'grammar, TokenSet> = LR::new(grammar, start, eof);
            lr1.set_permit_early_stop(true);
            lr1.build_states()
        }
    }
}

pub fn build_lr0_states<'grammar>(grammar: &'grammar Grammar,
                                  start: NonterminalString)
                                  -> Result<Vec<LR0State<'grammar>>,
                                            LR0TableConstructionError<'grammar>>
{
    let lr1 = LR::new(grammar, start, Nil);
    lr1.build_states()
}

pub struct LR<'grammar, L: LookaheadBuild> {
    grammar: &'grammar Grammar,
    first_sets: first::FirstSets,
    start_nt: NonterminalString,
    start_lookahead: L,
    permit_early_stop: bool,
}

impl<'grammar, L: LookaheadBuild> LR<'grammar, L> {
    fn new(grammar: &'grammar Grammar,
           start_nt: NonterminalString,
           start_lookahead: L)
           -> Self {
        LR {
            grammar: grammar,
            first_sets: first::FirstSets::new(grammar),
            start_nt: start_nt,
            start_lookahead: start_lookahead,
            permit_early_stop: false,
        }
    }

    fn set_permit_early_stop(&mut self, v: bool) {
        self.permit_early_stop = v;
    }

    fn build_states(&self)
                    -> Result<Vec<State<'grammar, L>>,
                              TableConstructionError<'grammar, L>>
    {
        let session = Tls::session();
        let mut kernel_set = kernel_set::KernelSet::new();
        let mut states = vec![];
        let mut conflicts = vec![];

        // create the starting state
        kernel_set.add_state(Kernel::start(self.items(self.start_nt,
                                                      0,
                                                      &self.start_lookahead)));

        while let Some(Kernel { items: seed_items }) = kernel_set.next() {
            let items = self.transitive_closure(seed_items);
            let index = StateIndex(states.len());

            if index.0 % 5000 == 0 && index.0 > 0 {
                log!(session, Verbose, "{} states created so far.", index.0);
            }

            let mut this_state = State { index: index,
                                         items: items.clone(),
                                         shifts: map(),
                                         reductions: vec!(),
                                         gotos: map() };

            // group the items that we can transition into by shifting
            // over a term or nonterm
            let transitions: Multimap<Symbol, Multimap<LR0Item<'grammar>, L>> =
                items.vec
                     .iter()
                     .filter_map(|item| item.shifted_item())
                     .map(|(symbol, Item { production, index, lookahead })| {
                         (symbol, (Item::lr0(production, index), lookahead))
                     })
                     .collect();

            for (symbol, shifted_items) in transitions.into_iter() {
                let shifted_items: Vec<Item<'grammar, L>> =
                    shifted_items.into_iter()
                                 .map(|(lr0_item, lookahead)| {
                                     lr0_item.with_lookahead(lookahead)
                                 })
                                 .collect();

                // Not entirely obvious: if the original set of items
                // is sorted to begin with (and it is), then this new
                // set of shifted items is *also* sorted. This is
                // because it is produced from the old items by simply
                // incrementing the index by 1.
                let next_state = kernel_set.add_state(Kernel::shifted(shifted_items));

                match symbol {
                    Symbol::Terminal(s) => {
                        let prev = this_state.shifts.insert(s, next_state);
                        assert!(prev.is_none()); // cannot have a shift/shift conflict
                    }

                    Symbol::Nonterminal(s) => {
                        let prev = this_state.gotos.insert(s, next_state);
                        assert!(prev.is_none());
                    }
                }
            }

            // finally, consider the reductions
            for item in items.vec.iter().filter(|i| i.can_reduce()) {
                this_state.reductions.push((item.lookahead.clone(), item.production));
            }

            // check for shift-reduce conflicts (reduce-reduce detected above)
            conflicts.extend(L::conflicts(&this_state));

            // extract a new state
            states.push(this_state);

            if self.permit_early_stop && session.stop_after(conflicts.len()) {
                log!(session, Verbose,
                     "{} conflicts encountered, stopping.", conflicts.len());
                break;
            }
        }

        if !conflicts.is_empty() {
            Err(TableConstructionError { states: states, conflicts: conflicts })
        } else {
            Ok(states)
        }
    }

    fn items(&self,
             id: NonterminalString,
             index: usize,
             lookahead: &L)
             -> Vec<Item<'grammar, L>>
    {
        self.grammar.productions_for(id)
                    .iter()
                    .map(|production| {
                        debug_assert!(index <= production.symbols.len());
                        Item { production: production,
                               index: index,
                               lookahead: lookahead.clone() }
                    })
                    .collect()
    }

    // expands `state` with epsilon moves
    fn transitive_closure(&self, items: Vec<Item<'grammar, L>>)
                          -> Items<'grammar, L>
    {
        let mut stack: Vec<LR0Item<'grammar>> =
            items.iter().map(|item| item.to_lr0()).collect();
        let mut map: Multimap<LR0Item<'grammar>, L> =
            items.into_iter()
                 .map(|item| (item.to_lr0(), item.lookahead))
                 .collect();

        while let Some(item) = stack.pop() {
            let lookahead = map.get(&item).unwrap().clone();

            let shift_symbol = item.shift_symbol();

            // Check whether this is an item where the cursor
            // is resting on a non-terminal:
            //
            // I = ... (*) X z... [lookahead]
            //
            // The `nt` will be X and the `remainder` will be `z...`.
            let (nt, remainder) = match shift_symbol {
                None => continue, // requires a reduce
                Some((Symbol::Terminal(_), _)) => {
                    continue; // requires a shift
                }
                Some((Symbol::Nonterminal(nt), remainder)) => {
                    (nt, remainder)
                }
            };

            // In that case, for each production of `X`, we are also
            // in a state where the cursor rests at the start of that production:
            //
            // X = (*) a... [lookahead']
            // X = (*) b... [lookahead']
            //
            // Here `lookahead'` is computed based on the `remainder` and our
            // `lookahead`. In LR1 at least, it is the union of:
            //
            //   (a) FIRST(remainder)
            //   (b) if remainder may match epsilon, also our lookahead.
            for new_item in L::epsilon_moves(self, nt, remainder, &lookahead) {
                let new_item0 = new_item.to_lr0();
                if map.push(new_item0, new_item.lookahead) {
                    stack.push(new_item0);
                }
            }
        }

        let final_items = map.into_iter()
                             .map(|(lr0_item, lookahead)| lr0_item.with_lookahead(lookahead))
                             .collect();

        Items { vec: Rc::new(final_items) }
    }
}

/// Except for the initial state, the kernel sets always contain
/// a set of "seed" items where something has been pushed (that is,
/// index > 0). In other words, items like this:
///
///    A = ...p (*) ...
///
/// where ...p is non-empty. We now have to expand to include any
/// epsilon moves:
///
///    A = ... (*) B ...
///    B = (*) ...        // added by transitive_closure algorithm
///
/// But note that the state is completely identified by its
/// kernel set: the same kernel sets always expand to the
/// same transitive closures, and different kernel sets
/// always expand to different transitive closures. The
/// first point is obvious, but the latter point follows
/// because the transitive closure algorithm only adds
/// items where `index == 0`, and hence it can never add an
/// item found in a kernel set.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct Kernel<'grammar, L: LookaheadBuild> {
    items: Vec<Item<'grammar, L>>
}

impl<'grammar, L: LookaheadBuild> Kernel<'grammar, L> {
    pub fn start(items: Vec<Item<'grammar, L>>) -> Kernel<'grammar, L> {
        // In start state, kernel should have only items with `index == 0`.
        debug_assert!(items.iter().all(|item| item.index == 0));
        Kernel { items: items }
    }

    pub fn shifted(items: Vec<Item<'grammar, L>>) -> Kernel<'grammar, L> {
        // Assert that this kernel consists only of shifted items
        // where `index > 0`. This assertion could cost real time to
        // check so only do it in debug mode.
        debug_assert!(items.iter().all(|item| item.index > 0));
        Kernel { items: items }
    }
}

impl<'grammar, L: LookaheadBuild> kernel_set::Kernel for Kernel<'grammar, L> {
    type Index = StateIndex;

    fn index(c: usize) -> StateIndex {
        StateIndex(c)
    }
}

pub trait LookaheadBuild: Lookahead {
    // Given that there exists an item
    //
    //     X = ... (*) Y ...s [L]
    //
    // where `nt` is `Y`, `remainder` is `...s`, and `lookahead` is
    // `L`, computes the new items resulting from epsilon moves (if
    // any). The technique of doing this will depend on the amount of
    // lookahead.
    //
    // For example, if we have an LR0 item, then for each `Y = ...`
    // production, we just add an `Y = (*) ...` item. But for LR1
    // items, we have to add multiple items where we consider the
    // lookahead from `FIRST(...s, L)`.
    fn epsilon_moves<'grammar>(lr: &LR<'grammar, Self>,
                               nt: NonterminalString,
                               remainder: &[Symbol],
                               lookahead: &Self)
                               -> Vec<Item<'grammar, Self>>;
}

impl LookaheadBuild for Nil {
    fn epsilon_moves<'grammar>(lr: &LR<'grammar, Self>,
                               nt: NonterminalString,
                               _remainder: &[Symbol],
                               lookahead: &Nil)
                               -> Vec<LR0Item<'grammar>>
    {
        lr.items(nt, 0, &lookahead)
    }
}

impl LookaheadBuild for TokenSet {
    fn epsilon_moves<'grammar>(lr: &LR<'grammar, Self>,
                               nt: NonterminalString,
                               remainder: &[Symbol],
                               lookahead: &Self)
                               -> Vec<LR1Item<'grammar>>
    {
        let first_set = lr.first_sets.first1(remainder, lookahead);
        lr.items(nt, 0, &first_set)
    }
}
<commit_msg>Enabling lane-table-algorithm via environment variable<commit_after>//! LR(1) state construction algorithm.

use collections::{map, Multimap};
use kernel_set;
use grammar::repr::*;
use lr1::core::*;
use lr1::first;
use lr1::lane_table::*;
use lr1::lookahead::*;
use std::rc::Rc;
use std::env;
use tls::Tls;

#[cfg(test)]
mod test;

fn build_lr1_states_legacy<'grammar>(grammar: &'grammar Grammar, start: NonterminalString) -> LR1Result<'grammar>
{
    let eof = TokenSet::eof();
    let mut lr1: LR<'grammar, TokenSet> = LR::new(grammar, start, eof);
    lr1.set_permit_early_stop(true);
    lr1.build_states()
}

type ConstructionFunction<'grammar> = fn(&'grammar Grammar, NonterminalString) -> LR1Result<'grammar> ;

pub fn build_lr1_states<'grammar>(grammar: &'grammar Grammar,
                                  start: NonterminalString)
                                  -> LR1Result<'grammar>
{

    let (method_name, method_fn) = match env::var("LALRPOP_LANE_TABLE") {
        Ok(ref s) if s == "enabled" => {
            ("lane", build_lane_table_states as ConstructionFunction)
        }
        _  => {
            ("legacy", build_lr1_states_legacy as ConstructionFunction)
        }
    };

    profile! {
        &Tls::session(),
        format!("LR(1) state construction ({})", method_name),
        {
            method_fn(grammar, start)
        }
    }
}

pub fn build_lr0_states<'grammar>(grammar: &'grammar Grammar,
                                  start: NonterminalString)
                                  -> Result<Vec<LR0State<'grammar>>,
                                            LR0TableConstructionError<'grammar>>
{
    let lr1 = LR::new(grammar, start, Nil);
    lr1.build_states()
}

pub struct LR<'grammar, L: LookaheadBuild> {
    grammar: &'grammar Grammar,
    first_sets: first::FirstSets,
    start_nt: NonterminalString,
    start_lookahead: L,
    permit_early_stop: bool,
}

impl<'grammar, L: LookaheadBuild> LR<'grammar, L> {
    fn new(grammar: &'grammar Grammar,
           start_nt: NonterminalString,
           start_lookahead: L)
           -> Self {
        LR {
            grammar: grammar,
            first_sets: first::FirstSets::new(grammar),
            start_nt: start_nt,
            start_lookahead: start_lookahead,
            permit_early_stop: false,
        }
    }

    fn set_permit_early_stop(&mut self, v: bool) {
        self.permit_early_stop = v;
    }

    fn build_states(&self)
                    -> Result<Vec<State<'grammar, L>>,
                              TableConstructionError<'grammar, L>>
    {
        let session = Tls::session();
        let mut kernel_set = kernel_set::KernelSet::new();
        let mut states = vec![];
        let mut conflicts = vec![];

        // create the starting state
        kernel_set.add_state(Kernel::start(self.items(self.start_nt,
                                                      0,
                                                      &self.start_lookahead)));

        while let Some(Kernel { items: seed_items }) = kernel_set.next() {
            let items = self.transitive_closure(seed_items);
            let index = StateIndex(states.len());

            if index.0 % 5000 == 0 && index.0 > 0 {
                log!(session, Verbose, "{} states created so far.", index.0);
            }

            let mut this_state = State { index: index,
                                         items: items.clone(),
                                         shifts: map(),
                                         reductions: vec!(),
                                         gotos: map() };

            // group the items that we can transition into by shifting
            // over a term or nonterm
            let transitions: Multimap<Symbol, Multimap<LR0Item<'grammar>, L>> =
                items.vec
                     .iter()
                     .filter_map(|item| item.shifted_item())
                     .map(|(symbol, Item { production, index, lookahead })| {
                         (symbol, (Item::lr0(production, index), lookahead))
                     })
                     .collect();

            for (symbol, shifted_items) in transitions.into_iter() {
                let shifted_items: Vec<Item<'grammar, L>> =
                    shifted_items.into_iter()
                                 .map(|(lr0_item, lookahead)| {
                                     lr0_item.with_lookahead(lookahead)
                                 })
                                 .collect();

                // Not entirely obvious: if the original set of items
                // is sorted to begin with (and it is), then this new
                // set of shifted items is *also* sorted. This is
                // because it is produced from the old items by simply
                // incrementing the index by 1.
                let next_state = kernel_set.add_state(Kernel::shifted(shifted_items));

                match symbol {
                    Symbol::Terminal(s) => {
                        let prev = this_state.shifts.insert(s, next_state);
                        assert!(prev.is_none()); // cannot have a shift/shift conflict
                    }

                    Symbol::Nonterminal(s) => {
                        let prev = this_state.gotos.insert(s, next_state);
                        assert!(prev.is_none());
                    }
                }
            }

            // finally, consider the reductions
            for item in items.vec.iter().filter(|i| i.can_reduce()) {
                this_state.reductions.push((item.lookahead.clone(), item.production));
            }

            // check for shift-reduce conflicts (reduce-reduce detected above)
            conflicts.extend(L::conflicts(&this_state));

            // extract a new state
            states.push(this_state);

            if self.permit_early_stop && session.stop_after(conflicts.len()) {
                log!(session, Verbose,
                     "{} conflicts encountered, stopping.", conflicts.len());
                break;
            }
        }

        if !conflicts.is_empty() {
            Err(TableConstructionError { states: states, conflicts: conflicts })
        } else {
            Ok(states)
        }
    }

    fn items(&self,
             id: NonterminalString,
             index: usize,
             lookahead: &L)
             -> Vec<Item<'grammar, L>>
    {
        self.grammar.productions_for(id)
                    .iter()
                    .map(|production| {
                        debug_assert!(index <= production.symbols.len());
                        Item { production: production,
                               index: index,
                               lookahead: lookahead.clone() }
                    })
                    .collect()
    }

    // expands `state` with epsilon moves
    fn transitive_closure(&self, items: Vec<Item<'grammar, L>>)
                          -> Items<'grammar, L>
    {
        let mut stack: Vec<LR0Item<'grammar>> =
            items.iter().map(|item| item.to_lr0()).collect();
        let mut map: Multimap<LR0Item<'grammar>, L> =
            items.into_iter()
                 .map(|item| (item.to_lr0(), item.lookahead))
                 .collect();

        while let Some(item) = stack.pop() {
            let lookahead = map.get(&item).unwrap().clone();

            let shift_symbol = item.shift_symbol();

            // Check whether this is an item where the cursor
            // is resting on a non-terminal:
            //
            // I = ... (*) X z... [lookahead]
            //
            // The `nt` will be X and the `remainder` will be `z...`.
            let (nt, remainder) = match shift_symbol {
                None => continue, // requires a reduce
                Some((Symbol::Terminal(_), _)) => {
                    continue; // requires a shift
                }
                Some((Symbol::Nonterminal(nt), remainder)) => {
                    (nt, remainder)
                }
            };

            // In that case, for each production of `X`, we are also
            // in a state where the cursor rests at the start of that production:
            //
            // X = (*) a... [lookahead']
            // X = (*) b... [lookahead']
            //
            // Here `lookahead'` is computed based on the `remainder` and our
            // `lookahead`. In LR1 at least, it is the union of:
            //
            //   (a) FIRST(remainder)
            //   (b) if remainder may match epsilon, also our lookahead.
            for new_item in L::epsilon_moves(self, nt, remainder, &lookahead) {
                let new_item0 = new_item.to_lr0();
                if map.push(new_item0, new_item.lookahead) {
                    stack.push(new_item0);
                }
            }
        }

        let final_items = map.into_iter()
                             .map(|(lr0_item, lookahead)| lr0_item.with_lookahead(lookahead))
                             .collect();

        Items { vec: Rc::new(final_items) }
    }
}

/// Except for the initial state, the kernel sets always contain
/// a set of "seed" items where something has been pushed (that is,
/// index > 0). In other words, items like this:
///
///    A = ...p (*) ...
///
/// where ...p is non-empty. We now have to expand to include any
/// epsilon moves:
///
///    A = ... (*) B ...
///    B = (*) ...        // added by transitive_closure algorithm
///
/// But note that the state is completely identified by its
/// kernel set: the same kernel sets always expand to the
/// same transitive closures, and different kernel sets
/// always expand to different transitive closures. The
/// first point is obvious, but the latter point follows
/// because the transitive closure algorithm only adds
/// items where `index == 0`, and hence it can never add an
/// item found in a kernel set.
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct Kernel<'grammar, L: LookaheadBuild> {
    items: Vec<Item<'grammar, L>>
}

impl<'grammar, L: LookaheadBuild> Kernel<'grammar, L> {
    pub fn start(items: Vec<Item<'grammar, L>>) -> Kernel<'grammar, L> {
        // In start state, kernel should have only items with `index == 0`.
        debug_assert!(items.iter().all(|item| item.index == 0));
        Kernel { items: items }
    }

    pub fn shifted(items: Vec<Item<'grammar, L>>) -> Kernel<'grammar, L> {
        // Assert that this kernel consists only of shifted items
        // where `index > 0`. This assertion could cost real time to
        // check so only do it in debug mode.
        debug_assert!(items.iter().all(|item| item.index > 0));
        Kernel { items: items }
    }
}

impl<'grammar, L: LookaheadBuild> kernel_set::Kernel for Kernel<'grammar, L> {
    type Index = StateIndex;

    fn index(c: usize) -> StateIndex {
        StateIndex(c)
    }
}

pub trait LookaheadBuild: Lookahead {
    // Given that there exists an item
    //
    //     X = ... (*) Y ...s [L]
    //
    // where `nt` is `Y`, `remainder` is `...s`, and `lookahead` is
    // `L`, computes the new items resulting from epsilon moves (if
    // any). The technique of doing this will depend on the amount of
    // lookahead.
    //
    // For example, if we have an LR0 item, then for each `Y = ...`
    // production, we just add an `Y = (*) ...` item. But for LR1
    // items, we have to add multiple items where we consider the
    // lookahead from `FIRST(...s, L)`.
    fn epsilon_moves<'grammar>(lr: &LR<'grammar, Self>,
                               nt: NonterminalString,
                               remainder: &[Symbol],
                               lookahead: &Self)
                               -> Vec<Item<'grammar, Self>>;
}

impl LookaheadBuild for Nil {
    fn epsilon_moves<'grammar>(lr: &LR<'grammar, Self>,
                               nt: NonterminalString,
                               _remainder: &[Symbol],
                               lookahead: &Nil)
                               -> Vec<LR0Item<'grammar>>
    {
        lr.items(nt, 0, &lookahead)
    }
}

impl LookaheadBuild for TokenSet {
    fn epsilon_moves<'grammar>(lr: &LR<'grammar, Self>,
                               nt: NonterminalString,
                               remainder: &[Symbol],
                               lookahead: &Self)
                               -> Vec<LR1Item<'grammar>>
    {
        let first_set = lr.first_sets.first1(remainder, lookahead);
        lr.items(nt, 0, &first_set)
    }
}
<|endoftext|>


<commit_before>#![feature(peek)]
extern crate encoding;
extern crate byteorder;

use byteorder::{ByteOrder, BigEndian, LittleEndian};
use std::{error}; //{env, error}
use std::io::prelude::*;
use std::net::TcpStream;
use encoding::{Encoding, EncoderTrap};
use encoding::all::ASCII;
use std::io;
use std::str;
use std::mem;
//use std::thread::sleep;
//use std::io::prelude::*;
//use std::time::Duration;

fn check_file(command: &str, mut stream: &mut TcpStream) -> Result<String, Box<error::Error + Send + Sync>> {

    /*
    ****************
    *  REQUEST AST *
    ****************
    */

    //get string size (in bytes)
    let mut string_size = command.len();
    string_size = string_size + 1;
    println!("sending {} bytes", string_size);

    let mut string_size_str = string_size.to_string();

    //prepare buffer to send size
    let mut string_size_bytes =  try!(ASCII.encode(&string_size_str, EncoderTrap::Strict).map_err(|x| x.into_owned()));

    //prepare buffer to send message itself
    let mut command_bytes = try!(ASCII.encode(command, EncoderTrap::Strict).map_err(|x| x.into_owned()));
    command_bytes.push('\r' as u8); //ending escape sequence

    let mut response = String::new();

    //send message size:
    stream.write_all(&string_size_bytes).unwrap();

    //send file path
    stream.write_all(&command_bytes).unwrap();

    //receive message length:
    let mut buf = [0u8; 8]; //make it bigger
    stream.read(&mut buf).unwrap();

    //interpret the buffer contents into a string slice
    //let mut cl = buf.clone();
    let mut msg_len_slice: &str = str::from_utf8(&mut buf).unwrap(); //string slice

    //convert string slice to string type
    let mut msg_len_str = msg_len_slice.to_string();

    //clean string

    /*
    server might send message size smaller than buffer,
    which is usually the case when the server is sending
    the message size:
    buffer:     _ _ _ _ _ _ _ (bytes)
    message:    1 2 _ _ _ _ _ (bytes)
    (empty characters trail the meaningful characters)
    if this is the case, we shrink the string using .truncate()
    */

    let mut numeric_chars = 0;
    for c in msg_len_str.chars() {
        if c.is_numeric() == true { numeric_chars = numeric_chars+1;}
    }

    //shrink:
    msg_len_str.truncate(numeric_chars);

    //let msg_len = LittleEndian::read_u64(&mut cl);
    println!("receiving {} bytes", msg_len_str);

    //receive actual message:
    let mut remainingData = msg_len_str.parse::<i32>().unwrap();
    let mut r = [0u8; 8]; //buffer
    //println!("integer: {}", remainingData);

    let mut accumulator: String = String::new();

    while remainingData != 0
    {
        //println!("{} bytes remaining", remainingData);
        //if remainingData >= 8 // slab >= 8 byte buffer
        //{
            let mut slab = stream.read(&mut r);
            match slab {
                Ok(n) => {
                    let r_str = str::from_utf8(&mut r).unwrap();
                    accumulator.push_str(r_str);
                    println!("wrote {} bytes", n);
                    remainingData = remainingData - n as i32;
                }
                _ => {},
            }
        //}

    }

    //println!("{}", accumulator);

    //let s_ref = <TcpStream as Read>::by_ref(&mut stream);
    //match s_ref.take(msg_len).read(&mut r) {

    /*
    let s_ref = <TcpStream as Read>::by_ref(&mut stream);
    match s_ref.take(msg_len).read(&mut r) {
        Ok(0) => {
            println!("0 bytes read");
        },
        Ok(n) => {
            println!("{} bytes read", n);
            let response = std::str::from_utf8(&r[..]).unwrap();
            println!("{} bytes read", response);
        },
        Err(e) => {
            panic!("{} panic!", e);
        }
    }*/

    //raw_bytes.read_to_string(&mut response);          //convert string

    //response = str::from_utf8(raw_bytes).unwrap();

    //let conv = String::from_utf8(&raw_bytes.to_vec()).unwrap();
    //let mut limited = stream.take(8);
    //let mut limited = stream.read_to_string(&mut response);
    //let _ = limited.read_to_string(&mut response);

    response = accumulator;
    Ok(response)
}

fn main() {
    //setup connection:
    //let mut stream = TcpStream::connect("127.0.0.1:5555")
    let mut stream = TcpStream::connect("127.0.0.1:5555") // try!(TcpStream::connect(HOST));
                                .expect("Couldn't connect to the server...");

    //make stream nonblocking
    //stream.set_nonblocking(true).expect("set_nonblocking call failed");

    loop {
        println!("Type command:");
        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let command = line.unwrap();
            println!("message: {}", command);

            match check_file(&command, &mut stream){
                Ok(response) => println!("response: {}", response),
                Err(err) => println!("An error occurred: {}", err),
            }
        }
    }
}

/* mob client
fn main()
{
    let mut stream = TcpStream::connect("127.0.0.1:5555").unwrap();

    let mut x = 0;
    while x < 10
    {
        let msg = "hello"; //11 or 12
        let mut buf = [0u8; 8];
        //println!("Sending over message length of {}", msg.len());
        //write the message length to buffer
        //BigEndian::write_u64(&mut buf, msg.len() as u64);
        //stream.write_all(buf.as_ref()).unwrap();

        //send message
        stream.write_all(msg.as_ref()).unwrap();

        //read message length
        let mut buf = [0u8; 8];
        stream.read(&mut buf).unwrap();
        let msg_len = BigEndian::read_u64(&mut buf);
        println!("Reading message length of {}", msg_len);

        //read message itself
        let mut r = [0u8; 256];
        let s_ref = <TcpStream as Read>::by_ref(&mut stream);

        match s_ref.take(msg_len).read(&mut r) {
            Ok(0) => {
                println!("0 bytes read");
            },
            Ok(n) => {
                println!("{} bytes read", n);
                let s = std::str::from_utf8(&r[..]).unwrap();
                println!("{} bytes read", s);
            },
            Err(e) => {
                panic!("{}", e);
            }
        }
        x = x+1;
    }
}*/<commit_msg>echop rust client / python server works with strings of any size<commit_after>extern crate encoding;
extern crate byteorder;

use std::{error}; //{env, error}
use std::io::prelude::*;
use std::net::TcpStream;
use encoding::{Encoding, EncoderTrap};
use encoding::all::ASCII;
use std::io;
use std::str;

fn check_file(command: &str, mut stream: &mut TcpStream) -> Result<String, Box<error::Error + Send + Sync>> {

    /*
    ****************
    *  REQUEST AST *
    ****************
    */

    //get string size (in bytes)
    let mut string_size = command.len();
    string_size = string_size + 1;
    println!("sending {} bytes", string_size);

    let string_size_str = string_size.to_string();

    //prepare buffer to send size
    let string_size_bytes =  try!(ASCII.encode(&string_size_str, EncoderTrap::Strict).map_err(|x| x.into_owned()));

    //prepare buffer to send message itself
    let mut command_bytes = try!(ASCII.encode(command, EncoderTrap::Strict).map_err(|x| x.into_owned()));
    command_bytes.push('\r' as u8); //ending escape sequence

    //send message size:
    stream.write_all(&string_size_bytes).unwrap();

    //send file path
    stream.write_all(&command_bytes).unwrap();

    //receive message length:
    let mut buf = [0u8; 8]; //make it bigger
    stream.read(&mut buf).unwrap();

    //interpret the buffer contents into a string slice
    //let mut cl = buf.clone();
    let msg_len_slice: &str = str::from_utf8(&mut buf).unwrap(); //string slice
    let mut msg_len_str = msg_len_slice.to_string(); //convert slice to string

    /*
    CLEAN STRING:
    server might send message size smaller than buffer,
    which is usually the case when the server is sending
    the message size:
    buffer:     _ _ _ _ _ _ _ (bytes)
    message:    1 2 _ _ _ _ _ (bytes)
    (empty characters trail the meaningful characters)
    if this is the case, we shrink the string using .truncate()
    */

    let mut numeric_chars = 0;
    for c in msg_len_str.chars() {
        if c.is_numeric() == true { numeric_chars = numeric_chars+1;}
    }

    //shrink:
    msg_len_str.truncate(numeric_chars);

    println!("receiving {} bytes", msg_len_str);

    //receive actual message:
    let mut remaining_data = msg_len_str.parse::<i32>().unwrap();

    let mut accumulator: String = String::new();
    let mut r = [0u8; 8]; //standard buffer

    while remaining_data != 0
    {
        if remaining_data >= 8 //slab >= 8 byte buffer
        {
            let slab = stream.read(&mut r);
            match slab {
                Ok(n) => {
                    let r_slice = str::from_utf8(&mut r).unwrap(); //string slice
                    accumulator.push_str(r_slice);
                    println!("wrote {} bytes", n);
                    remaining_data = remaining_data - n as i32;
                }
                _ => {},
            }
        }
        /*
        option 1) receive and read a smaller buffer
        option 2) receive and read same buffer; truncate it to the smaller slab size

        since we cannot instantiate an array with a non-constant:
            e.g.: let mut r = [0u8; remainingData];
        it is better to just put the byte in the 8 byte buffer, and shrink it with
        .truncate() method before pushing to the String
        */
        else //slab < 8 byte buffer
        {
            let slab = stream.read(&mut r);
            match slab {
                Ok(n) => {
                    let s_slice = str::from_utf8(&mut r).unwrap(); //string slice
                    let mut s_str = s_slice.to_string(); //convert slice to string
                    s_str.truncate(n);
                    accumulator.push_str(&s_str);
                    println!("wrote {} bytes", n);
                    remaining_data = remaining_data - n as i32;
                }
                _ => {},
            }
        }

    }

    let response = accumulator;
    Ok(response)
}

fn main() {
    //setup connection:
    //let mut stream = TcpStream::connect("127.0.0.1:5555")
    let mut stream = TcpStream::connect("127.0.0.1:5555") // try!(TcpStream::connect(HOST));
                                .expect("Couldn't connect to the server...");

    //make stream nonblocking
    //stream.set_nonblocking(true).expect("set_nonblocking call failed");

    loop {
        println!("Type command:");
        let stdin = io::stdin();
        for line in stdin.lock().lines() {
            let command = line.unwrap();
            println!("message: {}", command);

            match check_file(&command, &mut stream){
                Ok(response) => println!("response: {}", response),
                Err(err) => println!("An error occurred: {}", err),
            }
        }
    }
}<|endoftext|>


<commit_before>//! A simple first program in rust, actually I plan to do something even
//! useful. Trying to find all my audiobooks on many machines, identify them,
//! find duplicates (later trying to solve the problem, also including their permissions,
//! different names, but same albums, etc, get all stats about it).
//! It acts as a wrapper around adbflib, which holds major parts of the implemention.
//!
//! Use e.g. ADBF_LOG_LVL=file RUST_BACKTRACE=full RUST_LOG=adbflib::webui=trace system,
//! file for enabling logging according to your likes.

mod command_line;

use adbfbinlib::{
    common::{logit, paths::SearchPath},
    ctrl::{Ctrl, UiUpdateMsg},
    data::{
        collection::Collection,
        ipc::{IFCollectionOutputData, IPC},
    },
    net::subs::key_keeper,
    shared,
};
use async_std::task;
use crossbeam::{channel::unbounded, sync::WaitGroup};
use ctrlc;
use exitcode;
use log::{error, info, trace};
use num_cpus;
use std::{
    cmp, env, io, process,
    sync::{Arc as SArc, Mutex as SMutex},
};

/// The main application which is central part of communicating with
/// the adbflib, which is closely connected.
fn main() -> io::Result<()> {
    // get start values from the input parser!!!
    let (ui_paths, has_tui, has_webui, has_net, keep_alive, open_browser, web_port, has_ui) =
        command_line::get_start_values();

    // read into paths
    let cleaned_paths = SearchPath::new(&ui_paths);
    if cleaned_paths.len() != ui_paths.len() && !has_tui && !has_webui && !open_browser {
        println!("Some paths/folders intersect and will not be used!");
    }

    // only use the arc
    let search_path = SArc::new(SMutex::new(cleaned_paths));
    let search_path_ui = search_path.clone();

    // define collection thread pool
    let ctrlc_thread = 1;
    let assumed_number_of_threads_used = if has_webui { 1 } else { 0 }
        + if has_tui { 1 } else { 0 }
        + if has_net { 1 } else { 0 }
        + ctrlc_thread;
    let nr_cpus = num_cpus::get();
    let nr_threads_for_collection = if assumed_number_of_threads_used >= nr_cpus {
        1
    } else {
        nr_cpus - assumed_number_of_threads_used
    };
    rayon::ThreadPoolBuilder::new()
        .thread_name(|nr| format!("col_{}", nr))
        .num_threads(nr_threads_for_collection)
        .build_global()
        .unwrap();

    // for synced start of different threads
    let wait_collector = WaitGroup::new();
    let wait_ui = wait_collector.clone();
    let wait_net = wait_collector.clone();

    //
    // prepare the message system
    //
    let (tx, rx) = unbounded::<UiUpdateMsg>();

    // these will be taken directly
    let tx_net = tx.clone();

    // ui message from here (collection)
    let tx_col = tx.clone();

    // for now this will stay wrapped
    let tx_from_collector_to_ui = SArc::new(SMutex::new(tx.clone()));

    // start the logging
    logit::Logit::init(logit::read_env_level(
        &env::var("ADBF_LOG").unwrap_or("".into()),
    ));

    // all optional components are wrapped into threads
    // 1 - UI         ui_thread   (optional)
    // 2 - Net        net_thread  (optional)
    // 3 - Collector  no thread yet (uses multiple rayon worker threads)
    let ui_thread = std::thread::Builder::new()
        .name("ui".into())
        .spawn(move || {
            if has_ui {
                Ctrl::run(
                    key_keeper::get_p2p_server_id(),
                    search_path_ui,
                    rx,
                    has_net,
                    wait_ui,
                    has_webui,
                    has_tui,
                    open_browser,
                    web_port,
                )
            } else {
                info!("no ui was created!");
                drop(wait_ui);
                Ok::<(), std::io::Error>(())
            }
        })?;

    // create a crossbeam channel for massive data exchange
    // with data I called IPC ...
    // in contrast to normal channels crossbeam channels are
    // n:m, so senders can be cloned as well!
    let (ipc_send, ipc_receive) = unbounded::<IPC>();

    // 2 - Net Future
    let net_thread = std::thread::Builder::new()
        .name("net".into())
        .spawn(move || {
            if has_net {
                let sender = Some(tx_net);
                task::block_on(
                    async move { shared::net_search(wait_net, sender, ipc_receive).await },
                )
                .map_err(|error| {
                    std::io::Error::new(
                        std::io::ErrorKind::Other,
                        format!("network error: {}", error),
                    )
                })
            } else {
                info!("no net!");
                drop(wait_net);
                Ok::<(), std::io::Error>(())
            }
        })?;

    // CTRL-C exit handler (is wrapped inside a thread)
    ctrlc::set_handler(move || {
        println!("\n'{}' was manually exited!!!", env!("CARGO_PKG_NAME"));
        process::exit(exitcode::SOFTWARE);
    })
    .map_err(|error| {
        std::io::Error::new(
            std::io::ErrorKind::Other,
            format!(
                "{} terminate signal invokation doesn't work!({:?})",
                env!("CARGO_PKG_NAME"),
                error
            ),
        )
    })?;

    // finished ipc sender reserved before collector takes action
    let ipc_send_finished = ipc_send.clone();

    // the collector
    // but yet this simple bracket to enclose this a little
    {
        trace!("syncing with 2 other threads");
        wait_collector.wait();
        trace!("sync with net and ui done ... collector can start");

        let synced_to_ui_messages = tx_from_collector_to_ui.clone();

        // set up data
        let collection_protected = SArc::new(SMutex::new(Collection::new()));

        // search parallelly
        let output_data = shared::collection_search(
            collection_protected.clone(),
            search_path,
            synced_to_ui_messages,
            has_ui,
        );

        info!("collector finished!!");
        if has_ui || has_net {
            // send own peer and others that are finished
            let memory_used = collection_protected
                .lock()
                .and_then(|collection| Ok(collection.memory()))
                .unwrap_or_else(|_| {
                    error!("locking collection didn't work here!");
                    0
                });

            let mut collection_output = IFCollectionOutputData::from(&output_data);
            // to have not 0 as data size minimum
            collection_output.size_of_data_in_kb = cmp::max(1, (memory_used / 1000) as usize);

            // to others via IPC
            if has_net {
                ipc_send_finished
                    .send(IPC::DoneSearching(collection_output.clone()))
                    .unwrap_or_else(|_| {
                        error!("net has to be up and receiving this send!");
                    });
            }
            // to myself (ui) via UiUpdateMsg
            if has_ui {
                tx_col
                    .send(UiUpdateMsg::PeerSearchFinished(
                        key_keeper::get_p2p_server_id(),
                        collection_output,
                    ))
                    .unwrap_or_else(|e| error!("use one: {}", e));
            }
        }
        if !has_ui {
            collection_protected
                .lock()
                .and_then(|locked_collection| {
                    Ok(locked_collection
                        .print_stats(&key_keeper::get_p2p_server_id(), nr_threads_for_collection))
                })
                .unwrap_or(())
        }
    }

    // look for keeping alive argument if that was chosen
    if keep_alive {
        ui_thread
            .join()
            .unwrap_or_else(|_| {
                // it doesn't matter because we will terminate anyway
                error!("this should be result of ui thread!!");
                Ok(())
            })
            .unwrap();
        if has_ui {
            info!("Stopped ui thread");
        }
        // net thread
        if has_ui {
            // if had a ui , net_thread will stop also after ui quit
            drop(net_thread);
        } else {
            // if didn't have ui, net_thread will continue running
            println!(
                "Search is finished, but net thread is kept running!\nTo stop send break command (ctrl-c)!"
            );
            net_thread
                .join()
                .unwrap_or_else(|_| {
                    // it doesn't matter because we will terminate anyway
                    error!("is this normal when joining net thread???");
                    Ok(())
                })
                .unwrap();
            info!("Stopped net thread");
        }
    } else {
        drop(net_thread);
        drop(ui_thread);
    }
    Ok(())
}
<commit_msg>[refactor] correct debug inner info<commit_after>//! A simple first program in rust, actually I plan to do something even
//! useful. Trying to find all my audiobooks on many machines, identify them,
//! find duplicates (later trying to solve the problem, also including their permissions,
//! different names, but same albums, etc, get all stats about it).
//! It acts as a wrapper around adbflib, which holds major parts of the implemention.
//!
//! For debugging, use e.g.
//!   ADBF_LOG_LVL=file RUST_BACKTRACE=full RUST_LOG=adbfbinlib::webui=trace
//!
//!               ADBF_LOG_LVL=file   : is the method
//!               RUST_BACKTRACE=full : is Rust's trace level
//!  RUST_LOG=adbfbinlib::webui=trace : is program's inner debugging mod
//!

mod command_line;

use adbfbinlib::{
    common::{logit, paths::SearchPath},
    ctrl::{Ctrl, UiUpdateMsg},
    data::{
        collection::Collection,
        ipc::{IFCollectionOutputData, IPC},
    },
    net::subs::key_keeper,
    shared,
};
use async_std::task;
use crossbeam::{channel::unbounded, sync::WaitGroup};
use ctrlc;
use exitcode;
use log::{error, info, trace};
use num_cpus;
use std::{
    cmp, env, io, process,
    sync::{Arc as SArc, Mutex as SMutex},
};

/// The main application which is central part of communicating with
/// the adbflib, which is closely connected.
fn main() -> io::Result<()> {
    // get start values from the input parser!!!
    let (ui_paths, has_tui, has_webui, has_net, keep_alive, open_browser, web_port, has_ui) =
        command_line::get_start_values();

    // read into paths
    let cleaned_paths = SearchPath::new(&ui_paths);
    if cleaned_paths.len() != ui_paths.len() && !has_tui && !has_webui && !open_browser {
        println!("Some paths/folders intersect and will not be used!");
    }

    // only use the arc
    let search_path = SArc::new(SMutex::new(cleaned_paths));
    let search_path_ui = search_path.clone();

    // define collection thread pool
    let ctrlc_thread = 1;
    let assumed_number_of_threads_used = if has_webui { 1 } else { 0 }
        + if has_tui { 1 } else { 0 }
        + if has_net { 1 } else { 0 }
        + ctrlc_thread;
    let nr_cpus = num_cpus::get();
    let nr_threads_for_collection = if assumed_number_of_threads_used >= nr_cpus {
        1
    } else {
        nr_cpus - assumed_number_of_threads_used
    };
    rayon::ThreadPoolBuilder::new()
        .thread_name(|nr| format!("col_{}", nr))
        .num_threads(nr_threads_for_collection)
        .build_global()
        .unwrap();

    // for synced start of different threads
    let wait_collector = WaitGroup::new();
    let wait_ui = wait_collector.clone();
    let wait_net = wait_collector.clone();

    //
    // prepare the message system
    //
    let (tx, rx) = unbounded::<UiUpdateMsg>();

    // these will be taken directly
    let tx_net = tx.clone();

    // ui message from here (collection)
    let tx_col = tx.clone();

    // for now this will stay wrapped
    let tx_from_collector_to_ui = SArc::new(SMutex::new(tx.clone()));

    // start the logging
    logit::Logit::init(logit::read_env_level(
        &env::var("ADBF_LOG").unwrap_or("".into()),
    ));

    // all optional components are wrapped into threads
    // 1 - UI         ui_thread   (optional)
    // 2 - Net        net_thread  (optional)
    // 3 - Collector  no thread yet (uses multiple rayon worker threads)
    let ui_thread = std::thread::Builder::new()
        .name("ui".into())
        .spawn(move || {
            if has_ui {
                Ctrl::run(
                    key_keeper::get_p2p_server_id(),
                    search_path_ui,
                    rx,
                    has_net,
                    wait_ui,
                    has_webui,
                    has_tui,
                    open_browser,
                    web_port,
                )
            } else {
                info!("no ui was created!");
                drop(wait_ui);
                Ok::<(), std::io::Error>(())
            }
        })?;

    // create a crossbeam channel for massive data exchange
    // with data I called IPC ...
    // in contrast to normal channels crossbeam channels are
    // n:m, so senders can be cloned as well!
    let (ipc_send, ipc_receive) = unbounded::<IPC>();

    // 2 - Net Future
    let net_thread = std::thread::Builder::new()
        .name("net".into())
        .spawn(move || {
            if has_net {
                let sender = Some(tx_net);
                task::block_on(
                    async move { shared::net_search(wait_net, sender, ipc_receive).await },
                )
                .map_err(|error| {
                    std::io::Error::new(
                        std::io::ErrorKind::Other,
                        format!("network error: {}", error),
                    )
                })
            } else {
                info!("no net!");
                drop(wait_net);
                Ok::<(), std::io::Error>(())
            }
        })?;

    // CTRL-C exit handler (is wrapped inside a thread)
    ctrlc::set_handler(move || {
        println!("\n'{}' was manually exited!!!", env!("CARGO_PKG_NAME"));
        process::exit(exitcode::SOFTWARE);
    })
    .map_err(|error| {
        std::io::Error::new(
            std::io::ErrorKind::Other,
            format!(
                "{} terminate signal invokation doesn't work!({:?})",
                env!("CARGO_PKG_NAME"),
                error
            ),
        )
    })?;

    // finished ipc sender reserved before collector takes action
    let ipc_send_finished = ipc_send.clone();

    // the collector
    // but yet this simple bracket to enclose this a little
    {
        trace!("syncing with 2 other threads");
        wait_collector.wait();
        trace!("sync with net and ui done ... collector can start");

        let synced_to_ui_messages = tx_from_collector_to_ui.clone();

        // set up data
        let collection_protected = SArc::new(SMutex::new(Collection::new()));

        // search parallelly
        let output_data = shared::collection_search(
            collection_protected.clone(),
            search_path,
            synced_to_ui_messages,
            has_ui,
        );

        info!("collector finished!!");
        if has_ui || has_net {
            // send own peer and others that are finished
            let memory_used = collection_protected
                .lock()
                .and_then(|collection| Ok(collection.memory()))
                .unwrap_or_else(|_| {
                    error!("locking collection didn't work here!");
                    0
                });

            let mut collection_output = IFCollectionOutputData::from(&output_data);
            // to have not 0 as data size minimum
            collection_output.size_of_data_in_kb = cmp::max(1, (memory_used / 1000) as usize);

            // to others via IPC
            if has_net {
                ipc_send_finished
                    .send(IPC::DoneSearching(collection_output.clone()))
                    .unwrap_or_else(|_| {
                        error!("net has to be up and receiving this send!");
                    });
            }
            // to myself (ui) via UiUpdateMsg
            if has_ui {
                tx_col
                    .send(UiUpdateMsg::PeerSearchFinished(
                        key_keeper::get_p2p_server_id(),
                        collection_output,
                    ))
                    .unwrap_or_else(|e| error!("use one: {}", e));
            }
        }
        if !has_ui {
            collection_protected
                .lock()
                .and_then(|locked_collection| {
                    Ok(locked_collection
                        .print_stats(&key_keeper::get_p2p_server_id(), nr_threads_for_collection))
                })
                .unwrap_or(())
        }
    }

    // look for keeping alive argument if that was chosen
    if keep_alive {
        ui_thread
            .join()
            .unwrap_or_else(|_| {
                // it doesn't matter because we will terminate anyway
                error!("this should be result of ui thread!!");
                Ok(())
            })
            .unwrap();
        if has_ui {
            info!("Stopped ui thread");
        }
        // net thread
        if has_ui {
            // if had a ui , net_thread will stop also after ui quit
            drop(net_thread);
        } else {
            // if didn't have ui, net_thread will continue running
            println!(
                "Search is finished, but net thread is kept running!\nTo stop send break command (ctrl-c)!"
            );
            net_thread
                .join()
                .unwrap_or_else(|_| {
                    // it doesn't matter because we will terminate anyway
                    error!("is this normal when joining net thread???");
                    Ok(())
                })
                .unwrap();
            info!("Stopped net thread");
        }
    } else {
        drop(net_thread);
        drop(ui_thread);
    }
    Ok(())
}
<|endoftext|>


<commit_before>//! Web interface of cratesfyi

mod rustdoc;
mod releases;
mod page;
mod crate_details;
mod source;
mod pool;
mod file;
mod builds;
mod error;

use std::env;
use std::error::Error;
use std::time::Duration;
use std::path::PathBuf;
use iron::prelude::*;
use iron::Handler;
use router::{Router, NoRoute};
use staticfile::Static;
use handlebars_iron::{HandlebarsEngine, DirectorySource};
use time;
use postgres::Connection;
use semver::{Version, VersionReq};
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;

/// Duration of static files for staticfile and DatabaseFileHandler (in seconds)
const STATIC_FILE_CACHE_DURATION: u64 = 60 * 60 * 24 * 3;   // 3 days

struct CratesfyiHandler {
    router_handler: Box<Handler>,
    database_file_handler: Box<Handler>,
    static_handler: Box<Handler>,
}

impl CratesfyiHandler {
    fn chain<H: Handler>(base: H) -> Chain {
        // TODO: Use DocBuilderOptions for paths
        let mut hbse = HandlebarsEngine::new();
        hbse.add(Box::new(DirectorySource::new("./templates", ".hbs")));

        // load templates
        if let Err(e) = hbse.reload() {
            panic!("Failed to load handlebar templates: {}", e.description());
        }

        let mut chain = Chain::new(base);
        chain.link_before(pool::Pool::new());
        chain.link_after(hbse);
        chain
    }

    pub fn new() -> CratesfyiHandler {
        let mut router = Router::new();
        router.get("/", releases::home_page);
        router.get("/about", |_: &mut Request| {
            page::Page::new(false).title("About docs.rs").to_resp("about")
        });
        router.get("/releases", releases::releases_handler);
        router.get("/releases/recent/:page", releases::releases_handler);
        router.get("/releases/stars", releases::stars_handler);
        router.get("/releases/stars/:page", releases::stars_handler);
        router.get("/releases/:author", releases::author_handler);
        router.get("/releases/:author/:page", releases::author_handler);
        router.get("/releases/activity", releases::activity_handler);
        router.get("/releases/search", releases::search_handler);
        router.get("/crate/:name", crate_details::crate_details_handler);
        router.get("/crate/:name/", crate_details::crate_details_handler);
        router.get("/crate/:name/:version", crate_details::crate_details_handler);
        router.get("/crate/:name/:version/", crate_details::crate_details_handler);
        router.get("/crate/:name/:version/builds", builds::build_list_handler);
        router.get("/crate/:name/:version/builds/:id", builds::build_list_handler);
        router.get("/crate/:name/:version/source/", source::source_browser_handler);
        router.get("/crate/:name/:version/source/*", source::source_browser_handler);
        router.get("/:crate", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/search-index.js", rustdoc::rustdoc_html_server_handler);
        router.get("/:crate/:version/:target", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/:target/", rustdoc::rustdoc_html_server_handler);
        router.get("/:crate/:version/:target/*.html", rustdoc::rustdoc_html_server_handler);

        let router_chain = Self::chain(router);
        let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX").unwrap()).join("public_html");
        let static_handler = Static::new(prefix)
            .cache(Duration::from_secs(STATIC_FILE_CACHE_DURATION));

        CratesfyiHandler {
            router_handler: Box::new(router_chain),
            database_file_handler: Box::new(file::DatabaseFileHandler),
            static_handler: Box::new(static_handler),
        }
    }
}

impl Handler for CratesfyiHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // try router first then db/static file handler
        // return 404 if none of them return Ok
        self.router_handler
            .handle(req)
            .or_else(|e| {
                // if router fails try to serve files from database first
                self.database_file_handler.handle(req).or(Err(e))
            })
            .or_else(|e| {
                // and then try static handler. if all of them fails, return 404
                self.static_handler.handle(req).or(Err(e))
            })
            .or_else(|e| {
                debug!("{}", e.description());
                let err = if let Some(err) = e.error.downcast::<error::Nope>() {
                    *err
                } else if e.error.downcast::<NoRoute>().is_some() {
                    error::Nope::ResourceNotFound
                } else {
                    panic!("all cratesfyi errors should be of type Nope");
                };
                Self::chain(err).handle(req)
            })
    }
}

fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option<String> {

    // version is an Option<&str> from router::Router::get
    // need to decode first
    use url::percent_encoding::percent_decode;
    let req_version = version.and_then(|v| {
        match percent_decode(v.as_bytes()).decode_utf8() {
            Ok(p) => Some(p.into_owned()),
            Err(_) => None,
        }
    }).unwrap_or("*".to_string());

    let versions = {

        let mut versions = Vec::new();
        // get every version of a crate
        for row in &conn.query("SELECT version  \
                                FROM releases \
                                INNER JOIN crates ON crates.id = releases.crate_id \
                                WHERE crates.name = $1",
                                &[&name])
            .unwrap() {
                let version: String = row.get(0);
                versions.push(version);
            }

        // FIXME: Need to sort versions with semver, database is not keeping them sorted
        versions
    };

    // first check for exact match
    // we can't expect users to use semver in query
    for version in &versions {
        if version == &req_version {
            return Some(version.clone())
        }
    }

    // Now try to match with semver
    let req_sem_ver = VersionReq::parse(&req_version).unwrap();

    // we need to sort versions first
    let versions_sem = {
        let mut versions_sem: Vec<Version> = Vec::new();

        for version in &versions {
            versions_sem.push(Version::parse(&version).unwrap());
        }

        versions_sem.sort();
        versions_sem.reverse();
        versions_sem
    };

    for version in &versions_sem {
        if req_sem_ver.matches(&version) {
            return Some(format!("{}", version))
        }
    }

    None
}

/// Wrapper around the pulldown-cmark parser and renderer to render markdown
fn render_markdown(text: &str) -> String {
    // I got this from mdBook::src::utils
    use pulldown_cmark::{Parser, html, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
    let mut s = String::with_capacity(text.len() * 3 / 2);

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    let p = Parser::new_ext(&text, opts);
    html::push_html(&mut s, p);
    s
}

/// Starts cratesfyi web server
pub fn start_web_server(sock_addr: Option<&str>) {
    let cratesfyi = CratesfyiHandler::new();
    Iron::new(cratesfyi).http(sock_addr.unwrap_or("localhost:3000")).unwrap();
}

/// Converts Timespec to nice readable relative time string
fn duration_to_str(ts: time::Timespec) -> String {

    let tm = time::at(ts);
    let delta = time::now() - tm;

    if delta.num_days() > 5 {
        format!("{}", tm.strftime("%b %d, %Y").unwrap())
    } else if delta.num_days() > 1 {
        format!("{} days ago", delta.num_days())
    } else if delta.num_days() == 1 {
        "one day ago".to_string()
    } else if delta.num_hours() > 1 {
        format!("{} hours ago", delta.num_hours())
    } else if delta.num_hours() == 1 {
        "an hour ago".to_string()
    } else if delta.num_minutes() > 1 {
        format!("{} minutes ago", delta.num_minutes())
    } else if delta.num_minutes() == 1 {
        "one minute ago".to_string()
    } else if delta.num_seconds() > 0 {
        format!("{} seconds ago", delta.num_seconds())
    } else {
        "just now".to_string()
    }

}

/// MetaData used in header
#[derive(Debug)]
pub struct MetaData {
    pub name: String,
    pub version: String,
    pub description: Option<String>,
    pub target_name: Option<String>,
    pub rustdoc_status: bool,
}

impl MetaData {
    pub fn from_crate(conn: &Connection, name: &str, version: &str) -> Option<MetaData> {
        for row in &conn.query("SELECT crates.name,
                                       releases.version,
                                       releases.description,
                                       releases.target_name,
                                       releases.rustdoc_status
                                FROM releases
                                INNER JOIN crates ON crates.id = releases.crate_id
                                WHERE crates.name = $1 AND releases.version = $2",
                               &[&name, &version]).unwrap() {

            return Some(MetaData {
                name: row.get(0),
                version: row.get(1),
                description: row.get(2),
                target_name: row.get(3),
                rustdoc_status: row.get(4),
            });
        }

        None
    }
}

impl ToJson for MetaData {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("name".to_owned(), self.name.to_json());
        m.insert("version".to_owned(), self.version.to_json());
        m.insert("description".to_owned(), self.description.to_json());
        m.insert("target_name".to_owned(), self.target_name.to_json());
        m.insert("rustdoc_status".to_owned(), self.rustdoc_status.to_json());
        m.to_json()
    }
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::*;

    #[test]
    #[ignore]
    fn test_start_web_server() {
        // FIXME: This test is doing nothing
        let _ = env_logger::init();
        start_web_server(None);
    }
}
<commit_msg>Fix title of about page<commit_after>//! Web interface of cratesfyi

mod rustdoc;
mod releases;
mod page;
mod crate_details;
mod source;
mod pool;
mod file;
mod builds;
mod error;

use std::env;
use std::error::Error;
use std::time::Duration;
use std::path::PathBuf;
use iron::prelude::*;
use iron::Handler;
use router::{Router, NoRoute};
use staticfile::Static;
use handlebars_iron::{HandlebarsEngine, DirectorySource};
use time;
use postgres::Connection;
use semver::{Version, VersionReq};
use rustc_serialize::json::{Json, ToJson};
use std::collections::BTreeMap;

/// Duration of static files for staticfile and DatabaseFileHandler (in seconds)
const STATIC_FILE_CACHE_DURATION: u64 = 60 * 60 * 24 * 3;   // 3 days

struct CratesfyiHandler {
    router_handler: Box<Handler>,
    database_file_handler: Box<Handler>,
    static_handler: Box<Handler>,
}

impl CratesfyiHandler {
    fn chain<H: Handler>(base: H) -> Chain {
        // TODO: Use DocBuilderOptions for paths
        let mut hbse = HandlebarsEngine::new();
        hbse.add(Box::new(DirectorySource::new("./templates", ".hbs")));

        // load templates
        if let Err(e) = hbse.reload() {
            panic!("Failed to load handlebar templates: {}", e.description());
        }

        let mut chain = Chain::new(base);
        chain.link_before(pool::Pool::new());
        chain.link_after(hbse);
        chain
    }

    pub fn new() -> CratesfyiHandler {
        let mut router = Router::new();
        router.get("/", releases::home_page);
        router.get("/about", |_: &mut Request| {
            page::Page::new(false).title("About Docs.rs").to_resp("about")
        });
        router.get("/releases", releases::releases_handler);
        router.get("/releases/recent/:page", releases::releases_handler);
        router.get("/releases/stars", releases::stars_handler);
        router.get("/releases/stars/:page", releases::stars_handler);
        router.get("/releases/:author", releases::author_handler);
        router.get("/releases/:author/:page", releases::author_handler);
        router.get("/releases/activity", releases::activity_handler);
        router.get("/releases/search", releases::search_handler);
        router.get("/crate/:name", crate_details::crate_details_handler);
        router.get("/crate/:name/", crate_details::crate_details_handler);
        router.get("/crate/:name/:version", crate_details::crate_details_handler);
        router.get("/crate/:name/:version/", crate_details::crate_details_handler);
        router.get("/crate/:name/:version/builds", builds::build_list_handler);
        router.get("/crate/:name/:version/builds/:id", builds::build_list_handler);
        router.get("/crate/:name/:version/source/", source::source_browser_handler);
        router.get("/crate/:name/:version/source/*", source::source_browser_handler);
        router.get("/:crate", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/search-index.js", rustdoc::rustdoc_html_server_handler);
        router.get("/:crate/:version/:target", rustdoc::rustdoc_redirector_handler);
        router.get("/:crate/:version/:target/", rustdoc::rustdoc_html_server_handler);
        router.get("/:crate/:version/:target/*.html", rustdoc::rustdoc_html_server_handler);

        let router_chain = Self::chain(router);
        let prefix = PathBuf::from(env::var("CRATESFYI_PREFIX").unwrap()).join("public_html");
        let static_handler = Static::new(prefix)
            .cache(Duration::from_secs(STATIC_FILE_CACHE_DURATION));

        CratesfyiHandler {
            router_handler: Box::new(router_chain),
            database_file_handler: Box::new(file::DatabaseFileHandler),
            static_handler: Box::new(static_handler),
        }
    }
}

impl Handler for CratesfyiHandler {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        // try router first then db/static file handler
        // return 404 if none of them return Ok
        self.router_handler
            .handle(req)
            .or_else(|e| {
                // if router fails try to serve files from database first
                self.database_file_handler.handle(req).or(Err(e))
            })
            .or_else(|e| {
                // and then try static handler. if all of them fails, return 404
                self.static_handler.handle(req).or(Err(e))
            })
            .or_else(|e| {
                debug!("{}", e.description());
                let err = if let Some(err) = e.error.downcast::<error::Nope>() {
                    *err
                } else if e.error.downcast::<NoRoute>().is_some() {
                    error::Nope::ResourceNotFound
                } else {
                    panic!("all cratesfyi errors should be of type Nope");
                };
                Self::chain(err).handle(req)
            })
    }
}

fn match_version(conn: &Connection, name: &str, version: Option<&str>) -> Option<String> {

    // version is an Option<&str> from router::Router::get
    // need to decode first
    use url::percent_encoding::percent_decode;
    let req_version = version.and_then(|v| {
        match percent_decode(v.as_bytes()).decode_utf8() {
            Ok(p) => Some(p.into_owned()),
            Err(_) => None,
        }
    }).unwrap_or("*".to_string());

    let versions = {

        let mut versions = Vec::new();
        // get every version of a crate
        for row in &conn.query("SELECT version  \
                                FROM releases \
                                INNER JOIN crates ON crates.id = releases.crate_id \
                                WHERE crates.name = $1",
                                &[&name])
            .unwrap() {
                let version: String = row.get(0);
                versions.push(version);
            }

        // FIXME: Need to sort versions with semver, database is not keeping them sorted
        versions
    };

    // first check for exact match
    // we can't expect users to use semver in query
    for version in &versions {
        if version == &req_version {
            return Some(version.clone())
        }
    }

    // Now try to match with semver
    let req_sem_ver = VersionReq::parse(&req_version).unwrap();

    // we need to sort versions first
    let versions_sem = {
        let mut versions_sem: Vec<Version> = Vec::new();

        for version in &versions {
            versions_sem.push(Version::parse(&version).unwrap());
        }

        versions_sem.sort();
        versions_sem.reverse();
        versions_sem
    };

    for version in &versions_sem {
        if req_sem_ver.matches(&version) {
            return Some(format!("{}", version))
        }
    }

    None
}

/// Wrapper around the pulldown-cmark parser and renderer to render markdown
fn render_markdown(text: &str) -> String {
    // I got this from mdBook::src::utils
    use pulldown_cmark::{Parser, html, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};
    let mut s = String::with_capacity(text.len() * 3 / 2);

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    let p = Parser::new_ext(&text, opts);
    html::push_html(&mut s, p);
    s
}

/// Starts cratesfyi web server
pub fn start_web_server(sock_addr: Option<&str>) {
    let cratesfyi = CratesfyiHandler::new();
    Iron::new(cratesfyi).http(sock_addr.unwrap_or("localhost:3000")).unwrap();
}

/// Converts Timespec to nice readable relative time string
fn duration_to_str(ts: time::Timespec) -> String {

    let tm = time::at(ts);
    let delta = time::now() - tm;

    if delta.num_days() > 5 {
        format!("{}", tm.strftime("%b %d, %Y").unwrap())
    } else if delta.num_days() > 1 {
        format!("{} days ago", delta.num_days())
    } else if delta.num_days() == 1 {
        "one day ago".to_string()
    } else if delta.num_hours() > 1 {
        format!("{} hours ago", delta.num_hours())
    } else if delta.num_hours() == 1 {
        "an hour ago".to_string()
    } else if delta.num_minutes() > 1 {
        format!("{} minutes ago", delta.num_minutes())
    } else if delta.num_minutes() == 1 {
        "one minute ago".to_string()
    } else if delta.num_seconds() > 0 {
        format!("{} seconds ago", delta.num_seconds())
    } else {
        "just now".to_string()
    }

}

/// MetaData used in header
#[derive(Debug)]
pub struct MetaData {
    pub name: String,
    pub version: String,
    pub description: Option<String>,
    pub target_name: Option<String>,
    pub rustdoc_status: bool,
}

impl MetaData {
    pub fn from_crate(conn: &Connection, name: &str, version: &str) -> Option<MetaData> {
        for row in &conn.query("SELECT crates.name,
                                       releases.version,
                                       releases.description,
                                       releases.target_name,
                                       releases.rustdoc_status
                                FROM releases
                                INNER JOIN crates ON crates.id = releases.crate_id
                                WHERE crates.name = $1 AND releases.version = $2",
                               &[&name, &version]).unwrap() {

            return Some(MetaData {
                name: row.get(0),
                version: row.get(1),
                description: row.get(2),
                target_name: row.get(3),
                rustdoc_status: row.get(4),
            });
        }

        None
    }
}

impl ToJson for MetaData {
    fn to_json(&self) -> Json {
        let mut m: BTreeMap<String, Json> = BTreeMap::new();
        m.insert("name".to_owned(), self.name.to_json());
        m.insert("version".to_owned(), self.version.to_json());
        m.insert("description".to_owned(), self.description.to_json());
        m.insert("target_name".to_owned(), self.target_name.to_json());
        m.insert("rustdoc_status".to_owned(), self.rustdoc_status.to_json());
        m.to_json()
    }
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::*;

    #[test]
    #[ignore]
    fn test_start_web_server() {
        // FIXME: This test is doing nothing
        let _ = env_logger::init();
        start_web_server(None);
    }
}
<|endoftext|>


<commit_before>extern crate user;

pub fn open(name: &str, width: usize, height: usize) -> bool {
    let s = CString::new(name).unwrap();

	unsafe {
		let class = winapi::WNDCLASSEXA {
			cbSize: mem::size_of::<winapi::WNDCLASSEXW>() as winapi::UINT,
			style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC,
			lpfnWndProc: Some(callback::callback),
			cbClsExtra: 0,
			cbWndExtra: 0,
			hInstance: kernel32::GetModuleHandleA(ptr::null()),
			hIcon: ptr::null_mut(),
			hCursor: ptr::null_mut(),
			hbrBackground: ptr::null_mut(),
			lpszMenuName: ptr::null(),
			lpszClassName: s.as_ptr(),
			hIconSm: ptr::null_mut(),
		};

		user32::RegisterClassExA(&class);

		let mut rect = winapi::RECT {
			left: 0, right: width as winapi::LONG,
			top: 0, bottom: height as winapi::LONG,
		}

		user32::AdjustWindowRect(&mut rect, winapi::WS_POPUP | winapi::WS_SYSMENU | winapi::WS_CAPTION, 0);

		rect.right -= rect.left;
		rect.bottom -= rect.top;

		let handle = user32::CreateWindowExA(0,
            s.as_ptr(), s.as_ptr(),
            winapi::WS_OVERLAPPEDWINDOW & ~winapi::WS_MAXIMIZEBOX & ~winapi::WS_TICKFRAME,
            winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT,
            rect.right, rect.bottom,
            ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), ptr::null_mut());

		if handle.is_null() {
			return false;
		}

		user32::ShowWindow(handle, winapi::SW_NORMAL);
}

<commit_msg>WIP on Windows version<commit_after>extern crate user;

static winapi::HANDLE HWND_HANDLE = 0;
static bool CLOSE_APP = false;

unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
                                   wparam: winapi::WPARAM, lparam: winapi::LPARAM)
                                   -> winapi::LRESULT
{
    match msg {
        winapi::WM_PAINT => {

        }
    }
}

pub fn open(name: &str, width: usize, height: usize) -> bool {
    let class_name = CString::new("minifb_window").unwrap();
    let s = CString::new(name).unwrap();

	unsafe {
		let class = winapi::WNDCLASSEXA {
			cbSize: mem::size_of::<winapi::WNDCLASSEXW>() as winapi::UINT,
			style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC,
			lpfnWndProc: Some(callback::callback),
			cbClsExtra: 0,
			cbWndExtra: 0,
			hInstance: kernel32::GetModuleHandleA(ptr::null()),
			hIcon: ptr::null_mut(),
			hCursor: ptr::null_mut(),
			hbrBackground: ptr::null_mut(),
			lpszMenuName: ptr::null(),
			lpszClassName: class_name.as_ptr(),
			hIconSm: ptr::null_mut(),
		};

		user32::RegisterClassExA(&class);

		let mut rect = winapi::RECT {
			left: 0, right: width as winapi::LONG,
			top: 0, bottom: height as winapi::LONG,
		}

		user32::AdjustWindowRect(&mut rect, winapi::WS_POPUP | winapi::WS_SYSMENU | winapi::WS_CAPTION, 0);

		rect.right -= rect.left;
		rect.bottom -= rect.top;

		let handle = user32::CreateWindowExA(0,
            class_name.as_ptr(), s.as_ptr(),
            winapi::WS_OVERLAPPEDWINDOW & ~winapi::WS_MAXIMIZEBOX & ~winapi::WS_TICKFRAME,
            winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT,
            rect.right, rect.bottom,
            ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), ptr::null_mut());

		if handle.is_null() {
			return false;
		}

		user32::ShowWindow(handle, winapi::SW_NORMAL);

        HWND_HANDLE = handle;
}

fn update() -> bool {

    let mut msg = mem::uninitialized();

    user32::InvalidateRect(HWND_HANDLE, ptr::null_mut(), winapi::TRUE);

    while user32::PeekMessage(&mut msg, HWND_HANDLE, 0, 0, winapi::PM_REMOTE) {
        user32::TranslateMessage(&mut msg);
        user32::DispatchMessage(&mut msg);
    }

    CLOSE_APP,
}

<|endoftext|>


<commit_before>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.

use crate::utils;

use clap::{CommandFactory, Parser, Subcommand, ValueEnum};

#[test]
fn doc_comments() {
    /// Lorem ipsum
    #[derive(Parser, PartialEq, Debug)]
    struct LoremIpsum {
        /// Fooify a bar
        /// and a baz
        #[arg(short, long)]
        foo: bool,
    }

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.contains("Lorem ipsum"));
    assert!(help.contains("Fooify a bar and a baz"));
}

#[test]
fn help_is_better_than_comments() {
    /// Lorem ipsum
    #[derive(Parser, PartialEq, Debug)]
    #[command(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        /// Fooify a bar
        #[arg(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")]
        foo: bool,
    }

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.contains("Dolor sit amet"));
    assert!(!help.contains("Lorem ipsum"));
    assert!(help.contains("DO NOT PASS A BAR"));
}

#[test]
fn empty_line_in_doc_comment_is_double_linefeed() {
    /// Foo.
    ///
    /// Bar
    #[derive(Parser, PartialEq, Debug)]
    #[command(name = "lorem-ipsum")]
    struct LoremIpsum {}

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.starts_with(
        "\
Foo.

Bar

Usage:"
    ));
}

#[test]
fn field_long_doc_comment_both_help_long_help() {
    /// Lorem ipsumclap
    #[derive(Parser, PartialEq, Debug)]
    #[command(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        /// Dot is removed from multiline comments.
        ///
        /// Long help
        #[arg(long)]
        foo: bool,

        /// Dot is removed from one short comment.
        #[arg(long)]
        bar: bool,
    }

    let short_help = utils::get_help::<LoremIpsum>();
    let long_help = utils::get_long_help::<LoremIpsum>();

    assert!(short_help.contains("Dot is removed from one short comment"));
    assert!(!short_help.contains("Dot is removed from one short comment."));
    assert!(short_help.contains("Dot is removed from multiline comments"));
    assert!(!short_help.contains("Dot is removed from multiline comments."));
    assert!(long_help.contains("Long help"));
    assert!(!short_help.contains("Long help"));
}

#[test]
fn top_long_doc_comment_both_help_long_help() {
    /// Lorem ipsumclap
    #[derive(Parser, Debug)]
    #[command(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        #[command(subcommand)]
        foo: SubCommand,
    }

    #[derive(Parser, Debug)]
    pub enum SubCommand {
        /// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES
        ///
        /// Or something else
        Foo {
            #[arg(help = "foo")]
            bars: String,
        },
    }

    let short_help = utils::get_help::<LoremIpsum>();
    let long_help = utils::get_subcommand_long_help::<LoremIpsum>("foo");

    assert!(!short_help.contains("Or something else"));
    assert!(long_help.contains("DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES"));
    assert!(long_help.contains("Or something else"));
}

#[test]
fn verbatim_doc_comment() {
    /// DANCE!
    ///
    ///                    ()
    ///                    |
    ///               (   ()   )
    ///     ) ________    //  )
    ///  ()  |\       \  //
    /// ( \\__ \ ______\//
    ///    \__) |       |
    ///      |  |       |
    ///       \ |       |
    ///        \|_______|
    ///        //    \\
    ///       ((     ||
    ///        \\    ||
    ///      ( ()    ||
    ///       (      () ) )
    #[derive(Parser, Debug)]
    #[command(verbatim_doc_comment)]
    struct SeeFigure1 {
        #[arg(long)]
        foo: bool,
    }

    let help = utils::get_long_help::<SeeFigure1>();
    let sample = r#"
                   ()
                   |
              (   ()   )
    ) ________    //  )
 ()  |\       \  //
( \\__ \ ______\//
   \__) |       |
     |  |       |
      \ |       |
       \|_______|
       //    \\
      ((     ||
       \\    ||
     ( ()    ||
      (      () ) )"#;

    assert!(help.contains(sample))
}

#[test]
fn verbatim_doc_comment_field() {
    #[derive(Parser, Debug)]
    struct Command {
        /// This help ends in a period.
        #[arg(long, verbatim_doc_comment)]
        foo: bool,
        /// This help does not end in a period.
        #[arg(long)]
        bar: bool,
    }

    let help = utils::get_long_help::<Command>();

    assert!(help.contains("This help ends in a period."));
    assert!(help.contains("This help does not end in a period"));
}

#[test]
fn multiline_separates_default() {
    #[derive(Parser, Debug)]
    struct Command {
        /// Multiline
        ///
        /// Doc comment
        #[arg(long, default_value = "x")]
        x: String,
    }

    let help = utils::get_long_help::<Command>();
    assert!(!help.contains("Doc comment [default"));
    assert!(help.lines().any(|s| s.trim().starts_with("[default")));

    // The short help should still have the default on the same line
    let help = utils::get_help::<Command>();
    assert!(help.contains("Multiline [default"));
}

#[test]
fn value_enum_multiline_doc_comment() {
    #[derive(ValueEnum, Clone)]
    enum LoremIpsum {
        /// Multiline
        ///
        /// Doc comment
        Bar,
    }
}

#[test]
fn doc_comment_about_handles_both_abouts() {
    /// Opts doc comment summary
    #[derive(Parser, Debug)]
    pub struct Opts {
        #[command(subcommand)]
        pub cmd: Sub,
    }

    /// Sub doc comment summary
    ///
    /// Sub doc comment body
    #[derive(Parser, PartialEq, Eq, Debug)]
    pub enum Sub {
        Compress { output: String },
    }

    let cmd = Opts::command();
    assert_eq!(
        cmd.get_about().map(|s| s.to_string()),
        Some("Opts doc comment summary".to_owned())
    );
    // clap will fallback to `about` on `None`.  The main care about is not providing a `Sub` doc
    // comment.
    assert_eq!(cmd.get_long_about(), None);
}

#[test]
fn respect_subcommand_doc_comment() {
    #[derive(Parser, Debug)]
    pub enum Cmd {
        /// For child
        #[command(subcommand)]
        Child(Child),
    }

    #[derive(Subcommand, Debug)]
    pub enum Child {
        One,
        Twp,
    }

    const OUTPUT: &str = "\
Usage: cmd <COMMAND>

Commands:
  child  For child
  help   Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help information
";
    utils::assert_output::<Cmd>("cmd --help", OUTPUT, false);
}
<commit_msg>test(derive): Clarify doc comment<commit_after>// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
// Ana Hobden (@hoverbear) <operator@hoverbear.org>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.

use crate::utils;

use clap::{CommandFactory, Parser, Subcommand, ValueEnum};

#[test]
fn doc_comments() {
    /// Lorem ipsum
    #[derive(Parser, PartialEq, Debug)]
    struct LoremIpsum {
        /// Fooify a bar
        /// and a baz
        #[arg(short, long)]
        foo: bool,
    }

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.contains("Lorem ipsum"));
    assert!(help.contains("Fooify a bar and a baz"));
}

#[test]
fn help_is_better_than_comments() {
    /// Lorem ipsum
    #[derive(Parser, PartialEq, Debug)]
    #[command(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        /// Fooify a bar
        #[arg(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")]
        foo: bool,
    }

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.contains("Dolor sit amet"));
    assert!(!help.contains("Lorem ipsum"));
    assert!(help.contains("DO NOT PASS A BAR"));
}

#[test]
fn empty_line_in_doc_comment_is_double_linefeed() {
    /// Foo.
    ///
    /// Bar
    #[derive(Parser, PartialEq, Debug)]
    #[command(name = "lorem-ipsum")]
    struct LoremIpsum {}

    let help = utils::get_long_help::<LoremIpsum>();
    assert!(help.starts_with(
        "\
Foo.

Bar

Usage:"
    ));
}

#[test]
fn field_long_doc_comment_both_help_long_help() {
    /// Lorem ipsumclap
    #[derive(Parser, PartialEq, Debug)]
    #[command(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        /// Dot is removed from multiline comments.
        ///
        /// Long help
        #[arg(long)]
        foo: bool,

        /// Dot is removed from one short comment.
        #[arg(long)]
        bar: bool,
    }

    let short_help = utils::get_help::<LoremIpsum>();
    let long_help = utils::get_long_help::<LoremIpsum>();

    assert!(short_help.contains("Dot is removed from one short comment"));
    assert!(!short_help.contains("Dot is removed from one short comment."));
    assert!(short_help.contains("Dot is removed from multiline comments"));
    assert!(!short_help.contains("Dot is removed from multiline comments."));
    assert!(long_help.contains("Long help"));
    assert!(!short_help.contains("Long help"));
}

#[test]
fn top_long_doc_comment_both_help_long_help() {
    /// Lorem ipsumclap
    #[derive(Parser, Debug)]
    #[command(name = "lorem-ipsum", about = "Dolor sit amet")]
    struct LoremIpsum {
        #[command(subcommand)]
        foo: SubCommand,
    }

    #[derive(Parser, Debug)]
    pub enum SubCommand {
        /// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES
        ///
        /// Or something else
        Foo {
            #[arg(help = "foo")]
            bars: String,
        },
    }

    let short_help = utils::get_help::<LoremIpsum>();
    let long_help = utils::get_subcommand_long_help::<LoremIpsum>("foo");

    assert!(!short_help.contains("Or something else"));
    assert!(long_help.contains("DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES"));
    assert!(long_help.contains("Or something else"));
}

#[test]
fn verbatim_doc_comment() {
    /// DANCE!
    ///
    ///                    ()
    ///                    |
    ///               (   ()   )
    ///     ) ________    //  )
    ///  ()  |\       \  //
    /// ( \\__ \ ______\//
    ///    \__) |       |
    ///      |  |       |
    ///       \ |       |
    ///        \|_______|
    ///        //    \\
    ///       ((     ||
    ///        \\    ||
    ///      ( ()    ||
    ///       (      () ) )
    #[derive(Parser, Debug)]
    #[command(verbatim_doc_comment)]
    struct SeeFigure1 {
        #[arg(long)]
        foo: bool,
    }

    let help = utils::get_long_help::<SeeFigure1>();
    let sample = r#"
                   ()
                   |
              (   ()   )
    ) ________    //  )
 ()  |\       \  //
( \\__ \ ______\//
   \__) |       |
     |  |       |
      \ |       |
       \|_______|
       //    \\
      ((     ||
       \\    ||
     ( ()    ||
      (      () ) )"#;

    assert!(help.contains(sample))
}

#[test]
fn verbatim_doc_comment_field() {
    #[derive(Parser, Debug)]
    struct Command {
        /// This help ends in a period.
        #[arg(long, verbatim_doc_comment)]
        foo: bool,
        /// This help does not end in a period.
        #[arg(long)]
        bar: bool,
    }

    let help = utils::get_long_help::<Command>();

    assert!(help.contains("This help ends in a period."));
    assert!(help.contains("This help does not end in a period"));
}

#[test]
fn multiline_separates_default() {
    #[derive(Parser, Debug)]
    struct Command {
        /// Multiline
        ///
        /// Doc comment
        #[arg(long, default_value = "x")]
        x: String,
    }

    let help = utils::get_long_help::<Command>();
    assert!(!help.contains("Doc comment [default"));
    assert!(help.lines().any(|s| s.trim().starts_with("[default")));

    // The short help should still have the default on the same line
    let help = utils::get_help::<Command>();
    assert!(help.contains("Multiline [default"));
}

#[test]
fn value_enum_multiline_doc_comment() {
    #[derive(Parser, Debug)]
    struct Command {
        x: LoremIpsum,
    }

    #[derive(ValueEnum, Clone, PartialEq, Debug)]
    enum LoremIpsum {
        /// Doc comment summary
        ///
        /// The doc comment body is ignored
        Bar,
    }

    let help = utils::get_long_help::<Command>();

    assert!(help.contains("Doc comment summary"));

    // There is no long help text for possible values. The long help only contains the summary.
    assert!(!help.contains("The doc comment body is ignored"));
}

#[test]
fn doc_comment_about_handles_both_abouts() {
    /// Opts doc comment summary
    #[derive(Parser, Debug)]
    pub struct Opts {
        #[command(subcommand)]
        pub cmd: Sub,
    }

    /// Sub doc comment summary
    ///
    /// Sub doc comment body
    #[derive(Parser, PartialEq, Eq, Debug)]
    pub enum Sub {
        Compress { output: String },
    }

    let cmd = Opts::command();
    assert_eq!(
        cmd.get_about().map(|s| s.to_string()),
        Some("Opts doc comment summary".to_owned())
    );
    // clap will fallback to `about` on `None`.  The main care about is not providing a `Sub` doc
    // comment.
    assert_eq!(cmd.get_long_about(), None);
}

#[test]
fn respect_subcommand_doc_comment() {
    #[derive(Parser, Debug)]
    pub enum Cmd {
        /// For child
        #[command(subcommand)]
        Child(Child),
    }

    #[derive(Subcommand, Debug)]
    pub enum Child {
        One,
        Twp,
    }

    const OUTPUT: &str = "\
Usage: cmd <COMMAND>

Commands:
  child  For child
  help   Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help information
";
    utils::assert_output::<Cmd>("cmd --help", OUTPUT, false);
}
<|endoftext|>