<commit_before>use std::env::var_os;

use output::Colours;
use output::{View, Mode, grid, details};
use output::table::{TimeTypes, Environment, SizeFormat, Options as TableOptions};
use output::file_name::{Classify, FileStyle};
use output::time::TimeFormat;

use options::{flags, Misfire};
use options::parser::Matches;

use fs::feature::xattr;
use info::filetype::FileExtensions;

impl View {

    /// Determine which view to use and all of that view’s arguments.
    pub fn deduce(matches: &Matches) -> Result<View, Misfire> {
        let mode = Mode::deduce(matches)?;
        let colours = Colours::deduce(matches)?;
        let style = FileStyle::deduce(matches);
        Ok(View { mode, colours, style })
    }
}

impl Mode {

    /// Determine the mode from the command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<Mode, Misfire> {
        use options::misfire::Misfire::*;

        let long = || {
            if matches.has(&flags::ACROSS) && !matches.has(&flags::GRID) {
                Err(Useless(&flags::ACROSS, true, &flags::LONG))
            }
            else if matches.has(&flags::ONE_LINE) {
                Err(Useless(&flags::ONE_LINE, true, &flags::LONG))
            }
            else {
                Ok(details::Options {
                    table: Some(TableOptions::deduce(matches)?),
                    header: matches.has(&flags::HEADER),
                    xattr: xattr::ENABLED && matches.has(&flags::EXTENDED),
                })
            }
        };

        let long_options_scan = || {
            for option in &[ &flags::BINARY, &flags::BYTES, &flags::INODE, &flags::LINKS,
                             &flags::HEADER, &flags::BLOCKS, &flags::TIME, &flags::GROUP ] {
                if matches.has(option) {
                    return Err(Useless(*option, false, &flags::LONG));
                }
            }

            if cfg!(feature="git") && matches.has(&flags::GIT) {
                Err(Useless(&flags::GIT, false, &flags::LONG))
            }
            else if matches.has(&flags::LEVEL) && !matches.has(&flags::RECURSE) && !matches.has(&flags::TREE) {
                Err(Useless2(&flags::LEVEL, &flags::RECURSE, &flags::TREE))
            }
            else if xattr::ENABLED && matches.has(&flags::EXTENDED) {
                Err(Useless(&flags::EXTENDED, false, &flags::LONG))
            }
            else {
                Ok(())
            }
        };

        let other_options_scan = || {
            if let Some(width) = TerminalWidth::deduce()?.width() {
                if matches.has(&flags::ONE_LINE) {
                    if matches.has(&flags::ACROSS) {
                        Err(Useless(&flags::ACROSS, true, &flags::ONE_LINE))
                    }
                    else {
                        Ok(Mode::Lines)
                    }
                }
                else if matches.has(&flags::TREE) {
                    let details = details::Options {
                        table: None,
                        header: false,
                        xattr: false,
                    };

                    Ok(Mode::Details(details))
                }
                else {
                    let grid = grid::Options {
                        across: matches.has(&flags::ACROSS),
                        console_width: width,
                    };

                    Ok(Mode::Grid(grid))
                }
            }
            else {
                // If the terminal width couldn’t be matched for some reason, such
                // as the program’s stdout being connected to a file, then
                // fallback to the lines view.

                if matches.has(&flags::TREE) {
                    let details = details::Options {
                        table: None,
                        header: false,
                        xattr: false,
                    };

                    Ok(Mode::Details(details))
                }
                else {
                    Ok(Mode::Lines)
                }
            }
        };

        if matches.has(&flags::LONG) {
            let details = long()?;
            if matches.has(&flags::GRID) {
                match other_options_scan()? {
                    Mode::Grid(grid)  => return Ok(Mode::GridDetails(grid, details)),
                    others            => return Ok(others),
                };
            }
            else {
                return Ok(Mode::Details(details));
            }
        }

        long_options_scan()?;

        other_options_scan()
    }
}

/// The width of the terminal requested by the user.
#[derive(PartialEq, Debug)]
enum TerminalWidth {

    /// The user requested this specific number of columns.
    Set(usize),

    /// The terminal was found to have this number of columns.
    Terminal(usize),

    /// The user didn’t request any particular terminal width.
    Unset,
}

impl TerminalWidth {

    /// Determine a requested terminal width from the command-line arguments.
    ///
    /// Returns an error if a requested width doesn’t parse to an integer.
    fn deduce() -> Result<TerminalWidth, Misfire> {
        if let Some(columns) = var_os("COLUMNS").and_then(|s| s.into_string().ok()) {
            match columns.parse() {
                Ok(width)  => Ok(TerminalWidth::Set(width)),
                Err(e)     => Err(Misfire::FailedParse(e)),
            }
        }
        else if let Some(width) = *TERM_WIDTH {
            Ok(TerminalWidth::Terminal(width))
        }
        else {
            Ok(TerminalWidth::Unset)
        }
    }

    fn width(&self) -> Option<usize> {
        match *self {
            TerminalWidth::Set(width)       |
            TerminalWidth::Terminal(width)  => Some(width),
            TerminalWidth::Unset            => None,
        }
    }
}

impl TableOptions {
    fn deduce(matches: &Matches) -> Result<Self, Misfire> {
        Ok(TableOptions {
            env:         Environment::load_all(),
            time_format: TimeFormat::deduce(matches)?,
            size_format: SizeFormat::deduce(matches)?,
            time_types:  TimeTypes::deduce(matches)?,
            inode:  matches.has(&flags::INODE),
            links:  matches.has(&flags::LINKS),
            blocks: matches.has(&flags::BLOCKS),
            group:  matches.has(&flags::GROUP),
            git:    cfg!(feature="git") && matches.has(&flags::GIT),
        })
    }
}

impl SizeFormat {

    /// Determine which file size to use in the file size column based on
    /// the user’s options.
    ///
    /// The default mode is to use the decimal prefixes, as they are the
    /// most commonly-understood, and don’t involve trying to parse large
    /// strings of digits in your head. Changing the format to anything else
    /// involves the `--binary` or `--bytes` flags, and these conflict with
    /// each other.
    fn deduce(matches: &Matches) -> Result<SizeFormat, Misfire> {
        let binary = matches.has(&flags::BINARY);
        let bytes  = matches.has(&flags::BYTES);

        match (binary, bytes) {
            (true,  true )  => Err(Misfire::Conflict(&flags::BINARY, &flags::BYTES)),
            (true,  false)  => Ok(SizeFormat::BinaryBytes),
            (false, true )  => Ok(SizeFormat::JustBytes),
            (false, false)  => Ok(SizeFormat::DecimalBytes),
        }
    }
}

impl TimeFormat {

    /// Determine how time should be formatted in timestamp columns.
    fn deduce(matches: &Matches) -> Result<TimeFormat, Misfire> {
        pub use output::time::{DefaultFormat, ISOFormat};
        const STYLES: &[&str] = &["default", "long-iso", "full-iso", "iso"];

        let word = match matches.get(&flags::TIME_STYLE) {
            Some(w) => w,
            None    => return Ok(TimeFormat::DefaultFormat(DefaultFormat::new())),
        };

        if word == "default" {
            Ok(TimeFormat::DefaultFormat(DefaultFormat::new()))
        }
        else if word == "iso" {
            Ok(TimeFormat::ISOFormat(ISOFormat::new()))
        }
        else if word == "long-iso" {
            Ok(TimeFormat::LongISO)
        }
        else if word == "full-iso" {
            Ok(TimeFormat::FullISO)
        }
        else {
            Err(Misfire::bad_argument(&flags::TIME_STYLE, word, STYLES))
        }
    }
}

impl TimeTypes {

    /// Determine which of a file’s time fields should be displayed for it
    /// based on the user’s options.
    ///
    /// There are two separate ways to pick which fields to show: with a
    /// flag (such as `--modified`) or with a parameter (such as
    /// `--time=modified`). An error is signaled if both ways are used.
    ///
    /// It’s valid to show more than one column by passing in more than one
    /// option, but passing *no* options means that the user just wants to
    /// see the default set.
    fn deduce(matches: &Matches) -> Result<TimeTypes, Misfire> {
        let possible_word = matches.get(&flags::TIME);
        let modified = matches.has(&flags::MODIFIED);
        let created  = matches.has(&flags::CREATED);
        let accessed = matches.has(&flags::ACCESSED);

        if let Some(word) = possible_word {
            if modified {
                return Err(Misfire::Useless(&flags::MODIFIED, true, &flags::TIME));
            }
            else if created {
                return Err(Misfire::Useless(&flags::CREATED, true, &flags::TIME));
            }
            else if accessed {
                return Err(Misfire::Useless(&flags::ACCESSED, true, &flags::TIME));
            }

            static TIMES: &[&str] = &["modified", "accessed", "created"];
            if word == "mod" || word == "modified" {
                Ok(TimeTypes { accessed: false, modified: true,  created: false })
            }
            else if word == "acc" || word == "accessed" {
                Ok(TimeTypes { accessed: true,  modified: false, created: false })
            }
            else if word == "cr" || word == "created" {
                Ok(TimeTypes { accessed: false, modified: false, created: true  })
            }
            else {
                Err(Misfire::bad_argument(&flags::TIME, word, TIMES))
            }
        }
        else if modified || created || accessed {
            Ok(TimeTypes { accessed, modified, created })
        }
        else {
            Ok(TimeTypes::default())
        }
    }
}

/// Under what circumstances we should display coloured, rather than plain,
/// output to the terminal.
///
/// By default, we want to display the colours when stdout can display them.
/// Turning them on when output is going to, say, a pipe, would make programs
/// such as `grep` or `more` not work properly. So the `Automatic` mode does
/// this check and only displays colours when they can be truly appreciated.
#[derive(PartialEq, Debug)]
enum TerminalColours {

    /// Display them even when output isn’t going to a terminal.
    Always,

    /// Display them when output is going to a terminal, but not otherwise.
    Automatic,

    /// Never display them, even when output is going to a terminal.
    Never,
}

impl Default for TerminalColours {
    fn default() -> TerminalColours {
        TerminalColours::Automatic
    }
}

impl TerminalColours {

    /// Determine which terminal colour conditions to use.
    fn deduce(matches: &Matches) -> Result<TerminalColours, Misfire> {
        const COLOURS: &[&str] = &["always", "auto", "never"];

        let word = match matches.get(&flags::COLOR).or_else(|| matches.get(&flags::COLOUR)) {
            Some(w) => w,
            None    => return Ok(TerminalColours::default()),
        };

        if word == "always" {
            Ok(TerminalColours::Always)
        }
        else if word == "auto" || word == "automatic" {
            Ok(TerminalColours::Automatic)
        }
        else if word == "never" {
            Ok(TerminalColours::Never)
        }
        else {
            Err(Misfire::bad_argument(&flags::COLOR, word, COLOURS))
        }
    }
}

impl Colours {
    fn deduce(matches: &Matches) -> Result<Colours, Misfire> {
        use self::TerminalColours::*;

        let tc = TerminalColours::deduce(matches)?;
        if tc == Always || (tc == Automatic && TERM_WIDTH.is_some()) {
            let scale = matches.has(&flags::COLOR_SCALE) || matches.has(&flags::COLOUR_SCALE);
            Ok(Colours::colourful(scale))
        }
        else {
            Ok(Colours::plain())
        }
    }
}

impl FileStyle {
    fn deduce(matches: &Matches) -> FileStyle {
        let classify = Classify::deduce(matches);
        let exts = FileExtensions;
        FileStyle { classify, exts }
    }
}

impl Classify {
    fn deduce(matches: &Matches) -> Classify {
        if matches.has(&flags::CLASSIFY) { Classify::AddFileIndicators }
                                    else { Classify::JustFilenames }
    }
}

// Gets, then caches, the width of the terminal that exa is running in.
// This gets used multiple times above, with no real guarantee of order,
// so it’s easier to just cache it the first time it runs.
lazy_static! {
    static ref TERM_WIDTH: Option<usize> = {
        use term::dimensions;
        dimensions().map(|t| t.0)
    };
}
<commit_msg>Add tests for size format<commit_after>use std::env::var_os;

use output::Colours;
use output::{View, Mode, grid, details};
use output::table::{TimeTypes, Environment, SizeFormat, Options as TableOptions};
use output::file_name::{Classify, FileStyle};
use output::time::TimeFormat;

use options::{flags, Misfire};
use options::parser::Matches;

use fs::feature::xattr;
use info::filetype::FileExtensions;

impl View {

    /// Determine which view to use and all of that view’s arguments.
    pub fn deduce(matches: &Matches) -> Result<View, Misfire> {
        let mode = Mode::deduce(matches)?;
        let colours = Colours::deduce(matches)?;
        let style = FileStyle::deduce(matches);
        Ok(View { mode, colours, style })
    }
}

impl Mode {

    /// Determine the mode from the command-line arguments.
    pub fn deduce(matches: &Matches) -> Result<Mode, Misfire> {
        use options::misfire::Misfire::*;

        let long = || {
            if matches.has(&flags::ACROSS) && !matches.has(&flags::GRID) {
                Err(Useless(&flags::ACROSS, true, &flags::LONG))
            }
            else if matches.has(&flags::ONE_LINE) {
                Err(Useless(&flags::ONE_LINE, true, &flags::LONG))
            }
            else {
                Ok(details::Options {
                    table: Some(TableOptions::deduce(matches)?),
                    header: matches.has(&flags::HEADER),
                    xattr: xattr::ENABLED && matches.has(&flags::EXTENDED),
                })
            }
        };

        let long_options_scan = || {
            for option in &[ &flags::BINARY, &flags::BYTES, &flags::INODE, &flags::LINKS,
                             &flags::HEADER, &flags::BLOCKS, &flags::TIME, &flags::GROUP ] {
                if matches.has(option) {
                    return Err(Useless(*option, false, &flags::LONG));
                }
            }

            if cfg!(feature="git") && matches.has(&flags::GIT) {
                Err(Useless(&flags::GIT, false, &flags::LONG))
            }
            else if matches.has(&flags::LEVEL) && !matches.has(&flags::RECURSE) && !matches.has(&flags::TREE) {
                Err(Useless2(&flags::LEVEL, &flags::RECURSE, &flags::TREE))
            }
            else if xattr::ENABLED && matches.has(&flags::EXTENDED) {
                Err(Useless(&flags::EXTENDED, false, &flags::LONG))
            }
            else {
                Ok(())
            }
        };

        let other_options_scan = || {
            if let Some(width) = TerminalWidth::deduce()?.width() {
                if matches.has(&flags::ONE_LINE) {
                    if matches.has(&flags::ACROSS) {
                        Err(Useless(&flags::ACROSS, true, &flags::ONE_LINE))
                    }
                    else {
                        Ok(Mode::Lines)
                    }
                }
                else if matches.has(&flags::TREE) {
                    let details = details::Options {
                        table: None,
                        header: false,
                        xattr: false,
                    };

                    Ok(Mode::Details(details))
                }
                else {
                    let grid = grid::Options {
                        across: matches.has(&flags::ACROSS),
                        console_width: width,
                    };

                    Ok(Mode::Grid(grid))
                }
            }
            else {
                // If the terminal width couldn’t be matched for some reason, such
                // as the program’s stdout being connected to a file, then
                // fallback to the lines view.

                if matches.has(&flags::TREE) {
                    let details = details::Options {
                        table: None,
                        header: false,
                        xattr: false,
                    };

                    Ok(Mode::Details(details))
                }
                else {
                    Ok(Mode::Lines)
                }
            }
        };

        if matches.has(&flags::LONG) {
            let details = long()?;
            if matches.has(&flags::GRID) {
                match other_options_scan()? {
                    Mode::Grid(grid)  => return Ok(Mode::GridDetails(grid, details)),
                    others            => return Ok(others),
                };
            }
            else {
                return Ok(Mode::Details(details));
            }
        }

        long_options_scan()?;

        other_options_scan()
    }
}

/// The width of the terminal requested by the user.
#[derive(PartialEq, Debug)]
enum TerminalWidth {

    /// The user requested this specific number of columns.
    Set(usize),

    /// The terminal was found to have this number of columns.
    Terminal(usize),

    /// The user didn’t request any particular terminal width.
    Unset,
}

impl TerminalWidth {

    /// Determine a requested terminal width from the command-line arguments.
    ///
    /// Returns an error if a requested width doesn’t parse to an integer.
    fn deduce() -> Result<TerminalWidth, Misfire> {
        if let Some(columns) = var_os("COLUMNS").and_then(|s| s.into_string().ok()) {
            match columns.parse() {
                Ok(width)  => Ok(TerminalWidth::Set(width)),
                Err(e)     => Err(Misfire::FailedParse(e)),
            }
        }
        else if let Some(width) = *TERM_WIDTH {
            Ok(TerminalWidth::Terminal(width))
        }
        else {
            Ok(TerminalWidth::Unset)
        }
    }

    fn width(&self) -> Option<usize> {
        match *self {
            TerminalWidth::Set(width)       |
            TerminalWidth::Terminal(width)  => Some(width),
            TerminalWidth::Unset            => None,
        }
    }
}

impl TableOptions {
    fn deduce(matches: &Matches) -> Result<Self, Misfire> {
        Ok(TableOptions {
            env:         Environment::load_all(),
            time_format: TimeFormat::deduce(matches)?,
            size_format: SizeFormat::deduce(matches)?,
            time_types:  TimeTypes::deduce(matches)?,
            inode:  matches.has(&flags::INODE),
            links:  matches.has(&flags::LINKS),
            blocks: matches.has(&flags::BLOCKS),
            group:  matches.has(&flags::GROUP),
            git:    cfg!(feature="git") && matches.has(&flags::GIT),
        })
    }
}

impl SizeFormat {

    /// Determine which file size to use in the file size column based on
    /// the user’s options.
    ///
    /// The default mode is to use the decimal prefixes, as they are the
    /// most commonly-understood, and don’t involve trying to parse large
    /// strings of digits in your head. Changing the format to anything else
    /// involves the `--binary` or `--bytes` flags, and these conflict with
    /// each other.
    fn deduce(matches: &Matches) -> Result<SizeFormat, Misfire> {
        let binary = matches.has(&flags::BINARY);
        let bytes  = matches.has(&flags::BYTES);

        match (binary, bytes) {
            (true,  true )  => Err(Misfire::Conflict(&flags::BINARY, &flags::BYTES)),
            (true,  false)  => Ok(SizeFormat::BinaryBytes),
            (false, true )  => Ok(SizeFormat::JustBytes),
            (false, false)  => Ok(SizeFormat::DecimalBytes),
        }
    }
}

impl TimeFormat {

    /// Determine how time should be formatted in timestamp columns.
    fn deduce(matches: &Matches) -> Result<TimeFormat, Misfire> {
        pub use output::time::{DefaultFormat, ISOFormat};
        const STYLES: &[&str] = &["default", "long-iso", "full-iso", "iso"];

        let word = match matches.get(&flags::TIME_STYLE) {
            Some(w) => w,
            None    => return Ok(TimeFormat::DefaultFormat(DefaultFormat::new())),
        };

        if word == "default" {
            Ok(TimeFormat::DefaultFormat(DefaultFormat::new()))
        }
        else if word == "iso" {
            Ok(TimeFormat::ISOFormat(ISOFormat::new()))
        }
        else if word == "long-iso" {
            Ok(TimeFormat::LongISO)
        }
        else if word == "full-iso" {
            Ok(TimeFormat::FullISO)
        }
        else {
            Err(Misfire::bad_argument(&flags::TIME_STYLE, word, STYLES))
        }
    }
}

impl TimeTypes {

    /// Determine which of a file’s time fields should be displayed for it
    /// based on the user’s options.
    ///
    /// There are two separate ways to pick which fields to show: with a
    /// flag (such as `--modified`) or with a parameter (such as
    /// `--time=modified`). An error is signaled if both ways are used.
    ///
    /// It’s valid to show more than one column by passing in more than one
    /// option, but passing *no* options means that the user just wants to
    /// see the default set.
    fn deduce(matches: &Matches) -> Result<TimeTypes, Misfire> {
        let possible_word = matches.get(&flags::TIME);
        let modified = matches.has(&flags::MODIFIED);
        let created  = matches.has(&flags::CREATED);
        let accessed = matches.has(&flags::ACCESSED);

        if let Some(word) = possible_word {
            if modified {
                return Err(Misfire::Useless(&flags::MODIFIED, true, &flags::TIME));
            }
            else if created {
                return Err(Misfire::Useless(&flags::CREATED, true, &flags::TIME));
            }
            else if accessed {
                return Err(Misfire::Useless(&flags::ACCESSED, true, &flags::TIME));
            }

            static TIMES: &[&str] = &["modified", "accessed", "created"];
            if word == "mod" || word == "modified" {
                Ok(TimeTypes { accessed: false, modified: true,  created: false })
            }
            else if word == "acc" || word == "accessed" {
                Ok(TimeTypes { accessed: true,  modified: false, created: false })
            }
            else if word == "cr" || word == "created" {
                Ok(TimeTypes { accessed: false, modified: false, created: true  })
            }
            else {
                Err(Misfire::bad_argument(&flags::TIME, word, TIMES))
            }
        }
        else if modified || created || accessed {
            Ok(TimeTypes { accessed, modified, created })
        }
        else {
            Ok(TimeTypes::default())
        }
    }
}

/// Under what circumstances we should display coloured, rather than plain,
/// output to the terminal.
///
/// By default, we want to display the colours when stdout can display them.
/// Turning them on when output is going to, say, a pipe, would make programs
/// such as `grep` or `more` not work properly. So the `Automatic` mode does
/// this check and only displays colours when they can be truly appreciated.
#[derive(PartialEq, Debug)]
enum TerminalColours {

    /// Display them even when output isn’t going to a terminal.
    Always,

    /// Display them when output is going to a terminal, but not otherwise.
    Automatic,

    /// Never display them, even when output is going to a terminal.
    Never,
}

impl Default for TerminalColours {
    fn default() -> TerminalColours {
        TerminalColours::Automatic
    }
}

impl TerminalColours {

    /// Determine which terminal colour conditions to use.
    fn deduce(matches: &Matches) -> Result<TerminalColours, Misfire> {
        const COLOURS: &[&str] = &["always", "auto", "never"];

        let word = match matches.get(&flags::COLOR).or_else(|| matches.get(&flags::COLOUR)) {
            Some(w) => w,
            None    => return Ok(TerminalColours::default()),
        };

        if word == "always" {
            Ok(TerminalColours::Always)
        }
        else if word == "auto" || word == "automatic" {
            Ok(TerminalColours::Automatic)
        }
        else if word == "never" {
            Ok(TerminalColours::Never)
        }
        else {
            Err(Misfire::bad_argument(&flags::COLOR, word, COLOURS))
        }
    }
}

impl Colours {
    fn deduce(matches: &Matches) -> Result<Colours, Misfire> {
        use self::TerminalColours::*;

        let tc = TerminalColours::deduce(matches)?;
        if tc == Always || (tc == Automatic && TERM_WIDTH.is_some()) {
            let scale = matches.has(&flags::COLOR_SCALE) || matches.has(&flags::COLOUR_SCALE);
            Ok(Colours::colourful(scale))
        }
        else {
            Ok(Colours::plain())
        }
    }
}

impl FileStyle {
    fn deduce(matches: &Matches) -> FileStyle {
        let classify = Classify::deduce(matches);
        let exts = FileExtensions;
        FileStyle { classify, exts }
    }
}

impl Classify {
    fn deduce(matches: &Matches) -> Classify {
        if matches.has(&flags::CLASSIFY) { Classify::AddFileIndicators }
                                    else { Classify::JustFilenames }
    }
}

// Gets, then caches, the width of the terminal that exa is running in.
// This gets used multiple times above, with no real guarantee of order,
// so it’s easier to just cache it the first time it runs.
lazy_static! {
    static ref TERM_WIDTH: Option<usize> = {
        use term::dimensions;
        dimensions().map(|t| t.0)
    };
}

#[cfg(test)]
mod test {
    use super::*;
    use std::ffi::OsString;
    use options::flags;

    pub fn os(input: &'static str) -> OsString {
        let mut os = OsString::new();
        os.push(input);
        os
    }

    macro_rules! test {
        ($name:ident: $type:ident <- $inputs:expr => $result:expr) => {
            #[test]
            fn $name() {
                use options::parser::{parse, Args, Arg};
                use std::ffi::OsString;

                static TEST_ARGS: &[&Arg] = &[ &flags::BINARY, &flags::BYTES ];

                let bits = $inputs.as_ref().into_iter().map(|&o| os(o)).collect::<Vec<OsString>>();
                let results = parse(&Args(TEST_ARGS), bits.iter());
                assert_eq!($type::deduce(results.as_ref().unwrap()), $result);
            }
        };
    }

    mod size_formats {
        use super::*;

        test!(empty:   SizeFormat <- []                       => Ok(SizeFormat::DecimalBytes));
        test!(binary:  SizeFormat <- ["--binary"]             => Ok(SizeFormat::BinaryBytes));
        test!(bytes:   SizeFormat <- ["--bytes"]              => Ok(SizeFormat::JustBytes));
        test!(both:    SizeFormat <- ["--binary", "--bytes"]  => Err(Misfire::Conflict(&flags::BINARY, &flags::BYTES)));
    }
}
<|endoftext|>


<commit_before>//! `completions` crate provides utilities for generating completions of user input.

mod config;
mod item;
mod context;
mod patterns;
mod generated_lint_completions;
#[cfg(test)]
mod test_utils;
mod render;

mod completions;

use ide_db::base_db::FilePosition;
use ide_db::RootDatabase;

use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};

pub use crate::{
    config::CompletionConfig,
    item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
};

//FIXME: split the following feature into fine-grained features.

// Feature: Magic Completions
//
// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
// completions as well:
//
// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
// is placed at the appropriate position. Even though `if` is easy to type, you
// still want to complete it, to get ` { }` for free! `return` is inserted with a
// space or `;` depending on the return type of the function.
//
// When completing a function call, `()` are automatically inserted. If a function
// takes arguments, the cursor is positioned inside the parenthesis.
//
// There are postfix completions, which can be triggered by typing something like
// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
//
// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
// - `expr.match` -> `match expr {}`
// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
// - `expr.ref` -> `&expr`
// - `expr.refm` -> `&mut expr`
// - `expr.not` -> `!expr`
// - `expr.dbg` -> `dbg!(expr)`
// - `expr.dbgr` -> `dbg!(&expr)`
// - `expr.call` -> `(expr)`
//
// There also snippet completions:
//
// .Expressions
// - `pd` -> `eprintln!(" = {:?}", );`
// - `ppd` -> `eprintln!(" = {:#?}", );`
//
// .Items
// - `tfn` -> `#[test] fn feature(){}`
// - `tmod` ->
// ```rust
// #[cfg(test)]
// mod tests {
//     use super::*;
//
//     #[test]
//     fn test_name() {}
// }
// ```

/// Main entry point for completion. We run completion as a two-phase process.
///
/// First, we look at the position and collect a so-called `CompletionContext.
/// This is a somewhat messy process, because, during completion, syntax tree is
/// incomplete and can look really weird.
///
/// Once the context is collected, we run a series of completion routines which
/// look at the context and produce completion items. One subtlety about this
/// phase is that completion engine should not filter by the substring which is
/// already present, it should give all possible variants for the identifier at
/// the caret. In other words, for
///
/// ```no_run
/// fn f() {
///     let foo = 92;
///     let _ = bar<|>
/// }
/// ```
///
/// `foo` *should* be present among the completion variants. Filtering by
/// identifier prefix/fuzzy match should be done higher in the stack, together
/// with ordering of completions (currently this is done by the client).
pub fn completions(
    db: &RootDatabase,
    config: &CompletionConfig,
    position: FilePosition,
) -> Option<Completions> {
    let ctx = CompletionContext::new(db, position, config)?;

    if ctx.no_completion_required() {
        // No work required here.
        return None;
    }

    let mut acc = Completions::default();
    completions::attribute::complete_attribute(&mut acc, &ctx);
    completions::fn_param::complete_fn_param(&mut acc, &ctx);
    completions::keyword::complete_expr_keyword(&mut acc, &ctx);
    completions::keyword::complete_use_tree_keyword(&mut acc, &ctx);
    completions::snippet::complete_expr_snippet(&mut acc, &ctx);
    completions::snippet::complete_item_snippet(&mut acc, &ctx);
    completions::qualified_path::complete_qualified_path(&mut acc, &ctx);
    completions::unqualified_path::complete_unqualified_path(&mut acc, &ctx);
    completions::dot::complete_dot(&mut acc, &ctx);
    completions::record::complete_record(&mut acc, &ctx);
    completions::pattern::complete_pattern(&mut acc, &ctx);
    completions::postfix::complete_postfix(&mut acc, &ctx);
    completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
    completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
    completions::mod_::complete_mod(&mut acc, &ctx);

    Some(acc)
}

#[cfg(test)]
mod tests {
    use crate::config::CompletionConfig;
    use crate::test_utils;

    struct DetailAndDocumentation<'a> {
        detail: &'a str,
        documentation: &'a str,
    }

    fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
        let (db, position) = test_utils::position(ra_fixture);
        let config = CompletionConfig::default();
        let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into();
        for item in completions {
            if item.detail() == Some(expected.detail) {
                let opt = item.documentation();
                let doc = opt.as_ref().map(|it| it.as_str());
                assert_eq!(doc, Some(expected.documentation));
                return;
            }
        }
        panic!("completion detail not found: {}", expected.detail)
    }

    fn check_no_completion(ra_fixture: &str) {
        let (db, position) = test_utils::position(ra_fixture);
        let config = CompletionConfig::default();

        let completions: Option<Vec<String>> = crate::completions(&db, &config, position)
            .and_then(|completions| {
                let completions: Vec<_> = completions.into();
                if completions.is_empty() {
                    None
                } else {
                    Some(completions)
                }
            })
            .map(|completions| {
                completions.into_iter().map(|completion| format!("{:?}", completion)).collect()
            });

        // `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic.
        assert_eq!(completions, None, "Completions were generated, but weren't expected");
    }

    #[test]
    fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
        check_detail_and_documentation(
            r#"
            //- /lib.rs
            macro_rules! bar {
                () => {
                    struct Bar;
                    impl Bar {
                        #[doc = "Do the foo"]
                        fn foo(&self) {}
                    }
                }
            }

            bar!();

            fn foo() {
                let bar = Bar;
                bar.fo<|>;
            }
            "#,
            DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
        );
    }

    #[test]
    fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
        check_detail_and_documentation(
            r#"
            //- /lib.rs
            macro_rules! bar {
                () => {
                    struct Bar;
                    impl Bar {
                        /// Do the foo
                        fn foo(&self) {}
                    }
                }
            }

            bar!();

            fn foo() {
                let bar = Bar;
                bar.fo<|>;
            }
            "#,
            DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
        );
    }

    #[test]
    fn test_no_completions_required() {
        // There must be no hint for 'in' keyword.
        check_no_completion(
            r#"
            fn foo() {
                for i i<|>
            }
            "#,
        );
        // After 'in' keyword hints may be spawned.
        check_detail_and_documentation(
            r#"
            /// Do the foo
            fn foo() -> &'static str { "foo" }

            fn bar() {
                for c in fo<|>
            }
            "#,
            DetailAndDocumentation {
                detail: "fn foo() -> &'static str",
                documentation: "Do the foo",
            },
        );
    }
}
<commit_msg>Document experimental completions<commit_after>//! `completions` crate provides utilities for generating completions of user input.

mod config;
mod item;
mod context;
mod patterns;
mod generated_lint_completions;
#[cfg(test)]
mod test_utils;
mod render;

mod completions;

use ide_db::base_db::FilePosition;
use ide_db::RootDatabase;

use crate::{completions::Completions, context::CompletionContext, item::CompletionKind};

pub use crate::{
    config::CompletionConfig,
    item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
};

//FIXME: split the following feature into fine-grained features.

// Feature: Magic Completions
//
// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
// completions as well:
//
// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
// is placed at the appropriate position. Even though `if` is easy to type, you
// still want to complete it, to get ` { }` for free! `return` is inserted with a
// space or `;` depending on the return type of the function.
//
// When completing a function call, `()` are automatically inserted. If a function
// takes arguments, the cursor is positioned inside the parenthesis.
//
// There are postfix completions, which can be triggered by typing something like
// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
//
// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
// - `expr.match` -> `match expr {}`
// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
// - `expr.ref` -> `&expr`
// - `expr.refm` -> `&mut expr`
// - `expr.not` -> `!expr`
// - `expr.dbg` -> `dbg!(expr)`
// - `expr.dbgr` -> `dbg!(&expr)`
// - `expr.call` -> `(expr)`
//
// There also snippet completions:
//
// .Expressions
// - `pd` -> `eprintln!(" = {:?}", );`
// - `ppd` -> `eprintln!(" = {:#?}", );`
//
// .Items
// - `tfn` -> `#[test] fn feature(){}`
// - `tmod` ->
// ```rust
// #[cfg(test)]
// mod tests {
//     use super::*;
//
//     #[test]
//     fn test_name() {}
// }
// ```
//
// And experimental completions, enabled with the `rust-analyzer.completion.enableExperimental` setting.
// This flag enables or disables:
//
// - Auto import: additional completion options with automatic `use` import and options from all project importable items, matched for the input
//
// Experimental completions might cause issues with performance and completion list look.

/// Main entry point for completion. We run completion as a two-phase process.
///
/// First, we look at the position and collect a so-called `CompletionContext.
/// This is a somewhat messy process, because, during completion, syntax tree is
/// incomplete and can look really weird.
///
/// Once the context is collected, we run a series of completion routines which
/// look at the context and produce completion items. One subtlety about this
/// phase is that completion engine should not filter by the substring which is
/// already present, it should give all possible variants for the identifier at
/// the caret. In other words, for
///
/// ```no_run
/// fn f() {
///     let foo = 92;
///     let _ = bar<|>
/// }
/// ```
///
/// `foo` *should* be present among the completion variants. Filtering by
/// identifier prefix/fuzzy match should be done higher in the stack, together
/// with ordering of completions (currently this is done by the client).
pub fn completions(
    db: &RootDatabase,
    config: &CompletionConfig,
    position: FilePosition,
) -> Option<Completions> {
    let ctx = CompletionContext::new(db, position, config)?;

    if ctx.no_completion_required() {
        // No work required here.
        return None;
    }

    let mut acc = Completions::default();
    completions::attribute::complete_attribute(&mut acc, &ctx);
    completions::fn_param::complete_fn_param(&mut acc, &ctx);
    completions::keyword::complete_expr_keyword(&mut acc, &ctx);
    completions::keyword::complete_use_tree_keyword(&mut acc, &ctx);
    completions::snippet::complete_expr_snippet(&mut acc, &ctx);
    completions::snippet::complete_item_snippet(&mut acc, &ctx);
    completions::qualified_path::complete_qualified_path(&mut acc, &ctx);
    completions::unqualified_path::complete_unqualified_path(&mut acc, &ctx);
    completions::dot::complete_dot(&mut acc, &ctx);
    completions::record::complete_record(&mut acc, &ctx);
    completions::pattern::complete_pattern(&mut acc, &ctx);
    completions::postfix::complete_postfix(&mut acc, &ctx);
    completions::macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
    completions::trait_impl::complete_trait_impl(&mut acc, &ctx);
    completions::mod_::complete_mod(&mut acc, &ctx);

    Some(acc)
}

#[cfg(test)]
mod tests {
    use crate::config::CompletionConfig;
    use crate::test_utils;

    struct DetailAndDocumentation<'a> {
        detail: &'a str,
        documentation: &'a str,
    }

    fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
        let (db, position) = test_utils::position(ra_fixture);
        let config = CompletionConfig::default();
        let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into();
        for item in completions {
            if item.detail() == Some(expected.detail) {
                let opt = item.documentation();
                let doc = opt.as_ref().map(|it| it.as_str());
                assert_eq!(doc, Some(expected.documentation));
                return;
            }
        }
        panic!("completion detail not found: {}", expected.detail)
    }

    fn check_no_completion(ra_fixture: &str) {
        let (db, position) = test_utils::position(ra_fixture);
        let config = CompletionConfig::default();

        let completions: Option<Vec<String>> = crate::completions(&db, &config, position)
            .and_then(|completions| {
                let completions: Vec<_> = completions.into();
                if completions.is_empty() {
                    None
                } else {
                    Some(completions)
                }
            })
            .map(|completions| {
                completions.into_iter().map(|completion| format!("{:?}", completion)).collect()
            });

        // `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic.
        assert_eq!(completions, None, "Completions were generated, but weren't expected");
    }

    #[test]
    fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
        check_detail_and_documentation(
            r#"
            //- /lib.rs
            macro_rules! bar {
                () => {
                    struct Bar;
                    impl Bar {
                        #[doc = "Do the foo"]
                        fn foo(&self) {}
                    }
                }
            }

            bar!();

            fn foo() {
                let bar = Bar;
                bar.fo<|>;
            }
            "#,
            DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
        );
    }

    #[test]
    fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
        check_detail_and_documentation(
            r#"
            //- /lib.rs
            macro_rules! bar {
                () => {
                    struct Bar;
                    impl Bar {
                        /// Do the foo
                        fn foo(&self) {}
                    }
                }
            }

            bar!();

            fn foo() {
                let bar = Bar;
                bar.fo<|>;
            }
            "#,
            DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
        );
    }

    #[test]
    fn test_no_completions_required() {
        // There must be no hint for 'in' keyword.
        check_no_completion(
            r#"
            fn foo() {
                for i i<|>
            }
            "#,
        );
        // After 'in' keyword hints may be spawned.
        check_detail_and_documentation(
            r#"
            /// Do the foo
            fn foo() -> &'static str { "foo" }

            fn bar() {
                for c in fo<|>
            }
            "#,
            DetailAndDocumentation {
                detail: "fn foo() -> &'static str",
                documentation: "Do the foo",
            },
        );
    }
}
<|endoftext|>


<commit_before>use std::char;
use std::fmt;
use parser::token;

pub struct Lexer {
	pub curr:  char,
	pub pos: uint,
	pub src: String,
	pub eof: bool
}

impl Lexer {
	pub fn new(src: &str) -> Lexer {
		let mut l = Lexer {
			curr: '\0',
			pos: 0,
			src: src.to_string(),
			eof: false
		};
		if l.pos >= src.len() {
			l.eof = true;
		} else {
			l.curr = src.char_at(0);
		}
		l
	}
	pub fn next_token(&mut self) -> token::Token {
		//println!("eof status: {}", self.eof);
		if self.eof {
//			println!("EOF!!!!!");
			return token::EOF;
		}
		self.consume_whitespace();
		match self.curr {
			'(' => {self.bump(); token::LPAREN}
			')' => {self.bump(); token::RPAREN}
			c if char::is_digit(c) => {
				let start = self.pos;
				let mut end = start + 1;
				self.bump();
				while (char::is_digit(self.curr) || self.curr == '.') && !self.eof{
					self.bump();
					end += 1;
				}
				token::NUMBER(from_str(self.src.as_slice().slice(start,end)).unwrap())
			}
			c if char::is_alphabetic(c) => {
				let start = self.pos;
				let mut end = start + 1;
				self.bump();
				while (char::is_alphabetic(self.curr) && !self.eof) {
					self.bump();
					end += 1;
				}
				token::SYMBOL(self.src.as_slice().slice(start,end).to_owned())
			}
			'+' => {self.bump(); token::ADD}
			'-' => {self.bump(); token::SUB}
			'*' => {self.bump(); token::MUL}
			'/' => {self.bump(); token::DIV}
			'^' => {self.bump(); token::CARET}
			c => { fail!("unexpected token {} at postion {}", c, self.pos); }
		}
	}
	pub fn bump(&mut self) {
		//println!("bumping to pos: {} of {}", self.pos+1, self.src.as_slice().len());
		self.pos += 1;
		if self.pos >= self.src.as_slice().len() {
			self.eof = true;
			return;
		}
		self.curr = self.src.as_slice().char_at(self.pos);

	}

	pub fn consume_whitespace(&mut self) {
		while is_whitespace(self.curr) {
			self.bump();
		}
	}
}
pub fn is_whitespace(c: char) -> bool {
	match c {
		' ' | '\n' | '\t' => true,
		_ => false
	}
}

impl fmt::Show for Lexer {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "{}", self.src)
	}
}
<commit_msg>small improvements in Lexer<commit_after>use std::char;
use std::fmt;
use parser::token;

pub struct Lexer {
	pub curr:  char,
	pub pos: uint,
	pub src: String,
	pub eof: bool
}

impl Lexer {
	pub fn new(src: &str) -> Lexer {
		let mut l = Lexer {
			curr: '\0',
			pos: 0,
			src: src.to_string(),
			eof: false
		};
		if l.pos >= src.len() {
			l.eof = true;
		} else {
			l.curr = src.char_at(0);
		}
		l
	}
	pub fn next_token(&mut self) -> token::Token {
		//println!("eof status: {}", self.eof);
		if self.eof {
//			println!("EOF!!!!!");
			return token::EOF;
		}
		self.consume_whitespace();
		match self.curr {
			'(' => {self.bump(); token::LPAREN}
			')' => {self.bump(); token::RPAREN}
			c if char::is_digit(c) => {
				let start = self.pos;
				let mut end = start + 1;
				self.bump();
				while (char::is_digit(self.curr) || self.curr == '.') && !self.eof{
					self.bump();
					end += 1;
				}
				token::NUMBER(from_str(self.src.as_slice().slice(start,end)).unwrap())
			}
			c if char::is_alphabetic(c) => {
				let start = self.pos;
				let mut end = start + 1;
				self.bump();
				while char::is_alphabetic(self.curr) && !self.eof {
					self.bump();
					end += 1;
				}
				token::SYMBOL(self.src.as_slice().slice(start,end).to_string())
			}
			'+' => {self.bump(); token::ADD}
			'-' => {self.bump(); token::SUB}
			'*' => {self.bump(); token::MUL}
			'/' => {self.bump(); token::DIV}
			'^' => {self.bump(); token::CARET}
			c => { fail!("unexpected token {} at postion {}", c, self.pos); }
		}
	}
	pub fn bump(&mut self) {
		//println!("bumping to pos: {} of {}", self.pos+1, self.src.as_slice().len());
		self.pos += 1;
		if self.pos >= self.src.as_slice().len() {
			self.eof = true;
			return;
		}
		self.curr = self.src.as_slice().char_at(self.pos);

	}

	pub fn consume_whitespace(&mut self) {
		while is_whitespace(self.curr) {
			self.bump();
		}
	}
}
pub fn is_whitespace(c: char) -> bool {
	match c {
		' ' | '\n' | '\t' => true,
		_ => false
	}
}

impl fmt::Show for Lexer {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "{}", self.src)
	}
}
<|endoftext|>


<commit_before>//! # Parser Nodes
//!
//! Parser nodes represent the grammar that is defined
//! by the currently permissible set of commands and their
//! parameters.

use std::rc::Rc;

/// Minimum priority.
pub const PRIORITY_MINIMUM: i32 = -10000;
/// The default priority for a parameter.
pub const PRIORITY_PARAMETER: i32 = -10;
/// The default priority.
pub const PRIORITY_DEFAULT: i32 = 0;

/// A node in the tree of commands and their parameters
/// used by the `Parser`.
pub trait Node {
    /// The data describing this node.
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields;

    /// Nodes that are children of this node. Used to
    /// by the `Parser` during `advance`, `complete`, etc.
    fn successors(&self) -> &Vec<Rc<Node>> {
        &self.node_data().successors
    }

    /// The text used to identify this node in help text.
    /// This is typically the node name, either in plain
    /// form or decorated for parameters.
    fn help_symbol(&self) -> String {
        self.node_data().name.to_string()
    }

    /// Help text describing this node.
    fn help_text(&self) -> &Option<String> {
        unimplemented!();
    }

    /// Hidden nodes are still found for matching, but are
    /// hidden from completion.
    fn hidden(&self) -> bool {
        self.node_data().hidden
    }

    /// The name of this node.
    fn name(&self) -> &String {
        &self.node_data().name
    }

    /// This priority of this node during matching and completion.
    fn priority(&self) -> i32 {
        self.node_data().priority
    }
}

impl PartialEq for Node {
    /// Nodes are equal based on pointer equality.
    fn eq(&self, other: &Self) -> bool {
        self as *const _ == other as *const _
    }
}

/// A parse tree node.
#[doc(hidden)]
pub struct NodeFields {
    /// Possible successor nodes. Collected while building.
    successors: Vec<Rc<Node>>,
    /// The name of this node.
    name: String,
    /// Match and complete priority.
    priority: i32,
    /// Hidden nodes are not completed. This doesn't modify matching.
    hidden: bool,
}

/// The root of a command tree.
pub struct RootNode {
    fields: NodeFields,
}

impl Node for RootNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.fields
    }
}

/// A node representing a command.
pub struct CommandNode {
    fields: CommandNodeFields,
}

struct CommandNodeFields {
    node: NodeFields,
    help: Option<String>,
    handler: Option<fn(&node: Node) -> ()>,
    parameters: Vec<Rc<ParameterNode>>,
}

impl Node for CommandNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.fields.node
    }

    fn help_text(&self) -> &Option<String> {
        &self.fields.help
    }
}

impl CommandNode {
    /// The handler which is executed once this node has been accepted.
    pub fn handler(&self) -> Option<fn(&node: Node) -> ()> {
        self.fields.handler
    }

    /// Get the parameter nodes for this command.
    pub fn parameters(&self) -> &Vec<Rc<ParameterNode>> {
        &self.fields.parameters
    }
}

/// A wrapper node wraps another command.
///
/// This is used for the help command so that it can complete
/// normal commands.
///
/// The `successors` will be those of the wrapped node.
pub struct WrapperNode {
    fields: WrapperNodeFields,
}

struct WrapperNodeFields {
    command: CommandNodeFields,
    root: Rc<Node>,
}

impl Node for WrapperNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.fields.command.node
    }

    fn successors(&self) -> &Vec<Rc<Node>> {
        self.fields.root.successors()
    }
}

/// A repeatable node is an internal helper for representing
/// nodes that can be repeated, like some parameters.
pub trait RepeatableNode: Node {
    /// Internal data for a repeatable node.
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields;

    /// Whether or not this node can be repeated. A repeated
    /// node can be `accept`ed multiple times.
    fn repeatable(&self) -> bool {
        self.repeatable_data().repeatable
    }

    /// If present, this node will no longer be `acceptable`.
    fn repeat_marker(&self) -> &Option<Rc<Node>> {
        &self.repeatable_data().repeat_marker
    }
}

/// The data for a repeatable node.
#[doc(hidden)]
pub struct RepeatableNodeFields {
    repeatable: bool,
    repeat_marker: Option<Rc<Node>>,
}

/// A node that represented the name portion of a named
/// parameter.
pub struct ParameterNameNode {
    fields: ParameterNameNodeFields,
}

struct ParameterNameNodeFields {
    node: NodeFields,
    repeatable: RepeatableNodeFields,
    help: Option<String>,
    parameter: Rc<Node>,
}

impl Node for ParameterNameNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.fields.node
    }

    fn help_symbol(&self) -> String {
        self.fields.node.name.clone() + " " + self.fields.parameter.help_symbol().as_str()
    }

    fn help_text(&self) -> &Option<String> {
        &self.fields.help
    }
}

impl RepeatableNode for ParameterNameNode {
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields {
        &self.fields.repeatable
    }
}

/// Parameter nodes.
pub trait ParameterNode {
    /// Internal data for a parameter node.
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields;

    /// A `required` parameter must be supplied for the
    /// command line being parsed to be valid.
    fn required(&self) -> bool {
        self.parameter_data().required
    }
}

impl RepeatableNode for ParameterNode {
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields {
        &self.parameter_data().repeatable
    }
}

/// Data for parameter nodes.
#[doc(hidden)]
pub struct ParameterNodeFields {
    node: NodeFields,
    repeatable: RepeatableNodeFields,
    help: Option<String>,
    required: bool,
}

impl Node for ParameterNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.parameter_data().node
    }

    fn help_symbol(&self) -> String {
        String::from("<") + self.node_data().name.as_str() +
        if self.repeatable() {
            ">..."
        } else {
            ">"
        }
    }

    fn help_text(&self) -> &Option<String> {
        &self.parameter_data().help
    }
}

/// A flag parameter node.
///
/// When implemented, this will only have a value of
/// true when it is present.
pub struct FlagParameterNode {
    fields: ParameterNodeFields,
}

impl ParameterNode for FlagParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.fields
    }
}

/// A named parameter node.
pub struct NamedParameterNode {
    fields: ParameterNodeFields,
}

impl ParameterNode for NamedParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.fields
    }
}

/// A simple parameter node. This is only present in a command
/// line as a value.
pub struct SimpleParameterNode {
    fields: ParameterNodeFields,
}

impl ParameterNode for SimpleParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.fields
    }
}
<commit_msg>Nodes: Reorganize shared fields some.<commit_after>//! # Parser Nodes
//!
//! Parser nodes represent the grammar that is defined
//! by the currently permissible set of commands and their
//! parameters.

use std::rc::Rc;

/// Minimum priority.
pub const PRIORITY_MINIMUM: i32 = -10000;
/// The default priority for a parameter.
pub const PRIORITY_PARAMETER: i32 = -10;
/// The default priority.
pub const PRIORITY_DEFAULT: i32 = 0;

/// A node in the tree of commands and their parameters
/// used by the `Parser`.
pub trait Node {
    /// The data describing this node.
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields;

    /// Nodes that are children of this node. Used to
    /// by the `Parser` during `advance`, `complete`, etc.
    fn successors(&self) -> &Vec<Rc<Node>> {
        &self.node_data().successors
    }

    /// The text used to identify this node in help text.
    /// This is typically the node name, either in plain
    /// form or decorated for parameters.
    fn help_symbol(&self) -> String {
        self.node_data().name.to_string()
    }

    /// Help text describing this node.
    fn help_text(&self) -> &Option<String> {
        unimplemented!();
    }

    /// Hidden nodes are still found for matching, but are
    /// hidden from completion.
    fn hidden(&self) -> bool {
        self.node_data().hidden
    }

    /// The name of this node.
    fn name(&self) -> &String {
        &self.node_data().name
    }

    /// This priority of this node during matching and completion.
    fn priority(&self) -> i32 {
        self.node_data().priority
    }
}

impl PartialEq for Node {
    /// Nodes are equal based on pointer equality.
    fn eq(&self, other: &Self) -> bool {
        self as *const _ == other as *const _
    }
}

/// A parse tree node.
#[doc(hidden)]
pub struct NodeFields {
    /// Possible successor nodes. Collected while building.
    successors: Vec<Rc<Node>>,
    /// The name of this node.
    name: String,
    /// Match and complete priority.
    priority: i32,
    /// Hidden nodes are not completed. This doesn't modify matching.
    hidden: bool,
}

/// The root of a command tree.
pub struct RootNode {
    node_fields: NodeFields,
}

impl Node for RootNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }
}

/// A node representing a command.
pub struct CommandNode {
    node_fields: NodeFields,
    command_fields: CommandNodeFields,
}

struct CommandNodeFields {
    help: Option<String>,
    handler: Option<fn(&node: Node) -> ()>,
    parameters: Vec<Rc<ParameterNode>>,
}

impl Node for CommandNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }

    fn help_text(&self) -> &Option<String> {
        &self.command_fields.help
    }
}

impl CommandNode {
    /// The handler which is executed once this node has been accepted.
    pub fn handler(&self) -> Option<fn(&node: Node) -> ()> {
        self.command_fields.handler
    }

    /// Get the parameter nodes for this command.
    pub fn parameters(&self) -> &Vec<Rc<ParameterNode>> {
        &self.command_fields.parameters
    }
}

/// A wrapper node wraps another command.
///
/// This is used for the help command so that it can complete
/// normal commands.
///
/// The `successors` will be those of the wrapped node.
pub struct WrapperNode {
    node_fields: NodeFields,
    #[allow(dead_code)]
    command_fields: CommandNodeFields,
    root: Rc<Node>,
}

impl Node for WrapperNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }

    fn successors(&self) -> &Vec<Rc<Node>> {
        self.root.successors()
    }
}

/// A repeatable node is an internal helper for representing
/// nodes that can be repeated, like some parameters.
pub trait RepeatableNode: Node {
    /// Internal data for a repeatable node.
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields;

    /// Whether or not this node can be repeated. A repeated
    /// node can be `accept`ed multiple times.
    fn repeatable(&self) -> bool {
        self.repeatable_data().repeatable
    }

    /// If present, this node will no longer be `acceptable`.
    fn repeat_marker(&self) -> &Option<Rc<Node>> {
        &self.repeatable_data().repeat_marker
    }
}

/// The data for a repeatable node.
#[doc(hidden)]
pub struct RepeatableNodeFields {
    repeatable: bool,
    repeat_marker: Option<Rc<Node>>,
}

/// A node that represented the name portion of a named
/// parameter.
pub struct ParameterNameNode {
    node_fields: NodeFields,
    repeatable_fields: RepeatableNodeFields,
    help: Option<String>,
    parameter: Rc<Node>,
}

impl Node for ParameterNameNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.node_fields
    }

    fn help_symbol(&self) -> String {
        self.node_fields.name.clone() + " " + self.parameter.help_symbol().as_str()
    }

    fn help_text(&self) -> &Option<String> {
        &self.help
    }
}

impl RepeatableNode for ParameterNameNode {
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields {
        &self.repeatable_fields
    }
}

/// Parameter nodes.
pub trait ParameterNode {
    /// Internal data for a parameter node.
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields;

    /// A `required` parameter must be supplied for the
    /// command line being parsed to be valid.
    fn required(&self) -> bool {
        self.parameter_data().required
    }
}

impl RepeatableNode for ParameterNode {
    #[doc(hidden)]
    fn repeatable_data(&self) -> &RepeatableNodeFields {
        &self.parameter_data().repeatable
    }
}

/// Data for parameter nodes.
#[doc(hidden)]
pub struct ParameterNodeFields {
    node: NodeFields,
    repeatable: RepeatableNodeFields,
    help: Option<String>,
    required: bool,
}

impl Node for ParameterNode {
    #[doc(hidden)]
    fn node_data(&self) -> &NodeFields {
        &self.parameter_data().node
    }

    fn help_symbol(&self) -> String {
        String::from("<") + self.node_data().name.as_str() +
        if self.repeatable() {
            ">..."
        } else {
            ">"
        }
    }

    fn help_text(&self) -> &Option<String> {
        &self.parameter_data().help
    }
}

/// A flag parameter node.
///
/// When implemented, this will only have a value of
/// true when it is present.
pub struct FlagParameterNode {
    parameter_fields: ParameterNodeFields,
}

impl ParameterNode for FlagParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.parameter_fields
    }
}

/// A named parameter node.
pub struct NamedParameterNode {
    parameter_fields: ParameterNodeFields,
}

impl ParameterNode for NamedParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.parameter_fields
    }
}

/// A simple parameter node. This is only present in a command
/// line as a value.
pub struct SimpleParameterNode {
    parameter_fields: ParameterNodeFields,
}

impl ParameterNode for SimpleParameterNode {
    #[doc(hidden)]
    fn parameter_data(&self) -> &ParameterNodeFields {
        &self.parameter_fields
    }
}
<|endoftext|>


<commit_before>use std::mem;
use nom::{self, IResult, InputLength, Slice};
use parser::{Span, Parser};
use parser::errors::{ErrorKind, is_custom};
use parser::trivia::{line_trailing, ws};
use parser::key::key;
use parser::LenWorkaround;
use ::decor::{InternalString, Repr};
use ::document::Document;
use ::table::{Table, TableEntry, Header, HeaderKind};

// table-key-sep   = ws %x2E ws  ; . Period
const TABLE_KEY_SEP: &'static str = ".";

// note: this rule is not present in the original grammar
// key-path = key *( table-key-sep key)
named!(key_path(Span) -> (Vec<InternalString>, Span),
       with_input!(
           err!(ErrorKind::InvalidHeader,
                separated_nonempty_list_complete!(
                    tag!(TABLE_KEY_SEP),
                    map!(ws!(key), |(key, _)| key)
                )
           )
       )
);

impl Default for Parser {
    fn default() -> Self {
        let mut doc = Document::new();
        let root = doc.root_mut() as *mut Table;
        Self {
            document: doc,
            current_table: root,
        }
    }
}

impl Parser {
    // ;; Table

    // table = std-table / array-table
    pub fn table(self, input: Span) -> (Self, IResult<Span, ()>) {
        let parser = self;
        let (parser, result) = parser.array_table(input);
        match result {
            IResult::Done(..) => return (parser, result),
            IResult::Error(e) => {
                if is_custom(&e) {
                    return (parser, IResult::Error(e));
                }
            }
            _ => unreachable!("array_table is complete"),
        }
        parser.std_table(input)
    }

    // ;; Standard Table

    // std-table-open  = %x5B ws     ; [ Left square bracket
    const STD_TABLE_OPEN: &'static str = "[";
    // std-table-close = ws %x5D     ; ] Right square bracket
    const STD_TABLE_CLOSE: &'static str = "]";

    // std-table = std-table-open key *( table-key-sep key) std-table-close
    method!(std_table<Parser>(Span) -> (), mut self,
        do_parse!(
            w: ws >>
            h: delimited!(
                   complete!(tag!(Parser::STD_TABLE_OPEN)),
                   key_path,
                   err_m!(self, ErrorKind::InvalidHeader,
                          complete!(tag!(Parser::STD_TABLE_CLOSE)))) >>
            t: line_trailing >>
               err_parser!(self, Self::on_std_header,
                           call!(w.fragment, h, t.fragment)) >>
               ()
        )
    );

    // ;; Array Table

    // array-table-open  = %x5B.5B ws  ; [[ Double left square bracket
    const ARRAY_TABLE_OPEN: &'static str = "[[";
    // array-table-close = ws %x5D.5D  ; ]] Double right quare bracket
    const ARRAY_TABLE_CLOSE: &'static str = "]]";

    // array-table = array-table-open key *( table-key-sep key) array-table-close
    method!(array_table<Parser>(Span) -> (), mut self,
            do_parse!(
             w: ws >>
             h: delimited!(
                    complete!(tag!(Parser::ARRAY_TABLE_OPEN)),
                    key_path,
                    err_m!(self, ErrorKind::InvalidHeader,
                           complete!(tag!(Parser::ARRAY_TABLE_CLOSE)))
                ) >>
             t: line_trailing >>
                err_parser!(self, Self::on_array_header,
                            call!(w.fragment, h, t.fragment)) >>
                ()
            )
    );

    fn descend_path<'a>(table: &'a mut Table, path: &[InternalString]) -> Option<&'a mut Table> {
        if let Some(key) = path.get(0) {
            let header = table.child_header(key, HeaderKind::Implicit);
            match table.append_table_with_header(key, header) {
                TableEntry::Value(..) => None,
                TableEntry::Array(array) => {
                    debug_assert!(!array.is_empty());

                    let i = array.len() - 1;
                    let last_child = array.get_mut(i).unwrap();

                    Self::descend_path(last_child, &path[1..])
                }
                TableEntry::Table(sweet_child_of_mine) => {
                    Parser::descend_path(sweet_child_of_mine, &path[1..])
                }
                _ => unreachable!("`insert_table` can't return a vacant entry"),
            }
        } else {
            Some(table)
        }
    }

    fn on_std_header<'a>(
        rest: Span<'a>,
        me: &mut Parser,
        whitespaces: &str,
        header: (Vec<InternalString>, Span<'a>),
        trailing: &str
    ) -> IResult<Span<'a>, ()>
    {
        let (header, span) = header;
        debug_assert!(!header.is_empty());

        let mut leading = mem::replace(&mut me.document.trailing, InternalString::new());
        leading.push_str(whitespaces);
        let mut table = me.document.root_mut();

        let table = Self::descend_path(table, &header[..header.len() - 1]);
        let key = &header[header.len() - 1];

        if let Some(table) = table {
            let header = Header {
                repr: Repr::new(leading, span.fragment.into(), trailing.into()),
                kind: HeaderKind::Standard,
            };

            match table.entry(key) {
                // if [a.b.c] header preceded [a.b]
                TableEntry::Table(ref mut t) if t.header.kind == HeaderKind::Implicit => {
                    t.move_to_end();
                    t.header = header;
                    me.current_table = *t;

                    return IResult::Done(rest, ());
                }
                TableEntry::Vacant(ref mut parent) => {
                    let mut table = parent.append_table_with_header(key, header);
                    me.current_table = table.as_table_mut().unwrap();

                    return IResult::Done(rest, ());
                }
                _ => {}
            }
        }
        e!(ErrorKind::DuplicateKey, span)
    }

    fn on_array_header<'a>(
        rest: Span<'a>,
        me: &mut Parser,
        whitespaces: &str,
        header: (Vec<InternalString>, Span<'a>),
        trailing: &str
    ) -> IResult<Span<'a>, ()>
    {
        let (header, span) = header;
        debug_assert!(!header.is_empty());

        let mut leading = mem::replace(&mut me.document.trailing, InternalString::new());
        leading.push_str(whitespaces);

        let mut table = me.document.root_mut();
        let key = &header[header.len() - 1];
        let table = Self::descend_path(table, &header[..header.len() - 1]);

        if let Some(table) = table {
            if !table.contains_table(key) && !table.contains_value(key) {
                let header = Header {
                    repr: Repr::new(leading, span.fragment.into(), trailing.into()),
                    kind: HeaderKind::Array,
                };

                let array = table.insert_array_assume_vacant(key);
                me.current_table = array.append_with_header(header, me.current_table);

                return IResult::Done(rest, ());
            }
        }
        e!(ErrorKind::DuplicateKey, span)
    }
}
<commit_msg>fix(build): move assoc constants to globals<commit_after>use std::mem;
use nom::{self, IResult, InputLength, Slice};
use parser::{Span, Parser};
use parser::errors::{ErrorKind, is_custom};
use parser::trivia::{line_trailing, ws};
use parser::key::key;
use parser::LenWorkaround;
use ::decor::{InternalString, Repr};
use ::document::Document;
use ::table::{Table, TableEntry, Header, HeaderKind};

// table-key-sep   = ws %x2E ws  ; . Period
const TABLE_KEY_SEP: &'static str = ".";
// std-table-open  = %x5B ws     ; [ Left square bracket
const STD_TABLE_OPEN: &'static str = "[";
// std-table-close = ws %x5D     ; ] Right square bracket
const STD_TABLE_CLOSE: &'static str = "]";
// array-table-open  = %x5B.5B ws  ; [[ Double left square bracket
const ARRAY_TABLE_OPEN: &'static str = "[[";
// array-table-close = ws %x5D.5D  ; ]] Double right quare bracket
const ARRAY_TABLE_CLOSE: &'static str = "]]";

// note: this rule is not present in the original grammar
// key-path = key *( table-key-sep key)
named!(key_path(Span) -> (Vec<InternalString>, Span),
       with_input!(
           err!(ErrorKind::InvalidHeader,
                separated_nonempty_list_complete!(
                    tag!(TABLE_KEY_SEP),
                    map!(ws!(key), |(key, _)| key)
                )
           )
       )
);

impl Default for Parser {
    fn default() -> Self {
        let mut doc = Document::new();
        let root = doc.root_mut() as *mut Table;
        Self {
            document: doc,
            current_table: root,
        }
    }
}

impl Parser {
    // ;; Table

    // table = std-table / array-table
    pub fn table(self, input: Span) -> (Self, IResult<Span, ()>) {
        let parser = self;
        let (parser, result) = parser.array_table(input);
        match result {
            IResult::Done(..) => return (parser, result),
            IResult::Error(e) => {
                if is_custom(&e) {
                    return (parser, IResult::Error(e));
                }
            }
            _ => unreachable!("array_table is complete"),
        }
        parser.std_table(input)
    }

    // ;; Standard Table

    // std-table = std-table-open key *( table-key-sep key) std-table-close
    method!(std_table<Parser>(Span) -> (), mut self,
        do_parse!(
            w: ws >>
            h: delimited!(
                   complete!(tag!(STD_TABLE_OPEN)),
                   key_path,
                   err_m!(self, ErrorKind::InvalidHeader,
                          complete!(tag!(STD_TABLE_CLOSE)))) >>
            t: line_trailing >>
               err_parser!(self, Self::on_std_header,
                           call!(w.fragment, h, t.fragment)) >>
               ()
        )
    );

    // ;; Array Table

    // array-table = array-table-open key *( table-key-sep key) array-table-close
    method!(array_table<Parser>(Span) -> (), mut self,
            do_parse!(
             w: ws >>
             h: delimited!(
                    complete!(tag!(ARRAY_TABLE_OPEN)),
                    key_path,
                    err_m!(self, ErrorKind::InvalidHeader,
                           complete!(tag!(ARRAY_TABLE_CLOSE)))
                ) >>
             t: line_trailing >>
                err_parser!(self, Self::on_array_header,
                            call!(w.fragment, h, t.fragment)) >>
                ()
            )
    );

    fn descend_path<'a>(table: &'a mut Table, path: &[InternalString]) -> Option<&'a mut Table> {
        if let Some(key) = path.get(0) {
            let header = table.child_header(key, HeaderKind::Implicit);
            match table.append_table_with_header(key, header) {
                TableEntry::Value(..) => None,
                TableEntry::Array(array) => {
                    debug_assert!(!array.is_empty());

                    let i = array.len() - 1;
                    let last_child = array.get_mut(i).unwrap();

                    Self::descend_path(last_child, &path[1..])
                }
                TableEntry::Table(sweet_child_of_mine) => {
                    Parser::descend_path(sweet_child_of_mine, &path[1..])
                }
                _ => unreachable!("`insert_table` can't return a vacant entry"),
            }
        } else {
            Some(table)
        }
    }

    fn on_std_header<'a>(
        rest: Span<'a>,
        me: &mut Parser,
        whitespaces: &str,
        header: (Vec<InternalString>, Span<'a>),
        trailing: &str
    ) -> IResult<Span<'a>, ()>
    {
        let (header, span) = header;
        debug_assert!(!header.is_empty());

        let mut leading = mem::replace(&mut me.document.trailing, InternalString::new());
        leading.push_str(whitespaces);
        let mut table = me.document.root_mut();

        let table = Self::descend_path(table, &header[..header.len() - 1]);
        let key = &header[header.len() - 1];

        if let Some(table) = table {
            let header = Header {
                repr: Repr::new(leading, span.fragment.into(), trailing.into()),
                kind: HeaderKind::Standard,
            };

            match table.entry(key) {
                // if [a.b.c] header preceded [a.b]
                TableEntry::Table(ref mut t) if t.header.kind == HeaderKind::Implicit => {
                    t.move_to_end();
                    t.header = header;
                    me.current_table = *t;

                    return IResult::Done(rest, ());
                }
                TableEntry::Vacant(ref mut parent) => {
                    let mut table = parent.append_table_with_header(key, header);
                    me.current_table = table.as_table_mut().unwrap();

                    return IResult::Done(rest, ());
                }
                _ => {}
            }
        }
        e!(ErrorKind::DuplicateKey, span)
    }

    fn on_array_header<'a>(
        rest: Span<'a>,
        me: &mut Parser,
        whitespaces: &str,
        header: (Vec<InternalString>, Span<'a>),
        trailing: &str
    ) -> IResult<Span<'a>, ()>
    {
        let (header, span) = header;
        debug_assert!(!header.is_empty());

        let mut leading = mem::replace(&mut me.document.trailing, InternalString::new());
        leading.push_str(whitespaces);

        let mut table = me.document.root_mut();
        let key = &header[header.len() - 1];
        let table = Self::descend_path(table, &header[..header.len() - 1]);

        if let Some(table) = table {
            if !table.contains_table(key) && !table.contains_value(key) {
                let header = Header {
                    repr: Repr::new(leading, span.fragment.into(), trailing.into()),
                    kind: HeaderKind::Array,
                };

                let array = table.insert_array_assume_vacant(key);
                me.current_table = array.append_with_header(header, me.current_table);

                return IResult::Done(rest, ());
            }
        }
        e!(ErrorKind::DuplicateKey, span)
    }
}
<|endoftext|>


<commit_before>//! This module reexports the OS-specific module that actually implements Platform.
pub mod common;
pub use self::common::*;

#[cfg(windows)]
pub mod windows;
#[cfg(windows)]
pub use self::windows::PlatformImpl;

#[cfg(unix)]
pub mod unix;

#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub mod bsd;

#[cfg(target_os = "freebsd")]
pub mod freebsd;
#[cfg(target_os = "freebsd")]
pub use self::freebsd::PlatformImpl;

#[cfg(target_os = "openbsd")]
pub mod openbsd;
#[cfg(target_os = "openbsd")]
pub use self::openbsd::PlatformImpl;

#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(target_os = "macos")]
pub use self::macos::PlatformImpl;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::linux::PlatformImpl;

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    use std::time::Duration;

    #[test]
    fn test_cpu_load() {
        let load = PlatformImpl::new().cpu_load().unwrap();
        thread::sleep(Duration::from_millis(300));
        let load = load.done().unwrap();
        assert!(load.len() >= 1);
        for cpu in load.iter() {
            let sum = cpu.user + cpu.nice + cpu.system + cpu.interrupt + cpu.idle;
            assert!(sum > 0.95 && sum < 1.05);
        }
    }

    #[test]
    fn test_cpu_load_aggregate() {
        let cpu = PlatformImpl::new().cpu_load_aggregate().unwrap();
        thread::sleep(Duration::from_millis(300));
        let cpu = cpu.done().unwrap();
        let sum = cpu.user + cpu.nice + cpu.system + cpu.interrupt + cpu.idle;
        assert!(sum > 0.95 && sum < 1.05);
    }

    #[test]
    fn test_load_average() {
        let load = PlatformImpl::new().load_average().unwrap();
        assert!(load.one > 0.00001 && load.five > 0.00001 && load.fifteen > 0.00001);
    }

    #[test]
    fn test_memory() {
        let mem = PlatformImpl::new().memory().unwrap();
        assert!(mem.free.as_u64() > 1024 && mem.total.as_u64() > 1024);
    }

    #[test]
    fn test_battery_life() {
        if let Ok(bat) = PlatformImpl::new().battery_life() {
            assert!(bat.remaining_capacity <= 100.0 && bat.remaining_capacity >= 0.0);
        }
    }

    #[test]
    fn test_on_ac_power() {
        PlatformImpl::new().on_ac_power().unwrap();
    }

    #[test]
    fn test_mounts() {
        let mounts = PlatformImpl::new().mounts().unwrap();
        assert!(mounts.len() > 0);
        assert!(mounts.iter().find(|m| m.fs_mounted_on == "/").unwrap().fs_mounted_on == "/");
    }

    #[test]
    fn test_mount_at() {
        // XXX: PathBuf required instead of constant string at least on FreeBSD??
        let mount = PlatformImpl::new().mount_at(std::path::PathBuf::from("/")).unwrap();
        assert!(mount.fs_mounted_on == "/");
    }

    #[test]
    fn test_networks() {
        let networks = PlatformImpl::new().networks().unwrap();
        assert!(networks.values().filter(|n| n.name == "lo" || n.name == "lo0").next().unwrap().addrs.len() > 0);
    }

    #[test]
    fn test_cpu_measurement_is_send() {
        use crate::{DelayedMeasurement, CPULoad};
        fn take_delayed(dm: DelayedMeasurement<Vec<CPULoad>>) {
            use std::thread;
            thread::spawn(move || dm);
        }
    }

}
<commit_msg>allow(dead_code) on compile only test<commit_after>//! This module reexports the OS-specific module that actually implements Platform.
pub mod common;
pub use self::common::*;

#[cfg(windows)]
pub mod windows;
#[cfg(windows)]
pub use self::windows::PlatformImpl;

#[cfg(unix)]
pub mod unix;

#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
pub mod bsd;

#[cfg(target_os = "freebsd")]
pub mod freebsd;
#[cfg(target_os = "freebsd")]
pub use self::freebsd::PlatformImpl;

#[cfg(target_os = "openbsd")]
pub mod openbsd;
#[cfg(target_os = "openbsd")]
pub use self::openbsd::PlatformImpl;

#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(target_os = "macos")]
pub use self::macos::PlatformImpl;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub mod linux;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::linux::PlatformImpl;

#[cfg(test)]
mod tests {
    use super::*;
    use std::thread;
    use std::time::Duration;

    #[test]
    fn test_cpu_load() {
        let load = PlatformImpl::new().cpu_load().unwrap();
        thread::sleep(Duration::from_millis(300));
        let load = load.done().unwrap();
        assert!(load.len() >= 1);
        for cpu in load.iter() {
            let sum = cpu.user + cpu.nice + cpu.system + cpu.interrupt + cpu.idle;
            assert!(sum > 0.95 && sum < 1.05);
        }
    }

    #[test]
    fn test_cpu_load_aggregate() {
        let cpu = PlatformImpl::new().cpu_load_aggregate().unwrap();
        thread::sleep(Duration::from_millis(300));
        let cpu = cpu.done().unwrap();
        let sum = cpu.user + cpu.nice + cpu.system + cpu.interrupt + cpu.idle;
        assert!(sum > 0.95 && sum < 1.05);
    }

    #[test]
    fn test_load_average() {
        let load = PlatformImpl::new().load_average().unwrap();
        assert!(load.one > 0.00001 && load.five > 0.00001 && load.fifteen > 0.00001);
    }

    #[test]
    fn test_memory() {
        let mem = PlatformImpl::new().memory().unwrap();
        assert!(mem.free.as_u64() > 1024 && mem.total.as_u64() > 1024);
    }

    #[test]
    fn test_battery_life() {
        if let Ok(bat) = PlatformImpl::new().battery_life() {
            assert!(bat.remaining_capacity <= 100.0 && bat.remaining_capacity >= 0.0);
        }
    }

    #[test]
    fn test_on_ac_power() {
        PlatformImpl::new().on_ac_power().unwrap();
    }

    #[test]
    fn test_mounts() {
        let mounts = PlatformImpl::new().mounts().unwrap();
        assert!(mounts.len() > 0);
        assert!(mounts.iter().find(|m| m.fs_mounted_on == "/").unwrap().fs_mounted_on == "/");
    }

    #[test]
    fn test_mount_at() {
        // XXX: PathBuf required instead of constant string at least on FreeBSD??
        let mount = PlatformImpl::new().mount_at(std::path::PathBuf::from("/")).unwrap();
        assert!(mount.fs_mounted_on == "/");
    }

    #[test]
    fn test_networks() {
        let networks = PlatformImpl::new().networks().unwrap();
        assert!(networks.values().filter(|n| n.name == "lo" || n.name == "lo0").next().unwrap().addrs.len() > 0);
    }

    #[test]
    fn test_cpu_measurement_is_send() {
        use crate::{DelayedMeasurement, CPULoad};
        #[allow(dead_code)]
        fn take_delayed(dm: DelayedMeasurement<Vec<CPULoad>>) {
            use std::thread;
            thread::spawn(move || dm);
        }
    }

}
<|endoftext|>


<commit_before>use redox::*;
use common::*;
mod crypto;
use crypto::{Sha512, PubKey, RsaSignature};

/// Version
#[derive(Hash, Clone)]
pub enum Version {
    NewerThan(u64),
    OlderThan(u64),
    Between(u64, u64),
    Excatly(u64),
    Newest,
}

// TODO: Implement format

#[derive(Hash, Clone)]
/// A package developer
pub struct Developer {
    /// The name of the developer
    pub name: String,
    /// The public key of the developer
    pub key: PubKey,
}

#[derive(Hash, Clone)]
/// An installable package for Oxide
pub struct Package {
    /// The id of this package
    pub id: Id,
    /// Description
    pub desc: String,
    /// The developer of the package
    pub dev: Developer,
    /// The developer's signature of this package's content (tarball)
    pub dev_sign: RsaSignature,
    /// The signatures of this package
    pub sign: Vec<RsaSignature>,
    /// The files this package will create on the computer.
    pub files: Vec<String>,
    /// Dependencies of this package
    pub deps: Vec<Id>,
}

impl Package {
    /// Get content
    pub fn get_content(&self) -> Tarball {

    }

    /// Get package from string
    pub fn from_string(s: String) -> Option<Package> {

        // TODO

        for i in resp {
            let data = i.substr(5, i.len() - 5);
            let key = i.substr(0, 4);

            if key == "name" {
                name = data;
            } else if key == "desc" {
                desc = data;
            } else if key == "host" {
                host = data;
            } else if key == "file" {
                files = data;
            } else if key == "vers" {
                version = data;
            }
        }

        Package {
            host: host,
            name: name,
            desc: desc,
            files: files.split(",".to_string()).collect::<Vec<_>>(),
            version: version,
        }
    }

    /// Install package
    pub fn install(&self, col: &Collection) -> Result<u64, InstallError> {
        // Install deps
        let mut installed = 0;
        for d in self.deps {
            let pkg = d.install();
            if let Err(n) = pkg {
                installed += n;
                if n > 10000 {
                    println!("Warning: Potential infinite recursion (cyclic dependencies)");
                }
            } else {
                return pkg;
            }

        }

        // TODO install + add to local package list
    }

    /// Check validity
    pub fn check(&self, col: &Collection) -> TrustLevel {
        let con = self.get_content();

        for s in self.sign {
            if s.check(con) && col.keys.contains(s) {
                return TrustLevel::TrustedPackage
            } else if !s.check(con) {
                return TrustLevel::InvalidSignature
            }
        }

        if !self.dev_sign.check(con) {
            TrustLevel::InvalidSignature
        } else if !col.devs.contains(self.dev_sign) {
            TrustLevel::UntrustedSignature
        } else {
            TrustLevel::TrustedDev
        }
    }

}

/// Trust level
pub enum TrustLevel {
    /// 0
    InvalidSignature,
    /// 1
    UntrustedSignature,
    /// 2
    TrustedDeveloper,
    /// 3
    TrustedPackage,
}

impl TrustLevel {
    /// Is this package trusted?
    pub fn is_trusted(&self) -> bool {
        match self {
            &TrustLevel::TrustedDeveloper | TrustLevel::TrustedPackage => true,
            _ => false,
        }
    }
}

/// An error
pub enum PackageError {
    InvalidSyntax,
    InvalidSignature,
    UntrustedSignature,
    UntrustedDev,
    NotFound,
    E404,
    InfiniteDeps,
    Unknown,
}

#[derive(Hash, Clone)]
/// An package descriptor
pub struct Id {
    pub name: String,
    pub version: Version,
    pub dist_type: DistType,
}

/// Distribution type
pub enum DistType {
    Binary,
    Source,
    Other,
}

impl Id {
    pub fn to_string(&self) -> String {
        format!("{}-{}-{}", self.name, self.dist_type, self.version)
    }
}

/// Database of trusted developers
#[derive(Hash, Clone)]
pub struct DevDb {
    pub data: HashSet<Developer>,
}

/// Database of trusted keys
#[derive(Hash, Clone)]
pub struct KeyDb {
    pub data: HashSet<PubKey>,
}

/// An index of packages
#[derive(Hash, Clone)]
pub struct Index {
    /// Where the search queries can be send to
    pub host: String,
}

impl Index {
    /// Get a given package
    pub fn get(&self, id: Id) -> Result<Package, PackageError> {
        let con = File::open("tcp://".to_string() + self.host);

        con.write("GET /ox/".to_string() + id.to_string() + " HTTP/1.1".to_string());

        let res = Vec::new();
        con.read_to_end(&mut res);

        Package::from_string(String::from_utf8(&res))
    }
}

/// A collection of indexes, trusted keys, and trusted developers (all stored on the users
/// computer)
#[derive(Hash, Clone)]
pub struct Collection {
    /// Indexes
    pub index: Vec<Index>,
    /// The trusted devs
    pub devs: DevDb,
    /// The trusted keys
    pub keys: KeyDb,
    /// The installed packages
    pub installed: Vec<LocalPackage>,
}

/// A package installed locally
pub struct LocalPackage {
    /// Files it owns
    pub owns: Vec<String>,
    /// Is this package installed as root (i.e. isnt just installed as a dep for another package)?
    pub root: bool,
    /// The package
    pub package: Package,
}

impl LocalPackage {
    pub fn uninstall(&self) -> bool {

    }
}

impl Collection {
    /// Get a given package (guaranteed to be valid)
    pub fn get(&self, id: Id) -> Result<Package, PackageError> {
        for i in self.index {
            if let Ok(p) = i.get(id) {
                if p.check().is_trusted() {
                    return Ok(p);
                }
            }
        }
        None
    }
}

<commit_msg>More work on Oxide<commit_after>use redox::*;
use common::*;
mod crypto;
use crypto::{Sha512, PubKey, RsaSignature};
use redox::time::Duration;

// TODO: Implement format

#[derive(Hash, Clone)]
/// A package developer
pub struct Developer {
    /// The name of the developer
    pub name: String,
    /// The public key of the developer
    pub key: PubKey,
}

#[derive(Hash, Clone)]
/// An installable package for Oxide
pub struct Package {
    /// Description
    pub desc: String,
    /// The developer of the package
    pub dev: Developer,
    /// The developer's signature of this package's content (tarball)
    pub dev_sign: RsaSignature,
    /// The signatures of this package
    pub sign: HashSet<RsaSignature>,
    /// The files this package will create on the computer.
    pub files: HashSet<String>,
    /// Dependencies of this package
    /// Making sure the newest (compatible) version is the one used in the deps is up to the
    /// package provider (and is thus NOT included in the signature because it's already
    /// signed).
    pub deps: HashSet<Id>,
}

impl Package {
    /// Get content
    pub fn get_content(&self) -> Tarball {

    }

    /// Get package from string
    pub fn from_string(s: String) -> Option<Package> {

        // TODO

        for i in resp {
            let data = i.substr(5, i.len() - 5);
            let key = i.substr(0, 4);

            if key == "name" {
                name = data;
            } else if key == "desc" {
                desc = data;
            } else if key == "host" {
                host = data;
            } else if key == "file" {
                files = data;
            } else if key == "vers" {
                version = data;
            }
        }

        Package {
            host: host,
            name: name,
            desc: desc,
            files: files.split(",".to_string()).collect::<Vec<_>>(),
            version: version,
        }
    }

    /// Install package
    pub fn install(&self, col: &Collection) -> Result<u64, PackageError> {
        // Install deps
        let mut installed = 0;
        for d in self.deps {
            let pkg = d.install();
            if let Err(n) = pkg {
                installed += n;
                if n > 10000 {
                    println!("Warning: Potential infinite recursion (cyclic dependencies)");
                }
            } else {
                return pkg;
            }

        }

        // TODO install + add to local package list
    }

    /// Update packages
    pub fn update(&self, col: &Collection) -> Result<u64, PackageError> {

        // TODO install + add to local package list
    }

    /// Check validity
    pub fn check(&self, col: &Collection) -> TrustLevel {
        let con = self.get_content();

        for s in self.sign {
            if s.check(con) && col.keys.contains(s) {
                return TrustLevel::TrustedPackage
            } else if !s.check(con) {
                return TrustLevel::InvalidSignature
            }
        }

        if !self.dev_sign.check(con) {
            TrustLevel::InvalidSignature
        } else if !col.devs.contains(self.dev_sign) {
            TrustLevel::UntrustedSignature
        } else {
            TrustLevel::TrustedDev
        }
    }

}

/// Trust level
pub enum TrustLevel {
    /// 0
    InvalidSignature,
    /// 1
    UntrustedSignature,
    /// 2
    TrustedDeveloper,
    /// 3
    TrustedPackage,
}

impl TrustLevel {
    /// Is this package trusted?
    pub fn is_trusted(&self) -> bool {
        match self {
            &TrustLevel::TrustedDeveloper | TrustLevel::TrustedPackage => true,
            _ => false,
        }
    }
}

/// An error
pub enum PackageError {
    InvalidSyntax,
    InvalidSignature,
    UntrustedSignature,
    UntrustedDev,
    NotFound,
    E404,
    InfiniteDeps,
    Unknown,
}

#[derive(Hash, Clone)]
/// An package descriptor
pub struct Id {
    pub name: String,
    pub version: String,
    pub dist_type: DistType,
}

/// Distribution type
pub enum DistType {
    Binary,
    Source,
    Other,
}

impl Id {
    pub fn to_string(&self) -> String {
        format!("{}-{}-{}", self.name, self.dist_type, self.version)
    }
}

/// Database of trusted developers
#[derive(Hash, Clone)]
pub struct DevDb {
    pub data: HashSet<Developer>,
}

/// Database of trusted keys
#[derive(Hash, Clone)]
pub struct KeyDb {
    pub data: HashSet<PubKey>,
}

/// An index of packages
#[derive(Hash, Clone)]
pub struct Index {
    /// Where the search queries can be send to
    pub host: String,
}

impl Index {
    /// Get a given package
    pub fn get(&self, id: Id) -> Result<Package, PackageError> {
        let con = File::open("tcp://".to_string() + self.host);

        con.write("GET /ox/".to_string() + id.to_string() + " HTTP/1.1".to_string());

        let res = Vec::new();
        con.read_to_end(&mut res);

        Package::from_string(String::from_utf8(&res))
    }
}

/// A collection of indexes, trusted keys, and trusted developers (all stored on the users
/// computer)
#[derive(Hash, Clone)]
pub struct Collection {
    /// Indexes
    pub index: Vec<Index>,
    /// The trusted devs
    pub devs: DevDb,
    /// The trusted keys
    pub keys: KeyDb,
    /// The installed packages
    pub installed: HashMap<Id, LocalPackage>,
    /// The root packages (packages which are not just installed as dependencies to other packages)
    pub root: HashSet<Id>,
}

/// A package installed locally
pub struct LocalPackage {
    /// Files it owns
    pub owns: HashSet<String>,
    /// The package
    pub package: Package,
    /// Dependency to
    pub dep_to: HashSet<Id>,
    /// Dependency for
    pub dep_for: HashSet<Id>,
}

impl LocalPackage {
    pub fn uninstall(&self) -> bool {

    }
}

impl Collection {
    /// Get a given package (guaranteed to be valid)
    pub fn get(&self, id: Id) -> Result<Package, PackageError> {
        for i in self.index {
            if let Ok(p) = i.get(id) {
                if p.check().is_trusted() {
                    return Ok(p);
                }
            }
        }
        None
    }
}

<|endoftext|>