<commit_before>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![feature(slice_patterns)]

extern crate rustc_driver;
extern crate getopts;
extern crate rustc;
extern crate syntax;
extern crate rustc_plugin;
extern crate clippy_lints;
extern crate rustc_serialize;

use rustc_driver::{driver, CompilerCalls, RustcDefaultCalls, Compilation};
use rustc::session::{config, Session};
use rustc::session::config::{Input, ErrorOutputType};
use syntax::diagnostics;
use std::path::PathBuf;
use std::process::Command;

use clippy_lints::utils::cargo;

struct ClippyCompilerCalls(RustcDefaultCalls);

impl std::default::Default for ClippyCompilerCalls {
    fn default() -> Self {
        Self::new()
    }
}

impl ClippyCompilerCalls {
    fn new() -> Self {
        ClippyCompilerCalls(RustcDefaultCalls)
    }
}

impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
    fn early_callback(&mut self,
                      matches: &getopts::Matches,
                      sopts: &config::Options,
                      descriptions: &diagnostics::registry::Registry,
                      output: ErrorOutputType)
                      -> Compilation {
        self.0.early_callback(matches, sopts, descriptions, output)
    }
    fn no_input(&mut self,
                matches: &getopts::Matches,
                sopts: &config::Options,
                odir: &Option<PathBuf>,
                ofile: &Option<PathBuf>,
                descriptions: &diagnostics::registry::Registry)
                -> Option<(Input, Option<PathBuf>)> {
        self.0.no_input(matches, sopts, odir, ofile, descriptions)
    }
    fn late_callback(&mut self,
                     matches: &getopts::Matches,
                     sess: &Session,
                     input: &Input,
                     odir: &Option<PathBuf>,
                     ofile: &Option<PathBuf>)
                     -> Compilation {
        self.0.late_callback(matches, sess, input, odir, ofile)
    }
    fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
        let mut control = self.0.build_controller(sess, matches);

        let old = std::mem::replace(&mut control.after_parse.callback, box |_| {});
        control.after_parse.callback = Box::new(move |state| {
            {
                let mut registry = rustc_plugin::registry::Registry::new(state.session, state.krate.as_ref().expect("at this compilation stage the krate must be parsed"));
                registry.args_hidden = Some(Vec::new());
                clippy_lints::register_plugins(&mut registry);

                let rustc_plugin::registry::Registry { early_lint_passes,
                                                       late_lint_passes,
                                                       lint_groups,
                                                       llvm_passes,
                                                       attributes,
                                                       mir_passes,
                                                       .. } = registry;
                let sess = &state.session;
                let mut ls = sess.lint_store.borrow_mut();
                for pass in early_lint_passes {
                    ls.register_early_pass(Some(sess), true, pass);
                }
                for pass in late_lint_passes {
                    ls.register_late_pass(Some(sess), true, pass);
                }

                for (name, to) in lint_groups {
                    ls.register_group(Some(sess), true, name, to);
                }

                sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
                sess.mir_passes.borrow_mut().extend(mir_passes);
                sess.plugin_attributes.borrow_mut().extend(attributes);
            }
            old(state);
        });

        control
    }
}

use std::path::Path;

pub fn main() {
    use std::env;

    if env::var("CLIPPY_DOGFOOD").map(|_| true).unwrap_or(false) {
        panic!("yummy");
    }

    let dep_path = env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps");

    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sys_root = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => {
            option_env!("SYSROOT")
                .map(|s| s.to_owned())
                .or(Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| s.trim().to_owned()))
                .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust")
        }
    };

    if let Some("clippy") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        let mut metadata = cargo::metadata().expect("could not obtain cargo metadata");
        assert_eq!(metadata.version, 1);
        for target in metadata.packages.remove(0).targets {
            let args = std::env::args().skip(2);
            if let Some(first) = target.kind.get(0) {
                if target.kind.len() > 1 || first.ends_with("lib") {
                    if let Err(code) = process(std::iter::once("--lib".to_owned()).chain(args), &dep_path, &sys_root) {
                        std::process::exit(code);
                    }
                } else if first == "bin" {
                    if let Err(code) = process(vec!["--bin".to_owned(), target.name].into_iter().chain(args), &dep_path, &sys_root) {
                        std::process::exit(code);
                    }
                }
            } else {
                panic!("badly formatted cargo metadata: target::kind is an empty array");
            }
        }
    } else {
        let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
            env::args().collect()
        } else {
            env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
        };

        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="clippy""#.to_owned()]);

        let (result, _) = rustc_driver::run_compiler(&args, &mut ClippyCompilerCalls::new());

        if let Err(err_count) = result {
            if err_count > 0 {
                std::process::exit(1);
            }
        }
    }
}

fn process<P, I>(old_args: I, dep_path: P, sysroot: &str) -> Result<(), i32>
    where P: AsRef<Path>,
          I: Iterator<Item = String>
{

    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("-L".to_owned());
    args.push(dep_path.as_ref().to_string_lossy().into_owned());
    args.push(String::from("--sysroot"));
    args.push(sysroot.to_owned());
    args.push("-Zno-trans".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="clippy""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn().expect("could not run cargo")
        .wait().expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<commit_msg>Dogfood<commit_after>// error-pattern:yummy
#![feature(box_syntax)]
#![feature(rustc_private)]
#![feature(slice_patterns)]

extern crate rustc_driver;
extern crate getopts;
extern crate rustc;
extern crate syntax;
extern crate rustc_plugin;
extern crate clippy_lints;
extern crate rustc_serialize;

use rustc_driver::{driver, CompilerCalls, RustcDefaultCalls, Compilation};
use rustc::session::{config, Session};
use rustc::session::config::{Input, ErrorOutputType};
use syntax::diagnostics;
use std::path::PathBuf;
use std::process::Command;

use clippy_lints::utils::cargo;

struct ClippyCompilerCalls(RustcDefaultCalls);

impl std::default::Default for ClippyCompilerCalls {
    fn default() -> Self {
        Self::new()
    }
}

impl ClippyCompilerCalls {
    fn new() -> Self {
        ClippyCompilerCalls(RustcDefaultCalls)
    }
}

impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
    fn early_callback(&mut self,
                      matches: &getopts::Matches,
                      sopts: &config::Options,
                      descriptions: &diagnostics::registry::Registry,
                      output: ErrorOutputType)
                      -> Compilation {
        self.0.early_callback(matches, sopts, descriptions, output)
    }
    fn no_input(&mut self,
                matches: &getopts::Matches,
                sopts: &config::Options,
                odir: &Option<PathBuf>,
                ofile: &Option<PathBuf>,
                descriptions: &diagnostics::registry::Registry)
                -> Option<(Input, Option<PathBuf>)> {
        self.0.no_input(matches, sopts, odir, ofile, descriptions)
    }
    fn late_callback(&mut self,
                     matches: &getopts::Matches,
                     sess: &Session,
                     input: &Input,
                     odir: &Option<PathBuf>,
                     ofile: &Option<PathBuf>)
                     -> Compilation {
        self.0.late_callback(matches, sess, input, odir, ofile)
    }
    fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
        let mut control = self.0.build_controller(sess, matches);

        let old = std::mem::replace(&mut control.after_parse.callback, box |_| {});
        control.after_parse.callback = Box::new(move |state| {
            {
                let mut registry = rustc_plugin::registry::Registry::new(state.session, state.krate.as_ref().expect("at this compilation stage the krate must be parsed"));
                registry.args_hidden = Some(Vec::new());
                clippy_lints::register_plugins(&mut registry);

                let rustc_plugin::registry::Registry { early_lint_passes,
                                                       late_lint_passes,
                                                       lint_groups,
                                                       llvm_passes,
                                                       attributes,
                                                       mir_passes,
                                                       .. } = registry;
                let sess = &state.session;
                let mut ls = sess.lint_store.borrow_mut();
                for pass in early_lint_passes {
                    ls.register_early_pass(Some(sess), true, pass);
                }
                for pass in late_lint_passes {
                    ls.register_late_pass(Some(sess), true, pass);
                }

                for (name, to) in lint_groups {
                    ls.register_group(Some(sess), true, name, to);
                }

                sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
                sess.mir_passes.borrow_mut().extend(mir_passes);
                sess.plugin_attributes.borrow_mut().extend(attributes);
            }
            old(state);
        });

        control
    }
}

use std::path::Path;

pub fn main() {
    use std::env;

    if env::var("CLIPPY_DOGFOOD").map(|_| true).unwrap_or(false) {
        panic!("yummy");
    }

    let dep_path = env::current_dir().expect("current dir is not readable").join("target").join("debug").join("deps");

    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sys_root = if let (Some(home), Some(toolchain)) = (home, toolchain) {
        format!("{}/toolchains/{}", home, toolchain)
    } else {
        option_env!("SYSROOT")
            .map(|s| s.to_owned())
            .or(Command::new("rustc")
                .arg("--print")
                .arg("sysroot")
                .output()
                .ok()
                .and_then(|out| String::from_utf8(out.stdout).ok())
                .map(|s| s.trim().to_owned()))
            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust")
    };

    if let Some("clippy") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
        let mut metadata = cargo::metadata().expect("could not obtain cargo metadata");
        assert_eq!(metadata.version, 1);
        for target in metadata.packages.remove(0).targets {
            let args = std::env::args().skip(2);
            if let Some(first) = target.kind.get(0) {
                if target.kind.len() > 1 || first.ends_with("lib") {
                    if let Err(code) = process(std::iter::once("--lib".to_owned()).chain(args), &dep_path, &sys_root) {
                        std::process::exit(code);
                    }
                } else if first == "bin" {
                    if let Err(code) = process(vec!["--bin".to_owned(), target.name].into_iter().chain(args), &dep_path, &sys_root) {
                        std::process::exit(code);
                    }
                }
            } else {
                panic!("badly formatted cargo metadata: target::kind is an empty array");
            }
        }
    } else {
        let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
            env::args().collect()
        } else {
            env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
        };

        args.extend_from_slice(&["--cfg".to_owned(), r#"feature="clippy""#.to_owned()]);

        let (result, _) = rustc_driver::run_compiler(&args, &mut ClippyCompilerCalls::new());

        if let Err(err_count) = result {
            if err_count > 0 {
                std::process::exit(1);
            }
        }
    }
}

fn process<P, I>(old_args: I, dep_path: P, sysroot: &str) -> Result<(), i32>
    where P: AsRef<Path>,
          I: Iterator<Item = String>
{

    let mut args = vec!["rustc".to_owned()];

    let mut found_dashes = false;
    for arg in old_args {
        found_dashes |= arg == "--";
        args.push(arg);
    }
    if !found_dashes {
        args.push("--".to_owned());
    }
    args.push("-L".to_owned());
    args.push(dep_path.as_ref().to_string_lossy().into_owned());
    args.push(String::from("--sysroot"));
    args.push(sysroot.to_owned());
    args.push("-Zno-trans".to_owned());
    args.push("--cfg".to_owned());
    args.push(r#"feature="clippy""#.to_owned());

    let path = std::env::current_exe().expect("current executable path invalid");
    let exit_status = std::process::Command::new("cargo")
        .args(&args)
        .env("RUSTC", path)
        .spawn().expect("could not run cargo")
        .wait().expect("failed to wait for cargo?");

    if exit_status.success() {
        Ok(())
    } else {
        Err(exit_status.code().unwrap_or(-1))
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Write};

#[derive(Copy, Clone)]
enum Operator {
    Add,
    Subtract,
    Divide,
    Multiply,
    Modulus,
    Power,
    Negate
}

#[derive(Clone)]
enum Token {
    LeftParen,
    RightParen,
    Comma,
    Operator{ operator: Operator, unary: bool, precedence: u32 },
    Function(String),
    Number(f64),
    None
}

fn get_variable(name: &str) -> Option<f64> {
    match name {
        "pi"  => Some(3.1415926535),
        "tau" => Some(3.1415926535*2.0),
        "e"   => Some(2.7182818284),
        _ => None
    }
}

fn params(name: &str, stack: &mut Vec<f64>, args: usize) -> Result<Vec<f64>, String> {
    if stack.len() >= args {
        let index = stack.len() - args;
        Ok(stack.split_off(index))
    }
    else { Err(format!("Not enough parameters for function '{}'", name)) }
}

fn run_function(name: &str, p: &mut Vec<f64>) -> Result<f64, String> {
    match name {
        "sin"  => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].sin()),
            Err(msg) => Err(msg)
        },
        "cos" => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].cos()),
            Err(msg) => Err(msg)
        },
        "tan" => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].tan()),
            Err(msg) => Err(msg)
        },
        "abs" => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].abs()),
            Err(msg) => Err(msg)
        },
        "sign" => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].signum()),
            Err(msg) => Err(msg)
        },
        "frac" => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].fract()),
            Err(msg) => Err(msg)
        },
        "min" => match params(&name, p, 2) {
            Ok(v) => Ok(f64::min(v[0], v[1])),
            Err(msg) => Err(msg)
        },
        "max" => match params(&name, p, 2) {
            Ok(v) => Ok(f64::max(v[0], v[1])),
            Err(msg) => Err(msg)
        },
        "pow" => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].powf(v[1])),
            Err(msg) => Err(msg)
        },
        "sqrt" => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].sqrt()),
            Err(msg) => Err(msg)
        },
        "rad" => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].to_radians()),
            Err(msg) => Err(msg)
        },
        "deg" => match params(&name, p, 1) {
            Ok(v) => Ok(v[0].to_degrees()),
            Err(msg) => Err(msg)
        },
        "add2" => match params(&name, p, 2) {
            Ok(v) => Ok(v[0] + v[1]),
            Err(msg) => Err(msg)
        },
        "add3" => match params(&name, p, 3) {
            Ok(v) => Ok(v[0] + v[1] + v[2]),
            Err(msg) => Err(msg)
        },
        "add4" => match params(&name, p, 4) {
            Ok(v) => Ok(v[0] + v[1] + v[2] + v[3]),
            Err(msg) => Err(msg)
        },
        _ => Err(format!("Unknown function '{}'", name))
    }
}

fn push_token(token: Token, stack: &mut Vec<Token>, output: &mut Vec<Token>) -> Result<Token, String> {
    let ret = token.clone();
    match token {
        Token::LeftParen | Token::Function(_) => stack.push(token),
        Token::RightParen => {
            while stack.len() > 0 {
                let t = stack.pop().unwrap();
                match t {
                    Token::LeftParen => return Ok(ret),
                    _ => output.push(t)
                }
            }
            return Err(String::from("Mismatched parentheses"))
        },
        Token::Operator{ unary, precedence: op_precedence, .. } => {
            if unary { stack.push(token) }
            else {
                while stack.len() > 0 {
                    match *stack.last().unwrap() {
                        Token::LeftParen | Token::RightParen => break,
                        Token::Operator{ precedence, .. } => if precedence < op_precedence { break },
                        _ => {}
                    }
                    output.push(stack.pop().unwrap());
                }
                stack.push(token);
            }
        },
        Token::Comma => {
            while stack.len() > 0 {
                match *stack.last().unwrap() {
                    Token::LeftParen => return Ok(ret),
                    _ => output.push(stack.pop().unwrap())
                }
            }
            return Err(String::from("Mismatched comma or mismatched parentheses"))
        },
        Token::Number(_) => output.push(token),
        _ => {}
    }
    Ok(ret)
}

fn is_unary(last_token: &Token) -> bool {
    match *last_token {
        Token::LeftParen | Token::Comma | Token::Operator{..} | Token::None => true,
        _ => false
    }
}

fn parse(input: &str) -> Result<Vec<Token>, String> {
    enum ReadState {
        Number,
        Name,
        None
    }

    let mut read_state = ReadState::None;
    let mut current_str = String::new();
    let mut stack: Vec<Token> = Vec::new();
    let mut output: Vec<Token> = Vec::new();
    let mut last_token: Token = Token::None;

    for(i, c) in input.chars().enumerate() {
        loop {
            match read_state {
                ReadState::Number => {
                    if c.is_digit(10) || c == '.' {
                        current_str.push(c);
                        if i+1 == input.len() {
                            match push_token(Token::Number(current_str.parse().unwrap()), &mut stack, &mut output) {
                                Ok(token) => last_token = token,
                                Err(msg) => return Err(msg)
                            }
                            current_str.clear();
                        }
                        break;
                    }
                    else {
                        read_state = ReadState::None;
                        match push_token(Token::Number(current_str.parse().unwrap()), &mut stack, &mut output) {
                            Ok(token) => last_token = token,
                            Err(msg) => return Err(msg)
                        }
                        current_str.clear();
                    }
                },
                ReadState::Name => {
                    if c.is_alphabetic() || c.is_digit(10) {
                        current_str.push(c);
                        if i+1 == input.len() {
                            if let Some(val) = get_variable(&current_str) {
                                match push_token(Token::Number(val), &mut stack, &mut output) {
                                    Ok(token) => last_token = token,
                                    Err(msg) => return Err(msg)
                                }
                            }
                            else { return Err(format!("Unknown variable '{}'", current_str)) }
                            current_str.clear();
                        }
                        break;
                    }
                    else if c == '(' {
                        read_state = ReadState::None;
                        match push_token(Token::Function(current_str.clone()), &mut stack, &mut output) {
                            Ok(token) => last_token = token,
                            Err(msg) => return Err(msg)
                        }
                        current_str.clear();
                    }
                    else {
                        read_state = ReadState::None;
                        if let Some(val) = get_variable(&current_str) {
                            match push_token(Token::Number(val), &mut stack, &mut output) {
                                Ok(token) => last_token = token,
                                Err(msg) => return Err(msg)
                            }
                        }
                        else { return Err(format!("Unknown variable '{}'", current_str)) }
                        current_str.clear();
                    }
                },
                ReadState::None => {
                    if c.is_digit(10) || c == '.' {
                        read_state = ReadState::Number;
                        continue;
                    }
                    else if c.is_alphabetic() {
                        read_state = ReadState::Name;
                        continue;
                    }
                    else if c.is_whitespace() { break }
                    else {
                        if let Some(token) = match c {
                            '(' => Some(Token::LeftParen),
                            ')' => Some(Token::RightParen),
                            '+' => if !is_unary(&last_token) { Some(Token::Operator{ operator: Operator::Add, unary: false, precedence: 1 }) }
                                else { Some(Token::None) },
                            '-' => if is_unary(&last_token) { Some(Token::Operator{ operator: Operator::Negate, unary: true, precedence: 3 }) }
                                else { Some(Token::Operator{ operator: Operator::Subtract, unary: false, precedence: 1 }) },
                            '/' => Some(Token::Operator{ operator: Operator::Divide, unary: false, precedence: 2 }),
                            '*' => Some(Token::Operator{ operator: Operator::Multiply, unary: false, precedence: 2 }),
                            '%' => Some(Token::Operator{ operator: Operator::Modulus, unary: false, precedence: 2 }),
                            '^' => Some(Token::Operator{ operator: Operator::Power, unary: false, precedence: 4 }),
                            ',' => Some(Token::Comma),
                            _ => None
                        } {
                            match push_token(token, &mut stack, &mut output) {
                                Ok(token) => last_token = token,
                                Err(msg) => return Err(msg)
                            }
                        }
                        else { return Err(format!("Invalid character '{}' in expression", c)) }
                    }
                    break;
                }
            }
        }
    }
    while stack.len() > 0 {
        match *stack.last().unwrap() {
            Token::LeftParen | Token::RightParen => return Err(String::from("Mismatched parentheses")),
            _ => output.push(stack.pop().unwrap())
        }
    }
    Ok(output)
}

fn solve(tokens: Vec<Token>) -> Result<f64, String> {
    let mut stack: Vec<f64> = Vec::new();
    for token in &tokens {
        let result: f64;
        match *token {
            Token::Number(n) => result = n,
            Token::Operator{ operator, .. } => match operator {
                Operator::Add => {
                    if stack.len() >= 2 { result = stack.pop().unwrap() + stack.pop().unwrap() }
                    else { return Err(String::from("Add operator requires 2 operands")) }
                },
                Operator::Subtract => {
                    if stack.len() >= 2 {
                        let n = stack.pop().unwrap();
                        result = stack.pop().unwrap() - n;
                    }
                    else { return Err(String::from("Minus operator requires 2 operands")) }
                },
                Operator::Multiply => {
                    if stack.len() >= 2 { result = stack.pop().unwrap() * stack.pop().unwrap() }
                    else { return Err(String::from("Multiply operator requires 2 operands")) }
                },
                Operator::Divide => {
                    if stack.len() >= 2 {
                        let n = stack.pop().unwrap();
                        result = stack.pop().unwrap() / n;
                    }
                    else { return Err(String::from("Divide operator requires 2 operands")) }
                }
                Operator::Modulus => {
                    if stack.len() >= 2 {
                        let n = stack.pop().unwrap();
                        result = stack.pop().unwrap() % n;
                    }
                    else { return Err(String::from("Modulus operator requires 2 operands")) }
                }
                Operator::Negate =>  {
                    if stack.len() >= 1 { result = -stack.pop().unwrap() }
                    else { return Err(String::from("Negate operator has no operand")) }
                },
                Operator::Power => {
                    if stack.len() >= 2 {
                        let n = stack.pop().unwrap();
                        result = stack.pop().unwrap().powf(n);
                    }
                    else { return Err(String::from("Exponent operator requires 2 operands")) }
                }
            },
            Token::Function(ref name) => {
                match run_function(name, &mut stack) {
                    Ok(value) => result = value,
                    Err(msg) => return Err(msg)
                }
            },
            _ => return Err(String::from("Invalid expression"))
        };
        stack.push(result);
    }
    if stack.len() != 1 { return Err(String::from("Invalid expression")) }
    Ok(*stack.last().unwrap())
}

fn solve_expression(input: &str) -> Result<f64, String> {
    match parse(input) {
        Ok(tokens) => solve(tokens),
        Err(msg) => Err(msg)
    }
}

fn main() {
    let mut input = String::new();
    let args: Vec<_> = std::env::args().collect();
    if args.len() > 1 {
        input = args[1].clone();
    }
    else {
        print!(">>> ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut input).expect("Failed to read input");
        input.pop();
    }
    match solve_expression(&input) {
        Ok(val) => println!("{}", val),
        Err(msg) => println!("Error: {}", msg)
    }
}

#[test]
fn long_expression() {
    assert_eq!(Ok(100.0), solve_expression(&String::from("
        ((abs(cos(((((--(abs((((1+1+(1+1)+1+1+(4*1))+1+(10-11))/10 * 10) % 9 - 10)^2-80+9))
        *10/10+(2*2 + 6)-5-2-(2+1))*2.00000000-5.6-4.400000000000000000000)-9)*pi))*10.0*
        sign(max(12.44343234, 11.84934))*add4(1.0, 2.0, -1.0, 1.0)/3)^2)+(2*2+2^2*2-2)-10")));
}
<commit_msg>Improve error handling<commit_after>use std::io::{self, Write};

#[derive(Copy, Clone)]
enum Operator {
    Add,
    Subtract,
    Divide,
    Multiply,
    Modulus,
    Power,
    Negate
}

#[derive(Clone)]
enum Token {
    LeftParen,
    RightParen,
    Comma,
    Operator{ operator: Operator, unary: bool, precedence: u32 },
    Function(String),
    Number(f64),
    None
}

fn get_variable(name: &str) -> Result<f64, String> {
    match name {
        "pi"  => Ok(3.1415926535),
        "tau" => Ok(3.1415926535*2.0),
        "e"   => Ok(2.7182818284),
        _ => Err(format!("Unknown variable '{}'", name))
    }
}

fn run_function(name: &str, p: &mut Vec<f64>) -> Result<f64, String> {
    match name {
        "sin"  => Ok(params(&name, p, 1, false)?[0].sin()),
        "cos"  => Ok(params(&name, p, 1, false)?[0].cos()),
        "tan"  => Ok(params(&name, p, 1, false)?[0].tan()),
        "abs"  => Ok(params(&name, p, 1, false)?[0].abs()),
        "sign" => Ok(params(&name, p, 1, false)?[0].signum()),
        "frac" => Ok(params(&name, p, 1, false)?[0].fract()),
        "sqrt" => Ok(params(&name, p, 1, false)?[0].sqrt()),
        "rad"  => Ok(params(&name, p, 1, false)?[0].to_radians()),
        "deg"  => Ok(params(&name, p, 1, false)?[0].to_degrees()),
        "min" => match params(&name, p, 2, false) {
            Ok(v) => Ok(f64::min(v[0], v[1])),
            Err(msg) => Err(msg)
        },
        "max" => match params(&name, p, 2, false) {
            Ok(v) => Ok(f64::max(v[0], v[1])),
            Err(msg) => Err(msg)
        },
        "pow" => match params(&name, p, 2, false) {
            Ok(v) => Ok(v[0].powf(v[1])),
            Err(msg) => Err(msg)
        },
        _ => Err(format!("Unknown function '{}'", name))
    }
}

fn push_token(token: Token, stack: &mut Vec<Token>, output: &mut Vec<Token>) -> Result<Token, String> {
    let ret = token.clone();
    match token {
        Token::LeftParen | Token::Function(_) => stack.push(token),
        Token::RightParen => {
            while stack.len() > 0 {
                let t = stack.pop().unwrap();
                match t {
                    Token::LeftParen => return Ok(ret),
                    _ => output.push(t)
                }
            }
            return Err(String::from("Mismatched parentheses"))
        },
        Token::Operator{ unary, precedence: op_precedence, .. } => {
            if unary { stack.push(token) }
            else {
                while stack.len() > 0 {
                    match *stack.last().unwrap() {
                        Token::LeftParen | Token::RightParen => break,
                        Token::Operator{ precedence, .. } => if precedence < op_precedence { break },
                        _ => {}
                    }
                    output.push(stack.pop().unwrap());
                }
                stack.push(token);
            }
        },
        Token::Comma => {
            while stack.len() > 0 {
                match *stack.last().unwrap() {
                    Token::LeftParen => return Ok(ret),
                    _ => output.push(stack.pop().unwrap())
                }
            }
            return Err(String::from("Mismatched comma or mismatched parentheses"))
        },
        Token::Number(_) => output.push(token),
        _ => {}
    }
    Ok(ret)
}

fn is_unary(last_token: &Token) -> bool {
    match *last_token {
        Token::LeftParen | Token::Comma | Token::Operator{..} | Token::None => true,
        _ => false
    }
}

fn parse(input: &str) -> Result<Vec<Token>, String> {
    enum ReadState {
        Number,
        Name,
        None
    }

    let mut read_state = ReadState::None;
    let mut current_str = String::new();
    let mut stack: Vec<Token> = Vec::new();
    let mut output: Vec<Token> = Vec::new();
    let mut last_token: Token = Token::None;

    for(i, c) in input.chars().enumerate() {
        loop {
            match read_state {
                ReadState::Number => {
                    if c.is_digit(10) || c == '.' {
                        current_str.push(c);
                        if i+1 == input.len() {
                            last_token = push_token(Token::Number(current_str.parse().unwrap()), &mut stack, &mut output)?;
                            current_str.clear();
                        }
                        break;
                    }
                    else {
                        read_state = ReadState::None;
                        last_token =  push_token(Token::Number(current_str.parse().unwrap()), &mut stack, &mut output)?;
                        current_str.clear();
                    }
                },
                ReadState::Name => {
                    if c.is_alphabetic() || c.is_digit(10) {
                        current_str.push(c);
                        if i+1 == input.len() {
                            last_token = push_token(Token::Number(get_variable(&current_str)?), &mut stack, &mut output)?;
                            current_str.clear();
                        }
                        break;
                    }
                    else if c == '(' {
                        read_state = ReadState::None;
                        last_token =  push_token(Token::Function(current_str.clone()), &mut stack, &mut output)?;
                        current_str.clear();
                    }
                    else {
                        read_state = ReadState::None;
                        last_token = push_token(Token::Number(get_variable(&current_str)?), &mut stack, &mut output)?;
                        current_str.clear();
                    }
                },
                ReadState::None => {
                    if c.is_digit(10) || c == '.' {
                        read_state = ReadState::Number;
                        continue;
                    }
                    else if c.is_alphabetic() {
                        read_state = ReadState::Name;
                        continue;
                    }
                    else if c.is_whitespace() { break }
                    else {
                        if let Some(token) = match c {
                            '(' => Some(Token::LeftParen),
                            ')' => Some(Token::RightParen),
                            '+' => if !is_unary(&last_token) { Some(Token::Operator{ operator: Operator::Add, unary: false, precedence: 1 }) }
                                else { Some(Token::None) },
                            '-' => if is_unary(&last_token) { Some(Token::Operator{ operator: Operator::Negate, unary: true, precedence: 3 }) }
                                else { Some(Token::Operator{ operator: Operator::Subtract, unary: false, precedence: 1 }) },
                            '/' => Some(Token::Operator{ operator: Operator::Divide, unary: false, precedence: 2 }),
                            '*' => Some(Token::Operator{ operator: Operator::Multiply, unary: false, precedence: 2 }),
                            '%' => Some(Token::Operator{ operator: Operator::Modulus, unary: false, precedence: 2 }),
                            '^' => Some(Token::Operator{ operator: Operator::Power, unary: false, precedence: 4 }),
                            ',' => Some(Token::Comma),
                            _ => None
                        } { last_token = push_token(token, &mut stack, &mut output)? }
                        else { return Err(format!("Invalid character '{}' in expression", c)) }
                    }
                    break;
                }
            }
        }
    }
    while stack.len() > 0 {
        match *stack.last().unwrap() {
            Token::LeftParen | Token::RightParen => return Err(String::from("Mismatched parentheses")),
            _ => output.push(stack.pop().unwrap())
        }
    }
    Ok(output)
}

fn params(name: &str, stack: &mut Vec<f64>, args: usize, is_operator: bool) -> Result<Vec<f64>, String> {
    if stack.len() >= args {
        let index = stack.len() - args;
        Ok(stack.split_off(index))
    }
    else {
        if is_operator {
            Err(format!("Operator '{}' requires {} {}", name, args,
                        if args > 1 { "operands" } else { "operand" }))
        }
        else {
            Err(format!("Function '{}' requires {} {}", name, args,
                        if args > 1 { "parameters" } else { "parameter" }))
        }
    }
}

fn solve(tokens: Vec<Token>) -> Result<f64, String> {
    let mut stack: Vec<f64> = Vec::new();
    for token in &tokens {
        let result: f64;
        match *token {
            Token::Number(n) => result = n,
            Token::Operator{ operator, .. } => match operator {
                Operator::Add => {
                    let v = params("Add", &mut stack, 2, true)?;
                    result = v[0] + v[1];
                },
                Operator::Subtract => {
                    let v = params("Minus", &mut stack, 2, true)?;
                    result = v[0] - v[1];
                },
                Operator::Multiply => {
                    let v = params("Multiply", &mut stack, 2, true)?;
                    result = v[0] * v[1];
                },
                Operator::Divide => {
                    let v = params("Divide", &mut stack, 2, true)?;
                    result = v[0] / v[1];
                }
                Operator::Modulus => {
                    let v = params("Modulus", &mut stack, 2, true)?;
                    result = v[0] % v[1];
                }
                Operator::Negate =>  {
                    result = -(params("Negate", &mut stack, 1, true)?[0]);
                },
                Operator::Power => {
                    let v = params("Exponent", &mut stack, 2, true)?;
                    result = v[0].powf(v[1]);
                }
            },
            Token::Function(ref name) => result = run_function(name, &mut stack)?,
            _ => return Err(String::from("Invalid expression"))
        };
        stack.push(result);
    }
    if stack.len() != 1 { return Err(String::from("Invalid expression")) }
    Ok(*stack.last().unwrap())
}

fn solve_expression(input: &str) -> Result<f64, String> {
    solve(parse(input)?)
}

fn main() {
    let mut input = String::new();
    let args: Vec<_> = std::env::args().collect();
    if args.len() > 1 {
        input = args[1].clone();
    }
    else {
        print!(">>> ");
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut input).expect("Failed to read input");
        input.pop();
    }
    match solve_expression(&input) {
        Ok(val) => println!("{}", val),
        Err(msg) => println!("Error: {}", msg)
    }
}

#[test]
fn long_expression() {
    assert_eq!(Ok(100.0), solve_expression(&String::from("
        (abs(cos(((((--(abs((((1+1+(1+1)+1+1+(4*1))+1+(10-11))/10 * 10) % 9 - 10)^2-80+9))
        *10/10+(2*2 + 6)-5-2-(2+1))*2.00000000-5.6-4.400000000000000000000)-9)*pi))*10.0*
        sign(max(12.44343234, 11.84934)))*(1+2+3+4)")));
}
<|endoftext|>


<commit_before>use std::rc::{Rc, Weak};

trait Observable<O: Observer<Observes = Self::Has>> {
    type Has;

    fn register_observer(&mut self, observer: Weak<O>);

    fn set(&mut self, data: Self::Has);

    fn set_without_update(&mut self, data: Self::Has);

    fn peek(&self) -> &Self::Has;
}

trait Observer {
    type Observes;

    fn update(&self, data: &Self::Observes);
}

impl<T> Observer for Box<Observer<Observes = T>> {
    type Observes = T;

    fn update(&self, data: &Self::Observes) {
        (**self).update(data);
    }
}

struct StrongObservable<O: Observer<Observes = usize>> {
    observables: Vec<Weak<O>>,
    value: usize,
}
impl<O: Observer<Observes = usize>> StrongObservable<O> {
    fn new(value: usize) -> StrongObservable<O> {
        StrongObservable {
            observables: vec![],
            value: value,
        }
    }

    fn clean(&mut self) {
        self.observables.retain(|o| o.upgrade().is_some());
    }
}
impl<O: Observer<Observes = usize>> Observable<O> for StrongObservable<O> {
    type Has = usize;

    fn register_observer(&mut self, observer: Weak<O>) {
        self.observables.push(observer);
    }

    fn set_without_update(&mut self, data: usize) {
        self.value = data;
    }

    fn peek(&self) -> &usize {
        &self.value
    }

    fn set(&mut self, data: usize) {
        self.clean();

        for o in &self.observables {
            match o.upgrade() {
                Some(o) => o.update(&data),
                _ => (),
            }
        }

        self.set_without_update(data);
    }
}

struct ObserverStruct {
    name: String,
}
impl ObserverStruct {
    fn new(name: &str) -> ObserverStruct {
        ObserverStruct { name: name.to_string() }
    }
}
impl Observer for ObserverStruct {
    type Observes = usize;

    fn update(&self, data: &usize) {
        println!("{}, {:?}", self.name, *data);
    }
}

fn main() {
    {
        let mut observable = StrongObservable::new(5);

        let observer_one = Rc::new(ObserverStruct::new("obs1"));
        let observer_two = Rc::new(ObserverStruct::new("obs2"));

        observable.register_observer(Rc::downgrade(&observer_one));
        observable.register_observer(Rc::downgrade(&observer_two));
        {
            let observer_three = Rc::new(ObserverStruct::new("obs3"));
            observable.register_observer(Rc::downgrade(&observer_three));
            observable.set(6usize);
        }

        observable.set(7usize);
    }
    {
        let mut observable = StrongObservable::new(100);
        let observer_one = Rc::new(Box::new(ObserverStruct::new("obs1")) as
                                   Box<Observer<Observes = usize>>);
        let observer_two = Rc::new(Box::new(ObserverStruct::new("obs2")) as
                                   Box<Observer<Observes = usize>>);

        observable.register_observer(Rc::downgrade(&observer_one));
        observable.register_observer(Rc::downgrade(&observer_two));
        {
            let observer_three = Rc::new(Box::new(ObserverStruct::new("obs3")) as
                                         Box<Observer<Observes = usize>>);
            observable.register_observer(Rc::downgrade(&observer_three));
            observable.set(200usize);
        }

        observable.set(300usize);
    }
}<commit_msg>Added mutate method<commit_after>use std::rc::{Rc, Weak};

trait Observable<O: Observer<Observes = Self::Has>> {
    type Has;

    fn register_observer(&mut self, observer: Weak<O>);

    fn set(&mut self, data: Self::Has);

    fn set_without_update(&mut self, data: Self::Has);

    fn peek(&self) -> &Self::Has;

    fn mutate<F>(&mut self, mut f: F)
        where F: FnMut(&Self::Has) -> Self::Has
    {
        let new_value = f(self.peek());
        self.set(new_value);
    }
}

trait Observer {
    type Observes;

    fn update(&self, data: &Self::Observes);
}

impl<T> Observer for Box<Observer<Observes = T>> {
    type Observes = T;

    fn update(&self, data: &Self::Observes) {
        (**self).update(data);
    }
}

struct ObservableValue<T, O: Observer<Observes = T>> {
    observables: Vec<Weak<O>>,
    value: T,
}
impl<T, O: Observer<Observes = T>> ObservableValue<T, O> {
    fn new(value: T) -> ObservableValue<T, O> {
        ObservableValue {
            observables: vec![],
            value: value,
        }
    }

    fn clean(&mut self) {
        self.observables.retain(|o| o.upgrade().is_some());
    }
}
impl<T, O: Observer<Observes = T>> Observable<O> for ObservableValue<T, O> {
    type Has = T;

    fn register_observer(&mut self, observer: Weak<O>) {
        self.observables.push(observer);
    }

    fn set_without_update(&mut self, data: T) {
        self.value = data;
    }

    fn peek(&self) -> &T {
        &self.value
    }

    fn set(&mut self, data: T) {
        self.clean();

        for o in &self.observables {
            match o.upgrade() {
                Some(o) => o.update(&data),
                _ => (),
            }
        }

        self.set_without_update(data);
    }
}

struct SimpleObserver {
    name: String,
}
impl SimpleObserver {
    fn new(name: &str) -> SimpleObserver {
        SimpleObserver { name: name.to_string() }
    }
}
impl Observer for SimpleObserver {
    type Observes = usize;

    fn update(&self, data: &usize) {
        println!("{}, {:?}", self.name, *data);
    }
}

fn main() {
    {
        let mut observable = ObservableValue::new(5);

        let observer_one = Rc::new(SimpleObserver::new("obs1"));
        let observer_two = Rc::new(SimpleObserver::new("obs2"));

        observable.register_observer(Rc::downgrade(&observer_one));
        observable.register_observer(Rc::downgrade(&observer_two));
        {
            let observer_three = Rc::new(SimpleObserver::new("obs3"));
            observable.register_observer(Rc::downgrade(&observer_three));
            observable.set(6usize);
        }

        observable.set(7usize);
    }
    {
        let mut observable = ObservableValue::new(100);
        let observer_one = Rc::new(Box::new(SimpleObserver::new("obs1")) as
                                   Box<Observer<Observes = usize>>);
        let observer_two = Rc::new(Box::new(SimpleObserver::new("obs2")) as
                                   Box<Observer<Observes = usize>>);

        observable.register_observer(Rc::downgrade(&observer_one));
        observable.register_observer(Rc::downgrade(&observer_two));
        {
            let observer_three = Rc::new(Box::new(SimpleObserver::new("obs3")) as
                                         Box<Observer<Observes = usize>>);
            observable.register_observer(Rc::downgrade(&observer_three));
            observable.set(200usize);
        }

        observable.mutate(|n| n + 10);
    }
}<|endoftext|>


<commit_before>extern crate clap;
extern crate notify;

mod gitignore;
mod notification_filter;
mod runner;

use std::sync::mpsc::{channel, Receiver, RecvError};
use std::{env, thread, time};
use std::path::Path;

use clap::{App, Arg};
use notify::{Event, RecommendedWatcher, Watcher};

use notification_filter::NotificationFilter;
use runner::Runner;

fn wait(rx: &Receiver<Event>, filter: &NotificationFilter, verbose: bool) -> Result<Event, RecvError> {
    loop {
        // Block on initial notification
        let e = try!(rx.recv());
        if let Some(ref path) = e.path {
            if filter.is_excluded(&path) {
                if verbose {
                    println!("*** Ignoring {} due to filter", path.to_str().unwrap());
                }
                continue;
            }
        }

        // Accumulate subsequent events
        thread::sleep(time::Duration::from_millis(250));

        // Drain rx buffer and drop them
        loop {
            match rx.try_recv() {
                Ok(_) => continue,
                Err(_) => break,
            }
        }

        return Ok(e);
    }
}

fn main() {
    let args = App::new("watchexec")
        .version("0.11.0")
        .about("Execute commands when watched files change")
        .arg(Arg::with_name("path")
            .help("Path to watch")
            .short("w")
            .long("watch")
            .number_of_values(1)
            .multiple(true)
            .takes_value(true)
            .default_value("."))
        .arg(Arg::with_name("command")
            .help("Command to execute")
            .multiple(true)
            .required(true))
        .arg(Arg::with_name("extensions")
             .help("Comma-separated list of file extensions to watch (js,css,html)")
             .short("e")
             .long("exts")
             .takes_value(true))
        .arg(Arg::with_name("clear")
            .help("Clear screen before executing command")
            .short("c")
            .long("clear"))
        .arg(Arg::with_name("restart")
             .help("Restart the process if it's still running")
             .short("r")
             .long("restart"))
        .arg(Arg::with_name("verbose")
             .help("Prints diagnostic messages")
             .short("v")
             .long("verbose"))
        .arg(Arg::with_name("filter")
             .help("Ignore all modifications except those matching the pattern")
             .short("f")
             .long("filter")
             .number_of_values(1)
             .multiple(true)
             .takes_value(true)
             .value_name("pattern"))
        .arg(Arg::with_name("ignore")
             .help("Ignore modifications to paths matching the pattern")
             .short("i")
             .long("ignore")
             .number_of_values(1)
             .multiple(true)
             .takes_value(true)
             .value_name("pattern"))
        .get_matches();

    let verbose = args.is_present("verbose");

    let cwd = env::current_dir().unwrap();

    let mut gitignore_file = None;
    let gitignore_path = cwd.join(".gitignore");
    if gitignore_path.exists() {
        if verbose {
            println!("*** Found .gitignore file: {}", gitignore_path.to_str().unwrap());
        }

        gitignore_file = gitignore::parse(&gitignore_path).ok();
    }

    let mut filter = NotificationFilter::new(&cwd, gitignore_file).expect("unable to create notification filter");

    // Add default ignore list
    let dotted_dirs = Path::new(".*").join("*");
    let default_filters = vec!["*.pyc", "*.swp", dotted_dirs.to_str().unwrap()];
    for p in default_filters {
        filter.add_ignore(p).expect("bad default filter");
    }

    if let Some(extensions) = args.values_of("extensions") {
        for ext in extensions {
            filter.add_extension(ext).expect("bad extension");
        }
    }

    if let Some(filters) = args.values_of("filter") {
        for p in filters {
            filter.add_filter(p).expect("bad filter");
        }
    }

    if let Some(ignores) = args.values_of("ignore") {
        for i in ignores {
            filter.add_ignore(i).expect("bad ignore pattern");
        }
    }

    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new(tx).expect("unable to create watcher");

    let paths = args.values_of("path").unwrap();
    for path in paths {
        match Path::new(path).canonicalize() {
            Ok(canonicalized)   => watcher.watch(canonicalized).expect("unable to watch path"),
            Err(_)              => {
                println!("invalid path: {}", path);
                return;
            }
        }
    }

    let cmd_parts: Vec<&str> = args.values_of("command").unwrap().collect();
    let cmd = cmd_parts.join(" ");
    let mut runner = Runner::new(args.is_present("restart"), args.is_present("clear"), verbose);

    loop {
        let e = wait(&rx, &filter, verbose).expect("error when waiting for filesystem changes");

        if verbose {
            println!("*** {:?}: {:?}", e.op, e.path);
        }

        runner.run_command(&cmd);
    }
}
<commit_msg>Canonicalize cwd; fixes gitignore patterns not matching on Windows<commit_after>extern crate clap;
extern crate notify;

mod gitignore;
mod notification_filter;
mod runner;

use std::sync::mpsc::{channel, Receiver, RecvError};
use std::{env, thread, time};
use std::path::Path;

use clap::{App, Arg};
use notify::{Event, RecommendedWatcher, Watcher};

use notification_filter::NotificationFilter;
use runner::Runner;

fn wait(rx: &Receiver<Event>, filter: &NotificationFilter, verbose: bool) -> Result<Event, RecvError> {
    loop {
        // Block on initial notification
        let e = try!(rx.recv());
        if let Some(ref path) = e.path {
            if filter.is_excluded(&path) {
                if verbose {
                    println!("*** Ignoring {} due to filter", path.to_str().unwrap());
                }
                continue;
            }
        }

        // Accumulate subsequent events
        thread::sleep(time::Duration::from_millis(250));

        // Drain rx buffer and drop them
        loop {
            match rx.try_recv() {
                Ok(_) => continue,
                Err(_) => break,
            }
        }

        return Ok(e);
    }
}

fn main() {
    let args = App::new("watchexec")
        .version("0.11.0")
        .about("Execute commands when watched files change")
        .arg(Arg::with_name("path")
            .help("Path to watch")
            .short("w")
            .long("watch")
            .number_of_values(1)
            .multiple(true)
            .takes_value(true)
            .default_value("."))
        .arg(Arg::with_name("command")
            .help("Command to execute")
            .multiple(true)
            .required(true))
        .arg(Arg::with_name("extensions")
             .help("Comma-separated list of file extensions to watch (js,css,html)")
             .short("e")
             .long("exts")
             .takes_value(true))
        .arg(Arg::with_name("clear")
            .help("Clear screen before executing command")
            .short("c")
            .long("clear"))
        .arg(Arg::with_name("restart")
             .help("Restart the process if it's still running")
             .short("r")
             .long("restart"))
        .arg(Arg::with_name("verbose")
             .help("Prints diagnostic messages")
             .short("v")
             .long("verbose"))
        .arg(Arg::with_name("filter")
             .help("Ignore all modifications except those matching the pattern")
             .short("f")
             .long("filter")
             .number_of_values(1)
             .multiple(true)
             .takes_value(true)
             .value_name("pattern"))
        .arg(Arg::with_name("ignore")
             .help("Ignore modifications to paths matching the pattern")
             .short("i")
             .long("ignore")
             .number_of_values(1)
             .multiple(true)
             .takes_value(true)
             .value_name("pattern"))
        .get_matches();

    let verbose = args.is_present("verbose");

    let uncanonicalized_cwd = env::current_dir().expect("unable to get cwd");
    let cwd = uncanonicalized_cwd.canonicalize().expect("unable to canonicalize cwd");

    let mut gitignore_file = None;
    let gitignore_path = cwd.join(".gitignore");
    if gitignore_path.exists() {
        if verbose {
            println!("*** Found .gitignore file: {}", gitignore_path.to_str().unwrap());
        }

        gitignore_file = gitignore::parse(&gitignore_path).ok();
    }

    let mut filter = NotificationFilter::new(&cwd, gitignore_file).expect("unable to create notification filter");

    // Add default ignore list
    let dotted_dirs = Path::new(".*").join("*");
    let default_filters = vec!["*.pyc", "*.swp", dotted_dirs.to_str().unwrap()];
    for p in default_filters {
        filter.add_ignore(p).expect("bad default filter");
    }

    if let Some(extensions) = args.values_of("extensions") {
        for ext in extensions {
            filter.add_extension(ext).expect("bad extension");
        }
    }

    if let Some(filters) = args.values_of("filter") {
        for p in filters {
            filter.add_filter(p).expect("bad filter");
        }
    }

    if let Some(ignores) = args.values_of("ignore") {
        for i in ignores {
            filter.add_ignore(i).expect("bad ignore pattern");
        }
    }

    let (tx, rx) = channel();
    let mut watcher: RecommendedWatcher = Watcher::new(tx).expect("unable to create watcher");

    let paths = args.values_of("path").unwrap();
    for path in paths {
        match Path::new(path).canonicalize() {
            Ok(canonicalized)   => watcher.watch(canonicalized).expect("unable to watch path"),
            Err(_)              => {
                println!("invalid path: {}", path);
                return;
            }
        }
    }

    let cmd_parts: Vec<&str> = args.values_of("command").unwrap().collect();
    let cmd = cmd_parts.join(" ");
    let mut runner = Runner::new(args.is_present("restart"), args.is_present("clear"), verbose);

    loop {
        let e = wait(&rx, &filter, verbose).expect("error when waiting for filesystem changes");

        if verbose {
            println!("*** {:?}: {:?}", e.op, e.path);
        }

        runner.run_command(&cmd);
    }
}
<|endoftext|>


<commit_before>#![forbid(unsafe_code)]

pub mod api;
pub mod db;
pub mod importer;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use std::{mem, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};

use axum::{
    extract::{Extension, Path, Query},
    handler::{get, put},
    http::StatusCode,
    AddExtensionLayer, Json, Router,
};
use clap::Parser;
use futures_util::stream::Stream;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};
use shakmaty::{
    san::{San, SanPlus},
    uci::Uci,
    variant::{VariantPosition},
};
use tokio::sync::watch;

use crate::{
    api::{
        Error, ExplorerGame, ExplorerGameWithUci, ExplorerMove, ExplorerResponse, LichessQuery,
        Limits, MastersQuery, NdJson, PlayerQuery, PlayerQueryFilter,
    },
    db::{Database, LichessDatabase},
    importer::{LichessGameImport, LichessImporter, MastersImporter},
    indexer::{IndexerOpt, IndexerStub},
    model::{GameId, KeyBuilder, KeyPrefix, MastersGame, MastersGameWithId, PreparedMove, UserId},
    opening::{Opening, Openings},
    util::DedupStreamExt as _,
};

#[derive(Parser)]
struct Opt {
    /// Binding address. Note that administrative endpoints must be protected
    /// using a reverse proxy.
    #[clap(long, default_value = "127.0.0.1:9001")]
    bind: SocketAddr,
    /// Path to RocksDB database
    #[clap(long, default_value = "_db")]
    db: PathBuf,
    /// Allow access from all origins.
    #[clap(long)]
    cors: bool,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::Builder::from_env(
        env_logger::Env::new()
            .filter("EXPLORER_LOG")
            .write_style("EXPLORER_LOG_STYLE"),
    )
    .format_timestamp(None)
    .format_module_path(false)
    .format_target(false)
    .init();

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::build_table()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handles) = IndexerStub::spawn(Arc::clone(&db), opt.indexer);
    let masters_importer = MastersImporter::new(Arc::clone(&db));
    let lichess_importer = LichessImporter::new(Arc::clone(&db));

    let app = Router::new()
        .route("/monitor/cf/:cf/:prop", get(cf_prop))
        .route("/monitor/db/:prop", get(db_prop))
        .route("/monitor/indexing", get(num_indexing))
        .route("/import/masters", put(masters_import))
        .route("/import/lichess", put(lichess_import))
        .route("/masters/pgn/:id", get(masters_pgn))
        .route("/masters", get(masters))
        .route("/lichess", get(lichess))
        .route("/player", get(player))
        .route("/master/pgn/:id", get(masters_pgn)) // bc
        .route("/master", get(masters)) // bc
        .route("/personal", get(player)) // bc
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(masters_importer))
        .layer(AddExtensionLayer::new(lichess_importer))
        .layer(AddExtensionLayer::new(indexer));

    let app = if opt.cors {
        app.layer(tower_http::set_header::SetResponseHeaderLayer::<
            _,
            axum::body::Body,
        >::if_not_present(
            axum::http::header::ACCESS_CONTROL_ALLOW_ORIGIN,
            axum::http::HeaderValue::from_static("*"),
        ))
    } else {
        app
    };

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .await
        .expect("bind");

    for join_handle in join_handles {
        join_handle.await.expect("indexer");
    }
}

#[derive(Deserialize)]
struct ColumnFamilyProp {
    cf: String,
    prop: String,
}

async fn cf_prop(
    Path(path): Path<ColumnFamilyProp>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<String, StatusCode> {
    db.inner
        .cf_handle(&path.cf)
        .and_then(|cf| {
            db.inner
                .property_value_cf(cf, &path.prop)
                .expect("property value")
        })
        .ok_or(StatusCode::NOT_FOUND)
}

async fn db_prop(
    Path(prop): Path<String>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<String, StatusCode> {
    db.inner
        .property_value(&prop)
        .expect("property value")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn num_indexing(Extension(indexer): Extension<IndexerStub>) -> String {
    indexer.num_indexing().await.to_string()
}

fn finalize_lichess_moves(
    moves: Vec<PreparedMove>,
    pos: &VariantPosition,
    lichess_db: &LichessDatabase,
) -> Vec<ExplorerMove> {
    moves
        .into_iter()
        .map(|p| ExplorerMove {
            stats: p.stats,
            san: p.uci.to_move(pos).map_or(
                SanPlus {
                    san: San::Null,
                    suffix: None,
                },
                |m| SanPlus::from_move(pos.clone(), &m),
            ),
            uci: p.uci,
            average_rating: p.average_rating,
            average_opponent_rating: p.average_opponent_rating,
            game: p.game.and_then(|id| {
                lichess_db
                    .game(id)
                    .expect("get game")
                    .map(|info| ExplorerGame::from_lichess(id, info))
            }),
        })
        .collect()
}

fn finalize_lichess_games(
    games: Vec<(Uci, GameId)>,
    lichess_db: &LichessDatabase,
) -> Vec<ExplorerGameWithUci> {
    games
        .into_iter()
        .flat_map(|(uci, id)| {
            lichess_db
                .game(id)
                .expect("get game")
                .map(|info| ExplorerGameWithUci {
                    uci,
                    row: ExplorerGame::from_lichess(id, info),
                })
        })
        .collect()
}

struct PlayerStreamState {
    indexing: Option<watch::Receiver<()>>,
    key: KeyPrefix,
    db: Arc<Database>,
    filter: PlayerQueryFilter,
    limits: Limits,
    pos: VariantPosition,
    opening: Option<&'static Opening>,
    first: bool,
    done: bool,
}

async fn player(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PlayerQuery>,
) -> Result<NdJson<impl Stream<Item = ExplorerResponse>>, Error> {
    let player = UserId::from(query.player);
    let indexing = indexer.index_player(&player).await;
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::player(&player, query.color).with_zobrist(variant, pos.zobrist_hash());

    let state = PlayerStreamState {
        filter: query.filter,
        limits: query.limits,
        db,
        indexing,
        opening,
        key,
        pos: pos.into_inner(),
        first: true,
        done: false,
    };

    Ok(NdJson(futures_util::stream::unfold(
        state,
        |mut state| async move {
            if state.done {
                return None;
            }

            let first = mem::replace(&mut state.first, false);
            state.done = match state.indexing {
                Some(ref mut indexing) => {
                    tokio::select! {
                        _ = indexing.changed() => true,
                        _ = tokio::time::sleep(Duration::from_millis(if first { 0 } else { 1000 })) => false,
                    }
                }
                None => true,
            };

            let lichess_db = state.db.lichess();
            let mut filtered = lichess_db
                .read_player(&state.key, state.filter.since, state.filter.until)
                .expect("read player")
                .prepare(&state.filter);

            filtered.moves.truncate(state.limits.moves.unwrap_or(usize::MAX));
            filtered.recent_games.truncate(state.limits.recent_games);

            Some((
                ExplorerResponse {
                    total: filtered.total,
                    moves: finalize_lichess_moves(filtered.moves, &state.pos, &lichess_db),
                    recent_games: Some(finalize_lichess_games(filtered.recent_games, &lichess_db)),
                    top_games: None,
                    opening: state.opening,
                },
                state,
            ))
        },
    ).dedup_by_key(|res| res.total.total())))
}

async fn masters_import(
    Json(body): Json<MastersGameWithId>,
    Extension(importer): Extension<MastersImporter>,
) -> Result<(), Error> {
    importer.import(body).await
}

#[serde_as]
#[derive(Deserialize)]
struct MastersGameId(#[serde_as(as = "DisplayFromStr")] GameId);

async fn masters_pgn(
    Path(MastersGameId(id)): Path<MastersGameId>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<MastersGame, StatusCode> {
    match db.masters().game(id).expect("get masters game") {
        Some(game) => Ok(game),
        None => Err(StatusCode::NOT_FOUND),
    }
}

async fn masters(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<MastersQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::masters().with_zobrist(variant, pos.zobrist_hash());
    let masters_db = db.masters();
    let mut entry = masters_db
        .read(key, query.since, query.until)
        .expect("get masters")
        .prepare();

    entry.moves.truncate(query.limits.moves.unwrap_or(12));
    entry.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: entry.total,
        moves: entry
            .moves
            .into_iter()
            .map(|p| ExplorerMove {
                san: p.uci.to_move(&pos).map_or(
                    SanPlus {
                        san: San::Null,
                        suffix: None,
                    },
                    |m| SanPlus::from_move(pos.clone(), &m),
                ),
                uci: p.uci,
                average_rating: p.average_rating,
                average_opponent_rating: p.average_opponent_rating,
                stats: p.stats,
                game: p.game.and_then(|id| {
                    masters_db
                        .game(id)
                        .expect("get masters game")
                        .map(|info| ExplorerGame::from_masters(id, info))
                }),
            })
            .collect(),
        top_games: Some(
            entry
                .top_games
                .into_iter()
                .flat_map(|(uci, id)| {
                    masters_db
                        .game(id)
                        .expect("get masters game")
                        .map(|info| ExplorerGameWithUci {
                            uci,
                            row: ExplorerGame::from_masters(id, info),
                        })
                })
                .collect(),
        ),
        opening,
        recent_games: None,
    }))
}

async fn lichess_import(
    Json(body): Json<Vec<LichessGameImport>>,
    Extension(importer): Extension<LichessImporter>,
) -> Result<(), Error> {
    for game in body {
        importer.import(game).await?;
    }
    Ok(())
}

async fn lichess(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<LichessQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::lichess().with_zobrist(variant, pos.zobrist_hash());
    let lichess_db = db.lichess();
    let mut filtered = lichess_db
        .read_lichess(&key, query.filter.since, query.filter.until)
        .expect("get lichess")
        .prepare(&dbg!(query.filter));

    filtered.moves.truncate(query.limits.moves.unwrap_or(12));
    filtered.recent_games.truncate(query.limits.recent_games);
    filtered.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: filtered.total,
        moves: finalize_lichess_moves(filtered.moves, pos.as_inner(), &lichess_db),
        recent_games: Some(finalize_lichess_games(filtered.recent_games, &lichess_db)),
        top_games: Some(finalize_lichess_games(filtered.top_games, &lichess_db)),
        opening,
    }))
}
<commit_msg>cargo fmt<commit_after>#![forbid(unsafe_code)]

pub mod api;
pub mod db;
pub mod importer;
pub mod indexer;
pub mod model;
pub mod opening;
pub mod util;

use std::{mem, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};

use axum::{
    extract::{Extension, Path, Query},
    handler::{get, put},
    http::StatusCode,
    AddExtensionLayer, Json, Router,
};
use clap::Parser;
use futures_util::stream::Stream;
use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};
use shakmaty::{
    san::{San, SanPlus},
    uci::Uci,
    variant::VariantPosition,
};
use tokio::sync::watch;

use crate::{
    api::{
        Error, ExplorerGame, ExplorerGameWithUci, ExplorerMove, ExplorerResponse, LichessQuery,
        Limits, MastersQuery, NdJson, PlayerQuery, PlayerQueryFilter,
    },
    db::{Database, LichessDatabase},
    importer::{LichessGameImport, LichessImporter, MastersImporter},
    indexer::{IndexerOpt, IndexerStub},
    model::{GameId, KeyBuilder, KeyPrefix, MastersGame, MastersGameWithId, PreparedMove, UserId},
    opening::{Opening, Openings},
    util::DedupStreamExt as _,
};

#[derive(Parser)]
struct Opt {
    /// Binding address. Note that administrative endpoints must be protected
    /// using a reverse proxy.
    #[clap(long, default_value = "127.0.0.1:9001")]
    bind: SocketAddr,
    /// Path to RocksDB database
    #[clap(long, default_value = "_db")]
    db: PathBuf,
    /// Allow access from all origins.
    #[clap(long)]
    cors: bool,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

#[tokio::main]
async fn main() {
    env_logger::Builder::from_env(
        env_logger::Env::new()
            .filter("EXPLORER_LOG")
            .write_style("EXPLORER_LOG_STYLE"),
    )
    .format_timestamp(None)
    .format_module_path(false)
    .format_target(false)
    .init();

    let opt = Opt::parse();

    let openings: &'static Openings = Box::leak(Box::new(Openings::build_table()));
    let db = Arc::new(Database::open(opt.db).expect("db"));
    let (indexer, join_handles) = IndexerStub::spawn(Arc::clone(&db), opt.indexer);
    let masters_importer = MastersImporter::new(Arc::clone(&db));
    let lichess_importer = LichessImporter::new(Arc::clone(&db));

    let app = Router::new()
        .route("/monitor/cf/:cf/:prop", get(cf_prop))
        .route("/monitor/db/:prop", get(db_prop))
        .route("/monitor/indexing", get(num_indexing))
        .route("/import/masters", put(masters_import))
        .route("/import/lichess", put(lichess_import))
        .route("/masters/pgn/:id", get(masters_pgn))
        .route("/masters", get(masters))
        .route("/lichess", get(lichess))
        .route("/player", get(player))
        .route("/master/pgn/:id", get(masters_pgn)) // bc
        .route("/master", get(masters)) // bc
        .route("/personal", get(player)) // bc
        .layer(AddExtensionLayer::new(openings))
        .layer(AddExtensionLayer::new(db))
        .layer(AddExtensionLayer::new(masters_importer))
        .layer(AddExtensionLayer::new(lichess_importer))
        .layer(AddExtensionLayer::new(indexer));

    let app = if opt.cors {
        app.layer(tower_http::set_header::SetResponseHeaderLayer::<
            _,
            axum::body::Body,
        >::if_not_present(
            axum::http::header::ACCESS_CONTROL_ALLOW_ORIGIN,
            axum::http::HeaderValue::from_static("*"),
        ))
    } else {
        app
    };

    axum::Server::bind(&opt.bind)
        .serve(app.into_make_service())
        .await
        .expect("bind");

    for join_handle in join_handles {
        join_handle.await.expect("indexer");
    }
}

#[derive(Deserialize)]
struct ColumnFamilyProp {
    cf: String,
    prop: String,
}

async fn cf_prop(
    Path(path): Path<ColumnFamilyProp>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<String, StatusCode> {
    db.inner
        .cf_handle(&path.cf)
        .and_then(|cf| {
            db.inner
                .property_value_cf(cf, &path.prop)
                .expect("property value")
        })
        .ok_or(StatusCode::NOT_FOUND)
}

async fn db_prop(
    Path(prop): Path<String>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<String, StatusCode> {
    db.inner
        .property_value(&prop)
        .expect("property value")
        .ok_or(StatusCode::NOT_FOUND)
}

async fn num_indexing(Extension(indexer): Extension<IndexerStub>) -> String {
    indexer.num_indexing().await.to_string()
}

fn finalize_lichess_moves(
    moves: Vec<PreparedMove>,
    pos: &VariantPosition,
    lichess_db: &LichessDatabase,
) -> Vec<ExplorerMove> {
    moves
        .into_iter()
        .map(|p| ExplorerMove {
            stats: p.stats,
            san: p.uci.to_move(pos).map_or(
                SanPlus {
                    san: San::Null,
                    suffix: None,
                },
                |m| SanPlus::from_move(pos.clone(), &m),
            ),
            uci: p.uci,
            average_rating: p.average_rating,
            average_opponent_rating: p.average_opponent_rating,
            game: p.game.and_then(|id| {
                lichess_db
                    .game(id)
                    .expect("get game")
                    .map(|info| ExplorerGame::from_lichess(id, info))
            }),
        })
        .collect()
}

fn finalize_lichess_games(
    games: Vec<(Uci, GameId)>,
    lichess_db: &LichessDatabase,
) -> Vec<ExplorerGameWithUci> {
    games
        .into_iter()
        .flat_map(|(uci, id)| {
            lichess_db
                .game(id)
                .expect("get game")
                .map(|info| ExplorerGameWithUci {
                    uci,
                    row: ExplorerGame::from_lichess(id, info),
                })
        })
        .collect()
}

struct PlayerStreamState {
    indexing: Option<watch::Receiver<()>>,
    key: KeyPrefix,
    db: Arc<Database>,
    filter: PlayerQueryFilter,
    limits: Limits,
    pos: VariantPosition,
    opening: Option<&'static Opening>,
    first: bool,
    done: bool,
}

async fn player(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PlayerQuery>,
) -> Result<NdJson<impl Stream<Item = ExplorerResponse>>, Error> {
    let player = UserId::from(query.player);
    let indexing = indexer.index_player(&player).await;
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::player(&player, query.color).with_zobrist(variant, pos.zobrist_hash());

    let state = PlayerStreamState {
        filter: query.filter,
        limits: query.limits,
        db,
        indexing,
        opening,
        key,
        pos: pos.into_inner(),
        first: true,
        done: false,
    };

    Ok(NdJson(futures_util::stream::unfold(
        state,
        |mut state| async move {
            if state.done {
                return None;
            }

            let first = mem::replace(&mut state.first, false);
            state.done = match state.indexing {
                Some(ref mut indexing) => {
                    tokio::select! {
                        _ = indexing.changed() => true,
                        _ = tokio::time::sleep(Duration::from_millis(if first { 0 } else { 1000 })) => false,
                    }
                }
                None => true,
            };

            let lichess_db = state.db.lichess();
            let mut filtered = lichess_db
                .read_player(&state.key, state.filter.since, state.filter.until)
                .expect("read player")
                .prepare(&state.filter);

            filtered.moves.truncate(state.limits.moves.unwrap_or(usize::MAX));
            filtered.recent_games.truncate(state.limits.recent_games);

            Some((
                ExplorerResponse {
                    total: filtered.total,
                    moves: finalize_lichess_moves(filtered.moves, &state.pos, &lichess_db),
                    recent_games: Some(finalize_lichess_games(filtered.recent_games, &lichess_db)),
                    top_games: None,
                    opening: state.opening,
                },
                state,
            ))
        },
    ).dedup_by_key(|res| res.total.total())))
}

async fn masters_import(
    Json(body): Json<MastersGameWithId>,
    Extension(importer): Extension<MastersImporter>,
) -> Result<(), Error> {
    importer.import(body).await
}

#[serde_as]
#[derive(Deserialize)]
struct MastersGameId(#[serde_as(as = "DisplayFromStr")] GameId);

async fn masters_pgn(
    Path(MastersGameId(id)): Path<MastersGameId>,
    Extension(db): Extension<Arc<Database>>,
) -> Result<MastersGame, StatusCode> {
    match db.masters().game(id).expect("get masters game") {
        Some(game) => Ok(game),
        None => Err(StatusCode::NOT_FOUND),
    }
}

async fn masters(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<MastersQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::masters().with_zobrist(variant, pos.zobrist_hash());
    let masters_db = db.masters();
    let mut entry = masters_db
        .read(key, query.since, query.until)
        .expect("get masters")
        .prepare();

    entry.moves.truncate(query.limits.moves.unwrap_or(12));
    entry.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: entry.total,
        moves: entry
            .moves
            .into_iter()
            .map(|p| ExplorerMove {
                san: p.uci.to_move(&pos).map_or(
                    SanPlus {
                        san: San::Null,
                        suffix: None,
                    },
                    |m| SanPlus::from_move(pos.clone(), &m),
                ),
                uci: p.uci,
                average_rating: p.average_rating,
                average_opponent_rating: p.average_opponent_rating,
                stats: p.stats,
                game: p.game.and_then(|id| {
                    masters_db
                        .game(id)
                        .expect("get masters game")
                        .map(|info| ExplorerGame::from_masters(id, info))
                }),
            })
            .collect(),
        top_games: Some(
            entry
                .top_games
                .into_iter()
                .flat_map(|(uci, id)| {
                    masters_db
                        .game(id)
                        .expect("get masters game")
                        .map(|info| ExplorerGameWithUci {
                            uci,
                            row: ExplorerGame::from_masters(id, info),
                        })
                })
                .collect(),
        ),
        opening,
        recent_games: None,
    }))
}

async fn lichess_import(
    Json(body): Json<Vec<LichessGameImport>>,
    Extension(importer): Extension<LichessImporter>,
) -> Result<(), Error> {
    for game in body {
        importer.import(game).await?;
    }
    Ok(())
}

async fn lichess(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<LichessQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let (variant, pos, opening) = query.play.position(openings)?;
    let key = KeyBuilder::lichess().with_zobrist(variant, pos.zobrist_hash());
    let lichess_db = db.lichess();
    let mut filtered = lichess_db
        .read_lichess(&key, query.filter.since, query.filter.until)
        .expect("get lichess")
        .prepare(&dbg!(query.filter));

    filtered.moves.truncate(query.limits.moves.unwrap_or(12));
    filtered.recent_games.truncate(query.limits.recent_games);
    filtered.top_games.truncate(query.limits.top_games);

    Ok(Json(ExplorerResponse {
        total: filtered.total,
        moves: finalize_lichess_moves(filtered.moves, pos.as_inner(), &lichess_db),
        recent_games: Some(finalize_lichess_games(filtered.recent_games, &lichess_db)),
        top_games: Some(finalize_lichess_games(filtered.top_games, &lichess_db)),
        opening,
    }))
}
<|endoftext|>


<commit_before>extern crate glob;
extern crate http;
extern crate hyper;
extern crate hyper_tls;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate structopt;
extern crate toml;

mod error;
mod ghclient;
mod statics;
mod structs;
#[macro_use]
mod syslog;

use statics::CONF_PATH;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
enum Command {
    /// Gets user public key
    #[structopt(name = "key")]
    Key {
        #[structopt(parse(from_str))]
        user: String,
    },
    /// Executes pam check
    #[structopt(name = "pam")]
    Pam,
    /// Check configuration
    #[structopt(name = "check")]
    Check {
        #[structopt(parse(from_os_str))]
        confpath: std::path::PathBuf,
    },
    /// Cleans caches up
    #[structopt(name = "cleanup")]
    CleanUp,
    /// Get rate limit for github api
    #[structopt(name = "ratelimit")]
    RateLimit,
    /// Displays version details
    #[structopt(name = "version")]
    Version,
}

fn main() {
    let command = Command::from_args();

    use ghclient::GithubClient;
    use std::env;
    use std::process;
    use structs::Config;

    match command {
        Command::Check { confpath } => match Config::new(&confpath) {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(11),
        },
        Command::Key { user } => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                              .and_then(|client| client.print_user_public_key(&user))
        {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(21),
        },
        Command::Pam => match env::var("PAM_USER") {
            Ok(user) => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                     .and_then(|client| client.check_pam(&user))
            {
                Ok(true) => process::exit(0),
                Ok(false) => process::exit(1),
                Err(_) => process::exit(31),
            },
            Err(_) => process::exit(41),
        },
        Command::CleanUp => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                         .and_then(|client| client.clear_all_caches())
        {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(51),
        },
        Command::RateLimit => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                           .and_then(|client| client.print_rate_limit())
        {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(61),
        },
        Command::Version => {
            println!("{}",
                     concat!(env!("CARGO_PKG_VERSION"),
                             include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))));
        }
    };
}
<commit_msg>Remove unused macro_use<commit_after>extern crate glob;
extern crate http;
extern crate hyper;
extern crate hyper_tls;
#[macro_use]
extern crate lazy_static;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate structopt;
extern crate toml;

mod error;
mod ghclient;
mod statics;
mod structs;
#[macro_use]
mod syslog;

use statics::CONF_PATH;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
enum Command {
    /// Gets user public key
    #[structopt(name = "key")]
    Key {
        #[structopt(parse(from_str))]
        user: String,
    },
    /// Executes pam check
    #[structopt(name = "pam")]
    Pam,
    /// Check configuration
    #[structopt(name = "check")]
    Check {
        #[structopt(parse(from_os_str))]
        confpath: std::path::PathBuf,
    },
    /// Cleans caches up
    #[structopt(name = "cleanup")]
    CleanUp,
    /// Get rate limit for github api
    #[structopt(name = "ratelimit")]
    RateLimit,
    /// Displays version details
    #[structopt(name = "version")]
    Version,
}

fn main() {
    let command = Command::from_args();

    use ghclient::GithubClient;
    use std::env;
    use std::process;
    use structs::Config;

    match command {
        Command::Check { confpath } => match Config::new(&confpath) {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(11),
        },
        Command::Key { user } => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                              .and_then(|client| client.print_user_public_key(&user))
        {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(21),
        },
        Command::Pam => match env::var("PAM_USER") {
            Ok(user) => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                     .and_then(|client| client.check_pam(&user))
            {
                Ok(true) => process::exit(0),
                Ok(false) => process::exit(1),
                Err(_) => process::exit(31),
            },
            Err(_) => process::exit(41),
        },
        Command::CleanUp => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                         .and_then(|client| client.clear_all_caches())
        {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(51),
        },
        Command::RateLimit => match Config::new(&CONF_PATH).and_then(|conf| Ok(GithubClient::new(&conf)))
                                                           .and_then(|client| client.print_rate_limit())
        {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(61),
        },
        Command::Version => {
            println!("{}",
                     concat!(env!("CARGO_PKG_VERSION"),
                             include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))));
        }
    };
}
<|endoftext|>


<commit_before>
extern crate crypto;

use std::io::prelude::*;
use crypto::digest::Digest;
use crypto::md5::Md5;

/// generate the md5sum of a given file
pub fn md5sum<T : Read> (f: &mut T) -> String  {
     let mut digest = Md5::new();

     let mut data: Vec<u8> = Vec::new();
     f.read_to_end(&mut data);
     digest.input(&data);
     digest.result_str()
}

#[cfg(test)]
mod tests {

    use std::fs::File;
    use super::md5sum;

    #[test]
    fn md5sum_of_file() {
        static p : &'static str = "/home/costa/pkg/lav/regione/attiweb/attività/20150107/definizione procedura di riavvio/Procedure_Riavvio_omnia.odt";

        let mut odt = match File::open(p) {
            Err(why) => panic!("Cannot open the test file: {}", why),
            Ok(file) => file,
        };

        assert_eq!(md5sum(&mut odt), "a381cbe043c38a94af0983ffa934f338");

    }

}
fn main() {
    println!("Hello, world!");
}
<commit_msg>eliminated use<commit_after>
extern crate crypto;

/// generate the md5sum of a given file
pub fn md5sum<T : std::io::Read> (f: &mut T) -> String  {
    use crypto::digest::Digest;

     let mut digest = crypto::md5::Md5::new();
     let mut data: Vec<u8> = Vec::new();

     f.read_to_end(&mut data);
     digest.input(&data);
     digest.result_str()
}

#[cfg(test)]
mod tests {

    use std::fs::File;
    use super::md5sum;

    #[test]
    fn md5sum_of_file() {
        static FP : &'static str = "/home/costa/pkg/lav/regione/attiweb/attività/20150107/definizione procedura di riavvio/Procedure_Riavvio_omnia.odt";

        let mut odt = match File::open(FP) {
            Err(why) => panic!("Cannot open the test file: {}", why),
            Ok(file) => file,
        };

        assert_eq!(md5sum(&mut odt), "a381cbe043c38a94af0983ffa934f338");
    }

}

fn main() {
    println!("Hello, world!");
}
<|endoftext|>