<commit_before>use nom::{IResult, anychar, is_space};
use std::fmt::Debug;
use std::str::{FromStr, from_utf8};

/// Parses a token (i.e. a terminal symbol or a non-terminal symbol).
/// A *token* can be of one of the following two forms:
///
/// * It is a string containing neither of the symbols `'"'`, `' '`, `'-'`, `'→'`, `','`, `';'`, `')'`, `']'`.
/// * It is delimited by the symbol `'"'` on both sides and each occurrence of `'\\'` or `'"'` inside the delimiters is escaped.
pub fn parse_token<A>(input: &[u8]) -> IResult<&[u8], A>
    where A: FromStr,
          A::Err: Debug,
{
    named!(
        parse_token_s<&str>,
        map_res!(
            alt!(
                delimited!(
                    char!('\"'),
                    escaped!(is_not!("\\\""), '\\', anychar),
                    char!('\"')
                ) |
                is_not!(" \"-→,;)]")
            ),
            from_utf8
        )
    );

    do_parse!(
        input,
        output: parse_token_s >>
        token: expr_res!(output.parse()) >>
        (token)
    )
}

/// Parses the `input` into a `Vec<A>` given an `inner_parser` for type `A`, an `opening` delimiter, a `closing` delimiter, and a `separator`.
/// The `inner_parser` must not consume the `separator`s or the `closing` delimiter of the given `input`.
pub fn parse_vec<'a, A, P>(input: &'a [u8], inner_parser: P, opening: &str, closing: &str, separator: &str) -> IResult<&'a [u8], Vec<A>>
    where P: Fn(&'a [u8]) -> IResult<&'a [u8], A>
{
    do_parse!(
        input,
        tag!(opening) >>
        take_while!(is_space) >>
        result: many0!(
            do_parse!(
                opt!(tag!(separator)) >>
                take_while!(is_space) >>
                the_token: inner_parser >>
                take_while!(is_space) >>
                (the_token)
            )
        ) >>
        tag!(closing) >>
        (result)
    )
}
/// parses initials of the form `initials: [...]` into a vector of type `N`
pub fn parse_initials<A>(input: &[u8]) -> IResult<&[u8], Vec<A>>
    where A: FromStr,
          A::Err: Debug,
{
    do_parse!(
        input,
        tag!("initial:") >>
            take_while!(is_space) >>
            result: call!(|x| parse_vec(x, parse_token, "[", "]", ",")) >>
            (result)
    )
}

#[cfg(test)]
pub mod tests {
    use super::*;

    #[test]
    fn test_parse_token_legal_input() {
        let legal_inputs = vec![
            ("abcxyz", "", String::from("abcxyz")),
            ("abc,xyz", ",xyz", String::from("abc")),
            ("\"abc\"xyz", "xyz", String::from("abc")),
            ("\"a\\\\b\\\"c\"xyz", "xyz", String::from("a\\\\b\\\"c")),
        ];

        for (legal_input, control_rest, control_parsed) in legal_inputs {
            assert_eq!(
                (control_rest.as_bytes(), control_parsed),
                parse_token::<String>(legal_input.as_bytes()).unwrap()
            );
        }
    }

    #[test]
    fn test_parse_token_illegal_input() {
        let illegal_inputs = vec![
            " xyz",
            "-xyz",
            "→xyz",
            ",xyz",
            ";xyz",
            ")xyz",
            "]xyz",
            "\"\\\"",
            "\"\"\"",
            " \"a\"",
        ];

        for illegal_input in illegal_inputs {
            match parse_token::<String>(illegal_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Incomplete(_) =>
                    panic!("Was able to parse the illegal input \'{}\'", illegal_input),
                IResult::Error(_) => (),
            }
        }

        let illegal_inputs = vec![
            "a",
            "\"a\"",
        ];

        for illegal_input in illegal_inputs {
            match parse_token::<u8>(illegal_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Incomplete(_) =>
                    panic!("Was able to parse the illegal input \'{}\'", illegal_input),
                IResult::Error(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_token_incomplete_input() {
        let incomplete_inputs = vec![
            "\"a",
        ];

        for incomplete_input in incomplete_inputs {
            match parse_token::<String>(incomplete_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Error(_) =>
                    panic!("The input was not handled as incomplete: \'{}\'", incomplete_input),
                IResult::Incomplete(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_vec_legal_input() {
        let legal_inputs = vec![
            ("[]xyz", "xyz", vec![]),
            ("[a, bc, d]xyz", "xyz",
                vec![String::from("a"), String::from("bc"), String::from("d")]),
            ("[  a, b ,c]xyz", "xyz",
                vec![String::from("a"), String::from("b"), String::from("c")]),
        ];

        for (legal_input, control_rest, control_parsed) in legal_inputs {
            assert_eq!(
                (control_rest.as_bytes(), control_parsed),
                parse_vec(legal_input.as_bytes(), parse_token, "[", "]", ",").unwrap()
            );
        }
    }

    #[test]
    fn test_parse_vec_illegal_input() {
        let illegal_inputs = vec![
            "(0)",
            "[0]",
            "[0, 1; 2)",
            " []",
            "[a)",
        ];

        for illegal_input in illegal_inputs {
            match parse_vec::<u8, _>(illegal_input.as_bytes(), parse_token, "[", ")", ",") {
                IResult::Done(_, _) | IResult::Incomplete(_) =>
                    panic!("Was able to parse the illegal input \'{}\'", illegal_input),
                IResult::Error(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_vec_incomplete_input() {
        let incomplete_inputs = vec![
            "[a",
            "["
        ];

        for incomplete_input in incomplete_inputs {
            match parse_vec::<String, _>(incomplete_input.as_bytes(), parse_token, "[", "]", ",") {
                IResult::Done(_, _) | IResult::Error(_) =>
                    panic!("The input was not handled as incomplete: \'{}\'", incomplete_input),
                IResult::Incomplete(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_initials_legal_input() {
        let legal_inputs = vec![
            ("initial: [a]xyz", "xyz", vec![String::from("a")]),
            ("initial:  []xyz", "xyz", vec![]),
        ];

        for (legal_input, control_rest, control_parsed) in legal_inputs {
            assert_eq!(
                (control_rest.as_bytes(), control_parsed),
                parse_initials(legal_input.as_bytes()).unwrap()
            );
        }
    }

    #[test]
    fn test_parse_initials_illegal_input() {
        let illegal_inputs = vec![
            "initials: []xyz",
            " initial: []xyz",
        ];

        for illegal_input in illegal_inputs {
            match parse_initials::<String>(illegal_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Incomplete(_) =>
                    panic!("Was able to parse the illegal input \'{}\'", illegal_input),
                IResult::Error(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_initials_incomplete_input() {
        let incomplete_inputs = vec![
            "init",
            "initial: [",
        ];

        for incomplete_input in incomplete_inputs {
            match parse_initials::<String>(incomplete_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Error(_) =>
                    panic!("The input was not handled as incomplete: \'{}\'", incomplete_input),
                IResult::Incomplete(_) => (),
            }
        }
    }
}
<commit_msg>src/util/parsing.rs: added initial_rule_grammar_from_str()<commit_after>use nom::{IResult, anychar, is_space};
use std::fmt::Debug;
use std::str::{FromStr, from_utf8};

/// Parses a token (i.e. a terminal symbol or a non-terminal symbol).
/// A *token* can be of one of the following two forms:
///
/// * It is a string containing neither of the symbols `'"'`, `' '`, `'-'`, `'→'`, `','`, `';'`, `')'`, `']'`.
/// * It is delimited by the symbol `'"'` on both sides and each occurrence of `'\\'` or `'"'` inside the delimiters is escaped.
pub fn parse_token<A>(input: &[u8])
        -> IResult<&[u8], A>
    where A: FromStr,
          A::Err: Debug,
{
    named!(
        parse_token_s<&str>,
        map_res!(
            alt!(
                delimited!(
                    char!('\"'),
                    escaped!(is_not!("\\\""), '\\', anychar),
                    char!('\"')
                ) |
                is_not!(" \"-→,;)]")
            ),
            from_utf8
        )
    );

    do_parse!(
        input,
        output: parse_token_s >>
        token: expr_res!(output.parse()) >>
        (token)
    )
}

/// Parses the `input` into a `Vec<A>` given an `inner_parser` for type `A`, an `opening` delimiter, a `closing` delimiter, and a `separator`.
/// The `inner_parser` must not consume the `separator`s or the `closing` delimiter of the given `input`.
pub fn parse_vec<'a, A, P>(input: &'a [u8], inner_parser: P, opening: &str, closing: &str, separator: &str)
        -> IResult<&'a [u8], Vec<A>>
    where P: Fn(&'a [u8]) -> IResult<&'a [u8], A>
{
    do_parse!(
        input,
        tag!(opening) >>
        take_while!(is_space) >>
        result: many0!(
            do_parse!(
                opt!(tag!(separator)) >>
                take_while!(is_space) >>
                the_token: inner_parser >>
                take_while!(is_space) >>
                (the_token)
            )
        ) >>
        tag!(closing) >>
        (result)
    )
}
/// Parses a string of the form `initials: [...]` as a vector of initial symbols of type `I`.
pub fn parse_initials<I>(input: &[u8])
        -> IResult<&[u8], Vec<I>>
    where I: FromStr,
          I::Err: Debug,
{
    do_parse!(
        input,
        tag!("initial:") >>
            take_while!(is_space) >>
            result: call!(|x| parse_vec(x, parse_token, "[", "]", ",")) >>
            (result)
    )
}

/// Parses a string as a grammar of initial symbols of type `I` and rules of type `R`.
/// The syntax of the initials must comply with `parse_initials`; the syntax of the rules is solely
/// determined by their type.
/// If the string contains multiple definitions of initials, then the initials of the grammar are
/// going to be the union of all defined initials.
pub fn initial_rule_grammar_from_str<I, R>(s: &str)
        -> Result<((Vec<I>, Vec<R>)), String>
    where I: FromStr,
          I::Err: Debug,
          R: FromStr,
          String: From<R::Err>,
{
    let mut it = s.lines();
    let mut initial = Vec::new();
    let mut rules: Vec<R> = Vec::new();

    while let Some(l) = it.next() {
        if l.trim_left().starts_with("initial:") {
            match parse_initials(l.as_bytes()) {
                IResult::Done(_, mut result) =>
                    initial.append(&mut result),
                _ =>
                    return Err(format!("Malformed declaration of initial nonterminals: \'{}\'", l))
            }
        } else if !l.is_empty() && !l.trim_left().starts_with("%") {
            rules.push(l.trim().parse()?);
        }
    }

    Ok((initial, rules))
}

#[cfg(test)]
pub mod tests {
    use super::*;

    #[test]
    fn test_parse_token_legal_input() {
        let legal_inputs = vec![
            ("abcxyz", "", String::from("abcxyz")),
            ("abc,xyz", ",xyz", String::from("abc")),
            ("\"abc\"xyz", "xyz", String::from("abc")),
            ("\"a\\\\b\\\"c\"xyz", "xyz", String::from("a\\\\b\\\"c")),
        ];

        for (legal_input, control_rest, control_parsed) in legal_inputs {
            assert_eq!(
                (control_rest.as_bytes(), control_parsed),
                parse_token::<String>(legal_input.as_bytes()).unwrap()
            );
        }
    }

    #[test]
    fn test_parse_token_illegal_input() {
        let illegal_inputs = vec![
            " xyz",
            "-xyz",
            "→xyz",
            ",xyz",
            ";xyz",
            ")xyz",
            "]xyz",
            "\"\\\"",
            "\"\"\"",
            " \"a\"",
        ];

        for illegal_input in illegal_inputs {
            match parse_token::<String>(illegal_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Incomplete(_) =>
                    panic!("Was able to parse the illegal input \'{}\'", illegal_input),
                IResult::Error(_) => (),
            }
        }

        let illegal_inputs = vec![
            "a",
            "\"a\"",
        ];

        for illegal_input in illegal_inputs {
            match parse_token::<u8>(illegal_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Incomplete(_) =>
                    panic!("Was able to parse the illegal input \'{}\'", illegal_input),
                IResult::Error(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_token_incomplete_input() {
        let incomplete_inputs = vec![
            "\"a",
        ];

        for incomplete_input in incomplete_inputs {
            match parse_token::<String>(incomplete_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Error(_) =>
                    panic!("The input was not handled as incomplete: \'{}\'", incomplete_input),
                IResult::Incomplete(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_vec_legal_input() {
        let legal_inputs = vec![
            ("[]xyz", "xyz", vec![]),
            ("[a, bc, d]xyz", "xyz",
                vec![String::from("a"), String::from("bc"), String::from("d")]),
            ("[  a, b ,c]xyz", "xyz",
                vec![String::from("a"), String::from("b"), String::from("c")]),
        ];

        for (legal_input, control_rest, control_parsed) in legal_inputs {
            assert_eq!(
                (control_rest.as_bytes(), control_parsed),
                parse_vec(legal_input.as_bytes(), parse_token, "[", "]", ",").unwrap()
            );
        }
    }

    #[test]
    fn test_parse_vec_illegal_input() {
        let illegal_inputs = vec![
            "(0)",
            "[0]",
            "[0, 1; 2)",
            " []",
            "[a)",
        ];

        for illegal_input in illegal_inputs {
            match parse_vec::<u8, _>(illegal_input.as_bytes(), parse_token, "[", ")", ",") {
                IResult::Done(_, _) | IResult::Incomplete(_) =>
                    panic!("Was able to parse the illegal input \'{}\'", illegal_input),
                IResult::Error(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_vec_incomplete_input() {
        let incomplete_inputs = vec![
            "[a",
            "["
        ];

        for incomplete_input in incomplete_inputs {
            match parse_vec::<String, _>(incomplete_input.as_bytes(), parse_token, "[", "]", ",") {
                IResult::Done(_, _) | IResult::Error(_) =>
                    panic!("The input was not handled as incomplete: \'{}\'", incomplete_input),
                IResult::Incomplete(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_initials_legal_input() {
        let legal_inputs = vec![
            ("initial: [a]xyz", "xyz", vec![String::from("a")]),
            ("initial:  []xyz", "xyz", vec![]),
        ];

        for (legal_input, control_rest, control_parsed) in legal_inputs {
            assert_eq!(
                (control_rest.as_bytes(), control_parsed),
                parse_initials(legal_input.as_bytes()).unwrap()
            );
        }
    }

    #[test]
    fn test_parse_initials_illegal_input() {
        let illegal_inputs = vec![
            "initials: []xyz",
            " initial: []xyz",
        ];

        for illegal_input in illegal_inputs {
            match parse_initials::<String>(illegal_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Incomplete(_) =>
                    panic!("Was able to parse the illegal input \'{}\'", illegal_input),
                IResult::Error(_) => (),
            }
        }
    }

    #[test]
    fn test_parse_initials_incomplete_input() {
        let incomplete_inputs = vec![
            "init",
            "initial: [",
        ];

        for incomplete_input in incomplete_inputs {
            match parse_initials::<String>(incomplete_input.as_bytes()) {
                IResult::Done(_, _) | IResult::Error(_) =>
                    panic!("The input was not handled as incomplete: \'{}\'", incomplete_input),
                IResult::Incomplete(_) => (),
            }
        }
    }
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Gehring <mg@ebfe.org>
//  * (c) kwantam <kwantam@gmail.com>
//  *     * 2015-04-28 ~ created `expand` module to eliminate most allocs during setup
//  * (c) Sergey "Shnatsel" Davidoff <shnatsel@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) allocs bset dflag cflag sflag tflag

#[macro_use]
extern crate uucore;

mod expand;

use bit_set::BitSet;
use clap::{App, Arg};
use fnv::FnvHashMap;
use std::io::{stdin, stdout, BufRead, BufWriter, Write};

use crate::expand::ExpandSet;
use uucore::InvalidEncodingHandling;

static NAME: &str = "tr";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str = "translate or delete characters";
static LONG_HELP: &str = "Translate,  squeeze, and/or delete characters from standard input,
writing to standard output.";
const BUFFER_LEN: usize = 1024;

mod options {
    pub const COMPLEMENT: &str = "complement";
    pub const DELETE: &str = "delete";
    pub const SQUEEZE: &str = "squeeze-repeats";
    pub const TRUNCATE: &str = "truncate";
    pub const SETS: &str = "sets";
}

trait SymbolTranslator {
    fn translate(&self, c: char, prev_c: char) -> Option<char>;
}

struct DeleteOperation {
    bset: BitSet,
    complement: bool,
}

impl DeleteOperation {
    fn new(set: ExpandSet, complement: bool) -> DeleteOperation {
        DeleteOperation {
            bset: set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        let uc = c as usize;
        if self.complement == self.bset.contains(uc) {
            Some(c)
        } else {
            None
        }
    }
}

struct SqueezeOperation {
    squeeze_set: BitSet,
    complement: bool,
}

impl SqueezeOperation {
    fn new(squeeze_set: ExpandSet, complement: bool) -> SqueezeOperation {
        SqueezeOperation {
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for SqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if prev_c == c && self.complement != self.squeeze_set.contains(c as usize) {
            None
        } else {
            Some(c)
        }
    }
}

struct DeleteAndSqueezeOperation {
    delete_set: BitSet,
    squeeze_set: BitSet,
    complement: bool,
}

impl DeleteAndSqueezeOperation {
    fn new(
        delete_set: ExpandSet,
        squeeze_set: ExpandSet,
        complement: bool,
    ) -> DeleteAndSqueezeOperation {
        DeleteAndSqueezeOperation {
            delete_set: delete_set.map(|c| c as usize).collect(),
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteAndSqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if self.complement != self.delete_set.contains(c as usize)
            || prev_c == c && self.squeeze_set.contains(c as usize)
        {
            None
        } else {
            Some(c)
        }
    }
}

struct TranslateOperation {
    translate_map: FnvHashMap<usize, char>,
}

impl TranslateOperation {
    fn new(set1: ExpandSet, set2: &mut ExpandSet, truncate: bool) -> TranslateOperation {
        let mut map = FnvHashMap::default();
        let mut s2_prev = '_';
        for i in set1 {
            let s2_next = set2.next();

            if s2_next.is_none() && truncate {
                map.insert(i as usize, i);
            } else {
                s2_prev = s2_next.unwrap_or(s2_prev);
                map.insert(i as usize, s2_prev);
            }
        }
        TranslateOperation { translate_map: map }
    }
}

impl SymbolTranslator for TranslateOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        Some(*self.translate_map.get(&(c as usize)).unwrap_or(&c))
    }
}

fn translate_input<T: SymbolTranslator>(
    input: &mut dyn BufRead,
    output: &mut dyn Write,
    translator: T,
) {
    let mut buf = String::with_capacity(BUFFER_LEN + 4);
    let mut output_buf = String::with_capacity(BUFFER_LEN + 4);

    while let Ok(length) = input.read_line(&mut buf) {
        let mut prev_c = 0 as char;
        if length == 0 {
            break;
        }
        {
            // isolation to make borrow checker happy
            let filtered = buf.chars().filter_map(|c| {
                let res = translator.translate(c, prev_c);
                if res.is_some() {
                    prev_c = c;
                }
                res
            });

            output_buf.extend(filtered);
            output.write_all(output_buf.as_bytes()).unwrap();
        }
        buf.clear();
        output_buf.clear();
    }
}

fn get_usage() -> String {
    format!("{} [OPTION]... SET1 [SET2]", executable!())
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .arg(
            Arg::with_name(options::COMPLEMENT)
                .short("C")
                .short("c")
                .long(options::COMPLEMENT)
                .help("use the complement of SET1"),
        )
        .arg(
            Arg::with_name(options::DELETE)
                .short("d")
                .long(options::DELETE)
                .help("delete characters in SET1, do not translate"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .long(options::SQUEEZE)
                .short("s")
                .help(
                    "replace each sequence  of  a  repeated  character  that  is
            listed  in the last specified SET, with a single occurrence
            of that character",
                ),
        )
        .arg(
            Arg::with_name(options::TRUNCATE)
                .long(options::TRUNCATE)
                .short("t")
                .help("first truncate SET1 to length of SET2"),
        )
        .arg(Arg::with_name(options::SETS).multiple(true))
        .get_matches_from(args);

    let delete_flag = matches.is_present(options::DELETE);
    let complement_flag = matches.is_present(options::COMPLEMENT);
    let squeeze_flag = matches.is_present(options::SQUEEZE);
    let truncate_flag = matches.is_present(options::TRUNCATE);

    let sets: Vec<String> = match matches.values_of(options::SETS) {
        Some(v) => v.map(|v| v.to_string()).collect(),
        None => vec![],
    };

    if sets.is_empty() {
        show_error!(
            "missing operand\nTry `{} --help` for more information.",
            NAME
        );
        return 1;
    }

    if !(delete_flag || squeeze_flag) && sets.len() < 2 {
        show_error!(
            "missing operand after ‘{}’\nTry `{} --help` for more information.",
            sets[0],
            NAME
        );
        return 1;
    }

    if complement_flag && !delete_flag && !squeeze_flag {
        show_error!("-c is only supported with -d or -s");
        return 1;
    }

    let stdin = stdin();
    let mut locked_stdin = stdin.lock();
    let stdout = stdout();
    let locked_stdout = stdout.lock();
    let mut buffered_stdout = BufWriter::new(locked_stdout);

    let set1 = ExpandSet::new(sets[0].as_ref());
    if delete_flag {
        if squeeze_flag {
            let set2 = ExpandSet::new(sets[1].as_ref());
            let op = DeleteAndSqueezeOperation::new(set1, set2, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            let op = DeleteOperation::new(set1, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        }
    } else if squeeze_flag {
        if sets.len() < 2 {
            let op = SqueezeOperation::new(set1, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            // Define a closure that computes the translation using a hash map.
            //
            // The `unwrap()` should never panic because the
            // `TranslateOperation.translate()` method always returns
            // `Some`.
            let mut set2 = ExpandSet::new(sets[1].as_ref());
            let translator = TranslateOperation::new(set1, &mut set2, truncate_flag);
            let translate = |c| translator.translate(c, 0 as char).unwrap();

            // Prepare some variables to be used for the closure that
            // computes the squeeze operation.
            //
            // The `squeeze()` closure needs to be defined anew for
            // each line of input, but these variables do not change
            // while reading the input so they can be defined before
            // the `while` loop.
            let set2 = ExpandSet::new(sets[1].as_ref());
            let squeezer = SqueezeOperation::new(set2, complement_flag);

            // Prepare some memory to read each line of the input (`buf`) and to write
            let mut buf = String::with_capacity(BUFFER_LEN + 4);

            // Loop over each line of stdin.
            while let Ok(length) = locked_stdin.read_line(&mut buf) {
                if length == 0 {
                    break;
                }

                // Define a closure that computes the squeeze operation.
                //
                // We keep track of the previously seen character on
                // each call to `squeeze()`, but we need to reset the
                // `prev_c` variable at the beginning of each line of
                // the input. That's why we define the closure inside
                // the `while` loop.
                let mut prev_c = 0 as char;
                let squeeze = |c| {
                    let result = squeezer.translate(c, prev_c);
                    prev_c = c;
                    result
                };

                // First translate, then squeeze each character of the input line.
                let filtered: String = buf.chars().map(translate).filter_map(squeeze).collect();
                buf.clear();
                buffered_stdout.write_all(filtered.as_bytes()).unwrap();
            }
        }
    } else {
        let mut set2 = ExpandSet::new(sets[1].as_ref());
        let op = TranslateOperation::new(set1, &mut set2, truncate_flag);
        translate_input(&mut locked_stdin, &mut buffered_stdout, op)
    }

    0
}
<commit_msg>fixup! tr: implement translate and squeeze (-s) mode<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Michael Gehring <mg@ebfe.org>
//  * (c) kwantam <kwantam@gmail.com>
//  *     * 2015-04-28 ~ created `expand` module to eliminate most allocs during setup
//  * (c) Sergey "Shnatsel" Davidoff <shnatsel@gmail.com>
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.

// spell-checker:ignore (ToDO) allocs bset dflag cflag sflag tflag

#[macro_use]
extern crate uucore;

mod expand;

use bit_set::BitSet;
use clap::{App, Arg};
use fnv::FnvHashMap;
use std::io::{stdin, stdout, BufRead, BufWriter, Write};

use crate::expand::ExpandSet;
use uucore::InvalidEncodingHandling;

static NAME: &str = "tr";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str = "translate or delete characters";
static LONG_HELP: &str = "Translate,  squeeze, and/or delete characters from standard input,
writing to standard output.";
const BUFFER_LEN: usize = 1024;

mod options {
    pub const COMPLEMENT: &str = "complement";
    pub const DELETE: &str = "delete";
    pub const SQUEEZE: &str = "squeeze-repeats";
    pub const TRUNCATE: &str = "truncate";
    pub const SETS: &str = "sets";
}

trait SymbolTranslator {
    fn translate(&self, c: char, prev_c: char) -> Option<char>;
}

struct DeleteOperation {
    bset: BitSet,
    complement: bool,
}

impl DeleteOperation {
    fn new(set: ExpandSet, complement: bool) -> DeleteOperation {
        DeleteOperation {
            bset: set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        let uc = c as usize;
        if self.complement == self.bset.contains(uc) {
            Some(c)
        } else {
            None
        }
    }
}

struct SqueezeOperation {
    squeeze_set: BitSet,
    complement: bool,
}

impl SqueezeOperation {
    fn new(squeeze_set: ExpandSet, complement: bool) -> SqueezeOperation {
        SqueezeOperation {
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for SqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if prev_c == c && self.complement != self.squeeze_set.contains(c as usize) {
            None
        } else {
            Some(c)
        }
    }
}

struct DeleteAndSqueezeOperation {
    delete_set: BitSet,
    squeeze_set: BitSet,
    complement: bool,
}

impl DeleteAndSqueezeOperation {
    fn new(
        delete_set: ExpandSet,
        squeeze_set: ExpandSet,
        complement: bool,
    ) -> DeleteAndSqueezeOperation {
        DeleteAndSqueezeOperation {
            delete_set: delete_set.map(|c| c as usize).collect(),
            squeeze_set: squeeze_set.map(|c| c as usize).collect(),
            complement,
        }
    }
}

impl SymbolTranslator for DeleteAndSqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        if self.complement != self.delete_set.contains(c as usize)
            || prev_c == c && self.squeeze_set.contains(c as usize)
        {
            None
        } else {
            Some(c)
        }
    }
}

struct TranslateOperation {
    translate_map: FnvHashMap<usize, char>,
}

impl TranslateOperation {
    fn new(set1: ExpandSet, set2: &mut ExpandSet, truncate: bool) -> TranslateOperation {
        let mut map = FnvHashMap::default();
        let mut s2_prev = '_';
        for i in set1 {
            let s2_next = set2.next();

            if s2_next.is_none() && truncate {
                map.insert(i as usize, i);
            } else {
                s2_prev = s2_next.unwrap_or(s2_prev);
                map.insert(i as usize, s2_prev);
            }
        }
        TranslateOperation { translate_map: map }
    }
}

impl SymbolTranslator for TranslateOperation {
    fn translate(&self, c: char, _prev_c: char) -> Option<char> {
        Some(*self.translate_map.get(&(c as usize)).unwrap_or(&c))
    }
}

struct TranslateAndSqueezeOperation {
    translate: TranslateOperation,
    squeeze: SqueezeOperation,
}

impl TranslateAndSqueezeOperation {
    fn new(
        set1: ExpandSet,
        set2: &mut ExpandSet,
        set2_: ExpandSet,
        truncate: bool,
        complement: bool,
    ) -> TranslateAndSqueezeOperation {
        TranslateAndSqueezeOperation {
            translate: TranslateOperation::new(set1, set2, truncate),
            squeeze: SqueezeOperation::new(set2_, complement),
        }
    }
}

impl SymbolTranslator for TranslateAndSqueezeOperation {
    fn translate(&self, c: char, prev_c: char) -> Option<char> {
        // `unwrap()` will never panic because `Translate.translate()`
        // always returns `Some`.
        self.squeeze
            .translate(self.translate.translate(c, 0 as char).unwrap(), prev_c)
    }
}

fn translate_input<T: SymbolTranslator>(
    input: &mut dyn BufRead,
    output: &mut dyn Write,
    translator: T,
) {
    let mut buf = String::with_capacity(BUFFER_LEN + 4);
    let mut output_buf = String::with_capacity(BUFFER_LEN + 4);

    while let Ok(length) = input.read_line(&mut buf) {
        let mut prev_c = 0 as char;
        if length == 0 {
            break;
        }
        {
            // isolation to make borrow checker happy
            let filtered = buf.chars().filter_map(|c| {
                let res = translator.translate(c, prev_c);
                // Set `prev_c` to the post-translate character. This
                // allows the squeeze operation to correctly function
                // after the translate operation.
                if res.is_some() {
                    prev_c = res.unwrap();
                }
                res
            });

            output_buf.extend(filtered);
            output.write_all(output_buf.as_bytes()).unwrap();
        }
        buf.clear();
        output_buf.clear();
    }
}

fn get_usage() -> String {
    format!("{} [OPTION]... SET1 [SET2]", executable!())
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let usage = get_usage();
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = App::new(executable!())
        .version(VERSION)
        .about(ABOUT)
        .usage(&usage[..])
        .after_help(LONG_HELP)
        .arg(
            Arg::with_name(options::COMPLEMENT)
                .short("C")
                .short("c")
                .long(options::COMPLEMENT)
                .help("use the complement of SET1"),
        )
        .arg(
            Arg::with_name(options::DELETE)
                .short("d")
                .long(options::DELETE)
                .help("delete characters in SET1, do not translate"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .long(options::SQUEEZE)
                .short("s")
                .help(
                    "replace each sequence  of  a  repeated  character  that  is
            listed  in the last specified SET, with a single occurrence
            of that character",
                ),
        )
        .arg(
            Arg::with_name(options::TRUNCATE)
                .long(options::TRUNCATE)
                .short("t")
                .help("first truncate SET1 to length of SET2"),
        )
        .arg(Arg::with_name(options::SETS).multiple(true))
        .get_matches_from(args);

    let delete_flag = matches.is_present(options::DELETE);
    let complement_flag = matches.is_present(options::COMPLEMENT);
    let squeeze_flag = matches.is_present(options::SQUEEZE);
    let truncate_flag = matches.is_present(options::TRUNCATE);

    let sets: Vec<String> = match matches.values_of(options::SETS) {
        Some(v) => v.map(|v| v.to_string()).collect(),
        None => vec![],
    };

    if sets.is_empty() {
        show_error!(
            "missing operand\nTry `{} --help` for more information.",
            NAME
        );
        return 1;
    }

    if !(delete_flag || squeeze_flag) && sets.len() < 2 {
        show_error!(
            "missing operand after ‘{}’\nTry `{} --help` for more information.",
            sets[0],
            NAME
        );
        return 1;
    }

    if complement_flag && !delete_flag && !squeeze_flag {
        show_error!("-c is only supported with -d or -s");
        return 1;
    }

    let stdin = stdin();
    let mut locked_stdin = stdin.lock();
    let stdout = stdout();
    let locked_stdout = stdout.lock();
    let mut buffered_stdout = BufWriter::new(locked_stdout);

    let set1 = ExpandSet::new(sets[0].as_ref());
    if delete_flag {
        if squeeze_flag {
            let set2 = ExpandSet::new(sets[1].as_ref());
            let op = DeleteAndSqueezeOperation::new(set1, set2, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            let op = DeleteOperation::new(set1, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        }
    } else if squeeze_flag {
        if sets.len() < 2 {
            let op = SqueezeOperation::new(set1, complement_flag);
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        } else {
            let mut set2 = ExpandSet::new(sets[1].as_ref());
            let set2_ = ExpandSet::new(sets[1].as_ref());
            let op = TranslateAndSqueezeOperation::new(
                set1,
                &mut set2,
                set2_,
                complement_flag,
                truncate_flag,
            );
            translate_input(&mut locked_stdin, &mut buffered_stdout, op);
        }
    } else {
        let mut set2 = ExpandSet::new(sets[1].as_ref());
        let op = TranslateOperation::new(set1, &mut set2, truncate_flag);
        translate_input(&mut locked_stdin, &mut buffered_stdout, op)
    }

    0
}
<|endoftext|>


<commit_before>//! Aim to provide platform-independent methods to obtain login records
//!
//! **ONLY** support linux, macos and freebsd for the time being

// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

use super::libc;
pub extern crate time;
use self::time::{Tm, Timespec};

use ::std::io::Result as IOResult;
use ::std::io::Error as IOError;
use ::std::ptr;
use ::std::ffi::CString;

pub use self::ut::*;
use libc::utmpx;
// pub use libc::getutxid;
// pub use libc::getutxline;
// pub use libc::pututxline;
pub use libc::getutxent;
pub use libc::setutxent;
pub use libc::endutxent;
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub use libc::utmpxname;
#[cfg(target_os = "freebsd")]
pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
    0
}

// In case the c_char array doesn' t end with NULL
macro_rules! chars2string {
    ($arr:expr) => (
        $arr.iter().take_while(|i| **i > 0).map(|&i| i as u8 as char).collect::<String>()
    )
}

#[cfg(target_os = "linux")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmp";

    pub use libc::__UT_LINESIZE as UT_LINESIZE;
    pub use libc::__UT_NAMESIZE as UT_NAMESIZE;
    pub use libc::__UT_HOSTSIZE as UT_HOSTSIZE;
    pub const UT_IDSIZE: usize = 4;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
}

#[cfg(target_os = "macos")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";

    pub use libc::_UTX_LINESIZE as UT_LINESIZE;
    pub use libc::_UTX_USERSIZE as UT_NAMESIZE;
    pub use libc::_UTX_HOSTSIZE as UT_HOSTSIZE;
    pub use libc::_UTX_IDSIZE as UT_IDSIZE;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
    pub use libc::SIGNATURE;
    pub use libc::SHUTDOWN_TIME;
}

#[cfg(target_os = "freebsd")]
mod ut {
    use super::libc;

    pub static DEFAULT_FILE: &'static str = "";

    pub const UT_LINESIZE: usize = 16;
    pub const UT_NAMESIZE: usize = 32;
    pub const UT_IDSIZE: usize = 8;
    pub const UT_HOSTSIZE: usize = 128;

    pub use libc::EMPTY;
    pub use libc::BOOT_TIME;
    pub use libc::OLD_TIME;
    pub use libc::NEW_TIME;
    pub use libc::USER_PROCESS;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::SHUTDOWN_TIME;
}

/// Login records
///
/// Examples:
/// ---------
///
/// ```
/// for ut in Utmpx::iter_all_records() {
///     if ut.is_user_process() {
///         println!("{}: {}", ut.host(), ut.user())
///     }
/// }
/// ```
///
/// Specifying the path to login record:
///
/// ```
/// for ut in Utmpx::iter_all_records().read_from("/some/where/else") {
///     if ut.is_user_process() {
///         println!("{}: {}", ut.host(), ut.user())
///     }
/// }
/// ```
pub struct Utmpx {
    inner: utmpx,
}

impl Utmpx {
    /// A.K.A. ut.ut_type
    pub fn record_type(&self) -> i16 {
        self.inner.ut_type as i16
    }
    /// A.K.A. ut.ut_pid
    pub fn pid(&self) -> i32 {
        self.inner.ut_pid as i32
    }
    /// A.K.A. ut.ut_id
    pub fn terminal_suffix(&self) -> String {
        chars2string!(self.inner.ut_id)
    }
    /// A.K.A. ut.ut_user
    pub fn user(&self) -> String {
        chars2string!(self.inner.ut_user)
    }
    /// A.K.A. ut.ut_host
    pub fn host(&self) -> String {
        chars2string!(self.inner.ut_host)
    }
    /// A.K.A. ut.ut_line
    pub fn tty_device(&self) -> String {
        chars2string!(self.inner.ut_line)
    }
    /// A.K.A. ut.ut_tv
    pub fn login_time(&self) -> Tm {
        time::at(Timespec::new(self.inner.ut_tv.tv_sec as i64,
                               self.inner.ut_tv.tv_usec as i32))
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (e_termination, e_exit)
    #[cfg(any(target_os = "linux", target_os = "android"))]
    pub fn exit_status(&self) -> (i16, i16) {
        (self.inner.ut_exit.e_termination, self.inner.ut_exit.e_exit)
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (0, 0) on Non-Linux platform
    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    pub fn exit_status(&self) -> (i16, i16) {
        (0, 0)
    }
    /// Consumes the `Utmpx`, returning the underlying C struct utmpx
    pub fn into_inner(self) -> utmpx {
        self.inner
    }
    pub fn is_user_process(&self) -> bool {
        !self.user().is_empty() && self.record_type() == USER_PROCESS
    }

    /// Canonicalize host name using DNS
    pub fn canon_host(&self) -> IOResult<String> {
        const AI_CANONNAME: libc::c_int = 0x2;
        let host = self.host();
        let host = host.split(':').nth(0).unwrap();
        let hints = libc::addrinfo {
            ai_flags: AI_CANONNAME,
            ai_family: 0,
            ai_socktype: 0,
            ai_protocol: 0,
            ai_addrlen: 0,
            ai_addr: ptr::null_mut(),
            ai_canonname: ptr::null_mut(),
            ai_next: ptr::null_mut(),
        };
        let c_host = CString::new(host).unwrap();
        let mut res = ptr::null_mut();
        let status = unsafe {
            libc::getaddrinfo(c_host.as_ptr(),
                              ptr::null(),
                              &hints as *const _,
                              &mut res as *mut _)
        };
        if status == 0 {
            let info: libc::addrinfo = unsafe { ptr::read(res as *const _) };
            // http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
            // says Darwin 7.9.0 getaddrinfo returns 0 but sets
            // res->ai_canonname to NULL.
            let ret = if info.ai_canonname.is_null() {
                Ok(String::from(host))
            } else {
                Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() })
            };
            unsafe {
                libc::freeaddrinfo(res);
            }
            ret
        } else {
            Err(IOError::last_os_error())
        }
    }
    pub fn iter_all_records() -> UtmpxIter {
        UtmpxIter
    }
}

/// Iterator of login records
pub struct UtmpxIter;

impl UtmpxIter {
    /// Sets the name of the utmpx-format file for the other utmpx functions to access.
    ///
    /// If not set, default record file will be used(file path depends on the target OS)
    pub fn read_from(self, f: &str) -> Self {
        let res = unsafe { utmpxname(CString::new(f).unwrap().as_ptr()) };
        if res != 0 {
            println!("Warning: {}", IOError::last_os_error());
        }
        unsafe {
            setutxent();
        }
        self
    }
}

impl Iterator for UtmpxIter {
    type Item = Utmpx;
    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let res = getutxent();
            if !res.is_null() {
                Some(Utmpx { inner: ptr::read(res as *const _) })
            } else {
                endutxent();
                None
            }
        }
    }
}
<commit_msg>uucore::utmpx: fix doctest errors<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//
//! Aims to provide platform-independent methods to obtain login records
//!
//! **ONLY** support linux, macos and freebsd for the time being
//!
//! # Examples:
//!
//! ```
//! use uucore::utmpx::Utmpx;
//! for ut in Utmpx::iter_all_records() {
//!     if ut.is_user_process() {
//!         println!("{}: {}", ut.host(), ut.user())
//!     }
//! }
//! ```
//!
//! Specifying the path to login record:
//!
//! ```
//! use uucore::utmpx::Utmpx;
//! for ut in Utmpx::iter_all_records().read_from("/some/where/else") {
//!     if ut.is_user_process() {
//!         println!("{}: {}", ut.host(), ut.user())
//!     }
//! }
//! ```

use super::libc;
pub extern crate time;
use self::time::{Tm, Timespec};

use ::std::io::Result as IOResult;
use ::std::io::Error as IOError;
use ::std::ptr;
use ::std::ffi::CString;

pub use self::ut::*;
use libc::utmpx;
// pub use libc::getutxid;
// pub use libc::getutxline;
// pub use libc::pututxline;
pub use libc::getutxent;
pub use libc::setutxent;
pub use libc::endutxent;
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub use libc::utmpxname;
#[cfg(target_os = "freebsd")]
pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
    0
}

// In case the c_char array doesn' t end with NULL
macro_rules! chars2string {
    ($arr:expr) => (
        $arr.iter().take_while(|i| **i > 0).map(|&i| i as u8 as char).collect::<String>()
    )
}

#[cfg(target_os = "linux")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmp";

    pub use libc::__UT_LINESIZE as UT_LINESIZE;
    pub use libc::__UT_NAMESIZE as UT_NAMESIZE;
    pub use libc::__UT_HOSTSIZE as UT_HOSTSIZE;
    pub const UT_IDSIZE: usize = 4;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
}

#[cfg(target_os = "macos")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";

    pub use libc::_UTX_LINESIZE as UT_LINESIZE;
    pub use libc::_UTX_USERSIZE as UT_NAMESIZE;
    pub use libc::_UTX_HOSTSIZE as UT_HOSTSIZE;
    pub use libc::_UTX_IDSIZE as UT_IDSIZE;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
    pub use libc::SIGNATURE;
    pub use libc::SHUTDOWN_TIME;
}

#[cfg(target_os = "freebsd")]
mod ut {
    use super::libc;

    pub static DEFAULT_FILE: &'static str = "";

    pub const UT_LINESIZE: usize = 16;
    pub const UT_NAMESIZE: usize = 32;
    pub const UT_IDSIZE: usize = 8;
    pub const UT_HOSTSIZE: usize = 128;

    pub use libc::EMPTY;
    pub use libc::BOOT_TIME;
    pub use libc::OLD_TIME;
    pub use libc::NEW_TIME;
    pub use libc::USER_PROCESS;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::SHUTDOWN_TIME;
}

pub struct Utmpx {
    inner: utmpx,
}

impl Utmpx {
    /// A.K.A. ut.ut_type
    pub fn record_type(&self) -> i16 {
        self.inner.ut_type as i16
    }
    /// A.K.A. ut.ut_pid
    pub fn pid(&self) -> i32 {
        self.inner.ut_pid as i32
    }
    /// A.K.A. ut.ut_id
    pub fn terminal_suffix(&self) -> String {
        chars2string!(self.inner.ut_id)
    }
    /// A.K.A. ut.ut_user
    pub fn user(&self) -> String {
        chars2string!(self.inner.ut_user)
    }
    /// A.K.A. ut.ut_host
    pub fn host(&self) -> String {
        chars2string!(self.inner.ut_host)
    }
    /// A.K.A. ut.ut_line
    pub fn tty_device(&self) -> String {
        chars2string!(self.inner.ut_line)
    }
    /// A.K.A. ut.ut_tv
    pub fn login_time(&self) -> Tm {
        time::at(Timespec::new(self.inner.ut_tv.tv_sec as i64,
                               self.inner.ut_tv.tv_usec as i32))
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (e_termination, e_exit)
    #[cfg(any(target_os = "linux", target_os = "android"))]
    pub fn exit_status(&self) -> (i16, i16) {
        (self.inner.ut_exit.e_termination, self.inner.ut_exit.e_exit)
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (0, 0) on Non-Linux platform
    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    pub fn exit_status(&self) -> (i16, i16) {
        (0, 0)
    }
    /// Consumes the `Utmpx`, returning the underlying C struct utmpx
    pub fn into_inner(self) -> utmpx {
        self.inner
    }
    pub fn is_user_process(&self) -> bool {
        !self.user().is_empty() && self.record_type() == USER_PROCESS
    }

    /// Canonicalize host name using DNS
    pub fn canon_host(&self) -> IOResult<String> {
        const AI_CANONNAME: libc::c_int = 0x2;
        let host = self.host();
        let host = host.split(':').nth(0).unwrap();
        let hints = libc::addrinfo {
            ai_flags: AI_CANONNAME,
            ai_family: 0,
            ai_socktype: 0,
            ai_protocol: 0,
            ai_addrlen: 0,
            ai_addr: ptr::null_mut(),
            ai_canonname: ptr::null_mut(),
            ai_next: ptr::null_mut(),
        };
        let c_host = CString::new(host).unwrap();
        let mut res = ptr::null_mut();
        let status = unsafe {
            libc::getaddrinfo(c_host.as_ptr(),
                              ptr::null(),
                              &hints as *const _,
                              &mut res as *mut _)
        };
        if status == 0 {
            let info: libc::addrinfo = unsafe { ptr::read(res as *const _) };
            // http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
            // says Darwin 7.9.0 getaddrinfo returns 0 but sets
            // res->ai_canonname to NULL.
            let ret = if info.ai_canonname.is_null() {
                Ok(String::from(host))
            } else {
                Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() })
            };
            unsafe {
                libc::freeaddrinfo(res);
            }
            ret
        } else {
            Err(IOError::last_os_error())
        }
    }
    pub fn iter_all_records() -> UtmpxIter {
        UtmpxIter
    }
}

/// Iterator of login records
pub struct UtmpxIter;

impl UtmpxIter {
    /// Sets the name of the utmpx-format file for the other utmpx functions to access.
    ///
    /// If not set, default record file will be used(file path depends on the target OS)
    pub fn read_from(self, f: &str) -> Self {
        let res = unsafe { utmpxname(CString::new(f).unwrap().as_ptr()) };
        if res != 0 {
            println!("Warning: {}", IOError::last_os_error());
        }
        unsafe {
            setutxent();
        }
        self
    }
}

impl Iterator for UtmpxIter {
    type Item = Utmpx;
    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let res = getutxent();
            if !res.is_null() {
                Some(Utmpx { inner: ptr::read(res as *const _) })
            } else {
                endutxent();
                None
            }
        }
    }
}
<|endoftext|>


<commit_before>use base::math::*;

/// Helper trait to easily convert various `cgmath` types into array form to
/// use them in glium
pub trait ToArr {
    type Output;

    fn to_arr(&self) -> Self::Output;
}

impl<T: BaseNum> ToArr for Matrix4<T> {
    type Output = [[T; 4]; 4];

    fn to_arr(&self) -> Self::Output {
        (*self).into()
    }
}

// TODO: Add more impls
<commit_msg>ad the to_arr function for vector and matrix<commit_after>use base::math::*;

/// Helper trait to easily convert various `cgmath` types into array form to
/// use them in glium
pub trait ToArr {
    type Output;

    fn to_arr(&self) -> Self::Output;
}
/// Convert a `Matrix4` into a 4x4 array
impl<T: BaseNum> ToArr for Matrix4<T> {
    type Output = [[T; 4]; 4];

    fn to_arr(&self) -> Self::Output {
        (*self).into()
    }
}

/// Convert a `Matrix3` into a 3x3 array
impl<T: BaseNum> ToArr for Matrix3<T> {
    type Output = [[T; 3]; 3];

    fn to_arr(&self) -> Self::Output {
        (*self).into()
    }
}

/// Convert a `Matrix2` into a 2x2 array
impl<T: BaseNum> ToArr for Matrix2<T> {
    type Output = [[T; 2]; 2];

    fn to_arr(&self) -> Self::Output {
        (*self).into()
    }
}

/// Convert a `Vector4` into an array
impl<T: BaseNum> ToArr for Vector4<T> {
    type Output = [T; 4];

    fn to_arr(&self) -> [T; 4] {
        (*self).into()
    }
}

/// Convert a `Vector3` into an array
impl<T: BaseNum> ToArr for Vector3<T> {
    type Output = [T; 3];

    fn to_arr(&self) -> Self::Output {
        (*self).into()
    }
}

/// Convert a `Vector2` into an array
impl<T: BaseFloat> ToArr for Vector2<T> {
    type Output = [T; 2];

    fn to_arr(&self) -> Self::Output {
        (*self).into()
    }
}
<|endoftext|>


<commit_before>use regex::Regex;

/// Defines the terminals that can be lexed.
#[derive(Debug, PartialEq)]
pub enum Token {
    LParen,
    RParen,
    Plus,
    Minus,
    Star,
    StarStar,
    Slash,
    Backslash,
    Percent,
    Comma,
    Eq,
    Ampersand,
    VBar,
    Caret,
    LtLt,
    GtGt,
    Log,
    Sqrt,
    Sin,
    Cos,
    Tan,
    Asin,
    Acos,
    Atan,
    Integer(i64),
    Float(f64),
    Ident(String),
    Let,
    Eof,
}

/// Tokenizes the given input string and returns a vector of `Token`s.
///
/// # Examples
///
/// ```
/// let tokens = lex("1 + 2");
/// assert_eq!(tokens, vec![Token::Integer(1), Token::Plus, Token::Integer(2)]);
/// ```
pub fn lex(input: &str) -> Result<Vec<Token>, String> {
    let map = token_map();
    let mut tokens = Vec::new();
    let mut index: usize = 0;
    let input = input.trim();

    'outer: while index < input.len() {
        let input_slice = &input[index..];

        for &(ref regex, ref f) in &map {
            if let Some((0, len)) = regex.find(input_slice) {
                let token = f(&input_slice[0..len]);
                tokens.push(token);
                index += len;
                continue 'outer;
            }
        }

        return Result::Err(format!("Could not lex symbol: {}", input_slice));
    }

    Result::Ok(tokens)
}

// ============================================================================

fn token_map() -> Vec<(Regex, Box<Fn(&str) -> Token>)> {
    let create_regex = |s| Regex::new(s).unwrap();
    vec![(create_regex(r"^\s*-?\d+\.\d+"), Box::new(|s| Token::Float(s.trim().parse::<f64>().unwrap()))),
         (create_regex(r"^\s*-?\d+"), Box::new(|s| Token::Integer(s.trim().parse::<i64>().unwrap()))),
         (create_regex(r"^\s*\("), Box::new(|_| Token::LParen)),
         (create_regex(r"^\s*\)"), Box::new(|_| Token::RParen)),
         (create_regex(r"^\s*\+"), Box::new(|_| Token::Plus)),
         (create_regex(r"^\s*-"), Box::new(|_| Token::Minus)),
         (create_regex(r"^\s*\*\*"), Box::new(|_| Token::StarStar)),
         (create_regex(r"^\s*\*"), Box::new(|_| Token::Star)),
         (create_regex(r"^\s*/"), Box::new(|_| Token::Slash)),
         (create_regex(r"^\s*\\"), Box::new(|_| Token::Backslash)),
         (create_regex(r"^\s*%"), Box::new(|_| Token::Percent)),
         (create_regex(r"^\s*,"), Box::new(|_| Token::Comma)),
         (create_regex(r"^\s*="), Box::new(|_| Token::Eq)),
         (create_regex(r"^\s*&"), Box::new(|_| Token::Ampersand)),
         (create_regex(r"^\s*\|"), Box::new(|_| Token::VBar)),
         (create_regex(r"^\s*\^"), Box::new(|_| Token::Caret)),
         (create_regex(r"^\s*<<"), Box::new(|_| Token::LtLt)),
         (create_regex(r"^\s*>>"), Box::new(|_| Token::GtGt)),
         (create_regex(r"^\s*log\b"), Box::new(|_| Token::Log)),
         (create_regex(r"^\s*sqrt\b"), Box::new(|_| Token::Sqrt)),
         (create_regex(r"^\s*sin\b"), Box::new(|_| Token::Sin)),
         (create_regex(r"^\s*cos\b"), Box::new(|_| Token::Cos)),
         (create_regex(r"^\s*tan\b"), Box::new(|_| Token::Tan)),
         (create_regex(r"^\s*asin\b"), Box::new(|_| Token::Asin)),
         (create_regex(r"^\s*acos\b"), Box::new(|_| Token::Acos)),
         (create_regex(r"^\s*atan\b"), Box::new(|_| Token::Atan)),
         (create_regex(r"^\s*let\b"), Box::new(|_| Token::Let)),
         (create_regex(r"^\s*[a-zA-z]+"), Box::new(|s| Token::Ident(s.trim().to_string())))]
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_lex_simple() {
        let tokens = lex("1+2").unwrap();
        assert_eq!(tokens,
                   vec![Token::Integer(1), Token::Plus, Token::Integer(2)]);
    }

    #[test]
    fn test_lex_with_whitespace() {
        let tokens = lex(" \t 1 + \t\r\n    2 \r\n ").unwrap();
        assert_eq!(tokens,
                   vec![Token::Integer(1), Token::Plus, Token::Integer(2)]);
    }

    #[test]
    fn test_lex_with_big_nums() {
        let tokens = lex("1002 + 123456789").unwrap();
        assert_eq!(tokens,
                   vec![Token::Integer(1002), Token::Plus, Token::Integer(123456789)]);
    }

    #[test]
    fn test_lex_with_ident() {
        let tokens = lex("abc 100").unwrap();
        assert_eq!(tokens,
                   vec![Token::Ident("abc".to_string()), Token::Integer(100)]);
    }

    #[test]
    fn test_lex_with_negative_integer() {
        let tokens = lex("- -123").unwrap();
        assert_eq!(tokens,
                   vec![Token::Minus, Token::Integer(-123)]);
    }

    #[test]
    fn test_lex_float() {
        let tokens = lex("1.0 -123.5 1003.125").unwrap();
        assert_eq!(tokens,
                   vec![Token::Float(1.0), Token::Float(-123.5), Token::Float(1003.125)]);
    }

    #[test]
    fn test_lex_all_tokens() {
        let tokens = lex(r"( ) + - * / \ % , = 100 1.25 abc let ** & | ^ << >> log sqrt sin cos tan asin acos atan").unwrap();
        assert_eq!(tokens,
                   vec![Token::LParen,
                        Token::RParen,
                        Token::Plus,
                        Token::Minus,
                        Token::Star,
                        Token::Slash,
                        Token::Backslash,
                        Token::Percent,
                        Token::Comma,
                        Token::Eq,
                        Token::Integer(100),
                        Token::Float(1.25),
                        Token::Ident("abc".to_string()),
                        Token::Let,
                        Token::StarStar,
                        Token::Ampersand,
                        Token::VBar,
                        Token::Caret,
                        Token::LtLt,
                        Token::GtGt,
                        Token::Log,
                        Token::Sqrt,
                        Token::Sin,
                        Token::Cos,
                        Token::Tan,
                        Token::Asin,
                        Token::Acos,
                        Token::Atan,
                        ]);
    }

    #[test]
    fn test_lex_unknown_token() {
        assert!(lex("123 < 234").is_err()); // '<' is unknown
    }
}
<commit_msg>resolved #6 (Support hexadecimal and binary literals)<commit_after>use regex::Regex;

/// Defines the terminals that can be lexed.
#[derive(Debug, PartialEq)]
pub enum Token {
    LParen,
    RParen,
    Plus,
    Minus,
    Star,
    StarStar,
    Slash,
    Backslash,
    Percent,
    Comma,
    Eq,
    Ampersand,
    VBar,
    Caret,
    LtLt,
    GtGt,
    Log,
    Sqrt,
    Sin,
    Cos,
    Tan,
    Asin,
    Acos,
    Atan,
    Integer(i64),
    Float(f64),
    Ident(String),
    Let,
    Eof,
}

/// Tokenizes the given input string and returns a vector of `Token`s.
///
/// # Examples
///
/// ```
/// let tokens = lex("1 + 2");
/// assert_eq!(tokens, vec![Token::Integer(1), Token::Plus, Token::Integer(2)]);
/// ```
pub fn lex(input: &str) -> Result<Vec<Token>, String> {
    let map = token_map();
    let mut tokens = Vec::new();
    let mut index: usize = 0;
    let input = input.trim();

    'outer: while index < input.len() {
        let input_slice = &input[index..];

        for &(ref regex, ref f) in &map {
            if let Some((0, len)) = regex.find(input_slice) {
                let token = f(&input_slice[0..len]);
                tokens.push(token);
                index += len;
                continue 'outer;
            }
        }

        return Result::Err(format!("Could not lex symbol: {}", input_slice));
    }

    Result::Ok(tokens)
}

// ============================================================================

fn strip_string_from_index(s: &str, ch_to_remove: char, start_index: usize, buffer: &mut String) {
    for ch in s.chars().skip(start_index) {
        if ch != ch_to_remove {
            buffer.push(ch);
        }
    }
}

fn token_map() -> Vec<(Regex, Box<Fn(&str) -> Token>)> {
    let create_regex = |s| Regex::new(s).unwrap();
    vec![(create_regex(r"^\s*-?\d+\.\d+"), Box::new(|s| Token::Float(s.trim().parse::<f64>().unwrap()))),
         (create_regex(r"^\s*0x[0-9a-fA-F][_0-9a-fA-F]*"), Box::new(|s| {
                     let mut buf = String::new();
                     strip_string_from_index(s.trim(), '_', 2, &mut buf);
                     Token::Integer(i64::from_str_radix(&buf, 16).unwrap())
                 })),
         (create_regex(r"^\s*0b[01][_01]*"), Box::new(|s| {
                     let mut buf = String::new();
                     strip_string_from_index(s.trim(), '_', 2, &mut buf);
                     Token::Integer(i64::from_str_radix(&buf, 2).unwrap())
                 })),
         (create_regex(r"^\s*-?\d[_\d]*"), Box::new(|s| {
                     let mut buf = String::new();
                     strip_string_from_index(s.trim(), '_', 0, &mut buf);
                     Token::Integer(buf.parse::<i64>().unwrap())
                 })),
         (create_regex(r"^\s*\("), Box::new(|_| Token::LParen)),
         (create_regex(r"^\s*\)"), Box::new(|_| Token::RParen)),
         (create_regex(r"^\s*\+"), Box::new(|_| Token::Plus)),
         (create_regex(r"^\s*-"), Box::new(|_| Token::Minus)),
         (create_regex(r"^\s*\*\*"), Box::new(|_| Token::StarStar)),
         (create_regex(r"^\s*\*"), Box::new(|_| Token::Star)),
         (create_regex(r"^\s*/"), Box::new(|_| Token::Slash)),
         (create_regex(r"^\s*\\"), Box::new(|_| Token::Backslash)),
         (create_regex(r"^\s*%"), Box::new(|_| Token::Percent)),
         (create_regex(r"^\s*,"), Box::new(|_| Token::Comma)),
         (create_regex(r"^\s*="), Box::new(|_| Token::Eq)),
         (create_regex(r"^\s*&"), Box::new(|_| Token::Ampersand)),
         (create_regex(r"^\s*\|"), Box::new(|_| Token::VBar)),
         (create_regex(r"^\s*\^"), Box::new(|_| Token::Caret)),
         (create_regex(r"^\s*<<"), Box::new(|_| Token::LtLt)),
         (create_regex(r"^\s*>>"), Box::new(|_| Token::GtGt)),
         (create_regex(r"^\s*log\b"), Box::new(|_| Token::Log)),
         (create_regex(r"^\s*sqrt\b"), Box::new(|_| Token::Sqrt)),
         (create_regex(r"^\s*sin\b"), Box::new(|_| Token::Sin)),
         (create_regex(r"^\s*cos\b"), Box::new(|_| Token::Cos)),
         (create_regex(r"^\s*tan\b"), Box::new(|_| Token::Tan)),
         (create_regex(r"^\s*asin\b"), Box::new(|_| Token::Asin)),
         (create_regex(r"^\s*acos\b"), Box::new(|_| Token::Acos)),
         (create_regex(r"^\s*atan\b"), Box::new(|_| Token::Atan)),
         (create_regex(r"^\s*let\b"), Box::new(|_| Token::Let)),
         (create_regex(r"^\s*[a-zA-z]+"), Box::new(|s| Token::Ident(s.trim().to_string())))]
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_lex_simple() {
        let tokens = lex("1+2").unwrap();
        assert_eq!(tokens,
                   vec![Token::Integer(1), Token::Plus, Token::Integer(2)]);
    }

    #[test]
    fn test_lex_with_whitespace() {
        let tokens = lex(" \t 1 + \t\r\n    2 \r\n ").unwrap();
        assert_eq!(tokens,
                   vec![Token::Integer(1), Token::Plus, Token::Integer(2)]);
    }

    #[test]
    fn test_lex_with_big_nums() {
        let tokens = lex("1_002 + 123_456_789").unwrap();
        assert_eq!(tokens,
                   vec![Token::Integer(1002), Token::Plus, Token::Integer(123_456_789)]);
    }

    #[test]
    fn test_lex_hex() {
        let tokens = lex("0xAB12_FFD4_CE01").unwrap();
        assert_eq!(tokens,
                   vec![Token::Integer(0xAB12_FFD4_CE01)]);
    }

    #[test]
    fn test_lex_bin() {
        let tokens = lex("0b1001_1100_0101_0011").unwrap();
        assert_eq!(tokens,
                   vec![Token::Integer(0b1001_1100_0101_0011)]);
    }

    #[test]
    fn test_lex_with_ident() {
        let tokens = lex("abc 100").unwrap();
        assert_eq!(tokens,
                   vec![Token::Ident("abc".to_string()), Token::Integer(100)]);
    }

    #[test]
    fn test_lex_with_negative_integer() {
        let tokens = lex("- -123").unwrap();
        assert_eq!(tokens,
                   vec![Token::Minus, Token::Integer(-123)]);
    }

    #[test]
    fn test_lex_float() {
        let tokens = lex("1.0 -123.5 1003.125").unwrap();
        assert_eq!(tokens,
                   vec![Token::Float(1.0), Token::Float(-123.5), Token::Float(1003.125)]);
    }

    #[test]
    fn test_lex_all_tokens() {
        let tokens = lex(r"( ) + - * / \ % , = 100 1.25 abc let ** & | ^ << >> log sqrt sin cos tan asin acos atan").unwrap();
        assert_eq!(tokens,
                   vec![Token::LParen,
                        Token::RParen,
                        Token::Plus,
                        Token::Minus,
                        Token::Star,
                        Token::Slash,
                        Token::Backslash,
                        Token::Percent,
                        Token::Comma,
                        Token::Eq,
                        Token::Integer(100),
                        Token::Float(1.25),
                        Token::Ident("abc".to_string()),
                        Token::Let,
                        Token::StarStar,
                        Token::Ampersand,
                        Token::VBar,
                        Token::Caret,
                        Token::LtLt,
                        Token::GtGt,
                        Token::Log,
                        Token::Sqrt,
                        Token::Sin,
                        Token::Cos,
                        Token::Tan,
                        Token::Asin,
                        Token::Acos,
                        Token::Atan,
                        ]);
    }

    #[test]
    fn test_lex_unknown_token() {
        assert!(lex("123 < 234").is_err()); // '<' is unknown
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

// TODO we're going to allocate a whole bunch of temp Strings, is it worth
// keeping some scratch mem for this and running our own StrPool?
// TODO for lint violations of names, emit a refactor script

#[macro_use]
extern crate log;

extern crate syntex_syntax as syntax;
extern crate rustc_serialize;

extern crate strings;

extern crate unicode_segmentation;
extern crate regex;
extern crate diff;
extern crate term;

use syntax::ast;
use syntax::codemap::{mk_sp, CodeMap, Span};
use syntax::errors::Handler;
use syntax::errors::emitter::{ColorConfig, EmitterWriter};
use syntax::parse::{self, ParseSess};

use std::io::stdout;
use std::ops::{Add, Sub};
use std::path::Path;
use std::rc::Rc;
use std::collections::HashMap;
use std::fmt;

use issues::{BadIssueSeeker, Issue};
use filemap::FileMap;
use visitor::FmtVisitor;
use config::Config;

#[macro_use]
mod utils;
pub mod config;
pub mod filemap;
pub mod visitor;
mod checkstyle;
mod items;
mod missed_spans;
mod lists;
mod types;
mod expr;
mod imports;
mod issues;
mod rewrite;
mod string;
mod comment;
pub mod modules;
pub mod rustfmt_diff;
mod chains;
mod macros;
mod patterns;

const MIN_STRING: usize = 10;
// When we get scoped annotations, we should have rustfmt::skip.
const SKIP_ANNOTATION: &'static str = "rustfmt_skip";

pub trait Spanned {
    fn span(&self) -> Span;
}

impl Spanned for ast::Expr {
    fn span(&self) -> Span {
        self.span
    }
}

impl Spanned for ast::Pat {
    fn span(&self) -> Span {
        self.span
    }
}

impl Spanned for ast::Ty {
    fn span(&self) -> Span {
        self.span
    }
}

impl Spanned for ast::Arg {
    fn span(&self) -> Span {
        if items::is_named_arg(self) {
            mk_sp(self.pat.span.lo, self.ty.span.hi)
        } else {
            self.ty.span
        }
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Indent {
    // Width of the block indent, in characters. Must be a multiple of
    // Config::tab_spaces.
    pub block_indent: usize,
    // Alignment in characters.
    pub alignment: usize,
}

impl Indent {
    pub fn new(block_indent: usize, alignment: usize) -> Indent {
        Indent {
            block_indent: block_indent,
            alignment: alignment,
        }
    }

    pub fn empty() -> Indent {
        Indent::new(0, 0)
    }

    pub fn block_indent(mut self, config: &Config) -> Indent {
        self.block_indent += config.tab_spaces;
        self
    }

    pub fn block_unindent(mut self, config: &Config) -> Indent {
        self.block_indent -= config.tab_spaces;
        self
    }

    pub fn width(&self) -> usize {
        self.block_indent + self.alignment
    }

    pub fn to_string(&self, config: &Config) -> String {
        let (num_tabs, num_spaces) = if config.hard_tabs {
            (self.block_indent / config.tab_spaces, self.alignment)
        } else {
            (0, self.block_indent + self.alignment)
        };
        let num_chars = num_tabs + num_spaces;
        let mut indent = String::with_capacity(num_chars);
        for _ in 0..num_tabs {
            indent.push('\t')
        }
        for _ in 0..num_spaces {
            indent.push(' ')
        }
        indent
    }
}

impl Add for Indent {
    type Output = Indent;

    fn add(self, rhs: Indent) -> Indent {
        Indent {
            block_indent: self.block_indent + rhs.block_indent,
            alignment: self.alignment + rhs.alignment,
        }
    }
}

impl Sub for Indent {
    type Output = Indent;

    fn sub(self, rhs: Indent) -> Indent {
        Indent::new(self.block_indent - rhs.block_indent,
                    self.alignment - rhs.alignment)
    }
}

impl Add<usize> for Indent {
    type Output = Indent;

    fn add(self, rhs: usize) -> Indent {
        Indent::new(self.block_indent, self.alignment + rhs)
    }
}

impl Sub<usize> for Indent {
    type Output = Indent;

    fn sub(self, rhs: usize) -> Indent {
        Indent::new(self.block_indent, self.alignment - rhs)
    }
}

pub enum ErrorKind {
    // Line has exceeded character limit
    LineOverflow,
    // Line ends in whitespace
    TrailingWhitespace,
    // TO-DO or FIX-ME item without an issue number
    BadIssue(Issue),
}

impl fmt::Display for ErrorKind {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ErrorKind::LineOverflow => write!(fmt, "line exceeded maximum length"),
            ErrorKind::TrailingWhitespace => write!(fmt, "left behind trailing whitespace"),
            ErrorKind::BadIssue(issue) => write!(fmt, "found {}", issue),
        }
    }
}

// Formatting errors that are identified *after* rustfmt has run.
pub struct FormattingError {
    line: u32,
    kind: ErrorKind,
}

impl FormattingError {
    fn msg_prefix(&self) -> &str {
        match self.kind {
            ErrorKind::LineOverflow |
            ErrorKind::TrailingWhitespace => "Rustfmt failed at",
            ErrorKind::BadIssue(_) => "WARNING:",
        }
    }

    fn msg_suffix(&self) -> &str {
        match self.kind {
            ErrorKind::LineOverflow |
            ErrorKind::TrailingWhitespace => "(sorry)",
            ErrorKind::BadIssue(_) => "",
        }
    }
}

pub struct FormatReport {
    // Maps stringified file paths to their associated formatting errors.
    file_error_map: HashMap<String, Vec<FormattingError>>,
}

impl FormatReport {
    pub fn warning_count(&self) -> usize {
        self.file_error_map.iter().map(|(_, ref errors)| errors.len()).fold(0, |acc, x| acc + x)
    }
}

impl fmt::Display for FormatReport {
    // Prints all the formatting errors.
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        for (file, errors) in &self.file_error_map {
            for error in errors {
                try!(write!(fmt,
                            "{} {}:{}: {} {}\n",
                            error.msg_prefix(),
                            file,
                            error.line,
                            error.kind,
                            error.msg_suffix()));
            }
        }
        Ok(())
    }
}

// Formatting which depends on the AST.
fn fmt_ast(krate: &ast::Crate,
           parse_session: &ParseSess,
           main_file: &Path,
           config: &Config)
           -> FileMap {
    let mut file_map = FileMap::new();
    for (path, module) in modules::list_files(krate, parse_session.codemap()) {
        if config.skip_children && path.as_path() != main_file {
            continue;
        }
        let path = path.to_str().unwrap();
        if config.verbose {
            println!("Formatting {}", path);
        }
        let mut visitor = FmtVisitor::from_codemap(parse_session, config);
        visitor.format_separate_mod(module);
        file_map.insert(path.to_owned(), visitor.buffer);
    }
    file_map
}

// Formatting done on a char by char or line by line basis.
// TODO(#209) warn on bad license
// TODO(#20) other stuff for parity with make tidy
pub fn fmt_lines(file_map: &mut FileMap, config: &Config) -> FormatReport {
    let mut truncate_todo = Vec::new();
    let mut report = FormatReport { file_error_map: HashMap::new() };

    // Iterate over the chars in the file map.
    for (f, text) in file_map.iter() {
        let mut trims = vec![];
        let mut last_wspace: Option<usize> = None;
        let mut line_len = 0;
        let mut cur_line = 1;
        let mut newline_count = 0;
        let mut errors = vec![];
        let mut issue_seeker = BadIssueSeeker::new(config.report_todo, config.report_fixme);

        for (c, b) in text.chars() {
            if c == '\r' {
                line_len += c.len_utf8();
                continue;
            }

            // Add warnings for bad todos/ fixmes
            if let Some(issue) = issue_seeker.inspect(c) {
                errors.push(FormattingError {
                    line: cur_line,
                    kind: ErrorKind::BadIssue(issue),
                });
            }

            if c == '\n' {
                // Check for (and record) trailing whitespace.
                if let Some(lw) = last_wspace {
                    trims.push((cur_line, lw, b));
                    line_len -= b - lw;
                }
                // Check for any line width errors we couldn't correct.
                if line_len > config.max_width {
                    errors.push(FormattingError {
                        line: cur_line,
                        kind: ErrorKind::LineOverflow,
                    });
                }
                line_len = 0;
                cur_line += 1;
                newline_count += 1;
                last_wspace = None;
            } else {
                newline_count = 0;
                line_len += c.len_utf8();
                if c.is_whitespace() {
                    if last_wspace.is_none() {
                        last_wspace = Some(b);
                    }
                } else {
                    last_wspace = None;
                }
            }
        }

        if newline_count > 1 {
            debug!("track truncate: {} {} {}", f, text.len, newline_count);
            truncate_todo.push((f.to_owned(), text.len - newline_count + 1))
        }

        for &(l, _, _) in &trims {
            errors.push(FormattingError {
                line: l,
                kind: ErrorKind::TrailingWhitespace,
            });
        }

        report.file_error_map.insert(f.to_owned(), errors);
    }

    for (f, l) in truncate_todo {
        file_map.get_mut(&f).unwrap().truncate(l);
    }

    report
}

pub fn format_string(input: String, config: &Config) -> FileMap {
    let path = "stdin";
    let codemap = Rc::new(CodeMap::new());

    let tty_handler = Handler::with_tty_emitter(ColorConfig::Auto,
                                                None,
                                                true,
                                                false,
                                                codemap.clone());
    let mut parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone());

    let krate = parse::parse_crate_from_source_str(path.to_owned(),
                                                   input,
                                                   Vec::new(),
                                                   &parse_session);

    // Suppress error output after parsing.
    let silent_emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None, codemap.clone()));
    parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);

    // FIXME: we still use a FileMap even though we only have
    // one file, because fmt_lines requires a FileMap
    let mut file_map = FileMap::new();

    // do the actual formatting
    let mut visitor = FmtVisitor::from_codemap(&parse_session, config);
    visitor.format_separate_mod(&krate.module);

    // append final newline
    visitor.buffer.push_str("\n");
    file_map.insert(path.to_owned(), visitor.buffer);

    file_map
}

pub fn format(file: &Path, config: &Config) -> FileMap {
    let codemap = Rc::new(CodeMap::new());

    let tty_handler = Handler::with_tty_emitter(ColorConfig::Auto,
                                                None,
                                                true,
                                                false,
                                                codemap.clone());
    let mut parse_session = ParseSess::with_span_handler(tty_handler, codemap.clone());

    let krate = parse::parse_crate_from_file(file, Vec::new(), &parse_session);

    // Suppress error output after parsing.
    let silent_emitter = Box::new(EmitterWriter::new(Box::new(Vec::new()), None, codemap.clone()));
    parse_session.span_diagnostic = Handler::with_emitter(true, false, silent_emitter);

    let mut file_map = fmt_ast(&krate, &parse_session, file, config);

    // For some reason, the codemap does not include terminating
    // newlines so we must add one on for each file. This is sad.
    filemap::append_newlines(&mut file_map);

    file_map
}

pub fn run(file: &Path, config: &Config) {
    let mut result = format(file, config);

    print!("{}", fmt_lines(&mut result, config));
    let out = stdout();
    let write_result = filemap::write_all_files(&result, out, config);

    if let Err(msg) = write_result {
        println!("Error writing files: {}", msg);
    }
}

// Similar to run, but takes an input String instead of a file to format
pub fn run_from_stdin(input: String, config: &Config) {
    let mut result = format_string(input, config);
    fmt_lines(&mut result, config);

    let mut out = stdout();
    let write_result = filemap::write_file(&result["stdin"], "stdin", &mut out, config);

    if let Err(msg) = write_result {
        panic!("Error writing to stdout: {}", msg);
    }
}
<commit_msg>Removed lib.rs<commit_after><|endoftext|>


<commit_before>// vim: tw=80
//! A method may have non-Send arguments and/or return values

use mockall::*;
use std::rc::Rc;

#[automock]
trait Foo {
    // Rc is not Send
    fn foo(&self, x: Rc<u32>) -> Rc<u32>;

    // Rc is not Send
    fn bar(x: Rc<u32>) -> Rc<u32>;
}

#[test]
fn returning_st() {
    let mut mock = MockFoo::new();
    let y = Rc::new(43u32);
    mock.expect_foo()
        .returning_st(move |_| y.clone());
    let x = Rc::new(42u32);
    assert_eq!(43, *mock.foo(x).as_ref());
}

#[test]
fn returning_st_static() {
    let mock = MockFoo::bar_context();
    let y = Rc::new(43u32);
    mock.expect()
        .returning_st(move |_| y.clone());
    let x = Rc::new(42u32);
    assert_eq!(43, *MockFoo::bar(x).as_ref());
}

#[test]
fn withf_st() {
    let mut mock = MockFoo::new();
    let x = Rc::new(42u32);
    let argument = x.clone();
    mock.expect_foo()
        .withf_st(move |x| *x == argument)
        .returning_st(|_| Rc::new(43u32));
    assert_eq!(43, *mock.foo(x).as_ref());
}

#[test]
fn withf_st_static() {
    let mock = MockFoo::bar_context();
    let x = Rc::new(42u32);
    let argument = x.clone();
    mock.expect()
        .withf_st(move |x| *x == argument)
        .returning_st(|_| Rc::new(43u32));
    assert_eq!(43, *MockFoo::bar(x).as_ref());
}
<commit_msg>Fix a race condition in a test<commit_after>// vim: tw=80
//! A method may have non-Send arguments and/or return values

use mockall::*;
use std::rc::Rc;
use std::sync::Mutex;

#[automock]
trait Foo {
    // Rc is not Send
    fn foo(&self, x: Rc<u32>) -> Rc<u32>;

    // Rc is not Send
    fn bar(x: Rc<u32>) -> Rc<u32>;
}

lazy_static! {
    static ref BAR_MTX: Mutex<()> = Mutex::new(());
}

#[test]
fn returning_st() {
    let mut mock = MockFoo::new();
    let y = Rc::new(43u32);
    mock.expect_foo()
        .returning_st(move |_| y.clone());
    let x = Rc::new(42u32);
    assert_eq!(43, *mock.foo(x).as_ref());
}

#[test]
fn returning_st_static() {
    let _m = BAR_MTX.lock().unwrap();

    let mock = MockFoo::bar_context();
    let y = Rc::new(43u32);
    mock.expect()
        .returning_st(move |_| y.clone());
    let x = Rc::new(42u32);
    assert_eq!(43, *MockFoo::bar(x).as_ref());
}

#[test]
fn withf_st() {
    let mut mock = MockFoo::new();
    let x = Rc::new(42u32);
    let argument = x.clone();
    mock.expect_foo()
        .withf_st(move |x| *x == argument)
        .returning_st(|_| Rc::new(43u32));
    assert_eq!(43, *mock.foo(x).as_ref());
}

#[test]
fn withf_st_static() {
    let _m = BAR_MTX.lock().unwrap();

    let mock = MockFoo::bar_context();
    let x = Rc::new(42u32);
    let argument = x.clone();
    mock.expect()
        .withf_st(move |x| *x == argument)
        .returning_st(|_| Rc::new(43u32));
    assert_eq!(43, *MockFoo::bar(x).as_ref());
}
<|endoftext|>