<commit_before>use dir::Dir;
use file::File;
use column::Column;
use column::Column::*;
use output::{Grid, Details};
use term::dimensions;

use std::ascii::AsciiExt;
use std::cmp::Ordering;
use std::fmt;

use getopts;
use natord;

use self::Misfire::*;

/// The *Options* struct represents a parsed version of the user's
/// command-line options.
#[derive(PartialEq, Debug, Copy)]
pub struct Options {
    pub dir_action: DirAction,
    pub filter: FileFilter,
    pub view: View,
}

#[derive(PartialEq, Debug, Copy)]
pub struct FileFilter {
    reverse: bool,
    show_invisibles: bool,
    sort_field: SortField,
}

#[derive(PartialEq, Copy, Debug)]
pub enum View {
    Details(Details),
    Lines,
    Grid(Grid),
}

impl Options {

    /// Call getopts on the given slice of command-line strings.
    pub fn getopts(args: &[String]) -> Result<(Options, Vec<String>), Misfire> {
        let mut opts = getopts::Options::new();
        opts.optflag("1", "oneline",   "display one entry per line");
        opts.optflag("a", "all",       "show dot-files");
        opts.optflag("b", "binary",    "use binary prefixes in file sizes");
        opts.optflag("B", "bytes",     "list file sizes in bytes, without prefixes");
        opts.optflag("d", "list-dirs", "list directories as regular files");
        opts.optflag("g", "group",     "show group as well as user");
        opts.optflag("h", "header",    "show a header row at the top");
        opts.optflag("H", "links",     "show number of hard links");
        opts.optflag("l", "long",      "display extended details and attributes");
        opts.optflag("i", "inode",     "show each file's inode number");
        opts.optflag("r", "reverse",   "reverse order of files");
        opts.optflag("R", "recurse",   "recurse into directories");
        opts.optopt ("s", "sort",      "field to sort by", "WORD");
        opts.optflag("S", "blocks",    "show number of file system blocks");
        opts.optflag("T", "tree",      "recurse into subdirectories in a tree view");
        opts.optflag("x", "across",    "sort multi-column view entries across");
        opts.optflag("?", "help",      "show list of command-line options");

        let matches = match opts.parse(args) {
            Ok(m) => m,
            Err(e) => return Err(Misfire::InvalidOptions(e)),
        };

        if matches.opt_present("help") {
            return Err(Misfire::Help(opts.usage("Usage:\n  exa [options] [files...]")));
        }

        let sort_field = match matches.opt_str("sort") {
            Some(word) => try!(SortField::from_word(word)),
            None => SortField::Name,
        };

        let filter = FileFilter {
            reverse:         matches.opt_present("reverse"),
            show_invisibles: matches.opt_present("all"),
            sort_field:      sort_field,
        };

        let path_strs = if matches.free.is_empty() {
            vec![ ".".to_string() ]
        }
        else {
            matches.free.clone()
        };

        Ok((Options {
            dir_action: try!(DirAction::deduce(&matches)),
            view:       try!(View::deduce(&matches, filter)),
            filter:     filter,
        }, path_strs))
    }

    pub fn transform_files<'a>(&self, files: &mut Vec<File<'a>>) {
        self.filter.transform_files(files)
    }
}

impl FileFilter {
    /// Transform the files (sorting, reversing, filtering) before listing them.
    pub fn transform_files<'a>(&self, files: &mut Vec<File<'a>>) {

        if !self.show_invisibles {
            files.retain(|f| !f.is_dotfile());
        }

        match self.sort_field {
            SortField::Unsorted => {},
            SortField::Name => files.sort_by(|a, b| natord::compare(&*a.name, &*b.name)),
            SortField::Size => files.sort_by(|a, b| a.stat.size.cmp(&b.stat.size)),
            SortField::FileInode => files.sort_by(|a, b| a.stat.unstable.inode.cmp(&b.stat.unstable.inode)),
            SortField::Extension => files.sort_by(|a, b| {
                if a.ext.cmp(&b.ext) == Ordering::Equal {
                    Ordering::Equal
                }
                else {
                    a.name.to_ascii_lowercase().cmp(&b.name.to_ascii_lowercase())
                }
            }),
        }

        if self.reverse {
            files.reverse();
        }
    }
}

/// User-supplied field to sort by.
#[derive(PartialEq, Debug, Copy)]
pub enum SortField {
    Unsorted, Name, Extension, Size, FileInode
}

impl SortField {

    /// Find which field to use based on a user-supplied word.
    fn from_word(word: String) -> Result<SortField, Misfire> {
        match word.as_slice() {
            "name"  => Ok(SortField::Name),
            "size"  => Ok(SortField::Size),
            "ext"   => Ok(SortField::Extension),
            "none"  => Ok(SortField::Unsorted),
            "inode" => Ok(SortField::FileInode),
            field   => Err(SortField::none(field))
        }
    }

    /// How to display an error when the word didn't match with anything.
    fn none(field: &str) -> Misfire {
        Misfire::InvalidOptions(getopts::Fail::UnrecognizedOption(format!("--sort {}", field)))
    }
}

/// One of these things could happen instead of listing files.
#[derive(PartialEq, Debug)]
pub enum Misfire {

    /// The getopts crate didn't like these arguments.
    InvalidOptions(getopts::Fail),

    /// The user asked for help. This isn't strictly an error, which is why
    /// this enum isn't named Error!
    Help(String),

    /// Two options were given that conflict with one another
    Conflict(&'static str, &'static str),

    /// An option was given that does nothing when another one either is or
    /// isn't present.
    Useless(&'static str, bool, &'static str),
}

impl Misfire {
    /// The OS return code this misfire should signify.
    pub fn error_code(&self) -> i32 {
        if let Help(_) = *self { 2 }
                          else { 3 }
    }
}

impl fmt::Display for Misfire {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidOptions(ref e) => write!(f, "{}", e),
            Help(ref text)        => write!(f, "{}", text),
            Conflict(a, b)        => write!(f, "Option --{} conflicts with option {}.", a, b),
            Useless(a, false, b)  => write!(f, "Option --{} is useless without option --{}.", a, b),
            Useless(a, true, b)   => write!(f, "Option --{} is useless given option --{}.", a, b),
        }
    }
}

impl View {
    pub fn deduce(matches: &getopts::Matches, filter: FileFilter) -> Result<View, Misfire> {
        if matches.opt_present("long") {
            if matches.opt_present("across") {
                Err(Misfire::Useless("across", true, "long"))
            }
            else if matches.opt_present("oneline") {
                Err(Misfire::Useless("oneline", true, "long"))
            }
            else {
                let details = Details {
                        columns: try!(Columns::deduce(matches)),
                        header: matches.opt_present("tree"),
                        tree: matches.opt_present("recurse"),
                        filter: filter,
                };

                Ok(View::Details(details))
            }
        }
        else if matches.opt_present("binary") {
            Err(Misfire::Useless("binary", false, "long"))
        }
        else if matches.opt_present("bytes") {
            Err(Misfire::Useless("bytes", false, "long"))
        }
        else if matches.opt_present("inode") {
            Err(Misfire::Useless("inode", false, "long"))
        }
        else if matches.opt_present("links") {
            Err(Misfire::Useless("links", false, "long"))
        }
        else if matches.opt_present("header") {
            Err(Misfire::Useless("header", false, "long"))
        }
        else if matches.opt_present("blocks") {
            Err(Misfire::Useless("blocks", false, "long"))
        }
        else if matches.opt_present("oneline") {
            if matches.opt_present("across") {
                Err(Misfire::Useless("across", true, "oneline"))
            }
            else {
                Ok(View::Lines)
            }
        }
        else {
            if let Some((width, _)) = dimensions() {
                let grid = Grid {
                    across: matches.opt_present("across"),
                    console_width: width
                };

                Ok(View::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.
                Ok(View::Lines)
            }
        }
    }
}

#[derive(PartialEq, Debug, Copy)]
pub enum SizeFormat {
    DecimalBytes,
    BinaryBytes,
    JustBytes,
}

impl SizeFormat {
    pub fn deduce(matches: &getopts::Matches) -> Result<SizeFormat, Misfire> {
        let binary = matches.opt_present("binary");
        let bytes  = matches.opt_present("bytes");

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

/// What to do when encountering a directory?
#[derive(PartialEq, Debug, Copy)]
pub enum DirAction {
    AsFile, List, Recurse, Tree
}

impl DirAction {
    pub fn deduce(matches: &getopts::Matches) -> Result<DirAction, Misfire> {
        let recurse = matches.opt_present("recurse");
        let list    = matches.opt_present("list-dirs");
        let tree    = matches.opt_present("tree");

        match (recurse, list, tree) {
            (true,  true,  _    ) => Err(Misfire::Conflict("recurse", "list-dirs")),
            (true,  false, false) => Ok(DirAction::Recurse),
            (true,  false, true ) => Ok(DirAction::Tree),
            (false, true,  _    ) => Ok(DirAction::AsFile),
            (false, false, _    ) => Ok(DirAction::List),
        }
    }
}

#[derive(PartialEq, Copy, Debug)]
pub struct Columns {
    size_format: SizeFormat,
    inode: bool,
    links: bool,
    blocks: bool,
    group: bool,
}

impl Columns {
    pub fn deduce(matches: &getopts::Matches) -> Result<Columns, Misfire> {
        Ok(Columns {
            size_format: try!(SizeFormat::deduce(matches)),
            inode:  matches.opt_present("inode"),
            links:  matches.opt_present("links"),
            blocks: matches.opt_present("blocks"),
            group:  matches.opt_present("group"),
        })
    }

    pub fn for_dir(&self, dir: Option<&Dir>) -> Vec<Column> {
        let mut columns = vec![];

        if self.inode {
            columns.push(Inode);
        }

        columns.push(Permissions);

        if self.links {
            columns.push(HardLinks);
        }

        columns.push(FileSize(self.size_format));

        if self.blocks {
            columns.push(Blocks);
        }

        columns.push(User);

        if self.group {
            columns.push(Group);
        }

        if cfg!(feature="git") {
            if let Some(d) = dir {
                if d.has_git_repo() {
                    columns.push(GitStatus);
                }
            }
        }

        columns
    }
}

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

    fn is_helpful<T>(misfire: Result<T, Misfire>) -> bool {
        match misfire {
            Err(Help(_)) => true,
            _            => false,
        }
    }

    #[test]
    fn help() {
        let opts = Options::getopts(&[ "--help".to_string() ]);
        assert!(is_helpful(opts))
    }

    #[test]
    fn help_with_file() {
        let opts = Options::getopts(&[ "--help".to_string(), "me".to_string() ]);
        assert!(is_helpful(opts))
    }

    #[test]
    fn files() {
        let args = Options::getopts(&[ "this file".to_string(), "that file".to_string() ]).unwrap().1;
        assert_eq!(args, vec![ "this file".to_string(), "that file".to_string() ])
    }

    #[test]
    fn no_args() {
        let args = Options::getopts(&[]).unwrap().1;
        assert_eq!(args, vec![ ".".to_string() ])
    }

    #[test]
    fn file_sizes() {
        let opts = Options::getopts(&[ "--long".to_string(), "--binary".to_string(), "--bytes".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Conflict("binary", "bytes"))
    }

    #[test]
    fn just_binary() {
        let opts = Options::getopts(&[ "--binary".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("binary", false, "long"))
    }

    #[test]
    fn just_bytes() {
        let opts = Options::getopts(&[ "--bytes".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("bytes", false, "long"))
    }

    #[test]
    fn long_across() {
        let opts = Options::getopts(&[ "--long".to_string(), "--across".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("across", true, "long"))
    }

    #[test]
    fn oneline_across() {
        let opts = Options::getopts(&[ "--oneline".to_string(), "--across".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("across", true, "oneline"))
    }

    #[test]
    fn just_header() {
        let opts = Options::getopts(&[ "--header".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("header", false, "long"))
    }

    #[test]
    fn just_inode() {
        let opts = Options::getopts(&[ "--inode".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("inode", false, "long"))
    }

    #[test]
    fn just_links() {
        let opts = Options::getopts(&[ "--links".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("links", false, "long"))
    }

    #[test]
    fn just_blocks() {
        let opts = Options::getopts(&[ "--blocks".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("blocks", false, "long"))
    }
}
<commit_msg>Actually show the header all the time<commit_after>use dir::Dir;
use file::File;
use column::Column;
use column::Column::*;
use output::{Grid, Details};
use term::dimensions;

use std::ascii::AsciiExt;
use std::cmp::Ordering;
use std::fmt;

use getopts;
use natord;

use self::Misfire::*;

/// The *Options* struct represents a parsed version of the user's
/// command-line options.
#[derive(PartialEq, Debug, Copy)]
pub struct Options {
    pub dir_action: DirAction,
    pub filter: FileFilter,
    pub view: View,
}

#[derive(PartialEq, Debug, Copy)]
pub struct FileFilter {
    reverse: bool,
    show_invisibles: bool,
    sort_field: SortField,
}

#[derive(PartialEq, Copy, Debug)]
pub enum View {
    Details(Details),
    Lines,
    Grid(Grid),
}

impl Options {

    /// Call getopts on the given slice of command-line strings.
    pub fn getopts(args: &[String]) -> Result<(Options, Vec<String>), Misfire> {
        let mut opts = getopts::Options::new();
        opts.optflag("1", "oneline",   "display one entry per line");
        opts.optflag("a", "all",       "show dot-files");
        opts.optflag("b", "binary",    "use binary prefixes in file sizes");
        opts.optflag("B", "bytes",     "list file sizes in bytes, without prefixes");
        opts.optflag("d", "list-dirs", "list directories as regular files");
        opts.optflag("g", "group",     "show group as well as user");
        opts.optflag("h", "header",    "show a header row at the top");
        opts.optflag("H", "links",     "show number of hard links");
        opts.optflag("l", "long",      "display extended details and attributes");
        opts.optflag("i", "inode",     "show each file's inode number");
        opts.optflag("r", "reverse",   "reverse order of files");
        opts.optflag("R", "recurse",   "recurse into directories");
        opts.optopt ("s", "sort",      "field to sort by", "WORD");
        opts.optflag("S", "blocks",    "show number of file system blocks");
        opts.optflag("T", "tree",      "recurse into subdirectories in a tree view");
        opts.optflag("x", "across",    "sort multi-column view entries across");
        opts.optflag("?", "help",      "show list of command-line options");

        let matches = match opts.parse(args) {
            Ok(m) => m,
            Err(e) => return Err(Misfire::InvalidOptions(e)),
        };

        if matches.opt_present("help") {
            return Err(Misfire::Help(opts.usage("Usage:\n  exa [options] [files...]")));
        }

        let sort_field = match matches.opt_str("sort") {
            Some(word) => try!(SortField::from_word(word)),
            None => SortField::Name,
        };

        let filter = FileFilter {
            reverse:         matches.opt_present("reverse"),
            show_invisibles: matches.opt_present("all"),
            sort_field:      sort_field,
        };

        let path_strs = if matches.free.is_empty() {
            vec![ ".".to_string() ]
        }
        else {
            matches.free.clone()
        };

        Ok((Options {
            dir_action: try!(DirAction::deduce(&matches)),
            view:       try!(View::deduce(&matches, filter)),
            filter:     filter,
        }, path_strs))
    }

    pub fn transform_files<'a>(&self, files: &mut Vec<File<'a>>) {
        self.filter.transform_files(files)
    }
}

impl FileFilter {
    /// Transform the files (sorting, reversing, filtering) before listing them.
    pub fn transform_files<'a>(&self, files: &mut Vec<File<'a>>) {

        if !self.show_invisibles {
            files.retain(|f| !f.is_dotfile());
        }

        match self.sort_field {
            SortField::Unsorted => {},
            SortField::Name => files.sort_by(|a, b| natord::compare(&*a.name, &*b.name)),
            SortField::Size => files.sort_by(|a, b| a.stat.size.cmp(&b.stat.size)),
            SortField::FileInode => files.sort_by(|a, b| a.stat.unstable.inode.cmp(&b.stat.unstable.inode)),
            SortField::Extension => files.sort_by(|a, b| {
                if a.ext.cmp(&b.ext) == Ordering::Equal {
                    Ordering::Equal
                }
                else {
                    a.name.to_ascii_lowercase().cmp(&b.name.to_ascii_lowercase())
                }
            }),
        }

        if self.reverse {
            files.reverse();
        }
    }
}

/// User-supplied field to sort by.
#[derive(PartialEq, Debug, Copy)]
pub enum SortField {
    Unsorted, Name, Extension, Size, FileInode
}

impl SortField {

    /// Find which field to use based on a user-supplied word.
    fn from_word(word: String) -> Result<SortField, Misfire> {
        match word.as_slice() {
            "name"  => Ok(SortField::Name),
            "size"  => Ok(SortField::Size),
            "ext"   => Ok(SortField::Extension),
            "none"  => Ok(SortField::Unsorted),
            "inode" => Ok(SortField::FileInode),
            field   => Err(SortField::none(field))
        }
    }

    /// How to display an error when the word didn't match with anything.
    fn none(field: &str) -> Misfire {
        Misfire::InvalidOptions(getopts::Fail::UnrecognizedOption(format!("--sort {}", field)))
    }
}

/// One of these things could happen instead of listing files.
#[derive(PartialEq, Debug)]
pub enum Misfire {

    /// The getopts crate didn't like these arguments.
    InvalidOptions(getopts::Fail),

    /// The user asked for help. This isn't strictly an error, which is why
    /// this enum isn't named Error!
    Help(String),

    /// Two options were given that conflict with one another
    Conflict(&'static str, &'static str),

    /// An option was given that does nothing when another one either is or
    /// isn't present.
    Useless(&'static str, bool, &'static str),
}

impl Misfire {
    /// The OS return code this misfire should signify.
    pub fn error_code(&self) -> i32 {
        if let Help(_) = *self { 2 }
                          else { 3 }
    }
}

impl fmt::Display for Misfire {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            InvalidOptions(ref e) => write!(f, "{}", e),
            Help(ref text)        => write!(f, "{}", text),
            Conflict(a, b)        => write!(f, "Option --{} conflicts with option {}.", a, b),
            Useless(a, false, b)  => write!(f, "Option --{} is useless without option --{}.", a, b),
            Useless(a, true, b)   => write!(f, "Option --{} is useless given option --{}.", a, b),
        }
    }
}

impl View {
    pub fn deduce(matches: &getopts::Matches, filter: FileFilter) -> Result<View, Misfire> {
        if matches.opt_present("long") {
            if matches.opt_present("across") {
                Err(Misfire::Useless("across", true, "long"))
            }
            else if matches.opt_present("oneline") {
                Err(Misfire::Useless("oneline", true, "long"))
            }
            else {
                let details = Details {
                        columns: try!(Columns::deduce(matches)),
                        header: matches.opt_present("header"),
                        tree: matches.opt_present("recurse"),
                        filter: filter,
                };

                Ok(View::Details(details))
            }
        }
        else if matches.opt_present("binary") {
            Err(Misfire::Useless("binary", false, "long"))
        }
        else if matches.opt_present("bytes") {
            Err(Misfire::Useless("bytes", false, "long"))
        }
        else if matches.opt_present("inode") {
            Err(Misfire::Useless("inode", false, "long"))
        }
        else if matches.opt_present("links") {
            Err(Misfire::Useless("links", false, "long"))
        }
        else if matches.opt_present("header") {
            Err(Misfire::Useless("header", false, "long"))
        }
        else if matches.opt_present("blocks") {
            Err(Misfire::Useless("blocks", false, "long"))
        }
        else if matches.opt_present("oneline") {
            if matches.opt_present("across") {
                Err(Misfire::Useless("across", true, "oneline"))
            }
            else {
                Ok(View::Lines)
            }
        }
        else {
            if let Some((width, _)) = dimensions() {
                let grid = Grid {
                    across: matches.opt_present("across"),
                    console_width: width
                };

                Ok(View::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.
                Ok(View::Lines)
            }
        }
    }
}

#[derive(PartialEq, Debug, Copy)]
pub enum SizeFormat {
    DecimalBytes,
    BinaryBytes,
    JustBytes,
}

impl SizeFormat {
    pub fn deduce(matches: &getopts::Matches) -> Result<SizeFormat, Misfire> {
        let binary = matches.opt_present("binary");
        let bytes  = matches.opt_present("bytes");

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

/// What to do when encountering a directory?
#[derive(PartialEq, Debug, Copy)]
pub enum DirAction {
    AsFile, List, Recurse, Tree
}

impl DirAction {
    pub fn deduce(matches: &getopts::Matches) -> Result<DirAction, Misfire> {
        let recurse = matches.opt_present("recurse");
        let list    = matches.opt_present("list-dirs");
        let tree    = matches.opt_present("tree");

        match (recurse, list, tree) {
            (true,  true,  _    ) => Err(Misfire::Conflict("recurse", "list-dirs")),
            (true,  false, false) => Ok(DirAction::Recurse),
            (true,  false, true ) => Ok(DirAction::Tree),
            (false, true,  _    ) => Ok(DirAction::AsFile),
            (false, false, _    ) => Ok(DirAction::List),
        }
    }
}

#[derive(PartialEq, Copy, Debug)]
pub struct Columns {
    size_format: SizeFormat,
    inode: bool,
    links: bool,
    blocks: bool,
    group: bool,
}

impl Columns {
    pub fn deduce(matches: &getopts::Matches) -> Result<Columns, Misfire> {
        Ok(Columns {
            size_format: try!(SizeFormat::deduce(matches)),
            inode:  matches.opt_present("inode"),
            links:  matches.opt_present("links"),
            blocks: matches.opt_present("blocks"),
            group:  matches.opt_present("group"),
        })
    }

    pub fn for_dir(&self, dir: Option<&Dir>) -> Vec<Column> {
        let mut columns = vec![];

        if self.inode {
            columns.push(Inode);
        }

        columns.push(Permissions);

        if self.links {
            columns.push(HardLinks);
        }

        columns.push(FileSize(self.size_format));

        if self.blocks {
            columns.push(Blocks);
        }

        columns.push(User);

        if self.group {
            columns.push(Group);
        }

        if cfg!(feature="git") {
            if let Some(d) = dir {
                if d.has_git_repo() {
                    columns.push(GitStatus);
                }
            }
        }

        columns
    }
}

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

    fn is_helpful<T>(misfire: Result<T, Misfire>) -> bool {
        match misfire {
            Err(Help(_)) => true,
            _            => false,
        }
    }

    #[test]
    fn help() {
        let opts = Options::getopts(&[ "--help".to_string() ]);
        assert!(is_helpful(opts))
    }

    #[test]
    fn help_with_file() {
        let opts = Options::getopts(&[ "--help".to_string(), "me".to_string() ]);
        assert!(is_helpful(opts))
    }

    #[test]
    fn files() {
        let args = Options::getopts(&[ "this file".to_string(), "that file".to_string() ]).unwrap().1;
        assert_eq!(args, vec![ "this file".to_string(), "that file".to_string() ])
    }

    #[test]
    fn no_args() {
        let args = Options::getopts(&[]).unwrap().1;
        assert_eq!(args, vec![ ".".to_string() ])
    }

    #[test]
    fn file_sizes() {
        let opts = Options::getopts(&[ "--long".to_string(), "--binary".to_string(), "--bytes".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Conflict("binary", "bytes"))
    }

    #[test]
    fn just_binary() {
        let opts = Options::getopts(&[ "--binary".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("binary", false, "long"))
    }

    #[test]
    fn just_bytes() {
        let opts = Options::getopts(&[ "--bytes".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("bytes", false, "long"))
    }

    #[test]
    fn long_across() {
        let opts = Options::getopts(&[ "--long".to_string(), "--across".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("across", true, "long"))
    }

    #[test]
    fn oneline_across() {
        let opts = Options::getopts(&[ "--oneline".to_string(), "--across".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("across", true, "oneline"))
    }

    #[test]
    fn just_header() {
        let opts = Options::getopts(&[ "--header".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("header", false, "long"))
    }

    #[test]
    fn just_inode() {
        let opts = Options::getopts(&[ "--inode".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("inode", false, "long"))
    }

    #[test]
    fn just_links() {
        let opts = Options::getopts(&[ "--links".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("links", false, "long"))
    }

    #[test]
    fn just_blocks() {
        let opts = Options::getopts(&[ "--blocks".to_string() ]);
        assert_eq!(opts.unwrap_err(), Misfire::Useless("blocks", false, "long"))
    }
}
<|endoftext|>


<commit_before>extern crate extra;

use std::io;

#[main]
fn say_hi () {
   io::println("Hello!!");
}

#[test]
fn addition_works () {
   assert! (2 + 2 == 4);
}

#[bench]
fn addition_benchmarked (b: &mut extra::test::BenchHarness) {
   let mut sum = 0;
   b.iter(|| sum += 1)
}
<commit_msg>now updated for Rust 4243cad56bbd2 :D<commit_after>extern crate test;

use std::io;

#[main]
#[cfg(not(test))]
fn say_hi () {
   io::println("Hello!!");
}

#[test]
fn addition_works () {
   io::println("Hello!!");
   assert! (2 + 2 == 4);
}

#[bench]
fn addition_benchmarked (b: &mut test::BenchHarness) {
   let mut sum = 0;
   b.iter(|| sum += 1);
}
<|endoftext|>


<commit_before>use libc;

use partition::*;

pub static RAV1E_INTRA_MODES: &'static [PredictionMode] = &[PredictionMode::H_PRED];

extern {
    #[cfg(test)]
    fn highbd_dc_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    fn highbd_dc_left_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    fn highbd_dc_top_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    #[cfg(test)]
    fn highbd_h_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);

    #[cfg(test)]
    fn highbd_v_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
        bh: libc::c_int, above: *const u16,
        left: *const u16, bd: libc::c_int);
}

pub trait Dim {
    const W : usize;
    const H : usize;
}

pub struct Block4x4;

impl Dim for Block4x4 {
    const W : usize = 4;
    const H : usize = 4;
}

pub struct Block8x8;

impl Dim for Block8x8 {
    const W : usize = 8;
    const H : usize = 8;
}

pub struct Block16x16;

impl Dim for Block16x16 {
    const W : usize = 16;
    const H : usize = 16;
}

pub struct Block32x32;

impl Dim for Block32x32 {
    const W : usize = 32;
    const H : usize = 32;
}

pub trait Intra: Dim {
    fn pred_dc(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        let edges = left[..Self::H].iter().chain(above[..Self::W].iter());
        let len = (Self::W + Self::H) as u32;
        let avg = ((edges.fold(0, |acc, &v| acc + v as u32) + (len >> 1)) / len) as u16;

        for line in output.chunks_mut(stride).take(Self::H) {
            for v in &mut line[..Self::W] {
                *v = avg;
            }
        }
    }

    fn pred_dc_128(output: &mut [u16], stride: usize) {
        for y in 0..Self::H {
            for x in 0..Self::W {
                output[y*stride+x] = 128;
            }
        }
    }

    fn pred_dc_left(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_dc_left_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, Self::W as libc::c_int, Self::H as libc::c_int, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    fn pred_dc_top(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_dc_top_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, Self::W as libc::c_int, Self::H as libc::c_int, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    fn pred_h(output: &mut [u16], stride: usize, left: &[u16]) {
        for (line, l) in output.chunks_mut(stride).zip(left[..Self::H].iter()) {
            for v in &mut line[..Self::W] {
                *v = *l;
            }
        }
    }

    fn pred_v(output: &mut [u16], stride: usize, above: &[u16]) {
        for line in output.chunks_mut(stride).take(Self::H) {
            line[..Self::W].clone_from_slice(&above[..Self::W])
        }
    }
}

impl Intra for Block4x4 {}
impl Intra for Block8x8 {}
impl Intra for Block16x16 {}
impl Intra for Block32x32 {}

#[cfg(test)]
pub mod test {
    use super::*;
    use rand::{ChaChaRng, Rng};

    const MAX_ITER: usize = 50000;

    fn setup_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>, Vec<u16>, Vec<u16>) {
        let output = vec![0u16; 32 * 32];
        let above: Vec<u16> = (0..32).map(|_| ra.gen()).collect();
        let left: Vec<u16> = (0..32).map(|_| ra.gen()).collect();

        let o1 = output.clone();
        let o2 = output.clone();

        (above, left, o1, o2)
    }

    fn pred_dc_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_dc_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    pub fn pred_h_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_h_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    pub fn pred_v_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_v_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    fn do_dc_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_dc_4x4(&mut o1, 32, &above[..4], &left[..4]);
        Block4x4::pred_dc(&mut o2, 32, &above[..4], &left[..4]);

        (o1, o2)
    }

    fn do_h_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_h_4x4(&mut o1, 32, &above[..4], &left[..4]);
        Block4x4::pred_h(&mut o2, 32, &left[..4]);

        (o1, o2)
    }

    fn do_v_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_v_4x4(&mut o1, 32, &above[..4], &left[..4]);
        Block4x4::pred_v(&mut o2, 32, &above[..4]);

        (o1, o2)
    }

    fn assert_same(o2: Vec<u16>) {
        for l in o2.chunks(32).take(4) {
            for v in l[..4].windows(2) {
                assert_eq!(v[0], v[1]);
            }
        }
    }

    #[test]
    fn pred_matches() {
        let mut ra = ChaChaRng::new_unseeded();
        for _ in 0..MAX_ITER {
            let (o1, o2) = do_dc_pred(&mut ra);
            assert_eq!(o1, o2);

            let (o1, o2) = do_h_pred(&mut ra);
            assert_eq!(o1, o2);

            let (o1, o2) = do_v_pred(&mut ra);
            assert_eq!(o1, o2);
        }
    }

    #[test]
    fn pred_same() {
        let mut ra = ChaChaRng::new_unseeded();
        for _ in 0..MAX_ITER {
            let (_, o2) = do_dc_pred(&mut ra);

            assert_same(o2)
        }
    }

    #[test]
    fn pred_max() {
        let max12bit = 4096 - 1;
        let above = [max12bit; 32];
        let left = [max12bit; 32];

        let mut o = vec![0u16; 32 * 32];

        Block4x4::pred_dc(&mut o, 32, &above[..4], &left[..4]);

        for l in o.chunks(32).take(4) {
            for v in l[..4].iter() {
                assert_eq!(*v, max12bit);
            }
        }

        Block4x4::pred_h(&mut o, 32, &left[..4]);

        for l in o.chunks(32).take(4) {
          for v in l[..4].iter() {
            assert_eq!(*v, max12bit);
          }
        }

        Block4x4::pred_v(&mut o, 32, &above[..4]);

        for l in o.chunks(32).take(4) {
          for v in l[..4].iter() {
            assert_eq!(*v, max12bit);
          }
        }
    }
}
<commit_msg>Actually use DC pred rather than H pred.<commit_after>use libc;

use partition::*;

pub static RAV1E_INTRA_MODES: &'static [PredictionMode] = &[PredictionMode::DC_PRED];

extern {
    #[cfg(test)]
    fn highbd_dc_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    fn highbd_dc_left_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    fn highbd_dc_top_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);
    #[cfg(test)]
    fn highbd_h_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
                           bh: libc::c_int, above: *const u16,
                           left: *const u16, bd: libc::c_int);

    #[cfg(test)]
    fn highbd_v_predictor(dst: *mut u16, stride: libc::ptrdiff_t, bw: libc::c_int,
        bh: libc::c_int, above: *const u16,
        left: *const u16, bd: libc::c_int);
}

pub trait Dim {
    const W : usize;
    const H : usize;
}

pub struct Block4x4;

impl Dim for Block4x4 {
    const W : usize = 4;
    const H : usize = 4;
}

pub struct Block8x8;

impl Dim for Block8x8 {
    const W : usize = 8;
    const H : usize = 8;
}

pub struct Block16x16;

impl Dim for Block16x16 {
    const W : usize = 16;
    const H : usize = 16;
}

pub struct Block32x32;

impl Dim for Block32x32 {
    const W : usize = 32;
    const H : usize = 32;
}

pub trait Intra: Dim {
    fn pred_dc(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        let edges = left[..Self::H].iter().chain(above[..Self::W].iter());
        let len = (Self::W + Self::H) as u32;
        let avg = ((edges.fold(0, |acc, &v| acc + v as u32) + (len >> 1)) / len) as u16;

        for line in output.chunks_mut(stride).take(Self::H) {
            for v in &mut line[..Self::W] {
                *v = avg;
            }
        }
    }

    fn pred_dc_128(output: &mut [u16], stride: usize) {
        for y in 0..Self::H {
            for x in 0..Self::W {
                output[y*stride+x] = 128;
            }
        }
    }

    fn pred_dc_left(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_dc_left_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, Self::W as libc::c_int, Self::H as libc::c_int, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    fn pred_dc_top(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_dc_top_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, Self::W as libc::c_int, Self::H as libc::c_int, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    fn pred_h(output: &mut [u16], stride: usize, left: &[u16]) {
        for (line, l) in output.chunks_mut(stride).zip(left[..Self::H].iter()) {
            for v in &mut line[..Self::W] {
                *v = *l;
            }
        }
    }

    fn pred_v(output: &mut [u16], stride: usize, above: &[u16]) {
        for line in output.chunks_mut(stride).take(Self::H) {
            line[..Self::W].clone_from_slice(&above[..Self::W])
        }
    }
}

impl Intra for Block4x4 {}
impl Intra for Block8x8 {}
impl Intra for Block16x16 {}
impl Intra for Block32x32 {}

#[cfg(test)]
pub mod test {
    use super::*;
    use rand::{ChaChaRng, Rng};

    const MAX_ITER: usize = 50000;

    fn setup_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>, Vec<u16>, Vec<u16>) {
        let output = vec![0u16; 32 * 32];
        let above: Vec<u16> = (0..32).map(|_| ra.gen()).collect();
        let left: Vec<u16> = (0..32).map(|_| ra.gen()).collect();

        let o1 = output.clone();
        let o2 = output.clone();

        (above, left, o1, o2)
    }

    fn pred_dc_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_dc_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    pub fn pred_h_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_h_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    pub fn pred_v_4x4(output: &mut [u16], stride: usize, above: &[u16], left: &[u16]) {
        unsafe {
            highbd_v_predictor(output.as_mut_ptr(), stride as libc::ptrdiff_t, 4, 4, above.as_ptr(), left.as_ptr(), 8);
        }
    }

    fn do_dc_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_dc_4x4(&mut o1, 32, &above[..4], &left[..4]);
        Block4x4::pred_dc(&mut o2, 32, &above[..4], &left[..4]);

        (o1, o2)
    }

    fn do_h_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_h_4x4(&mut o1, 32, &above[..4], &left[..4]);
        Block4x4::pred_h(&mut o2, 32, &left[..4]);

        (o1, o2)
    }

    fn do_v_pred(ra: &mut ChaChaRng) -> (Vec<u16>, Vec<u16>) {
        let (above, left, mut o1, mut o2) = setup_pred(ra);

        pred_v_4x4(&mut o1, 32, &above[..4], &left[..4]);
        Block4x4::pred_v(&mut o2, 32, &above[..4]);

        (o1, o2)
    }

    fn assert_same(o2: Vec<u16>) {
        for l in o2.chunks(32).take(4) {
            for v in l[..4].windows(2) {
                assert_eq!(v[0], v[1]);
            }
        }
    }

    #[test]
    fn pred_matches() {
        let mut ra = ChaChaRng::new_unseeded();
        for _ in 0..MAX_ITER {
            let (o1, o2) = do_dc_pred(&mut ra);
            assert_eq!(o1, o2);

            let (o1, o2) = do_h_pred(&mut ra);
            assert_eq!(o1, o2);

            let (o1, o2) = do_v_pred(&mut ra);
            assert_eq!(o1, o2);
        }
    }

    #[test]
    fn pred_same() {
        let mut ra = ChaChaRng::new_unseeded();
        for _ in 0..MAX_ITER {
            let (_, o2) = do_dc_pred(&mut ra);

            assert_same(o2)
        }
    }

    #[test]
    fn pred_max() {
        let max12bit = 4096 - 1;
        let above = [max12bit; 32];
        let left = [max12bit; 32];

        let mut o = vec![0u16; 32 * 32];

        Block4x4::pred_dc(&mut o, 32, &above[..4], &left[..4]);

        for l in o.chunks(32).take(4) {
            for v in l[..4].iter() {
                assert_eq!(*v, max12bit);
            }
        }

        Block4x4::pred_h(&mut o, 32, &left[..4]);

        for l in o.chunks(32).take(4) {
          for v in l[..4].iter() {
            assert_eq!(*v, max12bit);
          }
        }

        Block4x4::pred_v(&mut o, 32, &above[..4]);

        for l in o.chunks(32).take(4) {
          for v in l[..4].iter() {
            assert_eq!(*v, max12bit);
          }
        }
    }
}
<|endoftext|>


<commit_before>use std;
use std::fs;
use variables::*;
use variables::LpExpression::*;
use variables::Constraint::*;
use std::rc::Rc;
use std::collections::HashMap;
use solvers::*;

//use variables::LpExpression::{AddExpr, MulExpr};
use std::ops::{AddAssign};

//use std::collections::HashMap;

/// Enum helping to specify the objective function of the linear problem.
///
/// # Exemples:
///
/// ```
/// use lp_modeler::problem::{LpObjective, LpProblem};
///
/// let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
/// ```
#[derive(Debug, PartialEq)]
pub enum LpObjective {
    Minimize,
    Maximize
}

/// Structure used for creating the model and solving a linear problem.
///
/// # Exemples:
///
/// ```
/// use lp_modeler::problem::{LpObjective, LpProblem};
/// use lp_modeler::operations::{LpOperations};
/// use lp_modeler::variables::{LpVariable, LpType};
/// use lp_modeler::solvers::Solver;
///
/// let ref a = LpVariable::new("a", LpType::Integer);
/// let ref b = LpVariable::new("b", LpType::Integer);
///
/// let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
/// problem += (a + b).le(100.0);
/// problem += a.ge(b);
/// problem += 2.0*a + 3.0*b;
///
/// problem.solve(Solver::Cbc);
/// ```
#[derive(Debug)]
pub struct LpProblem {
    name: &'static str,
    objective_type: LpObjective,
    obj_expr: Option<LpExpression>,
    constraints: Vec<LpConstraint>

}

impl LpProblem {

    /// Create a new problem
    pub fn new(name: &'static str, objective: LpObjective) -> LpProblem {
        LpProblem { name: name, objective_type: objective, obj_expr: None, constraints: Vec::new() }
    }

    // TODO: Call once and pass into parameter
    // TODO: Check variables on the objective function
    fn variables(&self) -> HashMap<String, &LpExpression> {

        fn var<'a>(expr: &'a LpExpression, lst: &mut Vec<(String, &'a LpExpression)>) {
            match expr {
                &BinaryVariable {ref name, ..} | &IntegerVariable {ref name, ..} | &ContinuousVariable {ref name, ..} => { lst.push((name.clone(), expr)); },
                &MulExpr(_, ref e) => { var(&*e, lst); },
                &AddExpr(ref e1, ref e2) | &SubExpr(ref e1, ref e2) => {
                    var(&*e1, lst);
                    var(&*e2, lst);
                },
                _ => ()
            }
        }

        let mut lst: Vec<_> = Vec::new();
        for e in &self.constraints {
            var(&e.0, &mut lst);
        }
        lst.iter().map(|&(ref n, ref x)| (n.clone(), *x)).collect::<HashMap<String, &LpExpression>>()
    }

    fn objective_string(&self) -> String {

        if let Some(ref expr) = self.obj_expr {
            expr.to_string()
        } else {
            String::new()
        }
    }

    fn constraints_string(&self) -> String {
        let mut res = String::new();
        let mut cstrs = self.constraints.iter();
        let mut index = 1;
        while let Some(&LpConstraint(ref e1, ref op, ref e2)) = cstrs.next() {
            res.push_str("  c");
            res.push_str(&index.to_string());
            res.push_str(": ");
            index += 1;
            res.push_str(&e1.to_string());
            match op {
                &GreaterOrEqual => res.push_str(" >= "),
                &LessOrEqual => res.push_str(" <= "),
                &Equal => res.push_str(" = "),
            }
            res.push_str(&e2.to_string());
            res.push_str("\n");
        }
        res
    }

    fn bounds_string(&self) -> String {
        let mut res = String::new();
        for (_,v) in self.variables() {
            match v {
                &IntegerVariable { ref name, lower_bound, upper_bound }
                    | &ContinuousVariable { ref name, lower_bound, upper_bound } => {
                    if let Some(l) = lower_bound {
                        res.push_str("  ");
                        res.push_str(&l.to_string());
                        res.push_str(" <= ");
                        res.push_str(&name);
                        if let Some(u) = upper_bound {
                            res.push_str(" <= ");
                            res.push_str(&u.to_string());
                        }
                        res.push_str("\n");
                    } else if let Some(u) = upper_bound {
                        res.push_str("  ");
                        res.push_str(&name);
                        res.push_str(" <= ");
                        res.push_str(&u.to_string());
                        res.push_str("\n");
                    } else {
                        match v {
                            &ContinuousVariable {..} => {
                                res.push_str("  ");
                                res.push_str(&name);
                                res.push_str(" free\n");
                            }, // TODO: IntegerVar => -INF to INF
                            _ => ()
                        }
                    }
                },
                _ => (),
            }
        }
        res
    }

    fn generals_string(&self) -> String {
        let mut res = String::new();
        for (_,v) in self.variables() {
            match v {
                &IntegerVariable { ref name, .. } => {
                    res.push_str(name);
                    res.push_str(" ");
                },
                _ => (),
            }
        }
        res
    }

    fn binaries_string(&self) -> String {
        let mut res = String::new();
        for (_,v) in self.variables() {
            match v {
                &BinaryVariable { ref name } => {
                    res.push_str(name);
                    res.push_str(" ");
                },
                _ => (),
            }
        }
        res
    }

    pub fn write_lp<T: Into<String>>(&self, file_name: T) -> std::io::Result<()> {
        use std::fs::File;
        use std::io::prelude::*;

        let mut buffer = try!(File::create(file_name.into()));

        try!(buffer.write("\\ ".as_bytes()));
        try!(buffer.write(self.name.as_bytes()));
        try!(buffer.write("\n\n".as_bytes()));

        // Write objectives
        match self.objective_type {
            LpObjective::Maximize => { try!(buffer.write(b"Maximize\n  ")); },
            LpObjective::Minimize => { try!(buffer.write(b"Minimize\n  ")); },
        }
        let obj_str = self.objective_string();
        try!(buffer.write(obj_str.as_bytes()));

        // Write constraints
        let cstr_str = self.constraints_string();
        if cstr_str.len() > 0 {
            try!(buffer.write(b"\n\nSubject To\n"));
            try!(buffer.write(cstr_str.as_bytes()));
        }

        // Write bounds for Integer and Continuous
        let bound_str = self.bounds_string();
        if bound_str.len() > 0 {
            try!(buffer.write(b"\nBounds\n"));
            try!(buffer.write(bound_str.as_bytes()));
        }

        // Write Integer vars
        let generals_str = self.generals_string();
        if generals_str.len() > 0 {
            try!(buffer.write(b"\nGenerals\n  "));
            try!(buffer.write(generals_str.as_bytes()));
            try!(buffer.write(b"\n"));
        }

        // Write Binaries vars
        let binaries_str = self.binaries_string();
        if binaries_str.len() > 0 {
            try!(buffer.write(b"\nBinary\n  "));
            try!(buffer.write(binaries_str.as_bytes()));
            try!(buffer.write(b"\n"));
        }

        try!(buffer.write(b"\nEnd"));

        Ok(())

    }

    /// Solve the LP model
    pub fn solve<T: SolverTrait>(&self, s: T) -> Result<(Status, HashMap<String,f32>), String> {

        let file_model = "test.lp";
        let file_solution = "sol.sol";

        match self.write_lp(file_model) {
            Ok(_) => {
                let status = try!(s.run_solver(file_model, file_solution));
                let (status_read, res) = try!(s.read_solution(file_solution));
                let _ = fs::remove_file(file_model);
                if s.name().contains("gurobi") {
                    match status {
                        Some(s) => Ok((s, res)),
                        _ => Ok((status_read, res))
                    }
                }else{
                    Ok((status_read, res))
                }
            },
            Err(e) => Err(e.to_string())
        }
    }
}

/// Add constraints
impl AddAssign<LpConstraint> for LpProblem {
    fn add_assign(&mut self, _rhs: LpConstraint) {
        self.constraints.push(_rhs);
    }
}

/// Add an expression as an objective function
impl<T> AddAssign<T> for LpProblem where T: Into<LpExpression>{
    fn add_assign(&mut self, _rhs: T) {
        //TODO: improve without cloning
        if let Some(e) = self.obj_expr.clone() {
            self.obj_expr = Some(AddExpr(Rc::new(_rhs.into()), Rc::new(e.clone())));
        } else {
            self.obj_expr = Some(_rhs.into());
        }
    }
}

impl ToString for LpExpression {
    fn to_string(&self) -> String {

        fn dfs(expr: &LpExpression, acc: &String) -> String {
            match expr {
                &MulExpr(ref e1, ref e2) => {
                    e1.to_string() + " " + &e2.to_string()
                },
                &AddExpr(ref e1, ref e2) => {
                    e1.to_string() + " + " + &e2.to_string()
                },
                &SubExpr(ref e1, ref e2) => {
                    e1.to_string() + " - " + &e2.to_string()
                },
                &BinaryVariable {name: ref n, .. } => {
                    acc.clone() + n
                },
                &IntegerVariable {name: ref n, .. } => {
                    acc.clone() + n
                },
                &ContinuousVariable {name: ref n, .. } => {
                    acc.clone() + n
                },
                &LitVal(n) => {
                    acc.clone() + &n.to_string()
                },
                _ => acc.clone()
            }
        }

        dfs(self, &String::new())
    }
}
<commit_msg>Trait for solver<commit_after>use std;
use std::fs;
use variables::*;
use variables::LpExpression::*;
use variables::Constraint::*;
use std::rc::Rc;
use std::collections::HashMap;
use solvers::*;

//use variables::LpExpression::{AddExpr, MulExpr};
use std::ops::{AddAssign};

//use std::collections::HashMap;

/// Enum helping to specify the objective function of the linear problem.
///
/// # Exemples:
///
/// ```
/// use lp_modeler::problem::{LpObjective, LpProblem};
///
/// let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
/// ```
#[derive(Debug, PartialEq)]
pub enum LpObjective {
    Minimize,
    Maximize
}

/// Structure used for creating the model and solving a linear problem.
///
/// # Exemples:
///
/// ```
/// use lp_modeler::problem::{LpObjective, LpProblem};
/// use lp_modeler::operations::{LpOperations};
/// use lp_modeler::variables::{LpVariable, LpType};
/// use lp_modeler::solvers::Solver;
///
/// let ref a = LpVariable::new("a", LpType::Integer);
/// let ref b = LpVariable::new("b", LpType::Integer);
///
/// let mut problem = LpProblem::new("One Problem", LpObjective::Maximize);
/// problem += (a + b).le(100.0);
/// problem += a.ge(b);
/// problem += 2.0*a + 3.0*b;
///
/// problem.solve(Solver::Cbc);
/// ```
#[derive(Debug)]
pub struct LpProblem {
    name: &'static str,
    objective_type: LpObjective,
    obj_expr: Option<LpExpression>,
    constraints: Vec<LpConstraint>

}

impl LpProblem {

    /// Create a new problem
    pub fn new(name: &'static str, objective: LpObjective) -> LpProblem {
        LpProblem { name: name, objective_type: objective, obj_expr: None, constraints: Vec::new() }
    }

    // TODO: Call once and pass into parameter
    // TODO: Check variables on the objective function
    fn variables(&self) -> HashMap<String, &LpExpression> {

        fn var<'a>(expr: &'a LpExpression, lst: &mut Vec<(String, &'a LpExpression)>) {
            match expr {
                &BinaryVariable {ref name, ..} | &IntegerVariable {ref name, ..} | &ContinuousVariable {ref name, ..} => { lst.push((name.clone(), expr)); },
                &MulExpr(_, ref e) => { var(&*e, lst); },
                &AddExpr(ref e1, ref e2) | &SubExpr(ref e1, ref e2) => {
                    var(&*e1, lst);
                    var(&*e2, lst);
                },
                _ => ()
            }
        }

        let mut lst: Vec<_> = Vec::new();
        for e in &self.constraints {
            var(&e.0, &mut lst);
        }
        lst.iter().map(|&(ref n, ref x)| (n.clone(), *x)).collect::<HashMap<String, &LpExpression>>()
    }

    fn objective_string(&self) -> String {

        if let Some(ref expr) = self.obj_expr {
            expr.to_string()
        } else {
            String::new()
        }
    }

    fn constraints_string(&self) -> String {
        let mut res = String::new();
        let mut cstrs = self.constraints.iter();
        let mut index = 1;
        while let Some(&LpConstraint(ref e1, ref op, ref e2)) = cstrs.next() {
            res.push_str("  c");
            res.push_str(&index.to_string());
            res.push_str(": ");
            index += 1;
            res.push_str(&e1.to_string());
            match op {
                &GreaterOrEqual => res.push_str(" >= "),
                &LessOrEqual => res.push_str(" <= "),
                &Equal => res.push_str(" = "),
            }
            res.push_str(&e2.to_string());
            res.push_str("\n");
        }
        res
    }

    fn bounds_string(&self) -> String {
        let mut res = String::new();
        for (_,v) in self.variables() {
            match v {
                &IntegerVariable { ref name, lower_bound, upper_bound }
                    | &ContinuousVariable { ref name, lower_bound, upper_bound } => {
                    if let Some(l) = lower_bound {
                        res.push_str("  ");
                        res.push_str(&l.to_string());
                        res.push_str(" <= ");
                        res.push_str(&name);
                        if let Some(u) = upper_bound {
                            res.push_str(" <= ");
                            res.push_str(&u.to_string());
                        }
                        res.push_str("\n");
                    } else if let Some(u) = upper_bound {
                        res.push_str("  ");
                        res.push_str(&name);
                        res.push_str(" <= ");
                        res.push_str(&u.to_string());
                        res.push_str("\n");
                    } else {
                        match v {
                            &ContinuousVariable {..} => {
                                res.push_str("  ");
                                res.push_str(&name);
                                res.push_str(" free\n");
                            }, // TODO: IntegerVar => -INF to INF
                            _ => ()
                        }
                    }
                },
                _ => (),
            }
        }
        res
    }

    fn generals_string(&self) -> String {
        let mut res = String::new();
        for (_,v) in self.variables() {
            match v {
                &IntegerVariable { ref name, .. } => {
                    res.push_str(name);
                    res.push_str(" ");
                },
                _ => (),
            }
        }
        res
    }

    fn binaries_string(&self) -> String {
        let mut res = String::new();
        for (_,v) in self.variables() {
            match v {
                &BinaryVariable { ref name } => {
                    res.push_str(name);
                    res.push_str(" ");
                },
                _ => (),
            }
        }
        res
    }

    pub fn write_lp<T: Into<String>>(&self, file_name: T) -> std::io::Result<()> {
        use std::fs::File;
        use std::io::prelude::*;

        let mut buffer = try!(File::create(file_name.into()));

        try!(buffer.write("\\ ".as_bytes()));
        try!(buffer.write(self.name.as_bytes()));
        try!(buffer.write("\n\n".as_bytes()));

        // Write objectives
        match self.objective_type {
            LpObjective::Maximize => { try!(buffer.write(b"Maximize\n  ")); },
            LpObjective::Minimize => { try!(buffer.write(b"Minimize\n  ")); },
        }
        let obj_str = self.objective_string();
        try!(buffer.write(obj_str.as_bytes()));

        // Write constraints
        let cstr_str = self.constraints_string();
        if cstr_str.len() > 0 {
            try!(buffer.write(b"\n\nSubject To\n"));
            try!(buffer.write(cstr_str.as_bytes()));
        }

        // Write bounds for Integer and Continuous
        let bound_str = self.bounds_string();
        if bound_str.len() > 0 {
            try!(buffer.write(b"\nBounds\n"));
            try!(buffer.write(bound_str.as_bytes()));
        }

        // Write Integer vars
        let generals_str = self.generals_string();
        if generals_str.len() > 0 {
            try!(buffer.write(b"\nGenerals\n  "));
            try!(buffer.write(generals_str.as_bytes()));
            try!(buffer.write(b"\n"));
        }

        // Write Binaries vars
        let binaries_str = self.binaries_string();
        if binaries_str.len() > 0 {
            try!(buffer.write(b"\nBinary\n  "));
            try!(buffer.write(binaries_str.as_bytes()));
            try!(buffer.write(b"\n"));
        }

        try!(buffer.write(b"\nEnd"));

        Ok(())

    }

    /// Solve the LP model
    pub fn solve<T: SolverTrait>(&self, s: T) -> Result<(Status, HashMap<String,f32>), String> {

        let file_model = "test.lp";
        let file_solution = "sol.sol";

        match self.write_lp(file_model) {
            Ok(_) => {
                let status = try!(s.run_solver(file_model, file_solution));
                let (status_read, res) = try!(s.read_solution(file_solution));
                let _ = fs::remove_file(file_model);
                match status {
                    Some(s) => Ok((s, res)),
                    _ => Ok((status_read, res))
                }
            },
            Err(e) => Err(e.to_string())
        }
    }
}

/// Add constraints
impl AddAssign<LpConstraint> for LpProblem {
    fn add_assign(&mut self, _rhs: LpConstraint) {
        self.constraints.push(_rhs);
    }
}

/// Add an expression as an objective function
impl<T> AddAssign<T> for LpProblem where T: Into<LpExpression>{
    fn add_assign(&mut self, _rhs: T) {
        //TODO: improve without cloning
        if let Some(e) = self.obj_expr.clone() {
            self.obj_expr = Some(AddExpr(Rc::new(_rhs.into()), Rc::new(e.clone())));
        } else {
            self.obj_expr = Some(_rhs.into());
        }
    }
}

impl ToString for LpExpression {
    fn to_string(&self) -> String {

        fn dfs(expr: &LpExpression, acc: &String) -> String {
            match expr {
                &MulExpr(ref e1, ref e2) => {
                    e1.to_string() + " " + &e2.to_string()
                },
                &AddExpr(ref e1, ref e2) => {
                    e1.to_string() + " + " + &e2.to_string()
                },
                &SubExpr(ref e1, ref e2) => {
                    e1.to_string() + " - " + &e2.to_string()
                },
                &BinaryVariable {name: ref n, .. } => {
                    acc.clone() + n
                },
                &IntegerVariable {name: ref n, .. } => {
                    acc.clone() + n
                },
                &ContinuousVariable {name: ref n, .. } => {
                    acc.clone() + n
                },
                &LitVal(n) => {
                    acc.clone() + &n.to_string()
                },
                _ => acc.clone()
            }
        }

        dfs(self, &String::new())
    }
}
<|endoftext|>


<commit_before>#![allow(unstable)]

use std::old_io::process::Command;
use std::str;

static PROGNAME: &'static str = "./seq";

#[test]
fn test_count_up() {
    let p = Command::new(PROGNAME).args(&["10"]).output().unwrap();
    let out = str::from_utf8(p.output.as_slice()).unwrap();
    assert_eq!(out, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
}

#[test]
fn test_count_down() {
    let p = Command::new(PROGNAME).args(&["--", "5", "-1", "1"]).output().unwrap();
    let out = str::from_utf8(p.output.as_slice()).unwrap();
    assert_eq!(out, "5\n4\n3\n2\n1\n");
}

#[test]
fn test_separator_and_terminator() {
    let p = Command::new(PROGNAME).args(&["-s", ",", "-t", "!", "2", "6"]).output().unwrap();
    let out = str::from_utf8(p.output.as_slice()).unwrap();
    assert_eq!(out, "2,3,4,5,6!");
}

#[test]
fn test_equalize_widths() {
    let p = Command::new(PROGNAME).args(&["-w", "5", "10"]).output().unwrap();
    let out = str::from_utf8(p.output.as_slice()).unwrap();
    assert_eq!(out, "05\n06\n07\n08\n09\n10\n");
}
<commit_msg>Fix broken seq tests.<commit_after>use std::process::Command;
use std::str;

static PROGNAME: &'static str = "./seq";

#[test]
fn test_count_up() {
    let p = Command::new(PROGNAME).args(&["10"]).output().unwrap();
    let out = str::from_utf8(&p.stdout).unwrap();
    assert_eq!(out, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n");
}

#[test]
fn test_count_down() {
    let p = Command::new(PROGNAME).args(&["--", "5", "-1", "1"]).output().unwrap();
    let out = str::from_utf8(&p.stdout).unwrap();
    assert_eq!(out, "5\n4\n3\n2\n1\n");
}

#[test]
fn test_separator_and_terminator() {
    let p = Command::new(PROGNAME).args(&["-s", ",", "-t", "!", "2", "6"]).output().unwrap();
    let out = str::from_utf8(&p.stdout).unwrap();
    assert_eq!(out, "2,3,4,5,6!");
}

#[test]
fn test_equalize_widths() {
    let p = Command::new(PROGNAME).args(&["-w", "5", "10"]).output().unwrap();
    let out = str::from_utf8(&p.stdout).unwrap();
    assert_eq!(out, "05\n06\n07\n08\n09\n10\n");
}
<|endoftext|>


<commit_before>#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate redis;
extern crate redis_serde;

use std::collections::HashMap;

use serde::Deserialize;
use redis_serde::Deserializer;

use redis::Value;

#[test]
fn deserialize_unit_struct_string() {
    let v = Value::Data(b"hello".to_vec());

    #[derive(Deserialize, Debug, PartialEq)]
    struct Unit(String);

    let mut de = Deserializer::new(v).unwrap();
    let actual: Unit = Deserialize::deserialize(&mut de).unwrap();

    let expected = Unit("hello".to_owned());
    assert_eq!(expected, actual);
}

#[test]
fn deserialize_unit_struct_u8_redis_int() {
    let num = 5u8;
    let v = Value::Int(num as i64);

    #[derive(Deserialize, Debug, PartialEq)]
    struct IntUnit(u8);

    let mut de = Deserializer::new(v).unwrap();
    let actual: IntUnit = Deserialize::deserialize(&mut de).unwrap();

    let expected = IntUnit(num);
    assert_eq!(expected, actual);
}

#[test]
fn deserialize_tuple() {
    let v = vec![Value::Int(5), Value::Data(b"hello".to_vec())];

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: (u8, String) = Deserialize::deserialize(&mut de).unwrap();

    let expected = (5, "hello".to_owned());
    assert_eq!(expected, actual);
}

#[test]
fn deserialize_struct() {
    let v = vec![
        Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
        Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec())
    ];

    #[derive(Debug, Deserialize, PartialEq)]
    struct Simple {
        a: String,
        b: String,
    }

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Simple = Deserialize::deserialize(&mut de).unwrap();

    let expected = Simple {
        a: "apple".to_owned(),
        b: "banana".to_owned(),
    };

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_hash_map_strings() {
    let v = vec![
        Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
        Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec())
    ];

    let mut expected = HashMap::new();
    expected.insert("a".to_string(), "apple".to_string());
    expected.insert("b".to_string(), "banana".to_string());

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: HashMap<String, String> = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_float() {
    let v = Value::Data(b"3.14159".to_vec());

    let expected = "3.14159".parse::<f32>().unwrap();

    let mut de = Deserializer::new(v).unwrap();
    let actual: f32 = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(actual, expected);
}

#[test]
fn deserialize_hash_map_string_u8() {
    let v = vec![
        Value::Data(b"a".to_vec()), Value::Data(b"1".to_vec()),
        Value::Data(b"b".to_vec()), Value::Data(b"2".to_vec())
    ];

    let mut expected = HashMap::new();
    expected.insert("a".to_string(), 1);
    expected.insert("b".to_string(), 2);

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: HashMap<String, u8> = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_struct_out_of_order() {
    let v = vec![
        Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec()),
        Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
    ];

    #[derive(Debug, Deserialize, PartialEq)]
    struct Simple {
        a: String,
        b: String,
    }

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Simple = Deserialize::deserialize(&mut de).unwrap();

    let expected = Simple {
        a: "apple".to_owned(),
        b: "banana".to_owned(),

    };

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_struct_extra_keys() {
    let v = vec![
        Value::Data(b"c".to_vec()), Value::Data(b"cranberry".to_vec()),
        Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec()),
        Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
    ];

    #[derive(Debug, Deserialize, PartialEq)]
    struct Simple {
        a: String,
        b: String,
    }

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Simple = Deserialize::deserialize(&mut de).unwrap();

    let expected = Simple {
        a: "apple".to_owned(),
        b: "banana".to_owned(),
    };

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_enum() {
    let v = Value::Data(b"Orange".to_vec());

    #[derive(Debug, Deserialize, PartialEq)]
    enum Fruit {
        Orange,
        Apple
    }

    let mut de = Deserializer::new(v).unwrap();
    let actual: Fruit = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(Fruit::Orange, actual);
}

#[test]
fn deserialize_option() {
    let mut de = Deserializer::new(Value::Nil).unwrap();
    let actual: Option<u8> = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(None, actual);
}

#[test]
fn deserialize_complex_struct() {
    let v = vec![
        Value::Data(b"num".to_vec()), Value::Data(b"10".to_vec()),
        Value::Data(b"opt".to_vec()), Value::Data(b"yes".to_vec()),
        Value::Data(b"s".to_vec()), Value::Data(b"yarn".to_vec()),
    ];

    #[derive(Debug, Deserialize, PartialEq)]
    struct Complex {
        num: usize,
        opt: Option<String>,
        not: Option<String>,
        s: String,
    }

    let expected = Complex {
        num: 10,
        opt: Some("yes".to_owned()),
        not: None,
        s: "yarn".to_owned()
    };

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Complex = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_vec_of_strings() {
    let v = vec![
        Value::Data(b"first".to_vec()),
        Value::Data(b"second".to_vec()),
        Value::Data(b"third".to_vec()),
    ];

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Vec<String> = Deserialize::deserialize(&mut de).unwrap();

    let expected = vec!["first".to_string(), "second".to_string(), "third".to_string()];
    assert_eq!(expected, actual);
}

#[test]
fn deserialize_vec_of_newtype() {
    let v = vec![
        Value::Data(b"first".to_vec()),
        Value::Data(b"second".to_vec()),
        Value::Data(b"third".to_vec()),
    ];

    #[derive(Debug, PartialEq, Deserialize)]
    struct Rank(String);

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Vec<Rank> = Deserialize::deserialize(&mut de).unwrap();

    let expected = vec![
        Rank("first".into()),
        Rank("second".into()),
        Rank("third".into()),
    ];

    assert_eq!(expected, actual);
}

/// Test for deserializing pipelined structs
///
/// In pipeline mode, there is nested bulk items that are returned. The original implementation did
/// not handle this.
#[test]
fn deserialize_pipelined_hmap() {
    let values =
        Value::Bulk(vec![
            Value::Bulk(vec![
                Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
                Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec())
            ]),
            Value::Bulk(vec![
                Value::Data(b"a".to_vec()), Value::Data(b"art".to_vec()),
                Value::Data(b"b".to_vec()), Value::Data(b"bold".to_vec())
            ])
        ]);

    #[derive(Debug, Deserialize, PartialEq)]
    struct Simple {
        a: String,
        b: String,
    }

    let mut de = Deserializer::new(values).unwrap();
    let actual: Vec<Simple> = Deserialize::deserialize(&mut de).unwrap();

    let expected = vec![Simple {
        a: "apple".to_owned(),
        b: "banana".to_owned(),
    }, Simple {
        a: "art".to_owned(),
        b: "bold".to_owned(),
    }];

    assert_eq!(expected, actual);
}
<commit_msg>Add test for single pipelined hmap<commit_after>#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate redis;
extern crate redis_serde;

use std::collections::HashMap;

use serde::Deserialize;
use redis_serde::Deserializer;

use redis::Value;

#[test]
fn deserialize_unit_struct_string() {
    let v = Value::Data(b"hello".to_vec());

    #[derive(Deserialize, Debug, PartialEq)]
    struct Unit(String);

    let mut de = Deserializer::new(v).unwrap();
    let actual: Unit = Deserialize::deserialize(&mut de).unwrap();

    let expected = Unit("hello".to_owned());
    assert_eq!(expected, actual);
}

#[test]
fn deserialize_unit_struct_u8_redis_int() {
    let num = 5u8;
    let v = Value::Int(num as i64);

    #[derive(Deserialize, Debug, PartialEq)]
    struct IntUnit(u8);

    let mut de = Deserializer::new(v).unwrap();
    let actual: IntUnit = Deserialize::deserialize(&mut de).unwrap();

    let expected = IntUnit(num);
    assert_eq!(expected, actual);
}

#[test]
fn deserialize_tuple() {
    let v = vec![Value::Int(5), Value::Data(b"hello".to_vec())];

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: (u8, String) = Deserialize::deserialize(&mut de).unwrap();

    let expected = (5, "hello".to_owned());
    assert_eq!(expected, actual);
}

#[test]
fn deserialize_struct() {
    let v = vec![
        Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
        Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec())
    ];

    #[derive(Debug, Deserialize, PartialEq)]
    struct Simple {
        a: String,
        b: String,
    }

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Simple = Deserialize::deserialize(&mut de).unwrap();

    let expected = Simple {
        a: "apple".to_owned(),
        b: "banana".to_owned(),
    };

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_hash_map_strings() {
    let v = vec![
        Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
        Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec())
    ];

    let mut expected = HashMap::new();
    expected.insert("a".to_string(), "apple".to_string());
    expected.insert("b".to_string(), "banana".to_string());

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: HashMap<String, String> = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_float() {
    let v = Value::Data(b"3.14159".to_vec());

    let expected = "3.14159".parse::<f32>().unwrap();

    let mut de = Deserializer::new(v).unwrap();
    let actual: f32 = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(actual, expected);
}

#[test]
fn deserialize_hash_map_string_u8() {
    let v = vec![
        Value::Data(b"a".to_vec()), Value::Data(b"1".to_vec()),
        Value::Data(b"b".to_vec()), Value::Data(b"2".to_vec())
    ];

    let mut expected = HashMap::new();
    expected.insert("a".to_string(), 1);
    expected.insert("b".to_string(), 2);

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: HashMap<String, u8> = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_struct_out_of_order() {
    let v = vec![
        Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec()),
        Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
    ];

    #[derive(Debug, Deserialize, PartialEq)]
    struct Simple {
        a: String,
        b: String,
    }

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Simple = Deserialize::deserialize(&mut de).unwrap();

    let expected = Simple {
        a: "apple".to_owned(),
        b: "banana".to_owned(),

    };

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_struct_extra_keys() {
    let v = vec![
        Value::Data(b"c".to_vec()), Value::Data(b"cranberry".to_vec()),
        Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec()),
        Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
    ];

    #[derive(Debug, Deserialize, PartialEq)]
    struct Simple {
        a: String,
        b: String,
    }

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Simple = Deserialize::deserialize(&mut de).unwrap();

    let expected = Simple {
        a: "apple".to_owned(),
        b: "banana".to_owned(),
    };

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_enum() {
    let v = Value::Data(b"Orange".to_vec());

    #[derive(Debug, Deserialize, PartialEq)]
    enum Fruit {
        Orange,
        Apple
    }

    let mut de = Deserializer::new(v).unwrap();
    let actual: Fruit = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(Fruit::Orange, actual);
}

#[test]
fn deserialize_option() {
    let mut de = Deserializer::new(Value::Nil).unwrap();
    let actual: Option<u8> = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(None, actual);
}

#[test]
fn deserialize_complex_struct() {
    let v = vec![
        Value::Data(b"num".to_vec()), Value::Data(b"10".to_vec()),
        Value::Data(b"opt".to_vec()), Value::Data(b"yes".to_vec()),
        Value::Data(b"s".to_vec()), Value::Data(b"yarn".to_vec()),
    ];

    #[derive(Debug, Deserialize, PartialEq)]
    struct Complex {
        num: usize,
        opt: Option<String>,
        not: Option<String>,
        s: String,
    }

    let expected = Complex {
        num: 10,
        opt: Some("yes".to_owned()),
        not: None,
        s: "yarn".to_owned()
    };

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Complex = Deserialize::deserialize(&mut de).unwrap();

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_vec_of_strings() {
    let v = vec![
        Value::Data(b"first".to_vec()),
        Value::Data(b"second".to_vec()),
        Value::Data(b"third".to_vec()),
    ];

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Vec<String> = Deserialize::deserialize(&mut de).unwrap();

    let expected = vec!["first".to_string(), "second".to_string(), "third".to_string()];
    assert_eq!(expected, actual);
}

#[test]
fn deserialize_vec_of_newtype() {
    let v = vec![
        Value::Data(b"first".to_vec()),
        Value::Data(b"second".to_vec()),
        Value::Data(b"third".to_vec()),
    ];

    #[derive(Debug, PartialEq, Deserialize)]
    struct Rank(String);

    let mut de = Deserializer::new(Value::Bulk(v)).unwrap();
    let actual: Vec<Rank> = Deserialize::deserialize(&mut de).unwrap();

    let expected = vec![
        Rank("first".into()),
        Rank("second".into()),
        Rank("third".into()),
    ];

    assert_eq!(expected, actual);
}

/// Test for deserializing pipelined structs
///
/// In pipeline mode, there is nested bulk items that are returned. The original implementation did
/// not handle this.
#[test]
fn deserialize_pipelined_hmap() {
    let values =
        Value::Bulk(vec![
            Value::Bulk(vec![
                Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
                Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec())
            ]),
            Value::Bulk(vec![
                Value::Data(b"a".to_vec()), Value::Data(b"art".to_vec()),
                Value::Data(b"b".to_vec()), Value::Data(b"bold".to_vec())
            ])
        ]);

    #[derive(Debug, Deserialize, PartialEq)]
    struct Simple {
        a: String,
        b: String,
    }

    let mut de = Deserializer::new(values).unwrap();
    let actual: Vec<Simple> = Deserialize::deserialize(&mut de).unwrap();

    let expected = vec![Simple {
        a: "apple".to_owned(),
        b: "banana".to_owned(),
    }, Simple {
        a: "art".to_owned(),
        b: "bold".to_owned(),
    }];

    assert_eq!(expected, actual);
}

#[test]
fn deserialize_pipelined_single_hmap() {
    let values =
        Value::Bulk(vec![
            Value::Bulk(vec![
                Value::Data(b"a".to_vec()), Value::Data(b"apple".to_vec()),
                Value::Data(b"b".to_vec()), Value::Data(b"banana".to_vec())
            ]),
        ]);

    #[derive(Debug, Deserialize, PartialEq)]
    struct Simple {
        a: String,
        b: String,
    }

    let mut de = Deserializer::new(values).unwrap();
    let actual: Vec<Simple> = Deserialize::deserialize(&mut de).unwrap();

    let expected = vec![Simple {
        a: "apple".to_owned(),
        b: "banana".to_owned(),
    }];

    assert_eq!(expected, actual);
}
<|endoftext|>


<commit_before>//! All routes related to managing owners of a crate

use serde_json;

use controllers::prelude::*;
use views::EncodableOwner;
use models::{Crate, Owner, Rights, Team, User};

/// Handles the `GET /crates/:crate_id/owners` route.
pub fn owners(req: &mut Request) -> CargoResult<Response> {
    let crate_name = &req.params()["crate_id"];
    let conn = req.db_conn()?;
    let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
    let owners = krate
        .owners(&conn)?
        .into_iter()
        .map(Owner::encodable)
        .collect();

    #[derive(Serialize)]
    struct R {
        users: Vec<EncodableOwner>,
    }
    Ok(req.json(&R { users: owners }))
}

/// Handles the `GET /crates/:crate_id/owner_team` route.
pub fn owner_team(req: &mut Request) -> CargoResult<Response> {
    let crate_name = &req.params()["crate_id"];
    let conn = req.db_conn()?;
    let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
    let owners = Team::owning(&krate, &conn)?
        .into_iter()
        .map(Owner::encodable)
        .collect();

    #[derive(Serialize)]
    struct R {
        teams: Vec<EncodableOwner>,
    }
    Ok(req.json(&R { teams: owners }))
}

/// Handles the `GET /crates/:crate_id/owner_user` route.
pub fn owner_user(req: &mut Request) -> CargoResult<Response> {
    let crate_name = &req.params()["crate_id"];
    let conn = req.db_conn()?;
    let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
    let owners = User::owning(&krate, &conn)?
        .into_iter()
        .map(Owner::encodable)
        .collect();

    #[derive(Serialize)]
    struct R {
        users: Vec<EncodableOwner>,
    }
    Ok(req.json(&R { users: owners }))
}

/// Handles the `PUT /crates/:crate_id/owners` route.
pub fn add_owners(req: &mut Request) -> CargoResult<Response> {
    modify_owners(req, true)
}

/// Handles the `DELETE /crates/:crate_id/owners` route.
pub fn remove_owners(req: &mut Request) -> CargoResult<Response> {
    modify_owners(req, false)
}

fn modify_owners(req: &mut Request, add: bool) -> CargoResult<Response> {
    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let user = req.user()?;
    let conn = req.db_conn()?;
    let krate = Crate::by_name(&req.params()["crate_id"]).first::<Crate>(&*conn)?;
    let owners = krate.owners(&conn)?;

    match user.rights(req.app(), &owners)? {
        Rights::Full => {}
        // Yes!
        Rights::Publish => {
            return Err(human("team members don't have permission to modify owners"));
        }
        Rights::None => {
            return Err(human("only owners have permission to modify owners"));
        }
    }

    #[derive(Deserialize)]
    struct Request {
        // identical, for back-compat (owners preferred)
        users: Option<Vec<String>>,
        owners: Option<Vec<String>>,
    }

    let request: Request = serde_json::from_str(&body).map_err(|_| human("invalid json request"))?;

    let logins = request
        .owners
        .or(request.users)
        .ok_or_else(|| human("invalid json request"))?;

    let mut msgs = Vec::new();

    for login in &logins {
        if add {
            if owners.iter().any(|owner| {
                owner.login().to_lowercase() == *login.to_lowercase()
            }) {
                return Err(human(&format_args!("`{}` is already an owner", login)));
            }
            let msg = krate.owner_add(req.app(), &conn, user, login)?;
            msgs.push(msg);
        } else {
            // Removing the team that gives you rights is prevented because
            // team members only have Rights::Publish
            if owners.len() == 1 {
                return Err(human("cannot remove the sole owner of a crate"));
            }
            krate.owner_remove(req.app(), &conn, user, login)?;
        }
    }

    let comma_sep_msg = msgs.join(",");

    #[derive(Serialize)]
    struct R {
        ok: bool,
        msg: String,
    }
    Ok(req.json(&R {
        ok: true,
        msg: comma_sep_msg,
    }))
}
<commit_msg>Extract closure into a variable to satisfy clippy<commit_after>//! All routes related to managing owners of a crate

use serde_json;

use controllers::prelude::*;
use views::EncodableOwner;
use models::{Crate, Owner, Rights, Team, User};

/// Handles the `GET /crates/:crate_id/owners` route.
pub fn owners(req: &mut Request) -> CargoResult<Response> {
    let crate_name = &req.params()["crate_id"];
    let conn = req.db_conn()?;
    let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
    let owners = krate
        .owners(&conn)?
        .into_iter()
        .map(Owner::encodable)
        .collect();

    #[derive(Serialize)]
    struct R {
        users: Vec<EncodableOwner>,
    }
    Ok(req.json(&R { users: owners }))
}

/// Handles the `GET /crates/:crate_id/owner_team` route.
pub fn owner_team(req: &mut Request) -> CargoResult<Response> {
    let crate_name = &req.params()["crate_id"];
    let conn = req.db_conn()?;
    let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
    let owners = Team::owning(&krate, &conn)?
        .into_iter()
        .map(Owner::encodable)
        .collect();

    #[derive(Serialize)]
    struct R {
        teams: Vec<EncodableOwner>,
    }
    Ok(req.json(&R { teams: owners }))
}

/// Handles the `GET /crates/:crate_id/owner_user` route.
pub fn owner_user(req: &mut Request) -> CargoResult<Response> {
    let crate_name = &req.params()["crate_id"];
    let conn = req.db_conn()?;
    let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
    let owners = User::owning(&krate, &conn)?
        .into_iter()
        .map(Owner::encodable)
        .collect();

    #[derive(Serialize)]
    struct R {
        users: Vec<EncodableOwner>,
    }
    Ok(req.json(&R { users: owners }))
}

/// Handles the `PUT /crates/:crate_id/owners` route.
pub fn add_owners(req: &mut Request) -> CargoResult<Response> {
    modify_owners(req, true)
}

/// Handles the `DELETE /crates/:crate_id/owners` route.
pub fn remove_owners(req: &mut Request) -> CargoResult<Response> {
    modify_owners(req, false)
}

fn modify_owners(req: &mut Request, add: bool) -> CargoResult<Response> {
    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let user = req.user()?;
    let conn = req.db_conn()?;
    let krate = Crate::by_name(&req.params()["crate_id"]).first::<Crate>(&*conn)?;
    let owners = krate.owners(&conn)?;

    match user.rights(req.app(), &owners)? {
        Rights::Full => {}
        // Yes!
        Rights::Publish => {
            return Err(human("team members don't have permission to modify owners"));
        }
        Rights::None => {
            return Err(human("only owners have permission to modify owners"));
        }
    }

    #[derive(Deserialize)]
    struct Request {
        // identical, for back-compat (owners preferred)
        users: Option<Vec<String>>,
        owners: Option<Vec<String>>,
    }

    let request: Request = serde_json::from_str(&body).map_err(|_| human("invalid json request"))?;

    let logins = request
        .owners
        .or(request.users)
        .ok_or_else(|| human("invalid json request"))?;

    let mut msgs = Vec::new();

    for login in &logins {
        if add {
            let login_test = |owner: &Owner| {
                owner.login().to_lowercase() == *login.to_lowercase()
            };
            if owners.iter().any(login_test) {
                return Err(human(&format_args!("`{}` is already an owner", login)));
            }
            let msg = krate.owner_add(req.app(), &conn, user, login)?;
            msgs.push(msg);
        } else {
            // Removing the team that gives you rights is prevented because
            // team members only have Rights::Publish
            if owners.len() == 1 {
                return Err(human("cannot remove the sole owner of a crate"));
            }
            krate.owner_remove(req.app(), &conn, user, login)?;
        }
    }

    let comma_sep_msg = msgs.join(",");

    #[derive(Serialize)]
    struct R {
        ok: bool,
        msg: String,
    }
    Ok(req.json(&R {
        ok: true,
        msg: comma_sep_msg,
    }))
}
<|endoftext|>


<commit_before>use racer::Match;
use racer::{do_local_search,first_match,to_refs};
use racer::ast;
use racer::codeiter;
use racer::scopes;
use racer;

use std::io::File;
use std::io::BufferedReader;
use std::str;
use std::strbuf::StrBuf;

fn find_start_of_function_body(src: &str) -> uint {
    // TODO: this should ignore anything inside parens so as to skip the arg list
    return src.find_str("{").unwrap();
}

fn get_type_of_fnarg(m: &Match, fpath: &Path, msrc: &str) -> Option<Match> {
    debug!("PHIL get type of fn arg {:?}",m);
    let point = scopes::find_stmt_start(msrc, m.point).unwrap();
    for (start,end) in codeiter::iter_stmts(msrc.slice_from(point)) {
        let blob = msrc.slice(point+start,point+end);

        // wrap in "impl blah { }" so that methods get parsed correctly too
        let mut s = StrBuf::new();
        s.push_str("impl blah {");
        let impl_header_len = s.len();
        s.push_str(blob.slice_to(find_start_of_function_body(blob)+1));
        s.push_str("}}");
        let fn_ = ast::parse_fn(s);
        let mut result = None;
        for (_/*name*/, pos, ty_) in fn_.args.move_iter() {
            let globalpos = pos - impl_header_len + start + point;
            if globalpos == m.point && ty_.len() != 0 {
                let v = to_refs(&ty_);
                let fqn = v.as_slice();
                result = first_match(|m| do_local_search(fqn,
                                                         fpath,
                                                         globalpos,
                                                         racer::ExactMatch, m));
            }
        }
        return result;
    }
    None
}

fn get_type_of_let_expr(m: &Match, fpath: &Path, msrc: &str) -> Option<Match> {
    // ASSUMPTION: this is being called on a let decl
    let mut result = None;

    let opoint = scopes::find_stmt_start(msrc, m.point);
    let point = opoint.unwrap();

    let src = msrc.slice_from(point);
    for (start,end) in codeiter::iter_stmts(src) {
        let blob = src.slice(start,end);

        ast::parse_let(StrBuf::from_str(blob)).map(|letres|{
            debug!("PHIL parse let result {}", &letres.init);
            // HACK, convert from &[~str] to &[&str]
            let v = to_refs(&letres.init);
            let fqn = v.as_slice();
            result = first_match(|m| do_local_search(fqn, fpath, point+start, racer::ExactMatch, m));
        });
        break;
    }

    debug!("PHIL let search result is {:?}",result);

    // if it's a struct then cool
    // if it's a function then we need to resolve the return type

    if result.is_none(){
        return result
    }

    let res = result.unwrap();
    match res.mtype {
        Struct => {
            return Some(res);
        }
        Function => {
            let path = get_return_type_of_function(&res);
            if path.len() == 0 {
                return None;
            }

            debug!("PHIL return type is {}", path);
            let v = to_refs(&path);
            let fqn = v.as_slice();
            return first_match(|m| do_local_search(fqn, &res.filepath, res.point, racer::ExactMatch, m));
        }
        _ => { return None }
    };
}

pub fn get_type_of_OLD(m: &Match, fpath: &Path, msrc: &str) -> Option<Match> {
    debug!("PHIL get_type_of OLD {:?}",m);
    debug!("PHIL get_type_of OLD {}",m.matchstr);

    match m.mtype {
        Let => get_type_of_let_expr(m, fpath, msrc),
        FnArg => get_type_of_fnarg(m, fpath, msrc),
        _ => { println!("Can't get type of {:?}",m.mtype); None }
    }
}

pub fn get_fields_of_struct(m: &Match) -> Vec<(StrBuf, uint)> {
    let filetxt = BufferedReader::new(File::open(&m.filepath)).read_to_end().unwrap();
    let src = str::from_utf8(filetxt.as_slice()).unwrap();

    let opoint = scopes::find_stmt_start(src, m.point);
    let structsrc = scopes::end_of_next_scope(src.slice_from(opoint.unwrap()));

    return ast::parse_struct_fields(StrBuf::from_str(structsrc));
}

pub fn get_return_type_of_function(fnmatch: &Match) -> Vec<~str> {
    let filetxt = BufferedReader::new(File::open(&fnmatch.filepath)).read_to_end().unwrap();
    let src = str::from_utf8(filetxt.as_slice()).unwrap();
    let point = scopes::find_stmt_start(src, fnmatch.point).unwrap();

    debug!("get_return_type_of_function |{}|",src.slice_from(point));

    let outputpath = src.slice_from(point).find_str("{").map(|n|{

        // TODO: surround this with 'impl { ... }' so that methods get parsed also
        let ss = src.slice(point, point+n+1);
        let mut decl = StrBuf::from_str(ss);
        decl = decl.append("}");
        debug!("PHIL: passing in {}",decl);
        return ast::parse_fn_output(decl);
    }).unwrap_or(Vec::new());

    return outputpath;
}
<commit_msg>import enum variants<commit_after>use racer::{FnArg, Function, Let, Match, Struct};
use racer::{do_local_search,first_match,to_refs};
use racer::ast;
use racer::codeiter;
use racer::scopes;
use racer;

use std::io::File;
use std::io::BufferedReader;
use std::str;
use std::strbuf::StrBuf;

fn find_start_of_function_body(src: &str) -> uint {
    // TODO: this should ignore anything inside parens so as to skip the arg list
    return src.find_str("{").unwrap();
}

fn get_type_of_fnarg(m: &Match, fpath: &Path, msrc: &str) -> Option<Match> {
    debug!("PHIL get type of fn arg {:?}",m);
    let point = scopes::find_stmt_start(msrc, m.point).unwrap();
    for (start,end) in codeiter::iter_stmts(msrc.slice_from(point)) {
        let blob = msrc.slice(point+start,point+end);

        // wrap in "impl blah { }" so that methods get parsed correctly too
        let mut s = StrBuf::new();
        s.push_str("impl blah {");
        let impl_header_len = s.len();
        s.push_str(blob.slice_to(find_start_of_function_body(blob)+1));
        s.push_str("}}");
        let fn_ = ast::parse_fn(s);
        let mut result = None;
        for (_/*name*/, pos, ty_) in fn_.args.move_iter() {
            let globalpos = pos - impl_header_len + start + point;
            if globalpos == m.point && ty_.len() != 0 {
                let v = to_refs(&ty_);
                let fqn = v.as_slice();
                result = first_match(|m| do_local_search(fqn,
                                                         fpath,
                                                         globalpos,
                                                         racer::ExactMatch, m));
            }
        }
        return result;
    }
    None
}

fn get_type_of_let_expr(m: &Match, fpath: &Path, msrc: &str) -> Option<Match> {
    // ASSUMPTION: this is being called on a let decl
    let mut result = None;

    let opoint = scopes::find_stmt_start(msrc, m.point);
    let point = opoint.unwrap();

    let src = msrc.slice_from(point);
    for (start,end) in codeiter::iter_stmts(src) {
        let blob = src.slice(start,end);

        ast::parse_let(StrBuf::from_str(blob)).map(|letres|{
            debug!("PHIL parse let result {}", &letres.init);
            // HACK, convert from &[~str] to &[&str]
            let v = to_refs(&letres.init);
            let fqn = v.as_slice();
            result = first_match(|m| do_local_search(fqn, fpath, point+start, racer::ExactMatch, m));
        });
        break;
    }

    debug!("PHIL let search result is {:?}",result);

    // if it's a struct then cool
    // if it's a function then we need to resolve the return type

    if result.is_none(){
        return result
    }

    let res = result.unwrap();
    match res.mtype {
        Struct => {
            return Some(res);
        }
        Function => {
            let path = get_return_type_of_function(&res);
            if path.len() == 0 {
                return None;
            }

            debug!("PHIL return type is {}", path);
            let v = to_refs(&path);
            let fqn = v.as_slice();
            return first_match(|m| do_local_search(fqn, &res.filepath, res.point, racer::ExactMatch, m));
        }
        _ => { return None }
    };
}

pub fn get_type_of_OLD(m: &Match, fpath: &Path, msrc: &str) -> Option<Match> {
    debug!("PHIL get_type_of OLD {:?}",m);
    debug!("PHIL get_type_of OLD {}",m.matchstr);

    match m.mtype {
        Let => get_type_of_let_expr(m, fpath, msrc),
        FnArg => get_type_of_fnarg(m, fpath, msrc),
        _ => { println!("Can't get type of {:?}",m.mtype); None }
    }
}

pub fn get_fields_of_struct(m: &Match) -> Vec<(StrBuf, uint)> {
    let filetxt = BufferedReader::new(File::open(&m.filepath)).read_to_end().unwrap();
    let src = str::from_utf8(filetxt.as_slice()).unwrap();

    let opoint = scopes::find_stmt_start(src, m.point);
    let structsrc = scopes::end_of_next_scope(src.slice_from(opoint.unwrap()));

    return ast::parse_struct_fields(StrBuf::from_str(structsrc));
}

pub fn get_return_type_of_function(fnmatch: &Match) -> Vec<~str> {
    let filetxt = BufferedReader::new(File::open(&fnmatch.filepath)).read_to_end().unwrap();
    let src = str::from_utf8(filetxt.as_slice()).unwrap();
    let point = scopes::find_stmt_start(src, fnmatch.point).unwrap();

    debug!("get_return_type_of_function |{}|",src.slice_from(point));

    let outputpath = src.slice_from(point).find_str("{").map(|n|{

        // TODO: surround this with 'impl { ... }' so that methods get parsed also
        let ss = src.slice(point, point+n+1);
        let mut decl = StrBuf::from_str(ss);
        decl = decl.append("}");
        debug!("PHIL: passing in {}",decl);
        return ast::parse_fn_output(decl);
    }).unwrap_or(Vec::new());

    return outputpath;
}
<|endoftext|>