<commit_before>use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::path::PathBuf;
use semver::Version;

use core::{PackageId, Package, Target};
use util::{self, CargoResult, Config, ProcessBuilder, process, join_paths};

/// A structure returning the result of a compilation.
pub struct Compilation<'cfg> {
    /// All libraries which were built for a package.
    ///
    /// This is currently used for passing --extern flags to rustdoc tests later
    /// on.
    pub libraries: HashMap<PackageId, Vec<(Target, PathBuf)>>,

    /// An array of all tests created during this compilation.
    pub tests: Vec<(Package, String, PathBuf)>,

    /// An array of all binaries created.
    pub binaries: Vec<PathBuf>,

    /// All directires for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    // TODO: deprecated, remove
    pub native_dirs: HashSet<PathBuf>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: PathBuf,

    /// Output directory for rust dependencies
    pub deps_output: PathBuf,

    /// Library search path for compiler plugins and build scripts
    /// which have dynamic dependencies.
    pub plugins_dylib_path: PathBuf,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<PackageId, Vec<(String, String)>>,

    pub to_doc_test: Vec<Package>,

    /// Features enabled during this compilation.
    pub cfgs: HashSet<String>,

    pub target: String,

    config: &'cfg Config,
}

impl<'cfg> Compilation<'cfg> {
    pub fn new(config: &'cfg Config) -> Compilation<'cfg> {
        Compilation {
            libraries: HashMap::new(),
            native_dirs: HashSet::new(),  // TODO: deprecated, remove
            root_output: PathBuf::from("/"),
            deps_output: PathBuf::from("/"),
            plugins_dylib_path: PathBuf::from("/"),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            to_doc_test: Vec::new(),
            cfgs: HashSet::new(),
            config: config,
            target: String::new(),
        }
    }

    /// See `process`.
    pub fn rustc_process(&self, pkg: &Package) -> CargoResult<ProcessBuilder> {
        self.fill_env(try!(self.config.rustc()).process(), pkg, true)
    }

    /// See `process`.
    pub fn rustdoc_process(&self, pkg: &Package) -> CargoResult<ProcessBuilder> {
        self.fill_env(process(&*try!(self.config.rustdoc())), pkg, true)
    }

    /// See `process`.
    pub fn host_process<T: AsRef<OsStr>>(&self, cmd: T, pkg: &Package)
                                         -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, true)
    }

    /// See `process`.
    pub fn target_process<T: AsRef<OsStr>>(&self, cmd: T, pkg: &Package)
                                           -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, false)
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    fn fill_env(&self, mut cmd: ProcessBuilder, pkg: &Package, is_host: bool)
                -> CargoResult<ProcessBuilder> {

        let mut search_path = if is_host {
            vec![self.plugins_dylib_path.clone()]
        } else {
            let mut search_path = vec![];

            // Add -L arguments, after stripping off prefixes like "native=" or "framework=".
            for dir in self.native_dirs.iter() {
                let dir = match dir.to_str() {
                    Some(s) => {
                        let mut parts = s.splitn(2, '=');
                        match (parts.next(), parts.next()) {
                            (Some("native"), Some(path)) |
                            (Some("crate"), Some(path)) |
                            (Some("dependency"), Some(path)) |
                            (Some("framework"), Some(path)) |
                            (Some("all"), Some(path)) => path.into(),
                            _ => dir.clone(),
                        }
                    }
                    None => dir.clone(),
                };
                search_path.push(dir);
            }
            search_path.push(self.root_output.clone());
            search_path.push(self.deps_output.clone());
            search_path
        };

        search_path.extend(util::dylib_path().into_iter());
        let search_path = try!(join_paths(&search_path, util::dylib_path_envvar()));

        cmd.env(util::dylib_path_envvar(), &search_path);
        if let Some(env) = self.extra_env.get(pkg.package_id()) {
            for &(ref k, ref v) in env {
                cmd.env(k, v);
            }
        }

        let metadata = pkg.manifest().metadata();

        cmd.env("CARGO_MANIFEST_DIR", pkg.root())
           .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
           .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
           .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
           .env("CARGO_PKG_VERSION_PRE", &pre_version_component(pkg.version()))
           .env("CARGO_PKG_VERSION", &pkg.version().to_string())
           .env("CARGO_PKG_NAME", &pkg.name())
           .env("CARGO_PKG_DESCRIPTION", metadata.description.as_ref().unwrap_or(&String::new()))
           .env("CARGO_PKG_HOMEPAGE", metadata.homepage.as_ref().unwrap_or(&String::new()))
           .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
           .cwd(pkg.root());
        Ok(cmd)
    }
}

fn pre_version_component(v: &Version) -> String {
    if v.pre.is_empty() {
        return String::new();
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 { ret.push('.') };
        ret.push_str(&x.to_string());
    }

    ret
}
<commit_msg>Pass target environment for rustdoc<commit_after>use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::path::PathBuf;
use semver::Version;

use core::{PackageId, Package, Target};
use util::{self, CargoResult, Config, ProcessBuilder, process, join_paths};

/// A structure returning the result of a compilation.
pub struct Compilation<'cfg> {
    /// All libraries which were built for a package.
    ///
    /// This is currently used for passing --extern flags to rustdoc tests later
    /// on.
    pub libraries: HashMap<PackageId, Vec<(Target, PathBuf)>>,

    /// An array of all tests created during this compilation.
    pub tests: Vec<(Package, String, PathBuf)>,

    /// An array of all binaries created.
    pub binaries: Vec<PathBuf>,

    /// All directires for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    // TODO: deprecated, remove
    pub native_dirs: HashSet<PathBuf>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: PathBuf,

    /// Output directory for rust dependencies
    pub deps_output: PathBuf,

    /// Library search path for compiler plugins and build scripts
    /// which have dynamic dependencies.
    pub plugins_dylib_path: PathBuf,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<PackageId, Vec<(String, String)>>,

    pub to_doc_test: Vec<Package>,

    /// Features enabled during this compilation.
    pub cfgs: HashSet<String>,

    pub target: String,

    config: &'cfg Config,
}

impl<'cfg> Compilation<'cfg> {
    pub fn new(config: &'cfg Config) -> Compilation<'cfg> {
        Compilation {
            libraries: HashMap::new(),
            native_dirs: HashSet::new(),  // TODO: deprecated, remove
            root_output: PathBuf::from("/"),
            deps_output: PathBuf::from("/"),
            plugins_dylib_path: PathBuf::from("/"),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            to_doc_test: Vec::new(),
            cfgs: HashSet::new(),
            config: config,
            target: String::new(),
        }
    }

    /// See `process`.
    pub fn rustc_process(&self, pkg: &Package) -> CargoResult<ProcessBuilder> {
        self.fill_env(try!(self.config.rustc()).process(), pkg, true)
    }

    /// See `process`.
    pub fn rustdoc_process(&self, pkg: &Package) -> CargoResult<ProcessBuilder> {
        self.fill_env(process(&*try!(self.config.rustdoc())), pkg, false)
    }

    /// See `process`.
    pub fn host_process<T: AsRef<OsStr>>(&self, cmd: T, pkg: &Package)
                                         -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, true)
    }

    /// See `process`.
    pub fn target_process<T: AsRef<OsStr>>(&self, cmd: T, pkg: &Package)
                                           -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, false)
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    fn fill_env(&self, mut cmd: ProcessBuilder, pkg: &Package, is_host: bool)
                -> CargoResult<ProcessBuilder> {

        let mut search_path = if is_host {
            vec![self.plugins_dylib_path.clone()]
        } else {
            let mut search_path = vec![];

            // Add -L arguments, after stripping off prefixes like "native=" or "framework=".
            for dir in self.native_dirs.iter() {
                let dir = match dir.to_str() {
                    Some(s) => {
                        let mut parts = s.splitn(2, '=');
                        match (parts.next(), parts.next()) {
                            (Some("native"), Some(path)) |
                            (Some("crate"), Some(path)) |
                            (Some("dependency"), Some(path)) |
                            (Some("framework"), Some(path)) |
                            (Some("all"), Some(path)) => path.into(),
                            _ => dir.clone(),
                        }
                    }
                    None => dir.clone(),
                };
                search_path.push(dir);
            }
            search_path.push(self.root_output.clone());
            search_path.push(self.deps_output.clone());
            search_path
        };

        search_path.extend(util::dylib_path().into_iter());
        let search_path = try!(join_paths(&search_path, util::dylib_path_envvar()));

        cmd.env(util::dylib_path_envvar(), &search_path);
        if let Some(env) = self.extra_env.get(pkg.package_id()) {
            for &(ref k, ref v) in env {
                cmd.env(k, v);
            }
        }

        let metadata = pkg.manifest().metadata();

        cmd.env("CARGO_MANIFEST_DIR", pkg.root())
           .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
           .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
           .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
           .env("CARGO_PKG_VERSION_PRE", &pre_version_component(pkg.version()))
           .env("CARGO_PKG_VERSION", &pkg.version().to_string())
           .env("CARGO_PKG_NAME", &pkg.name())
           .env("CARGO_PKG_DESCRIPTION", metadata.description.as_ref().unwrap_or(&String::new()))
           .env("CARGO_PKG_HOMEPAGE", metadata.homepage.as_ref().unwrap_or(&String::new()))
           .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
           .cwd(pkg.root());
        Ok(cmd)
    }
}

fn pre_version_component(v: &Version) -> String {
    if v.pre.is_empty() {
        return String::new();
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 { ret.push('.') };
        ret.push_str(&x.to_string());
    }

    ret
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;

use ast::parser;
use ast::stack;
use ast::expressions::*;

use ast::lexer::tokens::Keyword;

const DEBUG: bool = true;

fn ignore(_: &mut stack::Stack) -> bool {
    true
}

#[allow(dead_code)]
fn first(stack: &mut stack::Stack) {
    let (_second, first) = stack_unpack!(stack, single, single);
    stack.push_single(first);
}

fn second(stack: &mut stack::Stack) {
    let (second, _first) = stack_unpack!(stack, single, single);
    stack.push_single(second);
}

/// Function prepends element on stack before last to the last vector-element (for varlist and namelist)
fn prepend_vector_prefix(stack: &mut stack::Stack) {
    let (mut tail, head) = stack_unpack!(stack, repetition, single);
    tail.push_front(head);

    stack.push_repetition(tail);
}

// chunk ::= block
rule!(chunk, block);

//block ::= {stat} [retstat]
rule!(block, and![(repetition!(stat), retstat) => blocks::Block::new]);

/*stat ::=  ‘;’ |
        varlist ‘=’ explist |
        functioncall |
        label |
        break |
        goto Name |
        do block end |
        while exp do block end |
        repeat block until exp |
        if exp then block {elseif exp then block} [else block] end |
        for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end |
        for namelist in explist do block end |
        function funcname funcbody |
        local function Name funcbody |
        local namelist [‘=’ explist] !!!*/

rule!(stat, or![
    label,
    statements::Statement::breakstat,
    and![(terminal!(Keyword::GOTO), variables::Id::rule) => labels::Goto::new]
]);

// retstat ::= return [explist] [‘;’]
rule!(retstat, and![(terminal!(Keyword::RETURN),
                    optional!(and![(explist) => expression::Expressions::new], nil),
                    optional!(terminal!(Keyword::SEMICOLONS), nil)) =>
                    |stack: &mut stack::Stack| {
                        let (_semi, explist, _ret) = stack_unpack!(stack, optional, optional, single);
                        stack.push_single(Box::new(statements::Statement::Return(explist)))
                    }]);

// label ::= ‘::’ Name ‘::’
rule!(label, and![(terminal!(Keyword::PATH), variables::Id::rule, terminal!(Keyword::PATH)) => labels::Label::new]);

// funcname ::= Name {‘.’ Name} [‘:’ Name]
rule!(funcname,
    and![(
        and![(variables::Id::rule,
            repetition!(and![(terminal!(Keyword::DOT), variables::Id::rule) => second])) => prepend_vector_prefix],
        optional!(and![(terminal!(Keyword::COLONS), variables::Id::rule) => second], nil)) =>
        function::Funcname::new]);

// varlist ::= var {‘,’ var}
// We push vector on top to check assignment parity
rule!(varlist, and![(
    var,
    repetition!(and![(terminal!(Keyword::COMMA), var) => second])) =>
    prepend_vector_prefix]);

// var_suffix ::= ‘[’ exp ‘]’ [var_suffix] | ‘.’ Name [var_suffix]
// Note nexted and!'s. We use internal and! to remove braces and dot
rule!(var_suffix, or![
    and![(
        and![(terminal!(Keyword::LSBRACKET), exp, terminal!(Keyword::RSBRACKET)) =>
            |stack: &mut stack::Stack| {
                // Remove brackets from stack
                let (_rb, expression, _lb) = stack_unpack!(stack, single, single, single);
                stack.push_single(expression);
                tables::Indexing::new(stack)
            }],
        optional!(var_suffix)) => ignore],
    and![(
        and![(terminal!(Keyword::DOT), variables::Id::rule) =>
            |stack: &mut stack::Stack| {
                // Remove dot from stack
                let (name, _dot) = stack_unpack!(stack, single, single);
                stack.push_single(name);
                tables::Indexing::new(stack)
            }],
        optional!(var_suffix)) => ignore]
]);

// var ::=  Name [var_suffix] | functioncall var_suffix | ‘(’ exp ‘)’ var_suffix !!! no funcall
// Note nexted and!'s. We use internal and! to remove braces
rule!(var, or![
    and![(variables::Id::rule, optional!(var_suffix)) => ignore],
    and![(
        and![(terminal!(Keyword::LBRACE), exp, terminal!(Keyword::RBRACE)) =>
            |stack: &mut stack::Stack| {
                // Remove braces from stack
                let (_rb, expression, _lb) = stack_unpack!(stack, single, single, single);
                stack.push_single(expression)
            }],
        var_suffix) => ignore]]);

// namelist ::= Name {‘,’ Name}
// We push vector on top to check assignment parity
rule!(namelist, and![(
    variables::Id::rule,
    repetition!(and![(terminal!(Keyword::COMMA), variables::Id::rule) => second])) =>
    prepend_vector_prefix]);

// explist ::= exp {‘,’ exp}
rule!(explist, and![(
    exp,
    repetition!(and![(
        terminal!(Keyword::COMMA),
        exp) =>
        second])) =>
    prepend_vector_prefix]);

// exp_suffix ::= binop [exp_suffix]
rule!(exp_suffix, and![(binop, optional!(exp)) => ignore]);

// exp_prefix ::=  nil | false | true | Numeral | LiteralString | ‘...’ | functiondef |
//        prefixexp | tableconstructor | unop exp !!!!
rule!(exp_prefix, or![
    primitives::Nil::rule,
    primitives::Boolean::rule,
    primitives::Number::rule,
    primitives::String::rule,
    statements::Statement::ellipsis,
    unop
]);

// exp ::= exp_prefix [exp_suffix]
rule!(exp, and![(exp_prefix, optional!(exp_suffix)) => ignore]);

//prefixexp ::= var | functioncall | ‘(’ exp ‘)’
// -- This one is terrible. To resolve 3-way recursion (prefixexp, var, functioncall), we need this set of rules
// functioncall_suffix1 ::= args [functioncall_suffix1] | ‘:’ Name args [functioncall_suffix1]
rule!(functioncall_suffix1, or![
    and![(
        and![(args) => function::Funcall::new],
        optional!(functioncall_suffix1)) => ignore],
    and![(
        and![(terminal!(Keyword::COLONS), variables::Id::rule, args) => function::Funcall::new_self],
        optional!(functioncall_suffix1)) => ignore]
]);

// functioncall_suffix2 ::= var_suffix functioncall_suffix1 [functioncall_suffix2] -- resolved to var
rule!(functioncall_suffix2, and![(var_suffix, functioncall_suffix1, optional!(functioncall_suffix2)) => ignore]);

// functioncall_suffix3 ::= functioncall_suffix1 [functioncall_suffix2]
rule!(functioncall_suffix3, and![(functioncall_suffix1, optional!(functioncall_suffix2)) => ignore]);

// functioncall_suffix4 ::= var_suffix functioncall_suffix3 | functioncall_suffix3 -- either var expression or prefixexp expression
rule!(functioncall_suffix4, or![
    and![(var_suffix, functioncall_suffix3) => ignore],
    functioncall_suffix3
]);

// functioncall ::= Name [var_suffix] functioncall_suffix3 |                   -- var ID
//        ‘(’ exp ‘)’ functioncall_suffix4
rule!(functioncall, or![
    and![(variables::Id::rule, optional!(var_suffix), functioncall_suffix3) => ignore],
    and![(
        and![(terminal!(Keyword::LBRACE), exp, terminal!(Keyword::RBRACE)) =>
            |stack: &mut stack::Stack| {
                // Remove braces from stack
                let (_rb, expression, _lb) = stack_unpack!(stack, single, single, single);
                stack.push_single(expression)
            }],
        functioncall_suffix4) => ignore]
]);

// args ::=  ‘(’ [explist] ‘)’ | tableconstructor | LiteralString
rule!(args, or![
    and![(terminal!(Keyword::LBRACE), optional!(explist), terminal!(Keyword::RBRACE)) => function::Funcall::new_args],
    primitives::String::rule
]);

//functiondef ::= function funcbody*/

// funcbody ::= ‘(’ [parlist] ‘)’ block end

// -- Here we have a problem of prefix comma for both variants. Will resolve manually
// -- Names always will produce vector and ellipsis will produce single element, which is the indicator of the end
// See FunctionParameters::new* function for further documentation
// parlist_name ::= Name [parlist_suffix] | ‘...’ [parlist_suffix]
rule!(parlist_name, or![
    and![(and![(variables::Id::rule) => function::FunctionParameters::new_parameter], optional!(parlist_suffix)) => ignore],
    and![(and![(terminal!(Keyword::DOT3)) => function::FunctionParameters::new_final_varargs], optional!(parlist_suffix)) => ignore]
]);

// parlist_suffix ::= ‘,’ parlist_name
rule!(parlist_suffix, and![(terminal!(Keyword::COMMA), parlist_name) => ignore]);

// parlist ::= Name [parlist_suffix] | ‘...’
rule!(parlist, or![
    and![(
        and![(variables::Id::rule) =>
            |stack: &mut stack::Stack| {
                // Each name inside parameters list will produce repetition. Hence we do this with the first name too
                let name = stack.pop_single();

                let mut vec = VecDeque::new();
                vec.push_back(name);
                stack.push_repetition(vec)
            }],
        optional!(parlist_suffix)) => function::FunctionParameters::new_namelist],
    and![(terminal!(Keyword::DOT3)) => function::FunctionParameters::new_varargs]]);

/*

tableconstructor ::= ‘{’ [fieldlist] ‘}’
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp
fieldsep ::= ‘,’ | ‘;’*/

// binop ::=  ‘+’ | ‘-’ | ‘*’ | ‘/’ | ‘//’ | ‘^’ | ‘%’ |
//        ‘&’ | ‘~’ | ‘|’ | ‘>>’ | ‘<<’ | ‘..’ |
//        ‘<’ | ‘<=’ | ‘>’ | ‘>=’ | ‘==’ | ‘~=’ |
//        and | or
rule!(binop, operators::Binop::rule);
// unop ::= ‘-’ | not | ‘#’ | ‘~’
rule!(unop, operators::Unop::rule);
<commit_msg>Minor cleanup<commit_after>use std::collections::VecDeque;

use ast::parser;
use ast::stack;
use ast::expressions::*;

use ast::lexer::tokens::Keyword;

const DEBUG: bool = false;

fn ignore(_: &mut stack::Stack) -> bool {
    true
}

fn second(stack: &mut stack::Stack) {
    let (second, _first) = stack_unpack!(stack, single, single);
    stack.push_single(second);
}

/// Function prepends element on stack before last to the last vector-element (for varlist and namelist)
fn prepend_vector_prefix(stack: &mut stack::Stack) {
    let (mut tail, head) = stack_unpack!(stack, repetition, single);
    tail.push_front(head);

    stack.push_repetition(tail);
}

// chunk ::= block
rule!(chunk, block);

//block ::= {stat} [retstat]
rule!(block, and![(repetition!(stat), retstat) => blocks::Block::new]);

/*stat ::=  ‘;’ |
        varlist ‘=’ explist |
        functioncall |
        label |
        break |
        goto Name |
        do block end |
        while exp do block end |
        repeat block until exp |
        if exp then block {elseif exp then block} [else block] end |
        for Name ‘=’ exp ‘,’ exp [‘,’ exp] do block end |
        for namelist in explist do block end |
        function funcname funcbody |
        local function Name funcbody |
        local namelist [‘=’ explist] !!!*/

rule!(stat, or![
    label,
    statements::Statement::breakstat,
    and![(terminal!(Keyword::GOTO), variables::Id::rule) => labels::Goto::new]
]);

// retstat ::= return [explist] [‘;’]
rule!(retstat, and![(terminal!(Keyword::RETURN),
                    optional!(and![(explist) => expression::Expressions::new], nil),
                    optional!(terminal!(Keyword::SEMICOLONS), nil)) =>
                    |stack: &mut stack::Stack| {
                        let (_semi, explist, _ret) = stack_unpack!(stack, optional, optional, single);
                        stack.push_single(Box::new(statements::Statement::Return(explist)))
                    }]);

// label ::= ‘::’ Name ‘::’
rule!(label, and![(terminal!(Keyword::PATH), variables::Id::rule, terminal!(Keyword::PATH)) => labels::Label::new]);

// funcname ::= Name {‘.’ Name} [‘:’ Name]
rule!(funcname,
    and![(
        and![(variables::Id::rule,
            repetition!(and![(terminal!(Keyword::DOT), variables::Id::rule) => second])) => prepend_vector_prefix],
        optional!(and![(terminal!(Keyword::COLONS), variables::Id::rule) => second], nil)) =>
        function::Funcname::new]);

// varlist ::= var {‘,’ var}
// We push vector on top to check assignment parity
rule!(varlist, and![(
    var,
    repetition!(and![(terminal!(Keyword::COMMA), var) => second])) =>
    prepend_vector_prefix]);

// var_suffix ::= ‘[’ exp ‘]’ [var_suffix] | ‘.’ Name [var_suffix]
// Note nexted and!'s. We use internal and! to remove braces and dot
rule!(var_suffix, or![
    and![(
        and![(terminal!(Keyword::LSBRACKET), exp, terminal!(Keyword::RSBRACKET)) =>
            |stack: &mut stack::Stack| {
                // Remove brackets from stack
                let (_rb, expression, _lb) = stack_unpack!(stack, single, single, single);
                stack.push_single(expression);
                tables::Indexing::new(stack)
            }],
        optional!(var_suffix)) => ignore],
    and![(
        and![(terminal!(Keyword::DOT), variables::Id::rule) =>
            |stack: &mut stack::Stack| {
                // Remove dot from stack
                let (name, _dot) = stack_unpack!(stack, single, single);
                stack.push_single(name);
                tables::Indexing::new(stack)
            }],
        optional!(var_suffix)) => ignore]
]);

// var ::=  Name [var_suffix] | functioncall var_suffix | ‘(’ exp ‘)’ var_suffix !!! no funcall
// Note nexted and!'s. We use internal and! to remove braces
rule!(var, or![
    and![(variables::Id::rule, optional!(var_suffix)) => ignore],
    and![(
        and![(terminal!(Keyword::LBRACE), exp, terminal!(Keyword::RBRACE)) =>
            |stack: &mut stack::Stack| {
                // Remove braces from stack
                let (_rb, expression, _lb) = stack_unpack!(stack, single, single, single);
                stack.push_single(expression)
            }],
        var_suffix) => ignore]]);

// namelist ::= Name {‘,’ Name}
// We push vector on top to check assignment parity
rule!(namelist, and![(
    variables::Id::rule,
    repetition!(and![(terminal!(Keyword::COMMA), variables::Id::rule) => second])) =>
    prepend_vector_prefix]);

// explist ::= exp {‘,’ exp}
rule!(explist, and![(
    exp,
    repetition!(and![(
        terminal!(Keyword::COMMA),
        exp) =>
        second])) =>
    prepend_vector_prefix]);

// exp_suffix ::= binop [exp_suffix]
rule!(exp_suffix, and![(binop, optional!(exp)) => ignore]);

// exp_prefix ::=  nil | false | true | Numeral | LiteralString | ‘...’ | functiondef |
//        prefixexp | tableconstructor | unop exp !!!!
rule!(exp_prefix, or![
    primitives::Nil::rule,
    primitives::Boolean::rule,
    primitives::Number::rule,
    primitives::String::rule,
    statements::Statement::ellipsis,
    unop
]);

// exp ::= exp_prefix [exp_suffix]
rule!(exp, and![(exp_prefix, optional!(exp_suffix)) => ignore]);

//prefixexp ::= var | functioncall | ‘(’ exp ‘)’
// -- This one is terrible. To resolve 3-way recursion (prefixexp, var, functioncall), we need this set of rules
// functioncall_suffix1 ::= args [functioncall_suffix1] | ‘:’ Name args [functioncall_suffix1]
rule!(functioncall_suffix1, or![
    and![(
        and![(args) => function::Funcall::new],
        optional!(functioncall_suffix1)) => ignore],
    and![(
        and![(terminal!(Keyword::COLONS), variables::Id::rule, args) => function::Funcall::new_self],
        optional!(functioncall_suffix1)) => ignore]
]);

// functioncall_suffix2 ::= var_suffix functioncall_suffix1 [functioncall_suffix2] -- resolved to var
rule!(functioncall_suffix2, and![(var_suffix, functioncall_suffix1, optional!(functioncall_suffix2)) => ignore]);

// functioncall_suffix3 ::= functioncall_suffix1 [functioncall_suffix2]
rule!(functioncall_suffix3, and![(functioncall_suffix1, optional!(functioncall_suffix2)) => ignore]);

// functioncall_suffix4 ::= var_suffix functioncall_suffix3 | functioncall_suffix3 -- either var expression or prefixexp expression
rule!(functioncall_suffix4, or![
    and![(var_suffix, functioncall_suffix3) => ignore],
    functioncall_suffix3
]);

// functioncall ::= Name [var_suffix] functioncall_suffix3 |                   -- var ID
//        ‘(’ exp ‘)’ functioncall_suffix4
rule!(functioncall, or![
    and![(variables::Id::rule, optional!(var_suffix), functioncall_suffix3) => ignore],
    and![(
        and![(terminal!(Keyword::LBRACE), exp, terminal!(Keyword::RBRACE)) =>
            |stack: &mut stack::Stack| {
                // Remove braces from stack
                let (_rb, expression, _lb) = stack_unpack!(stack, single, single, single);
                stack.push_single(expression)
            }],
        functioncall_suffix4) => ignore]
]);

// args ::=  ‘(’ [explist] ‘)’ | tableconstructor | LiteralString
rule!(args, or![
    and![(terminal!(Keyword::LBRACE), optional!(explist), terminal!(Keyword::RBRACE)) => function::Funcall::new_args],
    primitives::String::rule
]);

//functiondef ::= function funcbody*/

// funcbody ::= ‘(’ [parlist] ‘)’ block end

// -- Here we have a problem of prefix comma for both variants. Will resolve manually
// -- Names always will produce vector and ellipsis will produce single element, which is the indicator of the end
// See FunctionParameters::new* function for further documentation
// parlist_name ::= Name [parlist_suffix] | ‘...’ [parlist_suffix]
rule!(parlist_name, or![
    and![(and![(variables::Id::rule) => function::FunctionParameters::new_parameter], optional!(parlist_suffix)) => ignore],
    and![(and![(terminal!(Keyword::DOT3)) => function::FunctionParameters::new_final_varargs], optional!(parlist_suffix)) => ignore]
]);

// parlist_suffix ::= ‘,’ parlist_name
rule!(parlist_suffix, and![(terminal!(Keyword::COMMA), parlist_name) => ignore]);

// parlist ::= Name [parlist_suffix] | ‘...’
rule!(parlist, or![
    and![(
        and![(variables::Id::rule) =>
            |stack: &mut stack::Stack| {
                // Each name inside parameters list will produce repetition. Hence we do this with the first name too
                let name = stack.pop_single();

                let mut vec = VecDeque::new();
                vec.push_back(name);
                stack.push_repetition(vec)
            }],
        optional!(parlist_suffix)) => function::FunctionParameters::new_namelist],
    and![(terminal!(Keyword::DOT3)) => function::FunctionParameters::new_varargs]]);

/*

tableconstructor ::= ‘{’ [fieldlist] ‘}’
fieldlist ::= field {fieldsep field} [fieldsep]
field ::= ‘[’ exp ‘]’ ‘=’ exp | Name ‘=’ exp | exp
fieldsep ::= ‘,’ | ‘;’*/

// binop ::=  ‘+’ | ‘-’ | ‘*’ | ‘/’ | ‘//’ | ‘^’ | ‘%’ |
//        ‘&’ | ‘~’ | ‘|’ | ‘>>’ | ‘<<’ | ‘..’ |
//        ‘<’ | ‘<=’ | ‘>’ | ‘>=’ | ‘==’ | ‘~=’ |
//        and | or
rule!(binop, operators::Binop::rule);
// unop ::= ‘-’ | not | ‘#’ | ‘~’
rule!(unop, operators::Unop::rule);
<|endoftext|>


<commit_before>extern crate cargo;
extern crate url;
extern crate env_logger;
extern crate git2_curl;
extern crate rustc_serialize;
extern crate toml;
#[macro_use] extern crate log;

use std::collections::BTreeSet;
use std::collections::HashMap;
use std::env;
use std::fs;
use std::path::{Path,PathBuf};

use cargo::core::shell::Verbosity;
use cargo::execute_main_without_stdin;
use cargo::util::{self, CliResult, lev_distance, Config, human, CargoResult};
use cargo::util::CliError;

#[derive(RustcDecodable)]
pub struct Flags {
    flag_list: bool,
    flag_version: bool,
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_explain: Option<String>,
    arg_command: String,
    arg_args: Vec<String>,
    flag_locked: bool,
    flag_frozen: bool,
}

const USAGE: &'static str = "
Rust's package manager

Usage:
    cargo <command> [<args>...]
    cargo [options]

Options:
    -h, --help          Display this message
    -V, --version       Print version info and exit
    --list              List installed commands
    --explain CODE      Run `rustc --explain CODE`
    -v, --verbose ...   Use verbose output
    -q, --quiet         No output printed to stdout
    --color WHEN        Coloring: auto, always, never
    --frozen            Require Cargo.lock and cache are up to date
    --locked            Require Cargo.lock is up to date

Some common cargo commands are (see all commands with --list):
    build       Compile the current project
    clean       Remove the target directory
    doc         Build this project's and its dependencies' documentation
    new         Create a new cargo project
    init        Create a new cargo project in an existing directory
    run         Build and execute src/main.rs
    test        Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this project to the registry
    install     Install a Rust binary

See 'cargo help <command>' for more information on a specific command.
";

fn main() {
    env_logger::init().unwrap();
    execute_main_without_stdin(execute, true, USAGE)
}

macro_rules! each_subcommand{
    ($mac:ident) => {
        $mac!(bench);
        $mac!(build);
        $mac!(clean);
        $mac!(doc);
        $mac!(fetch);
        $mac!(generate_lockfile);
        $mac!(git_checkout);
        $mac!(help);
        $mac!(init);
        $mac!(install);
        $mac!(locate_project);
        $mac!(login);
        $mac!(metadata);
        $mac!(new);
        $mac!(owner);
        $mac!(package);
        $mac!(pkgid);
        $mac!(publish);
        $mac!(read_manifest);
        $mac!(run);
        $mac!(rustc);
        $mac!(rustdoc);
        $mac!(search);
        $mac!(test);
        $mac!(uninstall);
        $mac!(update);
        $mac!(verify_project);
        $mac!(version);
        $mac!(yank);
    }
}

macro_rules! declare_mod {
    ($name:ident) => ( pub mod $name; )
}
each_subcommand!(declare_mod);

/**
  The top-level `cargo` command handles configuration and project location
  because they are fundamental (and intertwined). Other commands can rely
  on this top-level information.
*/
fn execute(flags: Flags, config: &Config) -> CliResult<Option<()>> {
    config.configure(flags.flag_verbose,
                     flags.flag_quiet,
                     &flags.flag_color,
                     flags.flag_frozen,
                     flags.flag_locked)?;

    init_git_transports(config);
    let _token = cargo::util::job::setup();

    if flags.flag_version {
        println!("{}", cargo::version());
        return Ok(None)
    }

    if flags.flag_list {
        println!("Installed Commands:");
        for command in list_commands(config) {
            println!("    {}", command);
        };
        return Ok(None)
    }

    if let Some(ref code) = flags.flag_explain {
        let mut procss = config.rustc()?.process();
        procss.arg("--explain").arg(code).exec().map_err(human)?;
        return Ok(None)
    }

    let args = match &flags.arg_command[..] {
        // For the commands `cargo` and `cargo help`, re-execute ourselves as
        // `cargo -h` so we can go through the normal process of printing the
        // help message.
        "" | "help" if flags.arg_args.is_empty() => {
            config.shell().set_verbosity(Verbosity::Verbose);
            let args = &["cargo".to_string(), "-h".to_string()];
            let r = cargo::call_main_without_stdin(execute, config, USAGE, args,
                                                   false);
            cargo::process_executed(r, &mut config.shell());
            return Ok(None)
        }

        // For `cargo help -h` and `cargo help --help`, print out the help
        // message for `cargo help`
        "help" if flags.arg_args[0] == "-h" ||
                  flags.arg_args[0] == "--help" => {
            vec!["cargo".to_string(), "help".to_string(), "-h".to_string()]
        }

        // For `cargo help foo`, print out the usage message for the specified
        // subcommand by executing the command with the `-h` flag.
        "help" => vec!["cargo".to_string(), flags.arg_args[0].clone(),
                       "-h".to_string()],

        // For all other invocations, we're of the form `cargo foo args...`. We
        // use the exact environment arguments to preserve tokens like `--` for
        // example.
        _ => {
            let mut default_alias = HashMap::new();
            default_alias.insert("b", "build".to_string());
            default_alias.insert("t", "test".to_string());
            default_alias.insert("r", "run".to_string());
            let mut args: Vec<String> = env::args().collect();
            if let Some(new_command) = default_alias.get(&args[1][..]){
                args[1] = new_command.clone();
            }
            args
        }
    };

    if try_execute(&config, &args) {
        return Ok(None)
    }

    let alias_list = aliased_command(&config, &args[1])?;
    let args = match alias_list {
        Some(alias_command) => {
            let chain = args.iter().take(1)
                .chain(alias_command.iter())
                .chain(args.iter().skip(2))
                .map(|s| s.to_string())
                .collect::<Vec<_>>();
            if try_execute(&config, &chain) {
                return Ok(None)
            } else {
                chain
            }
        }
        None => args,
    };
    execute_subcommand(config, &args[1], &args)?;
    Ok(None)
}

fn try_execute(config: &Config, args: &[String]) -> bool {
    macro_rules! cmd {
        ($name:ident) => (if args[1] == stringify!($name).replace("_", "-") {
            config.shell().set_verbosity(Verbosity::Verbose);
            let r = cargo::call_main_without_stdin($name::execute, config,
                                                   $name::USAGE,
                                                   &args,
                                                   false);
            cargo::process_executed(r, &mut config.shell());
            return true
        })
    }
    each_subcommand!(cmd);

    return false
}

fn aliased_command(config: &Config, command: &String) -> CargoResult<Option<Vec<String>>> {
    let alias_name = format!("alias.{}", command);
    let mut result = Ok(None);
    match config.get_string(&alias_name) {
        Ok(value) => {
            if let Some(record) = value {
                let alias_commands = record.val.split_whitespace()
                                               .map(|s| s.to_string())
                                               .collect();
                result = Ok(Some(alias_commands));
            }
        },
        Err(_) => {
            let value = config.get_list(&alias_name)?;
            if let Some(record) = value {
                let alias_commands: Vec<String> = record.val.iter()
                                .map(|s| s.0.to_string()).collect();
                result = Ok(Some(alias_commands));
            }
        }
    }
    result
}

fn find_closest(config: &Config, cmd: &str) -> Option<String> {
    let cmds = list_commands(config);
    // Only consider candidates with a lev_distance of 3 or less so we don't
    // suggest out-of-the-blue options.
    let mut filtered = cmds.iter().map(|c| (lev_distance(&c, cmd), c))
                                  .filter(|&(d, _)| d < 4)
                                  .collect::<Vec<_>>();
    filtered.sort_by(|a, b| a.0.cmp(&b.0));
    filtered.get(0).map(|slot| slot.1.clone())
}

fn execute_subcommand(config: &Config,
                      cmd: &str,
                      args: &[String]) -> CliResult<()> {
    let command_exe = format!("cargo-{}{}", cmd, env::consts::EXE_SUFFIX);
    let path = search_directories(config)
                    .iter()
                    .map(|dir| dir.join(&command_exe))
                    .find(|file| is_executable(file));
    let command = match path {
        Some(command) => command,
        None => {
            return Err(human(match find_closest(config, cmd) {
                Some(closest) => format!("no such subcommand: `{}`\n\n\t\
                                          Did you mean `{}`?\n", cmd, closest),
                None => format!("no such subcommand: `{}`", cmd)
            }).into())
        }
    };
    let err = match util::process(&command).args(&args[1..]).exec() {
        Ok(()) => return Ok(()),
        Err(e) => e,
    };

    if let Some(code) = err.exit.as_ref().and_then(|c| c.code()) {
        Err(CliError::code(code))
    } else {
        Err(CliError::new(Box::new(err), 101))
    }
}

/// List all runnable commands. find_command should always succeed
/// if given one of returned command.
fn list_commands(config: &Config) -> BTreeSet<String> {
    let prefix = "cargo-";
    let suffix = env::consts::EXE_SUFFIX;
    let mut commands = BTreeSet::new();
    for dir in search_directories(config) {
        let entries = match fs::read_dir(dir) {
            Ok(entries) => entries,
            _ => continue
        };
        for entry in entries.filter_map(|e| e.ok()) {
            let path = entry.path();
            let filename = match path.file_name().and_then(|s| s.to_str()) {
                Some(filename) => filename,
                _ => continue
            };
            if !filename.starts_with(prefix) || !filename.ends_with(suffix) {
                continue
            }
            if is_executable(entry.path()) {
                let end = filename.len() - suffix.len();
                commands.insert(filename[prefix.len()..end].to_string());
            }
        }
    }

    macro_rules! add_cmd {
        ($cmd:ident) => ({ commands.insert(stringify!($cmd).replace("_", "-")); })
    }
    each_subcommand!(add_cmd);
    commands
}

#[cfg(unix)]
fn is_executable<P: AsRef<Path>>(path: P) -> bool {
    use std::os::unix::prelude::*;
    fs::metadata(path).map(|metadata| {
        metadata.is_file() && metadata.permissions().mode() & 0o111 != 0
    }).unwrap_or(false)
}
#[cfg(windows)]
fn is_executable<P: AsRef<Path>>(path: P) -> bool {
    fs::metadata(path).map(|metadata| metadata.is_file()).unwrap_or(false)
}

fn search_directories(config: &Config) -> Vec<PathBuf> {
    let mut dirs = vec![config.home().clone().into_path_unlocked().join("bin")];
    if let Some(val) = env::var_os("PATH") {
        dirs.extend(env::split_paths(&val));
    }
    dirs
}

fn init_git_transports(config: &Config) {
    // Only use a custom transport if a proxy is configured, right now libgit2
    // doesn't support proxies and we have to use a custom transport in this
    // case. The custom transport, however, is not as well battle-tested.
    match cargo::ops::http_proxy_exists(config) {
        Ok(true) => {}
        _ => return
    }

    let handle = match cargo::ops::http_handle(config) {
        Ok(handle) => handle,
        Err(..) => return,
    };

    // The unsafety of the registration function derives from two aspects:
    //
    // 1. This call must be synchronized with all other registration calls as
    //    well as construction of new transports.
    // 2. The argument is leaked.
    //
    // We're clear on point (1) because this is only called at the start of this
    // binary (we know what the state of the world looks like) and we're mostly
    // clear on point (2) because we'd only free it after everything is done
    // anyway
    unsafe {
        git2_curl::register(handle);
    }
}
<commit_msg>doc: remove reference to non-existent function<commit_after>extern crate cargo;
extern crate url;
extern crate env_logger;
extern crate git2_curl;
extern crate rustc_serialize;
extern crate toml;
#[macro_use] extern crate log;

use std::collections::BTreeSet;
use std::collections::HashMap;
use std::env;
use std::fs;
use std::path::{Path,PathBuf};

use cargo::core::shell::Verbosity;
use cargo::execute_main_without_stdin;
use cargo::util::{self, CliResult, lev_distance, Config, human, CargoResult};
use cargo::util::CliError;

#[derive(RustcDecodable)]
pub struct Flags {
    flag_list: bool,
    flag_version: bool,
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_explain: Option<String>,
    arg_command: String,
    arg_args: Vec<String>,
    flag_locked: bool,
    flag_frozen: bool,
}

const USAGE: &'static str = "
Rust's package manager

Usage:
    cargo <command> [<args>...]
    cargo [options]

Options:
    -h, --help          Display this message
    -V, --version       Print version info and exit
    --list              List installed commands
    --explain CODE      Run `rustc --explain CODE`
    -v, --verbose ...   Use verbose output
    -q, --quiet         No output printed to stdout
    --color WHEN        Coloring: auto, always, never
    --frozen            Require Cargo.lock and cache are up to date
    --locked            Require Cargo.lock is up to date

Some common cargo commands are (see all commands with --list):
    build       Compile the current project
    clean       Remove the target directory
    doc         Build this project's and its dependencies' documentation
    new         Create a new cargo project
    init        Create a new cargo project in an existing directory
    run         Build and execute src/main.rs
    test        Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock
    search      Search registry for crates
    publish     Package and upload this project to the registry
    install     Install a Rust binary

See 'cargo help <command>' for more information on a specific command.
";

fn main() {
    env_logger::init().unwrap();
    execute_main_without_stdin(execute, true, USAGE)
}

macro_rules! each_subcommand{
    ($mac:ident) => {
        $mac!(bench);
        $mac!(build);
        $mac!(clean);
        $mac!(doc);
        $mac!(fetch);
        $mac!(generate_lockfile);
        $mac!(git_checkout);
        $mac!(help);
        $mac!(init);
        $mac!(install);
        $mac!(locate_project);
        $mac!(login);
        $mac!(metadata);
        $mac!(new);
        $mac!(owner);
        $mac!(package);
        $mac!(pkgid);
        $mac!(publish);
        $mac!(read_manifest);
        $mac!(run);
        $mac!(rustc);
        $mac!(rustdoc);
        $mac!(search);
        $mac!(test);
        $mac!(uninstall);
        $mac!(update);
        $mac!(verify_project);
        $mac!(version);
        $mac!(yank);
    }
}

macro_rules! declare_mod {
    ($name:ident) => ( pub mod $name; )
}
each_subcommand!(declare_mod);

/**
  The top-level `cargo` command handles configuration and project location
  because they are fundamental (and intertwined). Other commands can rely
  on this top-level information.
*/
fn execute(flags: Flags, config: &Config) -> CliResult<Option<()>> {
    config.configure(flags.flag_verbose,
                     flags.flag_quiet,
                     &flags.flag_color,
                     flags.flag_frozen,
                     flags.flag_locked)?;

    init_git_transports(config);
    let _token = cargo::util::job::setup();

    if flags.flag_version {
        println!("{}", cargo::version());
        return Ok(None)
    }

    if flags.flag_list {
        println!("Installed Commands:");
        for command in list_commands(config) {
            println!("    {}", command);
        };
        return Ok(None)
    }

    if let Some(ref code) = flags.flag_explain {
        let mut procss = config.rustc()?.process();
        procss.arg("--explain").arg(code).exec().map_err(human)?;
        return Ok(None)
    }

    let args = match &flags.arg_command[..] {
        // For the commands `cargo` and `cargo help`, re-execute ourselves as
        // `cargo -h` so we can go through the normal process of printing the
        // help message.
        "" | "help" if flags.arg_args.is_empty() => {
            config.shell().set_verbosity(Verbosity::Verbose);
            let args = &["cargo".to_string(), "-h".to_string()];
            let r = cargo::call_main_without_stdin(execute, config, USAGE, args,
                                                   false);
            cargo::process_executed(r, &mut config.shell());
            return Ok(None)
        }

        // For `cargo help -h` and `cargo help --help`, print out the help
        // message for `cargo help`
        "help" if flags.arg_args[0] == "-h" ||
                  flags.arg_args[0] == "--help" => {
            vec!["cargo".to_string(), "help".to_string(), "-h".to_string()]
        }

        // For `cargo help foo`, print out the usage message for the specified
        // subcommand by executing the command with the `-h` flag.
        "help" => vec!["cargo".to_string(), flags.arg_args[0].clone(),
                       "-h".to_string()],

        // For all other invocations, we're of the form `cargo foo args...`. We
        // use the exact environment arguments to preserve tokens like `--` for
        // example.
        _ => {
            let mut default_alias = HashMap::new();
            default_alias.insert("b", "build".to_string());
            default_alias.insert("t", "test".to_string());
            default_alias.insert("r", "run".to_string());
            let mut args: Vec<String> = env::args().collect();
            if let Some(new_command) = default_alias.get(&args[1][..]){
                args[1] = new_command.clone();
            }
            args
        }
    };

    if try_execute(&config, &args) {
        return Ok(None)
    }

    let alias_list = aliased_command(&config, &args[1])?;
    let args = match alias_list {
        Some(alias_command) => {
            let chain = args.iter().take(1)
                .chain(alias_command.iter())
                .chain(args.iter().skip(2))
                .map(|s| s.to_string())
                .collect::<Vec<_>>();
            if try_execute(&config, &chain) {
                return Ok(None)
            } else {
                chain
            }
        }
        None => args,
    };
    execute_subcommand(config, &args[1], &args)?;
    Ok(None)
}

fn try_execute(config: &Config, args: &[String]) -> bool {
    macro_rules! cmd {
        ($name:ident) => (if args[1] == stringify!($name).replace("_", "-") {
            config.shell().set_verbosity(Verbosity::Verbose);
            let r = cargo::call_main_without_stdin($name::execute, config,
                                                   $name::USAGE,
                                                   &args,
                                                   false);
            cargo::process_executed(r, &mut config.shell());
            return true
        })
    }
    each_subcommand!(cmd);

    return false
}

fn aliased_command(config: &Config, command: &String) -> CargoResult<Option<Vec<String>>> {
    let alias_name = format!("alias.{}", command);
    let mut result = Ok(None);
    match config.get_string(&alias_name) {
        Ok(value) => {
            if let Some(record) = value {
                let alias_commands = record.val.split_whitespace()
                                               .map(|s| s.to_string())
                                               .collect();
                result = Ok(Some(alias_commands));
            }
        },
        Err(_) => {
            let value = config.get_list(&alias_name)?;
            if let Some(record) = value {
                let alias_commands: Vec<String> = record.val.iter()
                                .map(|s| s.0.to_string()).collect();
                result = Ok(Some(alias_commands));
            }
        }
    }
    result
}

fn find_closest(config: &Config, cmd: &str) -> Option<String> {
    let cmds = list_commands(config);
    // Only consider candidates with a lev_distance of 3 or less so we don't
    // suggest out-of-the-blue options.
    let mut filtered = cmds.iter().map(|c| (lev_distance(&c, cmd), c))
                                  .filter(|&(d, _)| d < 4)
                                  .collect::<Vec<_>>();
    filtered.sort_by(|a, b| a.0.cmp(&b.0));
    filtered.get(0).map(|slot| slot.1.clone())
}

fn execute_subcommand(config: &Config,
                      cmd: &str,
                      args: &[String]) -> CliResult<()> {
    let command_exe = format!("cargo-{}{}", cmd, env::consts::EXE_SUFFIX);
    let path = search_directories(config)
                    .iter()
                    .map(|dir| dir.join(&command_exe))
                    .find(|file| is_executable(file));
    let command = match path {
        Some(command) => command,
        None => {
            return Err(human(match find_closest(config, cmd) {
                Some(closest) => format!("no such subcommand: `{}`\n\n\t\
                                          Did you mean `{}`?\n", cmd, closest),
                None => format!("no such subcommand: `{}`", cmd)
            }).into())
        }
    };
    let err = match util::process(&command).args(&args[1..]).exec() {
        Ok(()) => return Ok(()),
        Err(e) => e,
    };

    if let Some(code) = err.exit.as_ref().and_then(|c| c.code()) {
        Err(CliError::code(code))
    } else {
        Err(CliError::new(Box::new(err), 101))
    }
}

/// List all runnable commands
fn list_commands(config: &Config) -> BTreeSet<String> {
    let prefix = "cargo-";
    let suffix = env::consts::EXE_SUFFIX;
    let mut commands = BTreeSet::new();
    for dir in search_directories(config) {
        let entries = match fs::read_dir(dir) {
            Ok(entries) => entries,
            _ => continue
        };
        for entry in entries.filter_map(|e| e.ok()) {
            let path = entry.path();
            let filename = match path.file_name().and_then(|s| s.to_str()) {
                Some(filename) => filename,
                _ => continue
            };
            if !filename.starts_with(prefix) || !filename.ends_with(suffix) {
                continue
            }
            if is_executable(entry.path()) {
                let end = filename.len() - suffix.len();
                commands.insert(filename[prefix.len()..end].to_string());
            }
        }
    }

    macro_rules! add_cmd {
        ($cmd:ident) => ({ commands.insert(stringify!($cmd).replace("_", "-")); })
    }
    each_subcommand!(add_cmd);
    commands
}

#[cfg(unix)]
fn is_executable<P: AsRef<Path>>(path: P) -> bool {
    use std::os::unix::prelude::*;
    fs::metadata(path).map(|metadata| {
        metadata.is_file() && metadata.permissions().mode() & 0o111 != 0
    }).unwrap_or(false)
}
#[cfg(windows)]
fn is_executable<P: AsRef<Path>>(path: P) -> bool {
    fs::metadata(path).map(|metadata| metadata.is_file()).unwrap_or(false)
}

fn search_directories(config: &Config) -> Vec<PathBuf> {
    let mut dirs = vec![config.home().clone().into_path_unlocked().join("bin")];
    if let Some(val) = env::var_os("PATH") {
        dirs.extend(env::split_paths(&val));
    }
    dirs
}

fn init_git_transports(config: &Config) {
    // Only use a custom transport if a proxy is configured, right now libgit2
    // doesn't support proxies and we have to use a custom transport in this
    // case. The custom transport, however, is not as well battle-tested.
    match cargo::ops::http_proxy_exists(config) {
        Ok(true) => {}
        _ => return
    }

    let handle = match cargo::ops::http_handle(config) {
        Ok(handle) => handle,
        Err(..) => return,
    };

    // The unsafety of the registration function derives from two aspects:
    //
    // 1. This call must be synchronized with all other registration calls as
    //    well as construction of new transports.
    // 2. The argument is leaked.
    //
    // We're clear on point (1) because this is only called at the start of this
    // binary (we know what the state of the world looks like) and we're mostly
    // clear on point (2) because we'd only free it after everything is done
    // anyway
    unsafe {
        git2_curl::register(handle);
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::collections::HashMap;
use std::hash::Hash;

use toml;

use rustboylib::joypad::{JoypadKey, JOYPAD_KEYS};
use self::KeyboardBinding::*;

/// Enumerates the supported keyboard bindings for the virtual joypad.
#[derive(Clone, PartialEq)]
pub enum KeyboardBinding {
    /// QWERTY binding (the default one).
    QWERTY,
    /// AZERTY binding.
    AZERTY,
    /// The keyboard binding is to be loaded from the given configuration file.
    /// If this fails, revert to the default binding ('QWERTY').
    FromConfigFile(String),
}

impl fmt::Debug for KeyboardBinding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            QWERTY => write!(f, "QWERTY"),
            AZERTY => write!(f, "AZERTY"),
            FromConfigFile(ref file) => write!(f, "in configuration file \"{}\"", file),
        }
    }
}

/// Get the 'HashMap' translating between the emulator's 'JoypadKey' and the
/// backend's keycode type and corresponding to the given 'KeyboardBinding'.
/// The given 'HashMap' provides the keycode corresponding to the associated
/// symbol ; if it is not specified, the function fails.
///
/// The following (backend-agnostic) symbols should be supported :
/// "Numpad{0..9}"
/// "NumpadMinus" / "NumpadPlus" / "NumpadDivide" / "NumpadMultiply"
/// "{A..Z}"
/// "F{0..12}"
/// "Up" / "Down" / "Left" / "Right"
pub fn get_key_bindings<Key>(binding: KeyboardBinding,
                             symbol_backend_key_hm: HashMap<String, Key>)
                             -> Result<HashMap<Key, JoypadKey>, String>
    where Key: Hash + Eq + Copy
{
    let mut hm = HashMap::new();

    let keyboard_control_hm = try!(build_keyboard_control_hm(binding));
    for (symbol, control) in &keyboard_control_hm {
        let key = match symbol_backend_key_hm.get(symbol) {
            Some(ref k) => *(k.clone()),
            None => return Err(format!("backend does not support key \"{}\"", symbol)),
        };
        hm.insert(key, *control);
    }

    Ok(hm)
}

fn build_keyboard_control_hm(binding: KeyboardBinding) -> Result<HashMap<String, JoypadKey>, String> {

    match binding {
        QWERTY => {
            let mut hm = HashMap::new();
            hm.insert("W".into(), JoypadKey::Up);
            hm.insert("S".into(), JoypadKey::Down);
            hm.insert("A".into(), JoypadKey::Left);
            hm.insert("D".into(), JoypadKey::Right);
            hm.insert("Z".into(), JoypadKey::Select);
            hm.insert("C".into(), JoypadKey::Start);
            hm.insert("G".into(), JoypadKey::A);
            hm.insert("Y".into(), JoypadKey::B);
            assert_eq!(hm.len(), 8);
            Ok(hm)
        }
        AZERTY => {
            let mut hm = HashMap::new();
            hm.insert("W".into(), JoypadKey::Up);
            hm.insert("S".into(), JoypadKey::Down);
            hm.insert("A".into(), JoypadKey::Left);
            hm.insert("D".into(), JoypadKey::Right);
            hm.insert("Z".into(), JoypadKey::Select);
            hm.insert("C".into(), JoypadKey::Start);
            hm.insert("G".into(), JoypadKey::A);
            hm.insert("Y".into(), JoypadKey::B);
            assert_eq!(hm.len(), 8);
            Ok(hm)
        }
        FromConfigFile(ref config_file) => {
            let filepath = Path::new(&config_file[..]);
            let mut file_content = String::new();
            try!(File::open(filepath)
                     .and_then(|mut f| f.read_to_string(&mut file_content))
                     .map_err(|_| {
                         format!("could not load the input config file : {}",
                                 filepath.display())
                     }));
            keyboard_hm_from_config(&file_content[..], format!("{}", filepath.display()))
        }
    }
}

fn keyboard_hm_from_config<'a>(config_str: &'a str,
                               config_file: String)
                               -> Result<HashMap<String, JoypadKey>, String> {
    let mut hm = HashMap::new();

    let mut parser = toml::Parser::new(config_str);
    let table: toml::Value = match parser.parse() {
        Some(t) => toml::Value::Table(t),
        None => return Err(format!("parsing error in input config : {:?}", parser.errors)),
    };
    let keyboard_input = match table.lookup("input.keyboard") {
        Some(value) => value,
        None => {
            warn!(concat!("input config file \"{}\" does not specify",
                          " keyboard input, reverting to QWERTY."),
                  config_file);
            return build_keyboard_control_hm(QWERTY);
        }
    };

    for key in JOYPAD_KEYS.iter() {
        let key_symbol = match keyboard_input.lookup(key) {
            Some(value) => {
                match *value {
                    toml::Value::String(ref s) => &s[..],
                    _ => {
                        return Err(format!("key \"{}\" does not have a String value in input \
                                            config",
                                           key))
                    }
                }
            }
            None => return Err(format!("no key specified for \"{}\" in input config", key)),
        };
        if hm.insert(key_symbol.into(), JoypadKey::from_str(key).unwrap()).is_some() {
            warn!("input config file \"{}\" binds key \"{}\" more than once, earlier \
                   occurences will be erased",
                  config_file,
                  key_symbol);
            // N.B. : the order of priority is the one defined in JOYPAD_KEYS
            // which may not be naturally the one written in the configuration file
        }
    }
    if hm.len() == JOYPAD_KEYS.len() {
        Ok(hm)
    } else {
        Err(format!("missing joypad key(s) in input config file \"{}\"",
                    config_file))
    }
}

#[cfg(test)]
mod test {
    use rustboylib::joypad::JoypadKey;

    #[test]
    fn test_keyboard_hm_from_config() {
        let config = r#"
        [input]
            [input.keyboard]
            Up     = "Up"
            Down   = "Down"
            Left   = "Left"
            Right  = "Right"
            Select = "Numpad1"
            Start  = "Numpad3"
            A      = "E"
            B      = "T"
        "#;
        let r = super::keyboard_hm_from_config(config, "*test*".into());
        assert!(r.is_ok());
        let keys_hm = r.unwrap();
        assert_eq!(*keys_hm.get("Up".into()).unwrap(), JoypadKey::Up);
        assert_eq!(*keys_hm.get("Down".into()).unwrap(), JoypadKey::Down);
        assert_eq!(*keys_hm.get("Left".into()).unwrap(), JoypadKey::Left);
        assert_eq!(*keys_hm.get("Right".into()).unwrap(), JoypadKey::Right);
        assert_eq!(*keys_hm.get("Numpad1".into()).unwrap(), JoypadKey::Select);
        assert_eq!(*keys_hm.get("Numpad3".into()).unwrap(), JoypadKey::Start);
        assert_eq!(*keys_hm.get("E".into()).unwrap(), JoypadKey::A);
        assert_eq!(*keys_hm.get("T".into()).unwrap(), JoypadKey::B);
    }
}
<commit_msg>Fixed wrong AZERTY default binding keys<commit_after>use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::collections::HashMap;
use std::hash::Hash;

use toml;

use rustboylib::joypad::{JoypadKey, JOYPAD_KEYS};
use self::KeyboardBinding::*;

/// Enumerates the supported keyboard bindings for the virtual joypad.
#[derive(Clone, PartialEq)]
pub enum KeyboardBinding {
    /// QWERTY binding (the default one).
    QWERTY,
    /// AZERTY binding.
    AZERTY,
    /// The keyboard binding is to be loaded from the given configuration file.
    /// If this fails, revert to the default binding ('QWERTY').
    FromConfigFile(String),
}

impl fmt::Debug for KeyboardBinding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            QWERTY => write!(f, "QWERTY"),
            AZERTY => write!(f, "AZERTY"),
            FromConfigFile(ref file) => write!(f, "in configuration file \"{}\"", file),
        }
    }
}

/// Get the 'HashMap' translating between the emulator's 'JoypadKey' and the
/// backend's keycode type and corresponding to the given 'KeyboardBinding'.
/// The given 'HashMap' provides the keycode corresponding to the associated
/// symbol ; if it is not specified, the function fails.
///
/// The following (backend-agnostic) symbols should be supported :
/// "Numpad{0..9}"
/// "NumpadMinus" / "NumpadPlus" / "NumpadDivide" / "NumpadMultiply"
/// "{A..Z}"
/// "F{0..12}"
/// "Up" / "Down" / "Left" / "Right"
pub fn get_key_bindings<Key>(binding: KeyboardBinding,
                             symbol_backend_key_hm: HashMap<String, Key>)
                             -> Result<HashMap<Key, JoypadKey>, String>
    where Key: Hash + Eq + Copy
{
    let mut hm = HashMap::new();

    let keyboard_control_hm = try!(build_keyboard_control_hm(binding));
    for (symbol, control) in &keyboard_control_hm {
        let key = match symbol_backend_key_hm.get(symbol) {
            Some(ref k) => *(k.clone()),
            None => return Err(format!("backend does not support key \"{}\"", symbol)),
        };
        hm.insert(key, *control);
    }

    Ok(hm)
}

fn build_keyboard_control_hm(binding: KeyboardBinding) -> Result<HashMap<String, JoypadKey>, String> {

    match binding {
        QWERTY => {
            let mut hm = HashMap::new();
            hm.insert("W".into(), JoypadKey::Up);
            hm.insert("S".into(), JoypadKey::Down);
            hm.insert("A".into(), JoypadKey::Left);
            hm.insert("D".into(), JoypadKey::Right);
            hm.insert("Z".into(), JoypadKey::Select);
            hm.insert("C".into(), JoypadKey::Start);
            hm.insert("G".into(), JoypadKey::A);
            hm.insert("Y".into(), JoypadKey::B);
            assert_eq!(hm.len(), 8);
            Ok(hm)
        }
        AZERTY => {
            let mut hm = HashMap::new();
            hm.insert("Z".into(), JoypadKey::Up);
            hm.insert("S".into(), JoypadKey::Down);
            hm.insert("Q".into(), JoypadKey::Left);
            hm.insert("D".into(), JoypadKey::Right);
            hm.insert("W".into(), JoypadKey::Select);
            hm.insert("C".into(), JoypadKey::Start);
            hm.insert("G".into(), JoypadKey::A);
            hm.insert("Y".into(), JoypadKey::B);
            assert_eq!(hm.len(), 8);
            Ok(hm)
        }
        FromConfigFile(ref config_file) => {
            let filepath = Path::new(&config_file[..]);
            let mut file_content = String::new();
            try!(File::open(filepath)
                     .and_then(|mut f| f.read_to_string(&mut file_content))
                     .map_err(|_| {
                         format!("could not load the input config file : {}",
                                 filepath.display())
                     }));
            keyboard_hm_from_config(&file_content[..], format!("{}", filepath.display()))
        }
    }
}

fn keyboard_hm_from_config<'a>(config_str: &'a str,
                               config_file: String)
                               -> Result<HashMap<String, JoypadKey>, String> {
    let mut hm = HashMap::new();

    let mut parser = toml::Parser::new(config_str);
    let table: toml::Value = match parser.parse() {
        Some(t) => toml::Value::Table(t),
        None => return Err(format!("parsing error in input config : {:?}", parser.errors)),
    };
    let keyboard_input = match table.lookup("input.keyboard") {
        Some(value) => value,
        None => {
            warn!(concat!("input config file \"{}\" does not specify",
                          " keyboard input, reverting to QWERTY."),
                  config_file);
            return build_keyboard_control_hm(QWERTY);
        }
    };

    for key in JOYPAD_KEYS.iter() {
        let key_symbol = match keyboard_input.lookup(key) {
            Some(value) => {
                match *value {
                    toml::Value::String(ref s) => &s[..],
                    _ => {
                        return Err(format!("key \"{}\" does not have a String value in input \
                                            config",
                                           key))
                    }
                }
            }
            None => return Err(format!("no key specified for \"{}\" in input config", key)),
        };
        if hm.insert(key_symbol.into(), JoypadKey::from_str(key).unwrap()).is_some() {
            warn!("input config file \"{}\" binds key \"{}\" more than once, earlier \
                   occurences will be erased",
                  config_file,
                  key_symbol);
            // N.B. : the order of priority is the one defined in JOYPAD_KEYS
            // which may not be naturally the one written in the configuration file
        }
    }
    if hm.len() == JOYPAD_KEYS.len() {
        Ok(hm)
    } else {
        Err(format!("missing joypad key(s) in input config file \"{}\"",
                    config_file))
    }
}

#[cfg(test)]
mod test {
    use rustboylib::joypad::JoypadKey;

    #[test]
    fn test_keyboard_hm_from_config() {
        let config = r#"
        [input]
            [input.keyboard]
            Up     = "Up"
            Down   = "Down"
            Left   = "Left"
            Right  = "Right"
            Select = "Numpad1"
            Start  = "Numpad3"
            A      = "E"
            B      = "T"
        "#;
        let r = super::keyboard_hm_from_config(config, "*test*".into());
        assert!(r.is_ok());
        let keys_hm = r.unwrap();
        assert_eq!(*keys_hm.get("Up".into()).unwrap(), JoypadKey::Up);
        assert_eq!(*keys_hm.get("Down".into()).unwrap(), JoypadKey::Down);
        assert_eq!(*keys_hm.get("Left".into()).unwrap(), JoypadKey::Left);
        assert_eq!(*keys_hm.get("Right".into()).unwrap(), JoypadKey::Right);
        assert_eq!(*keys_hm.get("Numpad1".into()).unwrap(), JoypadKey::Select);
        assert_eq!(*keys_hm.get("Numpad3".into()).unwrap(), JoypadKey::Start);
        assert_eq!(*keys_hm.get("E".into()).unwrap(), JoypadKey::A);
        assert_eq!(*keys_hm.get("T".into()).unwrap(), JoypadKey::B);
    }
}
<|endoftext|>


<commit_before>use codespan::{ByteIndex, FileId, Files, LineIndex, Location, Span};
use std::io;
use termcolor::WriteColor;

use crate::diagnostic::{Diagnostic, Label};
use crate::term::Config;

use super::{Locus, NewLine};

mod border;
mod gutter;
mod note;
mod underline;

use self::border::{BorderLeft, BorderTop, BorderTopLeft};
use self::gutter::Gutter;
use self::note::Note;
use self::underline::{
    MarkStyle, Underline, UnderlineBottom, UnderlineLeft, UnderlineTop, UnderlineTopLeft,
};

/// An underlined snippet of source code.
///
/// ```text
///   ┌── test:2:9 ───
///   │
/// 2 │ (+ test "")
///   │         ^^ expected `Int` but found `String`
///   │
///   = expected type `Int`
///        found type `String`
/// ```
pub struct SourceSnippet<'a> {
    files: &'a Files,
    file_id: FileId,
    span: Span,
    message: &'a str,
    mark_style: MarkStyle,
    notes: &'a [String],
}

impl<'a> SourceSnippet<'a> {
    pub fn new_primary(files: &'a Files, diagnostic: &'a Diagnostic) -> SourceSnippet<'a> {
        SourceSnippet {
            files,
            file_id: diagnostic.primary_label.file_id,
            span: diagnostic.primary_label.span,
            message: &diagnostic.primary_label.message,
            mark_style: MarkStyle::Primary(diagnostic.severity),
            notes: &diagnostic.notes,
        }
    }

    pub fn new_secondary(files: &'a Files, label: &'a Label) -> SourceSnippet<'a> {
        SourceSnippet {
            files,
            file_id: label.file_id,
            span: label.span,
            message: &label.message,
            mark_style: MarkStyle::Secondary,
            notes: &[],
        }
    }

    fn file_name(&self) -> &'a str {
        self.files.name(self.file_id)
    }

    fn location(&self, byte_index: ByteIndex) -> Result<Location, impl std::error::Error> {
        self.files.location(self.file_id, byte_index)
    }

    fn source_slice(&self, span: Span, tab: &'a str) -> Result<String, impl std::error::Error> {
        // NOTE: Not sure if we can do this more efficiently? Perhaps a custom
        // writer might be better?
        self.files
            .source_slice(self.file_id, span)
            .map(|s| s.replace('\t', tab))
    }

    fn line_span(&self, line_index: LineIndex) -> Result<Span, impl std::error::Error> {
        self.files.line_span(self.file_id, line_index)
    }

    pub fn emit(&self, writer: &mut impl WriteColor, config: &Config) -> io::Result<()> {
        let start = self.location(self.span.start()).expect("location_start");
        let end = self.location(self.span.end()).expect("location_end");
        let start_line_span = self.line_span(start.line).expect("start_line_span");
        let end_line_span = self.line_span(end.line).expect("end_line_span");

        let label_style = self.mark_style.label_style(config);
        // Use the length of the last line number as the gutter padding
        let gutter_padding = format!("{}", end.line.number()).len();
        // Cache the tabs we'll be using to pad the source strings.
        let tab = config.tab_padding();

        // Top left border and locus.
        //
        // ```text
        // ┌── test:2:9 ───
        // ```

        Gutter::new(None, gutter_padding).emit(writer, config)?;
        BorderTopLeft::new().emit(writer, config)?;
        BorderTop::new(2).emit(writer, config)?;
        write!(writer, " ")?;

        Locus::new(self.file_name(), start).emit(writer, config)?;

        write!(writer, " ")?;
        BorderTop::new(3).emit(writer, config)?;
        NewLine::new().emit(writer, config)?;

        // Code snippet
        //
        // ```text
        //   │
        // 2 │ (+ test "")
        //   │         ^^ expected `Int` but found `String`
        //   │
        // ```

        // Write initial border
        Gutter::new(None, gutter_padding).emit(writer, config)?;
        BorderLeft::new().emit(writer, config)?;
        NewLine::new().emit(writer, config)?;

        fn trim_breaks(s: &str) -> &str {
            s.trim_end_matches(|ch| ch == '\r' || ch == '\n')
        }

        // Write underlined source section
        if start.line == end.line {
            // Single line
            //
            // ```text
            // 2 │ (+ test "")
            //   │         ^^ expected `Int` but found `String`
            // ```

            let prefix_source = {
                let span = Span::new(start_line_span.start(), self.span.start());
                self.source_slice(span, &tab).expect("prefix_source")
            };
            let highlighted_source = {
                let span = self.span;
                self.source_slice(span, &tab).expect("highlighted_source")
            };
            let suffix_source = {
                let span = Span::new(self.span.end(), end_line_span.end());
                self.source_slice(span, &tab).expect("suffix_source")
            };

            // Write line number and border
            Gutter::new(start.line.number(), gutter_padding).emit(writer, config)?;
            BorderLeft::new().emit(writer, config)?;

            // Write line source
            write!(writer, " {}", prefix_source)?;
            writer.set_color(label_style)?;
            write!(writer, "{}", highlighted_source)?;
            writer.reset()?;
            write!(writer, "{}", trim_breaks(&suffix_source))?;

            NewLine::new().emit(writer, config)?;

            // Write border, underline, and label
            Gutter::new(None, gutter_padding).emit(writer, config)?;
            BorderLeft::new().emit(writer, config)?;
            Underline::new(
                self.mark_style,
                &prefix_source,
                &highlighted_source,
                self.message,
            )
            .emit(writer, config)?;
            NewLine::new().emit(writer, config)?;
        } else {
            // Multiple lines
            //
            // ```text
            // 4 │   fizz₁ num = case (mod num 5) (mod num 3) of
            //   │ ╭─────────────^
            // 5 │ │     0 0 => "FizzBuzz"
            // 6 │ │     0 _ => "Fizz"
            // 7 │ │     _ 0 => "Buzz"
            // 8 │ │     _ _ => num
            //   │ ╰──────────────^ `case` clauses have incompatible types
            // ```

            let prefix_source = {
                let span = Span::new(start_line_span.start(), self.span.start());
                self.source_slice(span, &tab).expect("prefix_source")
            };
            let highlighted_source = {
                let span = Span::new(self.span.start(), start_line_span.end());
                self.source_slice(span, &tab).expect("highlighted_source_1")
            };

            if prefix_source.trim().is_empty() {
                // Section is prefixed by empty space, so we don't need to take
                // up a new line.
                //
                // ```text
                // 4 │ ╭     case (mod num 5) (mod num 3) of
                // ```

                // Write line number, border, and underline
                Gutter::new(start.line.number(), gutter_padding).emit(writer, config)?;
                BorderLeft::new().emit(writer, config)?;
                UnderlineTopLeft::new(self.mark_style).emit(writer, config)?;

                // Write source line
                write!(writer, " {}", prefix_source)?;
                writer.set_color(&label_style)?;
                write!(writer, "{}", trim_breaks(&highlighted_source))?;
                writer.reset()?;
                NewLine::new().emit(writer, config)?;
            } else {
                // There's source code in the prefix, so run an underline
                // underneath it to get to the start of the span.
                //
                // ```text
                // 4 │   fizz₁ num = case (mod num 5) (mod num 3) of
                //   │ ╭─────────────^
                // ```

                // Write line number and border
                Gutter::new(start.line.number(), gutter_padding).emit(writer, config)?;
                BorderLeft::new().emit(writer, config)?;

                // Write source line
                write!(writer, "   {}", prefix_source)?;
                writer.set_color(&label_style)?;
                write!(writer, "{}", trim_breaks(&highlighted_source))?;
                writer.reset()?;
                NewLine::new().emit(writer, config)?;

                // Write border and underline
                Gutter::new(None, gutter_padding).emit(writer, config)?;
                BorderLeft::new().emit(writer, config)?;
                UnderlineTop::new(self.mark_style, &prefix_source).emit(writer, config)?;
                NewLine::new().emit(writer, config)?;
            }

            // Write highlighted lines
            //
            // ```text
            // 5 │ │     0 0 => "FizzBuzz"
            // 6 │ │     0 _ => "Fizz"
            // 7 │ │     _ 0 => "Buzz"
            // ```
            for line_index in ((start.line.to_usize() + 1)..end.line.to_usize())
                .map(|i| LineIndex::from(i as u32))
            {
                let highlighted_source = {
                    let span = self.line_span(line_index).expect("highlighted_span");
                    self.source_slice(span, &tab).expect("highlighted_source_2")
                };

                // Write line number, border, and underline
                Gutter::new(line_index.number(), gutter_padding).emit(writer, config)?;
                BorderLeft::new().emit(writer, config)?;
                UnderlineLeft::new(self.mark_style).emit(writer, config)?;

                // Write highlighted source
                writer.set_color(label_style)?;
                write!(writer, " {}", trim_breaks(&highlighted_source))?;
                writer.reset()?;
                NewLine::new().emit(writer, config)?;
            }

            // Write last highlighted line
            //
            // ```text
            // 8 │ │     _ _ => num
            //   │ ╰──────────────^ `case` clauses have incompatible types
            // ```

            let highlighted_source = {
                let span = Span::new(end_line_span.start(), self.span.end());
                self.source_slice(span, &tab).expect("highlighted_source_3")
            };
            let suffix_source = {
                let span = Span::new(self.span.end(), end_line_span.end());
                self.source_slice(span, &tab).expect("suffix_source")
            };

            // Write line number, border, and underline
            Gutter::new(end.line.number(), gutter_padding).emit(writer, config)?;
            BorderLeft::new().emit(writer, config)?;
            UnderlineLeft::new(self.mark_style).emit(writer, config)?;

            // Write line source
            writer.set_color(label_style)?;
            write!(writer, " {}", highlighted_source)?;
            writer.reset()?;
            write!(writer, "{}", trim_breaks(&suffix_source))?;
            NewLine::new().emit(writer, config)?;

            // Write border, underline, and label
            Gutter::new(None, gutter_padding).emit(writer, config)?;
            BorderLeft::new().emit(writer, config)?;
            UnderlineBottom::new(self.mark_style, &highlighted_source, self.message)
                .emit(writer, config)?;
            NewLine::new().emit(writer, config)?;
        };

        // Write final border
        Gutter::new(None, gutter_padding).emit(writer, config)?;
        BorderLeft::new().emit(writer, config)?;
        NewLine::new().emit(writer, config)?;

        // Additional notes
        //
        // ```text
        // = expected type `Int`
        //      found type `String`
        // ```

        for note in self.notes {
            Note::new(gutter_padding, note).emit(writer, config)?;
        }

        Ok(())
    }
}
<commit_msg>Simplify line trimming<commit_after>use codespan::{ByteIndex, FileId, Files, LineIndex, Location, Span};
use std::io;
use termcolor::WriteColor;

use crate::diagnostic::{Diagnostic, Label};
use crate::term::Config;

use super::{Locus, NewLine};

mod border;
mod gutter;
mod note;
mod underline;

use self::border::{BorderLeft, BorderTop, BorderTopLeft};
use self::gutter::Gutter;
use self::note::Note;
use self::underline::{
    MarkStyle, Underline, UnderlineBottom, UnderlineLeft, UnderlineTop, UnderlineTopLeft,
};

/// An underlined snippet of source code.
///
/// ```text
///   ┌── test:2:9 ───
///   │
/// 2 │ (+ test "")
///   │         ^^ expected `Int` but found `String`
///   │
///   = expected type `Int`
///        found type `String`
/// ```
pub struct SourceSnippet<'a> {
    files: &'a Files,
    file_id: FileId,
    span: Span,
    message: &'a str,
    mark_style: MarkStyle,
    notes: &'a [String],
}

impl<'a> SourceSnippet<'a> {
    pub fn new_primary(files: &'a Files, diagnostic: &'a Diagnostic) -> SourceSnippet<'a> {
        SourceSnippet {
            files,
            file_id: diagnostic.primary_label.file_id,
            span: diagnostic.primary_label.span,
            message: &diagnostic.primary_label.message,
            mark_style: MarkStyle::Primary(diagnostic.severity),
            notes: &diagnostic.notes,
        }
    }

    pub fn new_secondary(files: &'a Files, label: &'a Label) -> SourceSnippet<'a> {
        SourceSnippet {
            files,
            file_id: label.file_id,
            span: label.span,
            message: &label.message,
            mark_style: MarkStyle::Secondary,
            notes: &[],
        }
    }

    fn file_name(&self) -> &'a str {
        self.files.name(self.file_id)
    }

    fn location(&self, byte_index: ByteIndex) -> Result<Location, impl std::error::Error> {
        self.files.location(self.file_id, byte_index)
    }

    fn source_slice(&self, span: Span, tab: &'a str) -> Result<String, impl std::error::Error> {
        // NOTE: Not sure if we can do this more efficiently? Perhaps a custom
        // writer might be better?
        self.files
            .source_slice(self.file_id, span)
            .map(|s| s.replace('\t', tab))
    }

    fn line_span(&self, line_index: LineIndex) -> Result<Span, impl std::error::Error> {
        self.files.line_span(self.file_id, line_index)
    }

    pub fn emit(&self, writer: &mut impl WriteColor, config: &Config) -> io::Result<()> {
        let start = self.location(self.span.start()).expect("location_start");
        let end = self.location(self.span.end()).expect("location_end");
        let start_line_span = self.line_span(start.line).expect("start_line_span");
        let end_line_span = self.line_span(end.line).expect("end_line_span");

        let label_style = self.mark_style.label_style(config);
        // Use the length of the last line number as the gutter padding
        let gutter_padding = format!("{}", end.line.number()).len();
        // Cache the tabs we'll be using to pad the source strings.
        let tab = config.tab_padding();

        // Top left border and locus.
        //
        // ```text
        // ┌── test:2:9 ───
        // ```

        Gutter::new(None, gutter_padding).emit(writer, config)?;
        BorderTopLeft::new().emit(writer, config)?;
        BorderTop::new(2).emit(writer, config)?;
        write!(writer, " ")?;

        Locus::new(self.file_name(), start).emit(writer, config)?;

        write!(writer, " ")?;
        BorderTop::new(3).emit(writer, config)?;
        NewLine::new().emit(writer, config)?;

        // Code snippet
        //
        // ```text
        //   │
        // 2 │ (+ test "")
        //   │         ^^ expected `Int` but found `String`
        //   │
        // ```

        // Write initial border
        Gutter::new(None, gutter_padding).emit(writer, config)?;
        BorderLeft::new().emit(writer, config)?;
        NewLine::new().emit(writer, config)?;

        // Write underlined source section
        if start.line == end.line {
            // Single line
            //
            // ```text
            // 2 │ (+ test "")
            //   │         ^^ expected `Int` but found `String`
            // ```

            let prefix_source = {
                let span = Span::new(start_line_span.start(), self.span.start());
                self.source_slice(span, &tab).expect("prefix_source")
            };
            let highlighted_source = {
                let span = self.span;
                self.source_slice(span, &tab).expect("highlighted_source")
            };
            let suffix_source = {
                let span = Span::new(self.span.end(), end_line_span.end());
                self.source_slice(span, &tab).expect("suffix_source")
            };

            // Write line number and border
            Gutter::new(start.line.number(), gutter_padding).emit(writer, config)?;
            BorderLeft::new().emit(writer, config)?;

            // Write line source
            write!(writer, " {}", prefix_source)?;
            writer.set_color(label_style)?;
            write!(writer, "{}", highlighted_source)?;
            writer.reset()?;
            write!(writer, "{}", suffix_source.trim_end())?;

            NewLine::new().emit(writer, config)?;

            // Write border, underline, and label
            Gutter::new(None, gutter_padding).emit(writer, config)?;
            BorderLeft::new().emit(writer, config)?;
            Underline::new(
                self.mark_style,
                &prefix_source,
                &highlighted_source,
                self.message,
            )
            .emit(writer, config)?;
            NewLine::new().emit(writer, config)?;
        } else {
            // Multiple lines
            //
            // ```text
            // 4 │   fizz₁ num = case (mod num 5) (mod num 3) of
            //   │ ╭─────────────^
            // 5 │ │     0 0 => "FizzBuzz"
            // 6 │ │     0 _ => "Fizz"
            // 7 │ │     _ 0 => "Buzz"
            // 8 │ │     _ _ => num
            //   │ ╰──────────────^ `case` clauses have incompatible types
            // ```

            let prefix_source = {
                let span = Span::new(start_line_span.start(), self.span.start());
                self.source_slice(span, &tab).expect("prefix_source")
            };
            let highlighted_source = {
                let span = Span::new(self.span.start(), start_line_span.end());
                self.source_slice(span, &tab).expect("highlighted_source_1")
            };

            if prefix_source.trim().is_empty() {
                // Section is prefixed by empty space, so we don't need to take
                // up a new line.
                //
                // ```text
                // 4 │ ╭     case (mod num 5) (mod num 3) of
                // ```

                // Write line number, border, and underline
                Gutter::new(start.line.number(), gutter_padding).emit(writer, config)?;
                BorderLeft::new().emit(writer, config)?;
                UnderlineTopLeft::new(self.mark_style).emit(writer, config)?;

                // Write source line
                write!(writer, " {}", prefix_source)?;
                writer.set_color(&label_style)?;
                write!(writer, "{}", highlighted_source.trim_end())?;
                writer.reset()?;
                NewLine::new().emit(writer, config)?;
            } else {
                // There's source code in the prefix, so run an underline
                // underneath it to get to the start of the span.
                //
                // ```text
                // 4 │   fizz₁ num = case (mod num 5) (mod num 3) of
                //   │ ╭─────────────^
                // ```

                // Write line number and border
                Gutter::new(start.line.number(), gutter_padding).emit(writer, config)?;
                BorderLeft::new().emit(writer, config)?;

                // Write source line
                write!(writer, "   {}", prefix_source)?;
                writer.set_color(&label_style)?;
                write!(writer, "{}", highlighted_source.trim_end())?;
                writer.reset()?;
                NewLine::new().emit(writer, config)?;

                // Write border and underline
                Gutter::new(None, gutter_padding).emit(writer, config)?;
                BorderLeft::new().emit(writer, config)?;
                UnderlineTop::new(self.mark_style, &prefix_source).emit(writer, config)?;
                NewLine::new().emit(writer, config)?;
            }

            // Write highlighted lines
            //
            // ```text
            // 5 │ │     0 0 => "FizzBuzz"
            // 6 │ │     0 _ => "Fizz"
            // 7 │ │     _ 0 => "Buzz"
            // ```
            for line_index in ((start.line.to_usize() + 1)..end.line.to_usize())
                .map(|i| LineIndex::from(i as u32))
            {
                let highlighted_source = {
                    let span = self.line_span(line_index).expect("highlighted_span");
                    self.source_slice(span, &tab).expect("highlighted_source_2")
                };

                // Write line number, border, and underline
                Gutter::new(line_index.number(), gutter_padding).emit(writer, config)?;
                BorderLeft::new().emit(writer, config)?;
                UnderlineLeft::new(self.mark_style).emit(writer, config)?;

                // Write highlighted source
                writer.set_color(label_style)?;
                write!(writer, " {}", highlighted_source.trim_end())?;
                writer.reset()?;
                NewLine::new().emit(writer, config)?;
            }

            // Write last highlighted line
            //
            // ```text
            // 8 │ │     _ _ => num
            //   │ ╰──────────────^ `case` clauses have incompatible types
            // ```

            let highlighted_source = {
                let span = Span::new(end_line_span.start(), self.span.end());
                self.source_slice(span, &tab).expect("highlighted_source_3")
            };
            let suffix_source = {
                let span = Span::new(self.span.end(), end_line_span.end());
                self.source_slice(span, &tab).expect("suffix_source")
            };

            // Write line number, border, and underline
            Gutter::new(end.line.number(), gutter_padding).emit(writer, config)?;
            BorderLeft::new().emit(writer, config)?;
            UnderlineLeft::new(self.mark_style).emit(writer, config)?;

            // Write line source
            writer.set_color(label_style)?;
            write!(writer, " {}", highlighted_source)?;
            writer.reset()?;
            write!(writer, "{}", suffix_source.trim_end())?;
            NewLine::new().emit(writer, config)?;

            // Write border, underline, and label
            Gutter::new(None, gutter_padding).emit(writer, config)?;
            BorderLeft::new().emit(writer, config)?;
            UnderlineBottom::new(self.mark_style, &highlighted_source, self.message)
                .emit(writer, config)?;
            NewLine::new().emit(writer, config)?;
        };

        // Write final border
        Gutter::new(None, gutter_padding).emit(writer, config)?;
        BorderLeft::new().emit(writer, config)?;
        NewLine::new().emit(writer, config)?;

        // Additional notes
        //
        // ```text
        // = expected type `Int`
        //      found type `String`
        // ```

        for note in self.notes {
            Note::new(gutter_padding, note).emit(writer, config)?;
        }

        Ok(())
    }
}
<|endoftext|>