<commit_before>#![warn(deprecated_in_future)]
#![warn(future_incompatible)]
#![warn(nonstandard_style)]
#![warn(rust_2018_compatibility)]
#![warn(rust_2018_idioms)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused)]

#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::cast_precision_loss)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_possible_wrap)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::enum_glob_use)]
#![allow(clippy::map_unwrap_or)]
#![allow(clippy::match_same_arms)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::non_ascii_literal)]
#![allow(clippy::option_if_let_else)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::unnested_or_patterns)] // TODO: remove this when we support Rust 1.53.0
#![allow(clippy::unused_self)]
#![allow(clippy::upper_case_acronyms)]
#![allow(clippy::wildcard_imports)]

use std::env;
use std::ffi::{OsStr, OsString};
use std::io::{self, Write, ErrorKind};
use std::path::{Component, PathBuf};

use ansi_term::{ANSIStrings, Style};

use log::*;

use crate::fs::{Dir, File};
use crate::fs::feature::git::GitCache;
use crate::fs::filter::GitIgnore;
use crate::options::{Options, Vars, vars, OptionsResult};
use crate::output::{escape, lines, grid, grid_details, details, View, Mode};
use crate::theme::Theme;

mod fs;
mod info;
mod logger;
mod options;
mod output;
mod theme;

fn main() {
    use std::process::exit;

    logger::configure(env::var_os(vars::EXA_DEBUG));

    let args: Vec<_> = env::args_os().skip(1).collect();
    match Options::parse(args.iter().map(|e| e.as_ref()), &LiveVars) {
        OptionsResult::Ok(options, mut input_paths) => {

            // List the current directory by default.
            // (This has to be done here, otherwise git_options won’t see it.)
            if input_paths.is_empty() {
                input_paths = vec![ OsStr::new(".") ];
            }

            let git = git_options(&options, &input_paths);
            let writer = io::stdout();

            let console_width = options.view.width.actual_terminal_width();
            let theme = options.theme.to_theme(console_width.is_some());
            let exa = Exa { options, writer, input_paths, theme, console_width, git };

            match exa.run() {
                Ok(exit_status) => {
                    exit(exit_status);
                }

                Err(e) if e.kind() == ErrorKind::BrokenPipe => {
                    warn!("Broken pipe error: {}", e);
                    exit(exits::SUCCESS);
                }

                Err(e) => {
                    eprintln!("{}", e);
                    exit(exits::RUNTIME_ERROR);
                }
            }
        }

        OptionsResult::Help(help_text) => {
            print!("{}", help_text);
        }

        OptionsResult::Version(version_str) => {
            print!("{}", version_str);
        }

        OptionsResult::InvalidOptions(error) => {
            eprintln!("exa: {}", error);

            if let Some(s) = error.suggestion() {
                eprintln!("{}", s);
            }

            exit(exits::OPTIONS_ERROR);
        }
    }
}

/// The main program wrapper.
pub struct Exa<'args> {

    /// List of command-line options, having been successfully parsed.
    pub options: Options,

    /// The output handle that we write to.
    pub writer: io::Stdout,

    /// List of the free command-line arguments that should correspond to file
    /// names (anything that isn’t an option).
    pub input_paths: Vec<&'args OsStr>,

    /// The theme that has been configured from the command-line options and
    /// environment variables. If colours are disabled, this is a theme with
    /// every style set to the default.
    pub theme: Theme,

    /// The detected width of the console. This is used to determine which
    /// view to use.
    pub console_width: Option<usize>,

    /// A global Git cache, if the option was passed in.
    /// This has to last the lifetime of the program, because the user might
    /// want to list several directories in the same repository.
    pub git: Option<GitCache>,
}

/// The “real” environment variables type.
/// Instead of just calling `var_os` from within the options module,
/// the method of looking up environment variables has to be passed in.
struct LiveVars;
impl Vars for LiveVars {
    fn get(&self, name: &'static str) -> Option<OsString> {
        env::var_os(name)
    }
}

/// Create a Git cache populated with the arguments that are going to be
/// listed before they’re actually listed, if the options demand it.
fn git_options(options: &Options, args: &[&OsStr]) -> Option<GitCache> {
    if options.should_scan_for_git() {
        Some(args.iter().map(PathBuf::from).collect())
    }
    else {
        None
    }
}

impl<'args> Exa<'args> {
    /// # Errors
    ///
    /// Will return `Err` if printing to stderr fails.
    pub fn run(mut self) -> io::Result<i32> {
        debug!("Running with options: {:#?}", self.options);

        let mut files = Vec::new();
        let mut dirs = Vec::new();
        let mut exit_status = 0;

        for file_path in &self.input_paths {
            match File::from_args(PathBuf::from(file_path), None, None) {
                Err(e) => {
                    exit_status = 2;
                    writeln!(io::stderr(), "{:?}: {}", file_path, e)?;
                }

                Ok(f) => {
                    if f.points_to_directory() && ! self.options.dir_action.treat_dirs_as_files() {
                        match f.to_dir() {
                            Ok(d)   => dirs.push(d),
                            Err(e)  => writeln!(io::stderr(), "{:?}: {}", file_path, e)?,
                        }
                    }
                    else {
                        files.push(f);
                    }
                }
            }
        }

        // We want to print a directory’s name before we list it, *except* in
        // the case where it’s the only directory, *except* if there are any
        // files to print as well. (It’s a double negative)

        let no_files = files.is_empty();
        let is_only_dir = dirs.len() == 1 && no_files;

        self.options.filter.filter_argument_files(&mut files);
        self.print_files(None, files)?;

        self.print_dirs(dirs, no_files, is_only_dir, exit_status)
    }

    fn print_dirs(&mut self, dir_files: Vec<Dir>, mut first: bool, is_only_dir: bool, exit_status: i32) -> io::Result<i32> {
        for dir in dir_files {

            // Put a gap between directories, or between the list of files and
            // the first directory.
            if first {
                first = false;
            }
            else {
                writeln!(&mut self.writer)?;
            }

            if ! is_only_dir {
                let mut bits = Vec::new();
                escape(dir.path.display().to_string(), &mut bits, Style::default(), Style::default());
                writeln!(&mut self.writer, "{}:", ANSIStrings(&bits))?;
            }

            let mut children = Vec::new();
            let git_ignore = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
            for file in dir.files(self.options.filter.dot_filter, self.git.as_ref(), git_ignore) {
                match file {
                    Ok(file)        => children.push(file),
                    Err((path, e))  => writeln!(io::stderr(), "[{}: {}]", path.display(), e)?,
                }
            };

            self.options.filter.filter_child_files(&mut children);
            self.options.filter.sort_files(&mut children);

            if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
                let depth = dir.path.components().filter(|&c| c != Component::CurDir).count() + 1;
                if ! recurse_opts.tree && ! recurse_opts.is_too_deep(depth) {

                    let mut child_dirs = Vec::new();
                    for child_dir in children.iter().filter(|f| f.is_directory() && ! f.is_all_all) {
                        match child_dir.to_dir() {
                            Ok(d)   => child_dirs.push(d),
                            Err(e)  => writeln!(io::stderr(), "{}: {}", child_dir.path.display(), e)?,
                        }
                    }

                    self.print_files(Some(&dir), children)?;
                    match self.print_dirs(child_dirs, false, false, exit_status) {
                        Ok(_)   => (),
                        Err(e)  => return Err(e),
                    }
                    continue;
                }
            }

            self.print_files(Some(&dir), children)?;
        }

        Ok(exit_status)
    }

    /// Prints the list of files using whichever view is selected.
    fn print_files(&mut self, dir: Option<&Dir>, files: Vec<File<'_>>) -> io::Result<()> {
        if files.is_empty() {
            return Ok(());
        }

        let theme = &self.theme;
        let View { ref mode, ref file_style, .. } = self.options.view;

        match (mode, self.console_width) {
            (Mode::Grid(ref opts), Some(console_width)) => {
                let filter = &self.options.filter;
                let r = grid::Render { files, theme, file_style, opts, console_width, filter };
                r.render(&mut self.writer)
            }

            (Mode::Grid(_), None) |
            (Mode::Lines,   _)    => {
                let filter = &self.options.filter;
                let r = lines::Render { files, theme, file_style, filter };
                r.render(&mut self.writer)
            }

            (Mode::Details(ref opts), _) => {
                let filter = &self.options.filter;
                let recurse = self.options.dir_action.recurse_options();

                let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
                let git = self.git.as_ref();
                let r = details::Render { dir, files, theme, file_style, opts, recurse, filter, git_ignoring, git };
                r.render(&mut self.writer)
            }

            (Mode::GridDetails(ref opts), Some(console_width)) => {
                let grid = &opts.grid;
                let details = &opts.details;
                let row_threshold = opts.row_threshold;

                let filter = &self.options.filter;
                let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
                let git = self.git.as_ref();

                let r = grid_details::Render { dir, files, theme, file_style, grid, details, filter, row_threshold, git_ignoring, git, console_width };
                r.render(&mut self.writer)
            }

            (Mode::GridDetails(ref opts), None) => {
                let opts = &opts.to_details_options();
                let filter = &self.options.filter;
                let recurse = self.options.dir_action.recurse_options();
                let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;

                let git = self.git.as_ref();
                let r = details::Render { dir, files, theme, file_style, opts, recurse, filter, git_ignoring, git };
                r.render(&mut self.writer)
            }
        }
    }
}

mod exits {

    /// Exit code for when exa runs OK.
    pub const SUCCESS: i32 = 0;

    /// Exit code for when there was at least one I/O error during execution.
    pub const RUNTIME_ERROR: i32 = 1;

    /// Exit code for when the command-line options are invalid.
    pub const OPTIONS_ERROR: i32 = 3;
}
<commit_msg>don’t display broken pipe error messages (see rust-lang/rust#46016)<commit_after>#![warn(deprecated_in_future)]
#![warn(future_incompatible)]
#![warn(nonstandard_style)]
#![warn(rust_2018_compatibility)]
#![warn(rust_2018_idioms)]
#![warn(trivial_casts, trivial_numeric_casts)]
#![warn(unused)]

#![warn(clippy::all, clippy::pedantic)]
#![allow(clippy::cast_precision_loss)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_possible_wrap)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::enum_glob_use)]
#![allow(clippy::map_unwrap_or)]
#![allow(clippy::match_same_arms)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::non_ascii_literal)]
#![allow(clippy::option_if_let_else)]
#![allow(clippy::too_many_lines)]
#![allow(clippy::unnested_or_patterns)] // TODO: remove this when we support Rust 1.53.0
#![allow(clippy::unused_self)]
#![allow(clippy::upper_case_acronyms)]
#![allow(clippy::wildcard_imports)]

use std::env;
use std::ffi::{OsStr, OsString};
use std::io::{self, Write, ErrorKind};
use std::path::{Component, PathBuf};

use ansi_term::{ANSIStrings, Style};

use log::*;

use crate::fs::{Dir, File};
use crate::fs::feature::git::GitCache;
use crate::fs::filter::GitIgnore;
use crate::options::{Options, Vars, vars, OptionsResult};
use crate::output::{escape, lines, grid, grid_details, details, View, Mode};
use crate::theme::Theme;

mod fs;
mod info;
mod logger;
mod options;
mod output;
mod theme;

fn main() {
    use std::process::exit;

    unsafe {
        libc::signal(libc::SIGPIPE, libc::SIG_DFL);
    }

    logger::configure(env::var_os(vars::EXA_DEBUG));

    let args: Vec<_> = env::args_os().skip(1).collect();
    match Options::parse(args.iter().map(|e| e.as_ref()), &LiveVars) {
        OptionsResult::Ok(options, mut input_paths) => {

            // List the current directory by default.
            // (This has to be done here, otherwise git_options won’t see it.)
            if input_paths.is_empty() {
                input_paths = vec![ OsStr::new(".") ];
            }

            let git = git_options(&options, &input_paths);
            let writer = io::stdout();

            let console_width = options.view.width.actual_terminal_width();
            let theme = options.theme.to_theme(console_width.is_some());
            let exa = Exa { options, writer, input_paths, theme, console_width, git };

            match exa.run() {
                Ok(exit_status) => {
                    exit(exit_status);
                }

                Err(e) if e.kind() == ErrorKind::BrokenPipe => {
                    warn!("Broken pipe error: {}", e);
                    exit(exits::SUCCESS);
                }

                Err(e) => {
                    eprintln!("{}", e);
                    exit(exits::RUNTIME_ERROR);
                }
            }
        }

        OptionsResult::Help(help_text) => {
            print!("{}", help_text);
        }

        OptionsResult::Version(version_str) => {
            print!("{}", version_str);
        }

        OptionsResult::InvalidOptions(error) => {
            eprintln!("exa: {}", error);

            if let Some(s) = error.suggestion() {
                eprintln!("{}", s);
            }

            exit(exits::OPTIONS_ERROR);
        }
    }
}

/// The main program wrapper.
pub struct Exa<'args> {

    /// List of command-line options, having been successfully parsed.
    pub options: Options,

    /// The output handle that we write to.
    pub writer: io::Stdout,

    /// List of the free command-line arguments that should correspond to file
    /// names (anything that isn’t an option).
    pub input_paths: Vec<&'args OsStr>,

    /// The theme that has been configured from the command-line options and
    /// environment variables. If colours are disabled, this is a theme with
    /// every style set to the default.
    pub theme: Theme,

    /// The detected width of the console. This is used to determine which
    /// view to use.
    pub console_width: Option<usize>,

    /// A global Git cache, if the option was passed in.
    /// This has to last the lifetime of the program, because the user might
    /// want to list several directories in the same repository.
    pub git: Option<GitCache>,
}

/// The “real” environment variables type.
/// Instead of just calling `var_os` from within the options module,
/// the method of looking up environment variables has to be passed in.
struct LiveVars;
impl Vars for LiveVars {
    fn get(&self, name: &'static str) -> Option<OsString> {
        env::var_os(name)
    }
}

/// Create a Git cache populated with the arguments that are going to be
/// listed before they’re actually listed, if the options demand it.
fn git_options(options: &Options, args: &[&OsStr]) -> Option<GitCache> {
    if options.should_scan_for_git() {
        Some(args.iter().map(PathBuf::from).collect())
    }
    else {
        None
    }
}

impl<'args> Exa<'args> {
    /// # Errors
    ///
    /// Will return `Err` if printing to stderr fails.
    pub fn run(mut self) -> io::Result<i32> {
        debug!("Running with options: {:#?}", self.options);

        let mut files = Vec::new();
        let mut dirs = Vec::new();
        let mut exit_status = 0;

        for file_path in &self.input_paths {
            match File::from_args(PathBuf::from(file_path), None, None) {
                Err(e) => {
                    exit_status = 2;
                    writeln!(io::stderr(), "{:?}: {}", file_path, e)?;
                }

                Ok(f) => {
                    if f.points_to_directory() && ! self.options.dir_action.treat_dirs_as_files() {
                        match f.to_dir() {
                            Ok(d)   => dirs.push(d),
                            Err(e)  => writeln!(io::stderr(), "{:?}: {}", file_path, e)?,
                        }
                    }
                    else {
                        files.push(f);
                    }
                }
            }
        }

        // We want to print a directory’s name before we list it, *except* in
        // the case where it’s the only directory, *except* if there are any
        // files to print as well. (It’s a double negative)

        let no_files = files.is_empty();
        let is_only_dir = dirs.len() == 1 && no_files;

        self.options.filter.filter_argument_files(&mut files);
        self.print_files(None, files)?;

        self.print_dirs(dirs, no_files, is_only_dir, exit_status)
    }

    fn print_dirs(&mut self, dir_files: Vec<Dir>, mut first: bool, is_only_dir: bool, exit_status: i32) -> io::Result<i32> {
        for dir in dir_files {

            // Put a gap between directories, or between the list of files and
            // the first directory.
            if first {
                first = false;
            }
            else {
                writeln!(&mut self.writer)?;
            }

            if ! is_only_dir {
                let mut bits = Vec::new();
                escape(dir.path.display().to_string(), &mut bits, Style::default(), Style::default());
                writeln!(&mut self.writer, "{}:", ANSIStrings(&bits))?;
            }

            let mut children = Vec::new();
            let git_ignore = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
            for file in dir.files(self.options.filter.dot_filter, self.git.as_ref(), git_ignore) {
                match file {
                    Ok(file)        => children.push(file),
                    Err((path, e))  => writeln!(io::stderr(), "[{}: {}]", path.display(), e)?,
                }
            };

            self.options.filter.filter_child_files(&mut children);
            self.options.filter.sort_files(&mut children);

            if let Some(recurse_opts) = self.options.dir_action.recurse_options() {
                let depth = dir.path.components().filter(|&c| c != Component::CurDir).count() + 1;
                if ! recurse_opts.tree && ! recurse_opts.is_too_deep(depth) {

                    let mut child_dirs = Vec::new();
                    for child_dir in children.iter().filter(|f| f.is_directory() && ! f.is_all_all) {
                        match child_dir.to_dir() {
                            Ok(d)   => child_dirs.push(d),
                            Err(e)  => writeln!(io::stderr(), "{}: {}", child_dir.path.display(), e)?,
                        }
                    }

                    self.print_files(Some(&dir), children)?;
                    match self.print_dirs(child_dirs, false, false, exit_status) {
                        Ok(_)   => (),
                        Err(e)  => return Err(e),
                    }
                    continue;
                }
            }

            self.print_files(Some(&dir), children)?;
        }

        Ok(exit_status)
    }

    /// Prints the list of files using whichever view is selected.
    fn print_files(&mut self, dir: Option<&Dir>, files: Vec<File<'_>>) -> io::Result<()> {
        if files.is_empty() {
            return Ok(());
        }

        let theme = &self.theme;
        let View { ref mode, ref file_style, .. } = self.options.view;

        match (mode, self.console_width) {
            (Mode::Grid(ref opts), Some(console_width)) => {
                let filter = &self.options.filter;
                let r = grid::Render { files, theme, file_style, opts, console_width, filter };
                r.render(&mut self.writer)
            }

            (Mode::Grid(_), None) |
            (Mode::Lines,   _)    => {
                let filter = &self.options.filter;
                let r = lines::Render { files, theme, file_style, filter };
                r.render(&mut self.writer)
            }

            (Mode::Details(ref opts), _) => {
                let filter = &self.options.filter;
                let recurse = self.options.dir_action.recurse_options();

                let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
                let git = self.git.as_ref();
                let r = details::Render { dir, files, theme, file_style, opts, recurse, filter, git_ignoring, git };
                r.render(&mut self.writer)
            }

            (Mode::GridDetails(ref opts), Some(console_width)) => {
                let grid = &opts.grid;
                let details = &opts.details;
                let row_threshold = opts.row_threshold;

                let filter = &self.options.filter;
                let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;
                let git = self.git.as_ref();

                let r = grid_details::Render { dir, files, theme, file_style, grid, details, filter, row_threshold, git_ignoring, git, console_width };
                r.render(&mut self.writer)
            }

            (Mode::GridDetails(ref opts), None) => {
                let opts = &opts.to_details_options();
                let filter = &self.options.filter;
                let recurse = self.options.dir_action.recurse_options();
                let git_ignoring = self.options.filter.git_ignore == GitIgnore::CheckAndIgnore;

                let git = self.git.as_ref();
                let r = details::Render { dir, files, theme, file_style, opts, recurse, filter, git_ignoring, git };
                r.render(&mut self.writer)
            }
        }
    }
}

mod exits {

    /// Exit code for when exa runs OK.
    pub const SUCCESS: i32 = 0;

    /// Exit code for when there was at least one I/O error during execution.
    pub const RUNTIME_ERROR: i32 = 1;

    /// Exit code for when the command-line options are invalid.
    pub const OPTIONS_ERROR: i32 = 3;
}
<|endoftext|>


<commit_before>extern crate nix;

use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use std::collections::HashSet;
use nix::sys::statvfs::vfs::Statvfs;

// http://stackoverflow.com/questions/5194057/better-way-to-convert-file-sizes-in-python
fn iec(n: u64) -> String {
    let units = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];

    let i = (n as f64).log(1024_f64).floor() as u32;
    let p = 1024_u64.pow(i);
    let s = (n as f64)/(p as f64);
    format!("{:.0}{}", s, units[i as usize])
}

// /dev/mapper/vg-lv -> /dev/vg/lv
// this needs much better error checking
fn shorten_lv(path: &str) -> String {
    if path.starts_with("/dev/mapper/") {
        let lv = path.split('/').nth(3).unwrap();
        let c: Vec<&str> = lv.split('-').collect();
        return format!("/dev/{}/{}", c[0], c[1]);
    }
    path.to_string()
}

fn main() {
    let file = File::open("/proc/mounts").unwrap();
    let reader = BufReader::new(&file);

    let mut excludes = HashSet::new();
    let exclude_types = "cgroup autofs securityfs configfs pstore binfmt_misc debugfs \
                         hugetlbfs devpts mqueue proc sysfs";
    for t in exclude_types.split_whitespace() {
        excludes.insert(t);
    }

    let headers = ["Filesystem", "Size", "Used", "Avail", "Mounted on"];
    println!("{:30} {:>9} {:>9} {:>9} {:16}",
             headers[0], headers[1], headers[2], headers[3], headers[4]);

    for line in reader.lines() {
        match line {
            Ok(line) => {
                let fields: Vec<&str> = line.split_whitespace().collect();
                if excludes.contains(fields[2]) {
                    continue;
                }
                let statvfs = Statvfs::for_path(fields[1]).unwrap();
                let size = statvfs.f_blocks * statvfs.f_bsize;
                let avail = statvfs.f_bavail * statvfs.f_bsize;
                let used = size - avail;
                //let percent =
                println!("{:30} {:>9} {:>9} {:>9} {:16}",
                         shorten_lv(fields[0]), iec(size), iec(avail), iec(used), fields[1]);
            },
            Err(_) => panic!(),
        }
    }
}
<commit_msg>Remove B from Size.<commit_after>extern crate nix;

use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use std::collections::HashSet;
use nix::sys::statvfs::vfs::Statvfs;

// http://stackoverflow.com/questions/5194057/better-way-to-convert-file-sizes-in-python
fn iec(n: u64) -> String {
    let units = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"];

    let i = (n as f64).log(1024_f64).floor() as u32;
    let p = 1024_u64.pow(i);
    let s = (n as f64)/(p as f64);
    format!("{:.0}{}", s, units[i as usize])
}

// /dev/mapper/vg-lv -> /dev/vg/lv
// this needs much better error checking
fn shorten_lv(path: &str) -> String {
    if path.starts_with("/dev/mapper/") {
        let lv = path.split('/').nth(3).unwrap();
        let c: Vec<&str> = lv.split('-').collect();
        return format!("/dev/{}/{}", c[0], c[1]);
    }
    path.to_string()
}

fn main() {
    let file = File::open("/proc/mounts").unwrap();
    let reader = BufReader::new(&file);

    let mut excludes = HashSet::new();
    let exclude_types = "cgroup autofs securityfs configfs pstore binfmt_misc debugfs \
                         hugetlbfs devpts mqueue proc sysfs";
    for t in exclude_types.split_whitespace() {
        excludes.insert(t);
    }

    let headers = ["Filesystem", "Size", "Used", "Avail", "Mounted on"];
    println!("{:30} {:>9} {:>9} {:>9} {:16}",
             headers[0], headers[1], headers[2], headers[3], headers[4]);

    for line in reader.lines() {
        match line {
            Ok(line) => {
                let fields: Vec<&str> = line.split_whitespace().collect();
                if excludes.contains(fields[2]) {
                    continue;
                }
                let statvfs = Statvfs::for_path(fields[1]).unwrap();
                let size = statvfs.f_blocks * statvfs.f_bsize;
                let avail = statvfs.f_bavail * statvfs.f_bsize;
                let used = size - avail;
                //let percent =
                println!("{:30} {:>9} {:>9} {:>9} {:16}",
                         shorten_lv(fields[0]), iec(size), iec(avail), iec(used), fields[1]);
            },
            Err(_) => panic!(),
        }
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;

use std::io::{Read, stdin, stderr, BufReader, Write, BufRead};
use std::fs::File;
use std::process::exit;
use std::str::from_utf8;
use std::cmp::max;

use docopt::Docopt;

static LF: char = '\n';
static VERSION: &'static str = "0.0.1";
static USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

#[derive(Debug)]
enum ProcessingError {
    IO(std::io::Error),
    Utf8(std::str::Utf8Error),
}

impl From<std::io::Error> for ProcessingError {
    fn from(e: std::io::Error) -> ProcessingError {
        ProcessingError::IO(e)
    }
}
impl From<std::str::Utf8Error> for ProcessingError {
    fn from(e: std::str::Utf8Error) -> ProcessingError {
        ProcessingError::Utf8(e)
    }
}

#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

type FileInfoResult = Result<FileInfo, ProcessingError>;

fn process_filehandle<T: Read>(reader: T) -> FileInfoResult {
    let mut info = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    let mut rbuf = BufReader::new(reader);
    let mut lbuf: Vec<u8> = Vec::new();
    loop {
        let size = try!(rbuf.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            let line = try!(from_utf8(&lbuf));
            let size = line.chars().count();
            info.max_line_length = match line.chars().last() {
                Some(c) => {
                    if c == LF {
                        info.lines += 1;
                        max(info.max_line_length, size - 1)
                    } else {
                        max(info.max_line_length, size)
                    }
                },
                None => max(info.max_line_length, size),
            };
            info.chars += size;
            let mut words: Vec<&str> = line
                .split(|c: char| c.is_whitespace())
                .collect();
            words.retain(|s: &&str| s.len() > 0);
            // words.retain(|s: &&str| s.len() > 0);
            info.words += words.len();
        }
        lbuf.clear()
    }
    Ok(info)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        match writeln!(&mut stderr(), "wc: file operands cannot be combined with --files0-from") {
            Ok(_) => {},
            Err(e) => panic!("Unable to write to stderr: {}", e)
        }
        match writeln!(&mut stderr(), "Try 'wc --help' for more information") {
            Ok(_) => {},
            Err(e) => panic!("Unable to write to stderr: {}", e)
        }
        exit(1);
    }

    // TODO: Process --files0-from

    let mut results = Vec::new();
    let mut totals = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    for file_arg in &args.arg_FILE {
        let filename = file_arg.as_ref();
        let result = match filename {
            "-" => process_filehandle(stdin()),
            _ => {
                let file = File::open(filename.to_string());
                match file {
                    Ok(f) => process_filehandle(f),
                    Err(e) => Err(ProcessingError::IO(e)),
                }
            }
        };
        match result {
            Ok(ref r) => {
                totals.chars += r.chars;
                totals.lines += r.lines;
                totals.bytes += r.bytes;
                totals.words += r.words;
                totals.max_line_length = max(totals.max_line_length,
                                             r.max_line_length);
            },
            Err(_) => {},
        }
        results.push((filename, result));
    }

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = totals.bytes.to_string().len();

    if results.len() > 1 {
        results.push(("total", Ok(totals)));
    }

    let mut errors_encountered = false;

    // Present the results
    for data in results.iter() {
        let (filename, ref result) = *data;
        match *result {
            Ok(ref r) => {
                // let mut parts = Vec::new();
                let mut requested_field = false;
                if args.flag_lines {
                    print!("{:1$} ", r.lines, field_size);
                    requested_field = true;
                }
                if args.flag_words {
                    print!("{:1$} ", r.words, field_size);
                    requested_field = true;
                }
                if args.flag_bytes {
                    print!("{:1$} ", r.bytes, field_size);
                    requested_field = true;
                }
                if args.flag_chars {
                    print!("{:1$} ", r.chars, field_size);
                    requested_field = true;
                }
                if args.flag_max_line_length {
                    print!("{:1$} ", r.max_line_length, field_size);
                    requested_field = true;
                }
                if !requested_field {
                    print!("{:1$} ", r.lines, field_size);
                    print!("{:1$} ", r.words, field_size);
                    print!("{:1$} ", r.bytes, field_size);
                }
                println!("{}", filename);
            },
            Err(ref e) => {

                errors_encountered = true;
                match *e {
                    ProcessingError::IO(ref e) => {
                        println!("wc: {}: {}", filename, e)
                    },
                    ProcessingError::Utf8(ref e) => {
                        println!("wc: {}: {}", filename, e)
                    },
                }
            },
        }
    }

    if errors_encountered {
        exit(1);
    }
}
<commit_msg>Avoid namespace pollution<commit_after>extern crate rustc_serialize;
extern crate docopt;

use std::io::{Write, BufRead};
use docopt::Docopt;

static LF: char = '\n';
static VERSION: &'static str = "0.0.1";
static USAGE: &'static str = "
Usage: wc [options] FILE...
       wc [options] --files0-from=F
       wc (-h|--help)
       wc (-v|--version)

Print newline, word, and byte counts for each FILE, and a total line if
more than one FILE is specified. With no FILE, or when FILE is -,
read standard input. A word is a non-zero-length sequence of characters
delimited by white space.
The options below may be used to select which counts are printed, always in
the following order: newline, word, character, byte, maximum line length.

Options:
   -c, --bytes            print the byte counts
   -m, --chars            print the character counts
   -l, --lines            print the newline counts
   --files0-from=F        read input from the files specified by NUL-terminated
                          names in file F; If F is - then read names from
                          standard input
   -L, --max-line-length  print the length of the longest line
   -w, --words            print the word counts
   -h, --help             display this help and quit
   -v, --version          output version information and exit
";

#[allow(non_snake_case)]
#[derive(RustcDecodable, Debug)]
struct Args {
    arg_FILE: Vec<String>,
    flag_bytes: bool,
    flag_chars: bool,
    flag_lines: bool,
    flag_max_line_length: bool,
    flag_words: bool,
    flag_help: bool,
    flag_version: bool,
    flag_files0_from: String,
}

#[derive(Debug)]
enum ProcessingError {
    IO(std::io::Error),
    Utf8(std::str::Utf8Error),
}

impl From<std::io::Error> for ProcessingError {
    fn from(e: std::io::Error) -> ProcessingError {
        ProcessingError::IO(e)
    }
}
impl From<std::str::Utf8Error> for ProcessingError {
    fn from(e: std::str::Utf8Error) -> ProcessingError {
        ProcessingError::Utf8(e)
    }
}

#[derive(Debug)]
struct FileInfo{
    bytes: usize,
    chars: usize,
    lines: usize,
    words: usize,
    max_line_length: usize,
}

type FileInfoResult = Result<FileInfo, ProcessingError>;

fn process_filehandle<T: std::io::Read>(reader: T) -> FileInfoResult {
    let mut info = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    let mut rbuf = std::io::BufReader::new(reader);
    let mut lbuf: Vec<u8> = Vec::new();
    loop {
        let size = try!(rbuf.read_until(LF as u8, &mut lbuf));
        info.bytes += size;
        if size == 0 {
            break;
        }
        // Create a scope because we're going to borrow lbuf and
        // the borrow must end before we can clear it.
        {
            let line = try!(std::str::from_utf8(&lbuf));
            let size = line.chars().count();
            info.max_line_length = match line.chars().last() {
                Some(c) => {
                    if c == LF {
                        info.lines += 1;
                        std::cmp::max(info.max_line_length, size - 1)
                    } else {
                        std::cmp::max(info.max_line_length, size)
                    }
                },
                None => std::cmp::max(info.max_line_length, size),
            };
            info.chars += size;
            let mut words: Vec<&str> = line
                .split(|c: char| c.is_whitespace())
                .collect();
            words.retain(|s: &&str| s.len() > 0);
            // words.retain(|s: &&str| s.len() > 0);
            info.words += words.len();
        }
        lbuf.clear()
    }
    Ok(info)
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("wc v{}", VERSION);
        std::process::exit(0);
    }

    if args.flag_files0_from.len() != 0  && args.arg_FILE.len() != 0 {
        match writeln!(&mut std::io::stderr(),
                       "wc: file operands cannot be combined with --files0-from") {
            Ok(_) => {},
            Err(e) => panic!("Unable to write to stderr: {}", e)
        }
        match writeln!(&mut std::io::stderr(),
                       "Try 'wc --help' for more information") {
            Ok(_) => {},
            Err(e) => panic!("Unable to write to stderr: {}", e)
        }
        std::process::exit(1);
    }

    // TODO: Process --files0-from

    let mut results = Vec::new();
    let mut totals = FileInfo{
        bytes: 0,
        chars: 0,
        lines: 0,
        words: 0,
        max_line_length: 0,
    };
    for file_arg in &args.arg_FILE {
        let filename = file_arg.as_ref();
        let result = match filename {
            "-" => process_filehandle(std::io::stdin()),
            _ => {
                let file = std::fs::File::open(filename.to_string());
                match file {
                    Ok(f) => process_filehandle(f),
                    Err(e) => Err(ProcessingError::IO(e)),
                }
            }
        };
        match result {
            Ok(ref r) => {
                totals.chars += r.chars;
                totals.lines += r.lines;
                totals.bytes += r.bytes;
                totals.words += r.words;
                totals.max_line_length = std::cmp::max(totals.max_line_length,
                                                       r.max_line_length);
            },
            Err(_) => {},
        }
        results.push((filename, result));
    }

    // This is used for formatting. The number in the byte count will
    // be the largest, and so will be the widest string, so it's
    // suitable for a field width.
    let field_size = totals.bytes.to_string().len();

    if results.len() > 1 {
        results.push(("total", Ok(totals)));
    }

    let mut errors_encountered = false;

    // Present the results
    for data in results.iter() {
        let (filename, ref result) = *data;
        match *result {
            Ok(ref r) => {
                // let mut parts = Vec::new();
                let mut requested_field = false;
                if args.flag_lines {
                    print!("{:1$} ", r.lines, field_size);
                    requested_field = true;
                }
                if args.flag_words {
                    print!("{:1$} ", r.words, field_size);
                    requested_field = true;
                }
                if args.flag_bytes {
                    print!("{:1$} ", r.bytes, field_size);
                    requested_field = true;
                }
                if args.flag_chars {
                    print!("{:1$} ", r.chars, field_size);
                    requested_field = true;
                }
                if args.flag_max_line_length {
                    print!("{:1$} ", r.max_line_length, field_size);
                    requested_field = true;
                }
                if !requested_field {
                    print!("{:1$} ", r.lines, field_size);
                    print!("{:1$} ", r.words, field_size);
                    print!("{:1$} ", r.bytes, field_size);
                }
                println!("{}", filename);
            },
            Err(ref e) => {

                errors_encountered = true;
                match *e {
                    ProcessingError::IO(ref e) => {
                        println!("wc: {}: {}", filename, e)
                    },
                    ProcessingError::Utf8(ref e) => {
                        println!("wc: {}: {}", filename, e)
                    },
                }
            },
        }
    }

    if errors_encountered {
        std::process::exit(1);
    }
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(test)]
mod uci;
mod board;
mod tests;
mod move_gen;
mod monte_carlo;
mod bit_board;

extern crate time;

use Score::{Val, Draw, MateW, MateB};
use board::*;

use board::PieceType::*;
use board::Color::*;

use std::sync::{Arc, Mutex};
use std::io;

use std::fmt::Error;
use std::cmp::*;
use std::fmt::Display;
use std::fmt::Formatter;

use std::fs;

#[macro_use]
extern crate lazy_static;

fn main() {
    let reader = io::stdin();
    let mut input = "".to_string();
    loop {
        // println!("Enter input");
        reader.read_line(&mut input).unwrap();
        match &input[..] {
            "uci\n" => { uci::connect_engine().unwrap() }, //TODO: Use error value
            //"play_self\n" => play_game(&board::START_BOARD.clone()),
            //"play\n" => play_human(),
            s => println!("Unrecognized command \"{}\".", s),
        }
    }

    //play_game(&board::START_BOARD.clone());
    //play_human();
}

/*
#[allow(dead_code)]
fn play_game(board : &Board) {
    println!("Board:\n{}\nHalf move count: {}", board, (*board).half_move_clock);
    println!("\n");
    let engine_comm = Mutex::new(uci::EngineComm::new());
    engine_comm.lock().unwrap().engine_is_running = true;

    let (score, moves, _) = find_best_move_ab (&board, 5, &engine_comm, None);
    if moves.len() > 0 {
        println!("Found move with score {}.", score);
        play_game(&board.do_move(moves[0]));
    }
    else {
        match score {
            Val(_) => panic!("Found no moves for {}, but it was not mate! Board:\n{}",
                             board.to_move, board),
            MateW(_) => println!("White won at move! Board:\n{}", board),
            MateB(_) => println!("Black won! Board:\n{}", board),
            Draw(0) => println!("The game was drawn! Board:\n{}", board),
            Draw(n) => println!("Game was marked as drawn, but with {} moves left. Board:\n{}",
                                n, board),
        }
    }
}
*/

/*
#[allow(dead_code)]
fn play_human() {
    let mut board = board::START_BOARD.clone();
    loop {
        println!("Board:\n{}\nHalf move count: {}", board, board.half_move_clock);
        // If black, play as human
        if board.to_move == White {
            println!("Type your move as long algebraic notation (e2-e4):");

            let reader = io::stdin();
            let mut input_str = "".to_string();
            let legal_moves = move_gen::all_legal_moves(&board);
            // Loop until user enters a valid move
            loop {
                match Move::from_alg(&input_str) {
                    Ok(val) => {
                        let mut is_legal = false;
                        for c_move in legal_moves.iter() {
                            if *c_move == val { is_legal = true; }
                        }
                        if is_legal { break; }
                        println!("Move {} is illegal!", val);
                        for c_move in legal_moves.iter() {
                            println!("{}", c_move);
                        }
                    }

                    Err(error) => {
                        println!("{}", error);
                    },
                }
                input_str = "".to_string();
                reader.read_line(&mut input_str).ok().expect("Failed to read line");
                input_str = input_str.trim().to_string();

            }

            let c_move = Move::from_alg(&input_str).unwrap();
            println!("Doing move");
            board = board.do_move(c_move);

        }
        else {
            let engine_comm = Mutex::new(uci::EngineComm::new());
            engine_comm.lock().unwrap().engine_is_running = true;

            let (score, moves, _) = find_best_move_ab (&board, 3, &engine_comm, None);
            if moves.len() > 0 {
                println!("Found move with score {}.", score);
                board = board.do_move(moves[0]);
            }
            else {
                match score {
                    Val(_) => panic!("Found no moves for {},
but it was not mate or staltemate! Board:\n{}",
                                     board.to_move, board),
                    MateW(_) => { println!("White won at move! Board:\n{}", board); break },
                    MateB(_) => { println!("Black won! Board:\n{}", board); break },
                    Draw(0) => { println!("The game was drawn! Board:\n{}", board); break },
                    Draw(n) => println!(
                        "Game was marked as drawn, but with {} moves left. Board:\n{}",  n, board),
                }
            }
        }
    }
}
*/

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NodeCount {
    intern : u64,
    leaf : u64,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Score {
    Val(f32),
    Draw(u8),
    MateW(u8),
    MateB(u8),
}
impl Display for Score {
    fn fmt(&self, fmt : &mut Formatter) -> Result<(), Error> {
        let _ = match self {
            &Val(f) => fmt.write_str(&format!("cp {}", f).to_string()),
            &MateW(moves) => fmt.write_str(&format!("mate {}", moves)),
            &MateB(moves) => fmt.write_str(&format!("mate -{}", moves)),
            &Draw(_) => fmt.write_str("0.0 (forced draw)"),
        };
        Ok(())
    }
}

impl PartialOrd for Score {
    fn partial_cmp (&self, other: &Score) -> Option<Ordering> {
        match (self, other) {
            (&MateW(n1), &MateW(n2)) => (&n2).partial_cmp(&n1),
            (&MateW(_), _) => Some(Ordering::Greater),

            (&Val(_), &MateW(_)) => Some(Ordering::Less),
            (&Val(_), &MateB(_)) => Some(Ordering::Greater),
            (&Val(n1), &Val(n2)) => (&n1).partial_cmp(&n2),
            (&Val(n1), &Draw(_)) => (&n1).partial_cmp(&0.0),

            (&Draw(_), &Val(n1)) => (&0.0).partial_cmp(&n1),
            (&Draw(_), &Draw(_)) => Some(Ordering::Equal),
            (&Draw(_), &MateW(_)) => Some(Ordering::Less),
            (&Draw(_), &MateB(_)) => Some(Ordering::Greater),

            (&MateB(n1), &MateB(n2)) => (&n1).partial_cmp(&n2),
            (&MateB(_), _) => Some(Ordering::Less),

        }
    }
}

fn score_board (board : &board::Board) -> Score {
    let center_proximity = |file, rank| {
        (3.5f32).powi(2) - ((3.5 - file as f32).powi(2) + (3.5 - rank as f32).powi(2)).sqrt()
    };
    let mut value = 0.0;
    for rank in 0..8 {
        for file in 0..8 {
            let piece_val = board.board[rank][file].value();
            let pos_val = center_proximity(file, rank) *
                match board.board[rank][file] {
                    Piece(Bishop, White) => 0.1,
                    Piece(Bishop, Black) => -0.1,
                    Piece(Knight, White) => 0.2,
                    Piece(Knight, Black) => -0.2,
                    Piece(Queen, White) => 0.2,
                    Piece(Queen, Black) => -0.2,
                    Piece(Pawn, White) => 0.05,
                    Piece(Pawn, Black) => -0.05,
                    _ => 0.0,
                };
            value += piece_val + pos_val;
            //println!("Value at {} is {}, {}, total: {}",
            //         Square::from_ints(file as u8, rank as u8), piece_val, pos_val, value);
        }
    }
    Val(value)
}

/// Old find_best_move method, without alpha-beta pruning
#[allow(dead_code)]
fn find_best_move (board : &Board, depth : u8) -> (Score, Option<Move>) {
    if depth == 0 {
        return (score_board(&board), None);
    }
    if board.half_move_clock > 50 {
        return (Draw(0), None);
    }
    let mut best_score = if board.to_move == White { MateB(0) } else { MateW(0) };
    let mut best_move : Option<Move> = None;

    let legal_moves = move_gen::all_legal_moves(&board);
    if legal_moves.len() == 0 {
        if move_gen::is_attacked(board, board.king_pos()) {
            if board.to_move == White {
                return (MateB(0), None);
            }
            else {
                return (MateW(0), None);
            }
        }
        else {
            return (Draw(0), None);
        }
    }
    for c_move in legal_moves.iter() {

        let tried_board = board.do_move(*c_move);

        let (tried_score, _) = find_best_move(&tried_board, depth - 1);

        if board.to_move == White && tried_score == MateB(0) && tried_score < best_score
             || board.to_move == Black && tried_score == MateW(0) && tried_score > best_score {
            panic!(format!("{} has best score {}, tried score {}.",
                           board.to_move, best_score, tried_score));
        }
        if board.to_move == White && tried_score > best_score
            || board.to_move == Black && tried_score < best_score {
                best_score = tried_score;
                best_move = Some(*c_move);
            }
    }
    (match best_score {
        MateB(i) => MateB(i + 1),
        MateW(i) => MateW(i + 1),
        Draw(i) => Draw(i + 1),
        Val(n) => Val(n), },
     best_move)
}

fn search_moves (board : Board, engine_comm : Arc<Mutex<uci::EngineComm>>,
                 time_restriction : uci::TimeRestriction,
                 log_writer : Arc<Mutex<io::BufWriter<fs::File>>>) {

    engine_comm.lock().unwrap().engine_is_running = true;

    let max_depth : u8 = match time_restriction {
        uci::TimeRestriction::Depth(d) => d,
        uci::TimeRestriction::Mate(d) => d as u8,
        _ => 128,
    };;

    let start_time = time::get_time();

    for depth in 1..max_depth {
        let (score, moves, node_count) =
            find_best_move_ab(&board, depth, &*engine_comm, time_restriction);

        let ms_taken = (time::get_time() - start_time).num_milliseconds();

        if moves.len() > 0 {
            engine_comm.lock().unwrap().best_move = Some(moves[0]);
        }
        else {
            engine_comm.lock().unwrap().best_move = None;
        }

        uci::send_eval_to_gui(log_writer.clone(), depth,
                         ms_taken, score, moves, node_count);
    }

}

/// Returns a score, and a list of moves representing the moves it evaluated
fn find_best_move_ab (board : &Board, depth : u8, engine_comm : &Mutex<uci::EngineComm>,
                      time_restriction : uci::TimeRestriction )
                      -> (Score, Vec<Move>, NodeCount) {

    fn find_best_move_ab_rec (board: &Board, depth : u8, mut alpha : Score, mut beta : Score,
                              engine_comm : &Mutex<uci::EngineComm>,
                              time_restriction : uci::TimeRestriction,
                              node_counter : &mut NodeCount)
                              -> (Score, Vec<Move>) {
        use uci::TimeRestriction::*;
        {
            let mut engine_comm = engine_comm.lock().unwrap();

            if engine_comm.engine_should_stop {
                engine_comm.engine_is_running = true;
                panic!()
            }
        }
        if depth == 0 {
            node_counter.leaf += 1;
            return (score_board(&board), vec![]);
        }
        else {
            node_counter.intern += 1;
        }

        match time_restriction {
            GameTime(_) => (),
            Depth(_) => (),
            Nodes(n) => if node_counter.leaf + node_counter.intern > n {
                engine_comm.lock().unwrap().engine_is_running = true;
                panic!();
            },
            Mate(_) => (),
            MoveTime(_) => (), //TODO: Might want to check the clock here, and not just on every depth increase
            Infinite => (),
        };

        if board.half_move_clock > 50 {
            return (Draw(0), vec![]);
        }

        // Helpful alias
        let color = board.to_move;
        let mut best_move = None;
        let mut best_line = vec![];

        let legal_moves = move_gen::all_legal_moves(board);

        // Check if the player is checkmated or in stalemate
        if legal_moves.len() == 0 {
            if move_gen::is_attacked(board, board.king_pos()) {
                if color == White {
                    return (MateB(0), vec![]);
                }
                else {
                    return (MateW(0), vec![]);
                }
            }
            else {
                return (Draw(0), vec![]);
            }
        }
        for c_move in legal_moves {
            // Score is greater than the minimizer will ever allow OR
            // Score is lower than the maximizer will ever allow
            if (color == White && alpha >= beta) ||
                (color == Black && beta <= alpha) {
                    break;
                }
            else {
                let tried_board = board.do_move(c_move);
                let (tried_score, tried_line) =
                    find_best_move_ab_rec( &tried_board, depth - 1, alpha, beta,
                                            engine_comm, time_restriction, node_counter);

                if color == White && tried_score > alpha {
                    alpha = tried_score;
                    best_line = tried_line;
                    best_move = Some(c_move);
                }
                else if color == Black && tried_score < beta {
                    beta = tried_score;
                    best_line = tried_line;
                    best_move = Some(c_move);
                }
            }
        }
        match best_move {
            Some(c_move) => best_line.push(c_move),
            None => (),
        }
        let score = if color == White { alpha } else { beta };
        (match score {
            MateB(i) => MateB(i + 1),
            MateW(i) => MateW(i + 1),
            Draw(i) => Draw(i + 1),
            Val(n) => Val(n), },
         best_line)

    };
    let mut node_counter = NodeCount { intern: 0, leaf: 0 };
    let (score, mut moves) =
        find_best_move_ab_rec(board, depth, MateB(0), MateW(0),
                              engine_comm, time_restriction, &mut node_counter);
    moves.reverse();
    // println!("Evaluated {} internal nodes and {} leaves", node_counter.intern, node_counter.leaf);
    (score, moves, node_counter)
}

<commit_msg>Removed old, non-A/B search algorithm<commit_after>#![feature(core)]
#![feature(test)]
mod uci;
mod board;
mod tests;
mod move_gen;
mod monte_carlo;
mod bit_board;

extern crate time;

use Score::{Val, Draw, MateW, MateB};
use board::*;

use board::PieceType::*;
use board::Color::*;

use std::sync::{Arc, Mutex};
use std::io;

use std::fmt::Error;
use std::cmp::*;
use std::fmt::Display;
use std::fmt::Formatter;

use std::fs;

#[macro_use]
extern crate lazy_static;

fn main() {
    let reader = io::stdin();
    let mut input = "".to_string();
    loop {
        // println!("Enter input");
        reader.read_line(&mut input).unwrap();
        match &input[..] {
            "uci\n" => { uci::connect_engine().unwrap() }, //TODO: Use error value
            //"play_self\n" => play_game(&board::START_BOARD.clone()),
            //"play\n" => play_human(),
            s => println!("Unrecognized command \"{}\".", s),
        }
    }

    //play_game(&board::START_BOARD.clone());
    //play_human();
}

/*
#[allow(dead_code)]
fn play_game(board : &Board) {
    println!("Board:\n{}\nHalf move count: {}", board, (*board).half_move_clock);
    println!("\n");
    let engine_comm = Mutex::new(uci::EngineComm::new());
    engine_comm.lock().unwrap().engine_is_running = true;

    let (score, moves, _) = find_best_move_ab (&board, 5, &engine_comm, None);
    if moves.len() > 0 {
        println!("Found move with score {}.", score);
        play_game(&board.do_move(moves[0]));
    }
    else {
        match score {
            Val(_) => panic!("Found no moves for {}, but it was not mate! Board:\n{}",
                             board.to_move, board),
            MateW(_) => println!("White won at move! Board:\n{}", board),
            MateB(_) => println!("Black won! Board:\n{}", board),
            Draw(0) => println!("The game was drawn! Board:\n{}", board),
            Draw(n) => println!("Game was marked as drawn, but with {} moves left. Board:\n{}",
                                n, board),
        }
    }
}
*/

/*
#[allow(dead_code)]
fn play_human() {
    let mut board = board::START_BOARD.clone();
    loop {
        println!("Board:\n{}\nHalf move count: {}", board, board.half_move_clock);
        // If black, play as human
        if board.to_move == White {
            println!("Type your move as long algebraic notation (e2-e4):");

            let reader = io::stdin();
            let mut input_str = "".to_string();
            let legal_moves = move_gen::all_legal_moves(&board);
            // Loop until user enters a valid move
            loop {
                match Move::from_alg(&input_str) {
                    Ok(val) => {
                        let mut is_legal = false;
                        for c_move in legal_moves.iter() {
                            if *c_move == val { is_legal = true; }
                        }
                        if is_legal { break; }
                        println!("Move {} is illegal!", val);
                        for c_move in legal_moves.iter() {
                            println!("{}", c_move);
                        }
                    }

                    Err(error) => {
                        println!("{}", error);
                    },
                }
                input_str = "".to_string();
                reader.read_line(&mut input_str).ok().expect("Failed to read line");
                input_str = input_str.trim().to_string();

            }

            let c_move = Move::from_alg(&input_str).unwrap();
            println!("Doing move");
            board = board.do_move(c_move);

        }
        else {
            let engine_comm = Mutex::new(uci::EngineComm::new());
            engine_comm.lock().unwrap().engine_is_running = true;

            let (score, moves, _) = find_best_move_ab (&board, 3, &engine_comm, None);
            if moves.len() > 0 {
                println!("Found move with score {}.", score);
                board = board.do_move(moves[0]);
            }
            else {
                match score {
                    Val(_) => panic!("Found no moves for {},
but it was not mate or staltemate! Board:\n{}",
                                     board.to_move, board),
                    MateW(_) => { println!("White won at move! Board:\n{}", board); break },
                    MateB(_) => { println!("Black won! Board:\n{}", board); break },
                    Draw(0) => { println!("The game was drawn! Board:\n{}", board); break },
                    Draw(n) => println!(
                        "Game was marked as drawn, but with {} moves left. Board:\n{}",  n, board),
                }
            }
        }
    }
}
*/

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NodeCount {
    intern : u64,
    leaf : u64,
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Score {
    Val(f32),
    Draw(u8),
    MateW(u8),
    MateB(u8),
}
impl Display for Score {
    fn fmt(&self, fmt : &mut Formatter) -> Result<(), Error> {
        let _ = match self {
            &Val(f) => fmt.write_str(&format!("cp {}", f).to_string()),
            &MateW(moves) => fmt.write_str(&format!("mate {}", moves)),
            &MateB(moves) => fmt.write_str(&format!("mate -{}", moves)),
            &Draw(_) => fmt.write_str("0.0 (forced draw)"),
        };
        Ok(())
    }
}

impl PartialOrd for Score {
    fn partial_cmp (&self, other: &Score) -> Option<Ordering> {
        match (self, other) {
            (&MateW(n1), &MateW(n2)) => (&n2).partial_cmp(&n1),
            (&MateW(_), _) => Some(Ordering::Greater),

            (&Val(_), &MateW(_)) => Some(Ordering::Less),
            (&Val(_), &MateB(_)) => Some(Ordering::Greater),
            (&Val(n1), &Val(n2)) => (&n1).partial_cmp(&n2),
            (&Val(n1), &Draw(_)) => (&n1).partial_cmp(&0.0),

            (&Draw(_), &Val(n1)) => (&0.0).partial_cmp(&n1),
            (&Draw(_), &Draw(_)) => Some(Ordering::Equal),
            (&Draw(_), &MateW(_)) => Some(Ordering::Less),
            (&Draw(_), &MateB(_)) => Some(Ordering::Greater),

            (&MateB(n1), &MateB(n2)) => (&n1).partial_cmp(&n2),
            (&MateB(_), _) => Some(Ordering::Less),

        }
    }
}

fn score_board (board : &board::Board) -> Score {
    let center_proximity = |file, rank| {
        (3.5f32).powi(2) - ((3.5 - file as f32).powi(2) + (3.5 - rank as f32).powi(2)).sqrt()
    };
    let mut value = 0.0;
    for rank in 0..8 {
        for file in 0..8 {
            let piece_val = board.board[rank][file].value();
            let pos_val = center_proximity(file, rank) *
                match board.board[rank][file] {
                    Piece(Bishop, White) => 0.1,
                    Piece(Bishop, Black) => -0.1,
                    Piece(Knight, White) => 0.2,
                    Piece(Knight, Black) => -0.2,
                    Piece(Queen, White) => 0.2,
                    Piece(Queen, Black) => -0.2,
                    Piece(Pawn, White) => 0.05,
                    Piece(Pawn, Black) => -0.05,
                    _ => 0.0,
                };
            value += piece_val + pos_val;
            //println!("Value at {} is {}, {}, total: {}",
            //         Square::from_ints(file as u8, rank as u8), piece_val, pos_val, value);
        }
    }
    Val(value)
}

fn search_moves (board : Board, engine_comm : Arc<Mutex<uci::EngineComm>>,
                 time_restriction : uci::TimeRestriction,
                 log_writer : Arc<Mutex<io::BufWriter<fs::File>>>) {

    engine_comm.lock().unwrap().engine_is_running = true;

    let max_depth : u8 = match time_restriction {
        uci::TimeRestriction::Depth(d) => d,
        uci::TimeRestriction::Mate(d) => d as u8,
        _ => 128,
    };;

    let start_time = time::get_time();

    for depth in 1..max_depth {
        let (score, moves, node_count) =
            find_best_move_ab(&board, depth, &*engine_comm, time_restriction);

        let ms_taken = (time::get_time() - start_time).num_milliseconds();

        if moves.len() > 0 {
            engine_comm.lock().unwrap().best_move = Some(moves[0]);
        }
        else {
            engine_comm.lock().unwrap().best_move = None;
        }

        uci::send_eval_to_gui(log_writer.clone(), depth,
                         ms_taken, score, moves, node_count);
    }

}

/// Returns a score, and a list of moves representing the moves it evaluated
fn find_best_move_ab (board : &Board, depth : u8, engine_comm : &Mutex<uci::EngineComm>,
                      time_restriction : uci::TimeRestriction )
                      -> (Score, Vec<Move>, NodeCount) {

    fn find_best_move_ab_rec (board: &Board, depth : u8, mut alpha : Score, mut beta : Score,
                              engine_comm : &Mutex<uci::EngineComm>,
                              time_restriction : uci::TimeRestriction,
                              node_counter : &mut NodeCount)
                              -> (Score, Vec<Move>) {
        use uci::TimeRestriction::*;
        {
            let mut engine_comm = engine_comm.lock().unwrap();

            if engine_comm.engine_should_stop {
                engine_comm.engine_is_running = true;
                panic!()
            }
        }
        if depth == 0 {
            node_counter.leaf += 1;
            return (score_board(&board), vec![]);
        }
        else {
            node_counter.intern += 1;
        }

        match time_restriction {
            GameTime(_) => (),
            Depth(_) => (),
            Nodes(n) => if node_counter.leaf + node_counter.intern > n {
                engine_comm.lock().unwrap().engine_is_running = true;
                panic!();
            },
            Mate(_) => (),
            MoveTime(_) => (), //TODO: Might want to check the clock here, and not just on every depth increase
            Infinite => (),
        };

        if board.half_move_clock > 50 {
            return (Draw(0), vec![]);
        }

        // Helpful alias
        let color = board.to_move;
        let mut best_move = None;
        let mut best_line = vec![];

        let legal_moves = move_gen::all_legal_moves(board);

        // Check if the player is checkmated or in stalemate
        if legal_moves.len() == 0 {
            if move_gen::is_attacked(board, board.king_pos()) {
                if color == White {
                    return (MateB(0), vec![]);
                }
                else {
                    return (MateW(0), vec![]);
                }
            }
            else {
                return (Draw(0), vec![]);
            }
        }
        for c_move in legal_moves {
            // Score is greater than the minimizer will ever allow OR
            // Score is lower than the maximizer will ever allow
            if (color == White && alpha >= beta) ||
                (color == Black && beta <= alpha) {
                    break;
                }
            else {
                let tried_board = board.do_move(c_move);
                let (tried_score, tried_line) =
                    find_best_move_ab_rec( &tried_board, depth - 1, alpha, beta,
                                            engine_comm, time_restriction, node_counter);

                if color == White && tried_score > alpha {
                    alpha = tried_score;
                    best_line = tried_line;
                    best_move = Some(c_move);
                }
                else if color == Black && tried_score < beta {
                    beta = tried_score;
                    best_line = tried_line;
                    best_move = Some(c_move);
                }
            }
        }
        match best_move {
            Some(c_move) => best_line.push(c_move),
            None => (),
        }
        let score = if color == White { alpha } else { beta };
        (match score {
            MateB(i) => MateB(i + 1),
            MateW(i) => MateW(i + 1),
            Draw(i) => Draw(i + 1),
            Val(n) => Val(n), },
         best_line)

    };
    let mut node_counter = NodeCount { intern: 0, leaf: 0 };
    let (score, mut moves) =
        find_best_move_ab_rec(board, depth, MateB(0), MateW(0),
                              engine_comm, time_restriction, &mut node_counter);
    moves.reverse();
    // println!("Evaluated {} internal nodes and {} leaves", node_counter.intern, node_counter.leaf);
    (score, moves, node_counter)
}

<|endoftext|>


<commit_before>use crate::exercise::{Exercise, ExerciseList};
use crate::run::run;
use crate::verify::verify;
use clap::{crate_version, App, Arg, SubCommand};
use notify::DebouncedEvent;
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
use std::sync::mpsc::channel;
use std::time::Duration;

mod exercise;
mod run;
mod verify;

fn main() {
    let matches = App::new("rustlings")
        .version(crate_version!())
        .author("Olivia Hugger, Carol Nichols")
        .about("Rustlings is a collection of small exercises to get you used to writing and reading Rust code")
        .subcommand(SubCommand::with_name("verify").alias("v").about("Verifies all exercises according to the recommended order"))
        .subcommand(SubCommand::with_name("watch").alias("w").about("Reruns `verify` when files were edited"))
        .subcommand(
            SubCommand::with_name("run")
                .alias("r")
                .about("Runs/Tests a single exercise")
                .arg(Arg::with_name("file").required(true).index(1))
                .arg(Arg::with_name("test").short("t").long("test").help("Run the file as a test")),
        )
        .get_matches();

    if None == matches.subcommand_name() {
        println!();
        println!(r#"       welcome to...                      "#);
        println!(r#"                 _   _ _                  "#);
        println!(r#"  _ __ _   _ ___| |_| (_)_ __   __ _ ___  "#);
        println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);
        println!(r#" | |  | |_| \__ \ |_| | | | | | (_| \__ \ "#);
        println!(r#" |_|   \__,_|___/\__|_|_|_| |_|\__, |___/ "#);
        println!(r#"                               |___/      "#);
        println!();
    }

    if !Path::new("info.toml").exists() {
        println!(
            "{} must be run from the rustlings directory",
            std::env::current_exe().unwrap().to_str().unwrap()
        );
        println!("Try `cd rustlings/`!");
        std::process::exit(1);
    }

    let toml_str = &fs::read_to_string("info.toml").unwrap();
    let exercises = toml::from_str::<ExerciseList>(toml_str).unwrap().exercises;

    if let Some(ref matches) = matches.subcommand_matches("run") {
        let filename = matches.value_of("file").unwrap_or_else(|| {
            println!("Please supply a file name!");
            std::process::exit(1);
        });

        let matching_exercise = |e: &&Exercise| {
            Path::new(filename)
                .canonicalize()
                .map(|p| p.ends_with(&e.path))
                .unwrap_or(false)
        };

        let exercise = exercises.iter().find(matching_exercise).unwrap_or_else(|| {
            println!("No exercise found for your file name!");
            std::process::exit(1)
        });

        run(&exercise).unwrap_or_else(|_| std::process::exit(1));
    }

    if matches.subcommand_matches("verify").is_some() {
        verify(&exercises).unwrap_or_else(|_| std::process::exit(1));
    }

    if matches.subcommand_matches("watch").is_some() {
        watch(&exercises).unwrap();
    }

    if matches.subcommand_name().is_none() {
        let text = fs::read_to_string("default_out.txt").unwrap();
        println!("{}", text);
    }

    println!("\x1b[0m");
}

fn watch(exercises: &[Exercise]) -> notify::Result<()> {
    let (tx, rx) = channel();

    let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
    watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;

    let _ignored = verify(exercises.iter());

    loop {
        match rx.recv() {
            Ok(event) => match event {
                DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
                    if b.extension() == Some(OsStr::new("rs")) {
                        println!("----------**********----------\n");
                        let filepath = b.as_path().canonicalize().unwrap();
                        let exercise = exercises
                            .iter()
                            .skip_while(|e| !filepath.ends_with(&e.path));
                        let _ignored = verify(exercise);
                    }
                }
                _ => {}
            },
            Err(e) => println!("watch error: {:?}", e),
        }
    }
}
<commit_msg>chore: Remove missed highlighting char from Issue #133<commit_after>use crate::exercise::{Exercise, ExerciseList};
use crate::run::run;
use crate::verify::verify;
use clap::{crate_version, App, Arg, SubCommand};
use notify::DebouncedEvent;
use notify::{RecommendedWatcher, RecursiveMode, Watcher};
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
use std::sync::mpsc::channel;
use std::time::Duration;

mod exercise;
mod run;
mod verify;

fn main() {
    let matches = App::new("rustlings")
        .version(crate_version!())
        .author("Olivia Hugger, Carol Nichols")
        .about("Rustlings is a collection of small exercises to get you used to writing and reading Rust code")
        .subcommand(SubCommand::with_name("verify").alias("v").about("Verifies all exercises according to the recommended order"))
        .subcommand(SubCommand::with_name("watch").alias("w").about("Reruns `verify` when files were edited"))
        .subcommand(
            SubCommand::with_name("run")
                .alias("r")
                .about("Runs/Tests a single exercise")
                .arg(Arg::with_name("file").required(true).index(1))
                .arg(Arg::with_name("test").short("t").long("test").help("Run the file as a test")),
        )
        .get_matches();

    if None == matches.subcommand_name() {
        println!();
        println!(r#"       welcome to...                      "#);
        println!(r#"                 _   _ _                  "#);
        println!(r#"  _ __ _   _ ___| |_| (_)_ __   __ _ ___  "#);
        println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#);
        println!(r#" | |  | |_| \__ \ |_| | | | | | (_| \__ \ "#);
        println!(r#" |_|   \__,_|___/\__|_|_|_| |_|\__, |___/ "#);
        println!(r#"                               |___/      "#);
        println!();
    }

    if !Path::new("info.toml").exists() {
        println!(
            "{} must be run from the rustlings directory",
            std::env::current_exe().unwrap().to_str().unwrap()
        );
        println!("Try `cd rustlings/`!");
        std::process::exit(1);
    }

    let toml_str = &fs::read_to_string("info.toml").unwrap();
    let exercises = toml::from_str::<ExerciseList>(toml_str).unwrap().exercises;

    if let Some(ref matches) = matches.subcommand_matches("run") {
        let filename = matches.value_of("file").unwrap_or_else(|| {
            println!("Please supply a file name!");
            std::process::exit(1);
        });

        let matching_exercise = |e: &&Exercise| {
            Path::new(filename)
                .canonicalize()
                .map(|p| p.ends_with(&e.path))
                .unwrap_or(false)
        };

        let exercise = exercises.iter().find(matching_exercise).unwrap_or_else(|| {
            println!("No exercise found for your file name!");
            std::process::exit(1)
        });

        run(&exercise).unwrap_or_else(|_| std::process::exit(1));
    }

    if matches.subcommand_matches("verify").is_some() {
        verify(&exercises).unwrap_or_else(|_| std::process::exit(1));
    }

    if matches.subcommand_matches("watch").is_some() {
        watch(&exercises).unwrap();
    }

    if matches.subcommand_name().is_none() {
        let text = fs::read_to_string("default_out.txt").unwrap();
        println!("{}", text);
    }
}

fn watch(exercises: &[Exercise]) -> notify::Result<()> {
    let (tx, rx) = channel();

    let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
    watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?;

    let _ignored = verify(exercises.iter());

    loop {
        match rx.recv() {
            Ok(event) => match event {
                DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
                    if b.extension() == Some(OsStr::new("rs")) {
                        println!("----------**********----------\n");
                        let filepath = b.as_path().canonicalize().unwrap();
                        let exercise = exercises
                            .iter()
                            .skip_while(|e| !filepath.ends_with(&e.path));
                        let _ignored = verify(exercise);
                    }
                }
                _ => {}
            },
            Err(e) => println!("watch error: {:?}", e),
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::collections::HashMap;
use std::str::Split;
use std::slice::Iter;

pub struct Command<'a> {
    command: Vec<&'a str>,
    exec: Box<FnMut(Vec<&str>) + 'a>,
    complete: Option<Box<FnMut(Vec<&str>) + 'a>>
}

impl<'a> Command<'a> {
    fn new<T: FnMut(Vec<&str>) + 'a, U: FnMut(Vec<&str>) + 'a>(cmd: Vec<&'a str>, exec_handler: T, complete_handler: Option<U>) -> Command<'a> {
        let mut obj = Command {
            command: cmd,
            exec: Box::new(exec_handler),
            complete: None
        };

        if let Some(cb) = complete_handler {
            obj.complete = Some(Box::new(cb));
        }

        obj
    }
}

pub struct Cli<'a> {
    commands: HashMap<&'a str, Option<Cli<'a>>>,
    handler: Option<Command<'a>>
}

impl<'a> Cli<'a>{

    fn new() -> Cli<'a> {
        Cli {
            commands: HashMap::new(),
            handler: None
        }
    }

    fn register<T: FnMut(Vec<&str>) + 'a>(&mut self, cmd: Vec<&'a str>, exec: T) -> Result<(), Command> {
        let tmp = Command {
            command: cmd.clone(),
            exec: Box::new(exec),
            complete: None
        };

        self._register(cmd.iter(), tmp)
    }

    fn _register(&mut self, it: Iter<&'a str>, command: Command, parent: &mut Cli) -> Result<(), Command> {
        let cli = Cli::new();

        if let Some(portion) = it.next() {
            if (!self.commands.contains_key(portion)) {
                self.commands.insert(portion, Some(cli));
            }
        }
        Ok(())
    }

    fn complete(&self, argv: &str) -> Vec<&str> {
        println!("complete for '{}'", argv.trim());
        self.commands.keys()
            .filter(|cmd| cmd.starts_with(argv.trim()))
            .cloned()
            .collect()
    }

    fn exec(&mut self, cmd: &str) {
        //if let Some(command) = self.commands.get_mut(cmd) {
            //TODO: figure out why I can't do that in one line
            //let ref mut x = command.exec;
            //x(vec!["blah"]);
        //}
    }
}

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

    #[test]
    fn test_register_and_execute() {
        let mut called = false;
        {
            let mut cli = Cli::new();
            cli.register(vec!["foo bar"], | args | { called=true } );
            cli.exec("foo bar");
        }
        assert!(called == true)
    }

    #[test]
    fn test_complete_empty_str() {
        let mut cli = Cli::new();
        cli.register(vec!["foo"], | args | { } );
        cli.register(vec!["bar"], | args | { } );
        assert!(vec!["foo", "bar"] == cli.complete(""))
    }

    #[test]
    fn test_complete_partial() {
        let mut cli = Cli::new();
        cli.register(vec!["foo"], | args | { } );
        assert!(vec!["foo"] == cli.complete("f"));
        assert!(vec!["foo"] == cli.complete("fo"));
        assert!(vec!["foo"] == cli.complete("foo"));
    }

    #[test]
    fn test_complete_composite() {
        let mut cli = Cli::new();
        cli.register(vec!["foo", "bar"], | args | { } );
        assert!(vec!["foo", "bar"] == cli.complete("f"));
        assert!(vec!["foo", "bar"] == cli.complete("foo"));
        assert!(vec!["foo", "bar"] == cli.complete("foo "));
        assert!(vec!["foo", "bar"] == cli.complete("foo b"));
    }
}

    //fn new(callback: Option<Box<Fn()>>) -> Cli<'a> {
        //Cli {
            //commands: HashMap::new(),
            //exec: callback,
            //complete_cb: None,
        //}
    //}

    //fn register<T: Fn() + 'static>(&mut self, command: Vec<&'a str>, callback: T) {
        //let mut it = command.iter();
        //self._register(it, callback);
    //}

    //fn _register<T: Fn() + 'static>(&mut self, mut it: std::slice::Iter<&'a str>, callback: T) {
        //if let Some(portion) = it.next() {
            //if !self.commands.contains_key(portion) {
                //self.commands.insert(portion, Cli::new(Some(Box::new(callback))));
            //}

            //self.commands.get_mut(portion).unwrap()._register(it, callback);
        //}
    //}

    //fn suggest(&mut self, command: &str) -> Vec<&str> {
        //let mut portions = command.trim().split(" ");
        //let mut suggestions = self._suggest(&mut portions);
        //if let Some(ref cb) = self.complete_cb {
            //cb();
            //println!("got callback");
        //}
        //suggestions
    //}

    //fn _suggest(&self, portions: &mut std::str::Split<&str>) -> Vec<&str> {
        //let mut ret = Vec::with_capacity(self.commands.len());

        //if let Some(portion) = portions.next() {
            //if !portion.is_empty() {
                //if let Some(cmd) = self.commands.get(portion) {
                    //ret = cmd._suggest(portions);
                //}
            //} else {
                //for key in self.commands.keys() {
                    //ret.push(*key);
                //}
            //}
        //} else {
            //for key in self.commands.keys() {
                //ret.push(*key);
            //}
        //}

        //ret
    //}
//}

fn foo(argv: Vec<&str>) {

}

fn main() {
    let mut cli = Cli::new();

    cli.register(vec!["show", "stuff"], foo);
    cli.register(vec!["show", "other"], foo);
    cli.register(vec!["list", "other cool"], foo);
    cli.register(vec!["list", "other uncool"], foo);

    loop {
        let mut line = String::new();
        std::io::stdin().read_line(&mut line).unwrap();

        println!("got: {:?}", cli.complete(&line));
    }
}
<commit_msg>registration and completion working, might have some issues still. Still have to register handlers properly, so that we can call exec<commit_after>use std::collections::HashMap;
use std::str::SplitWhitespace;
use std::slice::Iter;

pub struct Command<'a> {
    command: Vec<&'a str>,
    exec: Box<FnMut(Vec<&str>) + 'a>,
    complete: Option<Box<FnMut(Vec<&str>) + 'a>>
}

impl<'a> Command<'a> {
    fn new<T: FnMut(Vec<&str>) + 'a, U: FnMut(Vec<&str>) + 'a>(cmd: Vec<&'a str>, exec_handler: T, complete_handler: Option<U>) -> Command<'a> {
        let mut obj = Command {
            command: cmd,
            exec: Box::new(exec_handler),
            complete: None
        };

        if let Some(cb) = complete_handler {
            obj.complete = Some(Box::new(cb));
        }

        obj
    }
}

pub struct Cli<'a> {
    commands: HashMap<&'a str, Cli<'a>>,
    handler: Option<Command<'a>>
}

impl<'a> Cli<'a>{

    fn new() -> Cli<'a> {
        Cli {
            commands: HashMap::new(),
            handler: None
        }
    }

    fn register<T: FnMut(Vec<&str>) + 'a>(&mut self, cmd: Vec<&'a str>, exec: T) -> Result<(), ()> {
        //let tmp = Command::new(cmd, exec, None);
        let tmp = Command {
            command: cmd.clone(),
            exec: Box::new(exec),
            complete: None
        };

        match self._register(cmd.iter(), &tmp) {
            Ok(_) => Ok(()),
            Err(_) => {
                //this only happens if we register "Vec::new()" ... probably disallow that at all
                self.handler = Some(tmp);
                Err(())
            }
        }
    }

    fn _register(&mut self, mut it: Iter<&'a str>, command: &Command) -> Result<(), ()> {
        if let Some(portion) = it.next() {
            let current:Vec<&str> = self.commands.keys().cloned().collect();

            if !self.commands.contains_key(portion) {
                let mut cli = Cli::new();
                cli._register(it, command);
                self.commands.insert(portion, cli);
            } else {
                if let Some(cmd) = self.commands.get_mut(portion) {
                    //TODO: register handler if we get an error
                    cmd._register(it, command);
                }
            };
            Ok(())
        } else {
            Err(())
        }
    }

    fn complete(&self, argv: &str) -> Vec<&str> {
        println!("complete for '{}'", argv.trim());
        let mut portions = argv.trim().split_whitespace();

        match self._complete(portions) {
            Ok(ret) => ret,
            Err(_) => Vec::new()
        }
    }

    fn _complete(&self, mut portions: SplitWhitespace) -> Result<Vec<&str>, ()> {
        if let Some(ref portion) = portions.next() {
            if let Some(ref cmd) = self.commands.get(portion) {
                cmd._complete(portions)
            } else {
                Ok(self.commands.keys()
                    .filter(|cmd| cmd.starts_with(portion))
                    .cloned()
                    .collect())
            }
        } else {
            Ok(self.commands.keys()
                .cloned()
                .collect())
        }
    }

    fn exec(&mut self, cmd: &str) {
        let mut portions = cmd.trim().split_whitespace();
        self._exec(portions);
        //if let Some(command) = self.commands.get_mut(cmd) {
            ////TODO: figure out why I can't do that in one line
            ////let ref mut x = command.exec;
            ////x(vec!["blah"]);
            //println!("found cmd");
        //} else {
            //println!(" not found cmd");
        //}
    }

    fn _exec(&mut self, mut portions: SplitWhitespace) {
        if let Some(portion) = portions.next() {
            if let Some(cmd) = self.commands.get_mut(portion) {
                cmd._exec(portions);
            } else {
                match self.handler {
                    Some(ref x) => println!("handler for {:?}", x.command),
                    None => println!("no handler")
                }
            }
        } else {
            match self.handler {
                Some(ref x) => println!("handler for {:?}", x.command),
                None => println!("no handler")
            }
        }
    }
}

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

    #[test]
    fn test_register_and_execute() {
        let mut called = false;
        {
            let mut cli = Cli::new();
            cli.register(vec!["foo bar"], | args | { called=true } );
            cli.exec("foo bar");
        }
        assert!(called == true)
    }

    #[test]
    fn test_complete_empty_str() {
        let mut cli = Cli::new();
        cli.register(vec!["foo"], | args | { } );
        cli.register(vec!["bar"], | args | { } );
        assert!(vec!["foo", "bar"] == cli.complete(""))
    }

    #[test]
    fn test_complete_partial() {
        let mut cli = Cli::new();
        cli.register(vec!["foo"], | args | { } );
        assert!(vec!["foo"] == cli.complete("f"));
        assert!(vec!["foo"] == cli.complete("fo"));
        assert!(vec!["foo"] == cli.complete("foo"));
    }

    #[test]
    fn test_complete_composite() {
        let mut cli = Cli::new();
        cli.register(vec!["foo", "bar"], | args | { } );
        assert!(vec!["foo", "bar"] == cli.complete("f"));
        assert!(vec!["foo", "bar"] == cli.complete("foo"));
        assert!(vec!["foo", "bar"] == cli.complete("foo "));
        assert!(vec!["foo", "bar"] == cli.complete("foo b"));
    }
}

fn foo(argv: Vec<&str>) {

}

fn main() {
    let mut cli = Cli::new();

    cli.register(vec!["show", "stuff"], foo);
    cli.register(vec!["show", "other"], foo);
    cli.register(vec!["some", "other"], foo);
    cli.register(vec!["list", "other", "cool"], foo);
    cli.register(vec!["list", "other", "uncool"], foo);

    loop {
        let mut line = String::new();
        std::io::stdin().read_line(&mut line).unwrap();

        println!("got: {:?}", cli.complete(&line));
    }
}
<|endoftext|>


<commit_before>use std::str;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
enum Suit {
    Clubs,
    Diamonds,
    Hearts,
    Spades,
    Stars,
}

#[derive(Debug, Eq, PartialEq, PartialOrd)]
struct Card {
    suit: Suit,
    value: &'static str,
}

impl Card {
    fn new(suit: Suit, value: &'static str) -> Card {
        Card { suit: suit, value: value }
    }
}

impl Ord for Card {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.value.cmp(&other.value)
    }
}
fn make_standard_deck() -> Vec<Card> {
    let mut result = Vec::new();
    let suits  = &[Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades];
    let values = &["2", "3", "4", "5", "6", "7", "8", "9", "10",
                   "Jack", "Queen", "King", "Ace"];

    for suit in suits {
        for value in values {
            result.push(Card::new(*suit, value));
        }
    }
    return result;
}

fn make_fifth_dimension_deck() -> Vec<Card> {
    let mut result = Vec::new();
    let suits  = &[Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades, Suit::Stars];
    let values = &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
                   "Jack", "Princess", "Queen", "King", "Joker"];

    for suit in suits {
        for value in values {
            result.push(Card::new(*suit, value));
        }
    }
    return result;
}

fn main() {
}
<commit_msg>add shuffle<commit_after>extern crate rand;
use std::str;
use rand::Rng;

#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Copy, Clone)]
enum Suit {
    Clubs,
    Diamonds,
    Hearts,
    Spades,
    Stars,
}

#[derive(Debug, Eq, PartialEq, PartialOrd)]
struct Card {
    suit: Suit,
    value: &'static str,
}

impl Card {
    fn new(suit: Suit, value: &'static str) -> Card {
        Card { suit: suit, value: value }
    }
}

impl Ord for Card {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.value.cmp(&other.value)
    }
}
fn make_standard_deck() -> Vec<Card> {
    let mut result = Vec::new();
    let suits  = &[Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades];
    let values = &["2", "3", "4", "5", "6", "7", "8", "9", "10",
                   "Jack", "Queen", "King", "Ace"];

    for suit in suits {
        for value in values {
            result.push(Card::new(*suit, value));
        }
    }
    return result;
}

fn make_fifth_dimension_deck() -> Vec<Card> {
    let mut result = Vec::new();
    let suits  = &[Suit::Clubs, Suit::Diamonds, Suit::Hearts, Suit::Spades, Suit::Stars];
    let values = &["1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
                   "Jack", "Princess", "Queen", "King", "Joker"];

    for suit in suits {
        for value in values {
            result.push(Card::new(*suit, value));
        }
    }
    return result;
}

fn shuffle(deck: &mut Vec<Card>) {
    rand::thread_rng().shuffle(deck);
}

fn main() {
}
<|endoftext|>


<commit_before>#![feature(macro_rules, slicing_syntax, while_let, if_let, phase, globs)]

//! Lispy-rs

#[phase(plugin, link)] extern crate log;
extern crate term;

extern crate readline;

// FIXME(#18822): Remove `pub`
pub mod parser;
pub mod lval;
pub mod eval;

#[cfg(not(test))]
mod main {
    use readline;
    use term;

    use eval::eval;
    use lval::LVal;
    use parser::{Parser, ParserError};

    fn print_error(err: ParserError) {
        let mut t = term::stdout().unwrap();
        t.fg(term::color::RED).unwrap();
        (write!(t, "Error: ")).unwrap();
        t.reset().unwrap();
        (write!(t, "{}\n", err)).unwrap();
    }

    pub fn main() {
        println!("MLisp VErsion 0.0.0.1");
        println!("Press Ctrl+c to exit");

        loop {
            let input = if let Some(i) = readline::readline("mlisp> ") { i }
                        else { println!(""); break };
            readline::add_history(input[]);

            let ast = match Parser::parse(input[], "<input>") {
                Ok(lval) => lval,
                Err(err) => { print_error(err); continue }
            };
            let lval = LVal::from_ast(ast);

            println!("{}", eval(lval));
        }

        println!("Exiting...")
    }
}

#[cfg(not(test))]
fn main() {
    main::main()
}<commit_msg>Improve main a litle<commit_after>#![feature(macro_rules, slicing_syntax, while_let, if_let, phase, globs)]

//! Lispy-rs

#[phase(plugin, link)] extern crate log;
extern crate term;

extern crate readline;

// FIXME(#18822): Remove `pub`
pub mod parser;
pub mod lval;
pub mod eval;

#[cfg(not(test))]
mod main {
    use readline;
    use term;

    use eval::eval;
    use lval::LVal;
    use parser::{Parser, ParserError};

    fn print_error(err: ParserError) {
        let mut t = term::stdout().unwrap();
        t.fg(term::color::RED).unwrap();
        (write!(t, "Error: ")).unwrap();
        t.reset().unwrap();
        (write!(t, "{}\n", err)).unwrap();
    }

    pub fn main() {
        println!("MLisp Version 0.0.0.1");
        println!("Enter 'quit' to exit");
        println!("");

        loop {
            let input = if let Some(i) = readline::readline("> ") { i }
                        else { println!(""); break };
            readline::add_history(input[]);

            if input[] == "quit" { break }

            let ast = match Parser::parse(input[], "<input>") {
                Ok(lval) => lval,
                Err(err) => { print_error(err); continue }
            };
            let lval = LVal::from_ast(ast);

            println!("{}", eval(lval));
        }

        println!("Exiting...")
    }
}

#[cfg(not(test))]
fn main() {
    main::main()
}<|endoftext|>