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

// TODO: Check for proper error code to exit with.
fn exit_with_error(message: &str) {
    println!("ERR: {}", message);
    process::exit(1);
}

fn run(command: &str) {
    let valid_commands = &vec!["version"];

    if !valid_commands.contains(&command) {
        exit_with_error(&format!("Invalid command: {}", command));
    }
    println!("{}", command);
}

// parse action
// build
// execute
// parse response
// render

fn main() {
    match env::args().nth(1) {
        Some(command)   => run(&command),
        None            => exit_with_error("you need to provide a command"),
    }
}
<commit_msg>VALID_COMMANDS as global<commit_after>use std::env;
use std::process;

static VALID_COMMANDS: &'static [ &'static str ] = &["version"];

// TODO: Check for proper error code to exit with.
fn exit_with_error(message: &str) {
    println!("ERR: {}", message);
    process::exit(1);
}

fn run(command: &str) {
    if !&VALID_COMMANDS.contains(&command) {
        exit_with_error(&format!("Invalid command: {}", command));
    }

    println!("Executing command: {}", command);

}

// parse action
// build
// execute
// parse response
// render

fn main() {
    match env::args().nth(1) {
        Some(command)   => run(&command),
        None            => exit_with_error("you need to provide a command"),
    }
}
<|endoftext|>


<commit_before>use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;

extern crate time;
use time::Duration;

extern crate stats;
use stats::median;

extern crate clap;
use clap::{Arg, App};

mod percentile;
use percentile::percentile;

mod http_status;

mod request_response;
use request_response::*;

pub fn open_logfile(path: &str, time_filter: Option<Duration>) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(Request::new_from_log_line(&line_value));

            if time_filter.is_none() ||
              (time_filter.is_some() && r.is_between_times(time::now() - time_filter.unwrap(), time::now())) {
                requests.push(r);
            }
        }

        if line_value.contains("<-") {
            let r = try!(Response::new_from_log_line(&line_value));
            responses.push(r);
        }

    }

    responses.sort_by_key(|r| r.id);

    Ok((requests, responses))
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct RequestLogAnalyzerResult {
    count: usize,
    max: usize,
    min: usize,
    avg: usize,
    median: usize,
    percentile90: usize,
}

pub fn analyze(request_response_pairs: &Vec<RequestResponsePair>) -> Option<RequestLogAnalyzerResult> {
    if request_response_pairs.len() == 0 {
        return None;
    }

    let times: Vec<i64> = request_response_pairs.iter()
        .map(|rr: &RequestResponsePair| -> i64 {rr.response.response_time.num_milliseconds() })
        .collect();

    let sum: usize = times.iter().sum::<i64>() as usize;
    let avg: usize = sum / times.len();

    let max: usize = *times.iter().max().unwrap() as usize;
    let min: usize = *times.iter().min().unwrap() as usize;

    let percentile90: usize = percentile(&times, 0.9) as usize;

    let median = median(times.into_iter()).unwrap() as usize;

    Some(RequestLogAnalyzerResult {
        count: request_response_pairs.len().into(),
        max: max,
        min: min,
        avg: avg,
        median: median,
        percentile90: percentile90,
    })
}

fn render_terminal(result: RequestLogAnalyzerResult) {
    println!("count:\t{}", result.count);
    println!("time.avg:\t{}", result.avg);
    println!("time.min:\t{}", result.min);
    println!("time.median:\t{}", result.median);
    println!("time.90percent:\t{}", result.percentile90);
    println!("time.max:\t{}", result.max);
}

fn main() {
    let matches = App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .help("Log file to analyze")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
                .value_name("MINUTES")
                .short("t")
                .help("Limit to the last n minutes")
                .takes_value(true))
                .get_matches();

    let filename = matches.value_of("filename").unwrap();

    let time_filter = match matches.value_of("time_filter_minutes") {
        Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
        None => None
    };

    let lines = open_logfile(filename, time_filter);
    let (requests, responses) = lines.unwrap();

    let pairs: Vec<RequestResponsePair> = pair_requests_responses(requests, responses);

    match analyze(&pairs) {
        Some(result) => render_terminal(result),
        None => println!("No matching log lines in file.")
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    use request_response::*;
    extern crate time;
    use time::Duration;

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_open_logfile_time_filter() {
        let time_filter: Duration = Duration::minutes(1);
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 0);

        let time_filter: Duration = Duration::minutes(52560000); // 100 years
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
    }

    #[test]
    fn test_pair_requests_resonses() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        let result = pair_requests_responses(requests, responses);

        assert_eq!(result.len(), 2);

        assert_eq!(result[0].request.id, result[0].response.id);
        assert_eq!(result[1].request.id, result[1].response.id);
    }

    #[test]
    fn test_request_log_analyzer_result() {
        let lines = open_logfile("src/test/response-time-calculations.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = Some(RequestLogAnalyzerResult {
            count: 3,
            max: 100,
            min: 1,
            avg: 37,
            median: 10,
            percentile90: 100,
        });

        assert_eq!(result, expected);
    }

    #[test]
    fn test_request_log_analyze_none_matching() {
        let lines = open_logfile("src/test/simple-1.log", Some(Duration::minutes(0)));
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = None;

        assert_eq!(result, expected);
    }

    #[test]
    fn test_90_percentile_calculation() {
        let lines = open_logfile("src/test/percentile.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result: RequestLogAnalyzerResult = analyze(&request_response_pairs).unwrap();

        assert_eq!(result.percentile90, 9);
    }
}
<commit_msg>File argument is required<commit_after>use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;

extern crate time;
use time::Duration;

extern crate stats;
use stats::median;

extern crate clap;
use clap::{Arg, App};

mod percentile;
use percentile::percentile;

mod http_status;

mod request_response;
use request_response::*;

pub fn open_logfile(path: &str, time_filter: Option<Duration>) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(Request::new_from_log_line(&line_value));

            if time_filter.is_none() ||
              (time_filter.is_some() && r.is_between_times(time::now() - time_filter.unwrap(), time::now())) {
                requests.push(r);
            }
        }

        if line_value.contains("<-") {
            let r = try!(Response::new_from_log_line(&line_value));
            responses.push(r);
        }

    }

    responses.sort_by_key(|r| r.id);

    Ok((requests, responses))
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct RequestLogAnalyzerResult {
    count: usize,
    max: usize,
    min: usize,
    avg: usize,
    median: usize,
    percentile90: usize,
}

pub fn analyze(request_response_pairs: &Vec<RequestResponsePair>) -> Option<RequestLogAnalyzerResult> {
    if request_response_pairs.len() == 0 {
        return None;
    }

    let times: Vec<i64> = request_response_pairs.iter()
        .map(|rr: &RequestResponsePair| -> i64 {rr.response.response_time.num_milliseconds() })
        .collect();

    let sum: usize = times.iter().sum::<i64>() as usize;
    let avg: usize = sum / times.len();

    let max: usize = *times.iter().max().unwrap() as usize;
    let min: usize = *times.iter().min().unwrap() as usize;

    let percentile90: usize = percentile(&times, 0.9) as usize;

    let median = median(times.into_iter()).unwrap() as usize;

    Some(RequestLogAnalyzerResult {
        count: request_response_pairs.len().into(),
        max: max,
        min: min,
        avg: avg,
        median: median,
        percentile90: percentile90,
    })
}

fn render_terminal(result: RequestLogAnalyzerResult) {
    println!("count:\t{}", result.count);
    println!("time.avg:\t{}", result.avg);
    println!("time.min:\t{}", result.min);
    println!("time.median:\t{}", result.median);
    println!("time.90percent:\t{}", result.percentile90);
    println!("time.max:\t{}", result.max);
}

fn main() {
    let matches = App::new("Request.log Analyzer")
        .arg(Arg::with_name("filename")
            .index(1)
            .value_name("FILE")
            .required(true)
            .help("Log file to analyze")
            .takes_value(true))
        .arg(Arg::with_name("time_filter_minutes")
                .value_name("MINUTES")
                .short("t")
                .help("Limit to the last n minutes")
                .takes_value(true))
                .get_matches();

    let filename = matches.value_of("filename").unwrap();

    let time_filter = match matches.value_of("time_filter_minutes") {
        Some(minutes) => Some(Duration::minutes(minutes.parse().unwrap())),
        None => None
    };

    let lines = open_logfile(filename, time_filter);
    let (requests, responses) = lines.unwrap();

    let pairs: Vec<RequestResponsePair> = pair_requests_responses(requests, responses);

    match analyze(&pairs) {
        Some(result) => render_terminal(result),
        None => println!("No matching log lines in file.")
    }
}

#[cfg(test)]
mod tests {
	use super::*;
    use request_response::*;
    extern crate time;
    use time::Duration;

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_open_logfile_time_filter() {
        let time_filter: Duration = Duration::minutes(1);
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 0);

        let time_filter: Duration = Duration::minutes(52560000); // 100 years
        let lines = open_logfile("src/test/simple-1.log", Some(time_filter));
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
    }

    #[test]
    fn test_pair_requests_resonses() {
        let lines = open_logfile("src/test/simple-1.log", None);
        let (requests, responses) = lines.unwrap();

        let result = pair_requests_responses(requests, responses);

        assert_eq!(result.len(), 2);

        assert_eq!(result[0].request.id, result[0].response.id);
        assert_eq!(result[1].request.id, result[1].response.id);
    }

    #[test]
    fn test_request_log_analyzer_result() {
        let lines = open_logfile("src/test/response-time-calculations.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = Some(RequestLogAnalyzerResult {
            count: 3,
            max: 100,
            min: 1,
            avg: 37,
            median: 10,
            percentile90: 100,
        });

        assert_eq!(result, expected);
    }

    #[test]
    fn test_request_log_analyze_none_matching() {
        let lines = open_logfile("src/test/simple-1.log", Some(Duration::minutes(0)));
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result = analyze(&request_response_pairs);

        let expected = None;

        assert_eq!(result, expected);
    }

    #[test]
    fn test_90_percentile_calculation() {
        let lines = open_logfile("src/test/percentile.log", None);
        let (requests, responses) = lines.unwrap();

        let request_response_pairs = pair_requests_responses(requests, responses);

        let result: RequestLogAnalyzerResult = analyze(&request_response_pairs).unwrap();

        assert_eq!(result.percentile90, 9);
    }
}
<|endoftext|>


<commit_before>extern crate regex;
extern crate readline;

use regex::Regex;
use std::collections::HashMap;
use std::io::prelude::*;
use std::io::stdin;
use std::iter::Iterator;
use std::string::String;

#[derive(Debug, PartialEq, Clone)]
enum Atom {
    Integer(i64),
    Float(f64),
    Symbol(String),
    Nil
}

type Function = Fn(&Vec<Atom>) -> AtomResult;
type FunctionMap = HashMap<String, Box<Function>>;

#[derive(Debug)]
enum Error {
    UnexpectedType,
    Parser,
    InvalidArguments
}

type AtomResult = Result<Atom, Error>;
type ParseResult = Result<Node, Error>;

fn nil_func(_: &Vec<Atom>) -> AtomResult {
    return Ok(Atom::Nil);
}

struct Env {
    func_map: FunctionMap
}

fn add(v: &Vec<Atom>) -> AtomResult {
    let mut result = 0;

    for i in v {
        match *i {
            Atom::Integer(d) => result += d,
            _ => return Err(Error::UnexpectedType)
        }
    }

    return Ok(Atom::Integer(result));
}

fn sub(v: &Vec<Atom>) -> AtomResult {
    if v.len() < 1 {
        return Err(Error::InvalidArguments)
    }

    let mut result = match v[0] {
        Atom::Integer(d) => d,
        _ => return Err(Error::UnexpectedType)
    };

    for i in v.iter().skip(1) {
        match *i {
            Atom::Integer(d) => result -= d,
            _ => return Err(Error::UnexpectedType)
        }
    }

    return Ok(Atom::Integer(result));
}

fn default_env() -> Env {
    let mut env = Env{func_map: HashMap::new()};

    env.func_map.insert("nil".to_string(), Box::new(nil_func) as Box<Function>);
    env.func_map.insert("+".to_string(), Box::new(add) as Box<Function>);
    env.func_map.insert("-".to_string(), Box::new(sub) as Box<Function>);

    return env;
}

fn tokenize(line: &str) -> ParseResult {
    let r = Regex::new(r"\s+").unwrap();
    let l = line.replace("(", " ( ").replace(")", " ) ").trim().to_string();
    let iter = r.split(&l);
    return read_tokens(&mut iter.peekable());
}

fn atom(token: &str) -> Atom {
    let i = token.parse::<i64>();
     match i {
         Ok(v) => Atom::Integer(v),
         _ => {
             let f = token.parse::<f64>();
             match f {
                 Ok(x) => Atom::Float(x),
                 _ => Atom::Symbol(token.to_string())
             }
         },
     }
}

#[derive(Debug,PartialEq)]
struct Node {
    atom: Atom,
    children: Vec<Node>
}

fn read_tokens(iter: &mut std::iter::Peekable<regex::RegexSplits>) -> ParseResult {
    let mut node = Node{ atom: Atom::Nil, children: vec![] };

    match iter.next() {
        Some("(") => {
            loop {
                match iter.peek() {
                    Some(&")") => break,
                    Some(_) => {},
                    None => return Err(Error::Parser)
                }
                let token = try!(read_tokens(iter));
                node.children.push(token);
            }
        },
        Some(")") => return Err(Error::Parser),
        Some(x) => {
            node.atom = atom(x);
        },
        None => return Err(Error::Parser)
    }
    return Ok(node);
}

fn eval(node: &Node, env: &mut Env) -> AtomResult {
    match node.atom {
        Atom::Nil => {
            // TODO: use the iterators
            let mut args: Vec<Atom> = vec![];
            for i in node.children.iter().skip(1) {
                let a = try!(eval(i, env));
                args.push(a);
            }

            match node.children[0].atom {
                Atom::Symbol(ref s) => {
                    if *s == "define" {
                        println!("define!");
                        return Ok(Atom::Nil);
                    }

                    match env.func_map.get(s) {
                        Some(f) => return f(&args),
                        None => return Ok(Atom::Nil)
                    }
                },
                _ => unreachable!()
            }
        },
        Atom::Integer(i) => {
            println!("an integer: {}", i);
        },
        Atom::Float(f) => {
            println!("a float: {}", f);
        },
        Atom::Symbol(ref s) => {
            println!("a symbol, {}", s);
        }
    }

    return Ok(node.atom.clone());
}

fn parse(line: &str) -> ParseResult {
    tokenize(line)
}

fn flush() {
    let out = std::io::stdout();
    let mut o = out.lock();
    o.flush().unwrap();
}

fn repl() {
    println!("Rust Lisp!");
    let mut env = default_env();

    loop {
        let line = readline::readline(">");

        match line {
            Some(s) => {
                if let Ok(p) = parse(&s) {
                    match eval(&p, &mut env) {
                        Ok(r) => println!("{:?}", r),
                        Err(e) => println!("Error in evaluation: {:?}", e)
                    }
                } else {
                    println!("Error in parsing");
                }
            },
            None => {
                println!("Error");
                break;
            }
        }
    }
}

pub fn main() {
    repl();
}

#[cfg(test)]
mod test {
    use super::Node;
    use super::Atom;
    use super::atom;
    use super::parse;

    fn make_atom_node(s: &str) -> Node {
        return Node{ atom: atom(s), children: vec![]};
    }

    #[test]
    fn atom_test() {
        assert_eq!(Atom::Integer(32), atom("32"));
        assert_eq!(Atom::Symbol("symbol".to_string()), atom("symbol"));
    }

    #[test]
    fn simple_read_tokens() {
        let x = parse("(+ 1 2)");

        assert_eq!(Atom::Nil, x.atom);
        assert_eq!(3, x.children.len());

        let xx : Vec<Node> = vec![make_atom_node("+"), make_atom_node("1"), make_atom_node("2")];

        for pair in xx.iter().zip(x.children.iter()) {
            assert_eq!(pair.0, pair.1);
        }
    }

    #[test]
    fn nested_read_tokens() {
        let x = parse("(+ 1 (* 2 2))");

        assert_eq!( atom("+"), x.children[0].atom);
        assert_eq!( atom("1"), x.children[1].atom);
        assert_eq!( Atom::Nil, x.children[2].atom);

        assert_eq!( 3, x.children[2].children.len());
        assert_eq!( atom("*"), x.children[2].children[0].atom);
        assert_eq!( atom("2"), x.children[2].children[1].atom);
        assert_eq!( atom("2"), x.children[2].children[2].atom);
    }

    #[test]
    #[should_panic]
    fn unmatched_bracket() {
        let x = parse("(+ 1");
    }

    #[test]
    #[should_panic]
    fn eof() {
        let x = parse("");
    }
}
<commit_msg>exiting<commit_after>extern crate regex;
extern crate readline;

use regex::Regex;
use std::collections::HashMap;
use std::io::prelude::*;
use std::io::stdin;
use std::iter::Iterator;
use std::string::String;

#[derive(Debug, PartialEq, Clone)]
enum Atom {
    Integer(i64),
    Float(f64),
    Symbol(String),
    Nil
}

type Function = Fn(&Vec<Atom>) -> AtomResult;
type FunctionMap = HashMap<String, Box<Function>>;

#[derive(Debug)]
enum Error {
    UnexpectedType,
    Parser,
    InvalidArguments
}

type AtomResult = Result<Atom, Error>;
type ParseResult = Result<Node, Error>;

fn nil_func(_: &Vec<Atom>) -> AtomResult {
    return Ok(Atom::Nil);
}

struct Env {
    func_map: FunctionMap
}

fn add(v: &Vec<Atom>) -> AtomResult {
    let mut result = 0;

    for i in v {
        match *i {
            Atom::Integer(d) => result += d,
            _ => return Err(Error::UnexpectedType)
        }
    }

    return Ok(Atom::Integer(result));
}

fn sub(v: &Vec<Atom>) -> AtomResult {
    if v.len() < 1 {
        return Err(Error::InvalidArguments)
    }

    let mut result = match v[0] {
        Atom::Integer(d) => d,
        _ => return Err(Error::UnexpectedType)
    };

    for i in v.iter().skip(1) {
        match *i {
            Atom::Integer(d) => result -= d,
            _ => return Err(Error::UnexpectedType)
        }
    }

    return Ok(Atom::Integer(result));
}

fn default_env() -> Env {
    let mut env = Env{func_map: HashMap::new()};

    env.func_map.insert("nil".to_string(), Box::new(nil_func) as Box<Function>);
    env.func_map.insert("+".to_string(), Box::new(add) as Box<Function>);
    env.func_map.insert("-".to_string(), Box::new(sub) as Box<Function>);

    return env;
}

fn tokenize(line: &str) -> ParseResult {
    let r = Regex::new(r"\s+").unwrap();
    let l = line.replace("(", " ( ").replace(")", " ) ").trim().to_string();
    let iter = r.split(&l);
    return read_tokens(&mut iter.peekable());
}

fn atom(token: &str) -> Atom {
    let i = token.parse::<i64>();
     match i {
         Ok(v) => Atom::Integer(v),
         _ => {
             let f = token.parse::<f64>();
             match f {
                 Ok(x) => Atom::Float(x),
                 _ => Atom::Symbol(token.to_string())
             }
         },
     }
}

#[derive(Debug,PartialEq)]
struct Node {
    atom: Atom,
    children: Vec<Node>
}

fn read_tokens(iter: &mut std::iter::Peekable<regex::RegexSplits>) -> ParseResult {
    let mut node = Node{ atom: Atom::Nil, children: vec![] };

    match iter.next() {
        Some("(") => {
            loop {
                match iter.peek() {
                    Some(&")") => break,
                    Some(_) => {},
                    None => return Err(Error::Parser)
                }
                let token = try!(read_tokens(iter));
                node.children.push(token);
            }
        },
        Some(")") => return Err(Error::Parser),
        Some(x) => {
            node.atom = atom(x);
        },
        None => return Err(Error::Parser)
    }
    return Ok(node);
}

fn eval(node: &Node, env: &mut Env) -> AtomResult {
    match node.atom {
        Atom::Nil => {
            // TODO: use the iterators
            let mut args: Vec<Atom> = vec![];
            for i in node.children.iter().skip(1) {
                let a = try!(eval(i, env));
                args.push(a);
            }

            match node.children[0].atom {
                Atom::Symbol(ref s) => {
                    if *s == "define" {
                        println!("define!");
                        return Ok(Atom::Nil);
                    }

                    match env.func_map.get(s) {
                        Some(f) => return f(&args),
                        None => return Ok(Atom::Nil)
                    }
                },
                _ => unreachable!()
            }
        },
        Atom::Integer(i) => {
            println!("an integer: {}", i);
        },
        Atom::Float(f) => {
            println!("a float: {}", f);
        },
        Atom::Symbol(ref s) => {
            println!("a symbol, {}", s);
        }
    }

    return Ok(node.atom.clone());
}

fn parse(line: &str) -> ParseResult {
    tokenize(line)
}

fn flush() {
    let out = std::io::stdout();
    let mut o = out.lock();
    o.flush().unwrap();
}

fn repl() {
    println!("Rust Lisp!");
    let mut env = default_env();

    loop {
        match readline::readline(">") {
            Some(s) => {
                if let Ok(p) = parse(&s) {
                    match eval(&p, &mut env) {
                        Ok(r) => println!("{:?}", r),
                        Err(e) => println!("Error in evaluation: {:?}", e)
                    }
                } else {
                    println!("Error in parsing");
                }
            },
            None => {
                println!("Exiting...");
                break;
            }
        }
    }
}

pub fn main() {
    repl();
}

#[cfg(test)]
mod test {
    use super::Node;
    use super::Atom;
    use super::atom;
    use super::parse;

    fn make_atom_node(s: &str) -> Node {
        return Node{ atom: atom(s), children: vec![]};
    }

    #[test]
    fn atom_test() {
        assert_eq!(Atom::Integer(32), atom("32"));
        assert_eq!(Atom::Symbol("symbol".to_string()), atom("symbol"));
    }

    #[test]
    fn simple_read_tokens() {
        let x = parse("(+ 1 2)");

        assert_eq!(Atom::Nil, x.atom);
        assert_eq!(3, x.children.len());

        let xx : Vec<Node> = vec![make_atom_node("+"), make_atom_node("1"), make_atom_node("2")];

        for pair in xx.iter().zip(x.children.iter()) {
            assert_eq!(pair.0, pair.1);
        }
    }

    #[test]
    fn nested_read_tokens() {
        let x = parse("(+ 1 (* 2 2))");

        assert_eq!( atom("+"), x.children[0].atom);
        assert_eq!( atom("1"), x.children[1].atom);
        assert_eq!( Atom::Nil, x.children[2].atom);

        assert_eq!( 3, x.children[2].children.len());
        assert_eq!( atom("*"), x.children[2].children[0].atom);
        assert_eq!( atom("2"), x.children[2].children[1].atom);
        assert_eq!( atom("2"), x.children[2].children[2].atom);
    }

    #[test]
    #[should_panic]
    fn unmatched_bracket() {
        let x = parse("(+ 1");
    }

    #[test]
    #[should_panic]
    fn eof() {
        let x = parse("");
    }
}
<|endoftext|>


<commit_before>extern crate unicode_width;

mod args;
mod matching;
mod screen;
#[cfg(not(windows))] mod ansi;

use std::collections::HashSet;
use std::process;
use std::io::{stdin, BufRead};
use std::cmp::min;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;
use unicode_width::UnicodeWidthStr;

#[cfg(windows)] pub const NEWLINE: &'static str = "\r\n";
#[cfg(not(windows))] pub const NEWLINE: &'static str = "\n";

const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/version.txt"));
const TIMESTAMP: &'static str = include_str!(concat!(env!("OUT_DIR"), "/timestamp.txt"));
const TARGET: &'static str = include_str!(concat!(env!("OUT_DIR"), "/target.txt"));
const COMMIT: &'static str = include_str!(concat!(env!("OUT_DIR"), "/commit.txt"));

fn main() {
    let args = match args::parse_args() {
        Some(args) => args,
        None => {
            process::exit(1);
        },
    };

    if args.help { return; }

    if args.version {
        if COMMIT == "" {
            println!("heatseeker {} (built {} for {})", VERSION, TIMESTAMP, TARGET);
        } else {
            println!("heatseeker {}-{} (built {} for {})", VERSION, COMMIT, TIMESTAMP, TARGET);
        }
        return;
    }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();
    if args.use_first {
        let matches = matching::compute_matches(&choices, &initial_search, args.filter_only);
        println!("{}", matches.get(0).unwrap_or(&""));
        return;
    } else {
        let desired_rows = if args.full_screen { 999 } else { 20 };
        let selections = event_loop(desired_rows, &choices, &initial_search, args.filter_only);
        print!("{}", selections);
    }
}

fn event_loop(desired_rows: u16, choices: &[&str], initial_search: &str, filter_only: bool) -> String {
    let mut search = Search::new(choices, initial_search.to_string(), filter_only);
    let mut screen = screen::new(desired_rows);

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(screen.as_mut(), &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in &keys {
            handle_key(&mut search, key, screen.visible_choices());
        }
    }

    screen.blank_screen();
    search.get_selections()
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace |
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('c') |
        Control('g') => search.cancel(),
        Control('t') => { search.toggle_selection(); search.down(visible_choices); },
        Control('p') |
        Up |
        ShiftTab => search.up(visible_choices),
        Control('n') |
        Down |
        Tab => search.down(visible_choices),
        Enter => search.done(),
        _ => {}
    }
}

struct Search<'a> {
    choices: &'a [&'a str],
    query: String,
    matches: Vec<&'a str>,
    stale: bool,
    index: usize,
    state: SearchState,
    selections: HashSet<String>,
    filter_only: bool,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a [&'a str], initial_search: String, filter_only: bool) -> Search<'a> {
        let matches = choices.to_vec();
        Search {
            choices: choices,
            query: initial_search,
            matches: matches,
            stale: true,
            index: 0,
            state: InProgress,
            selections: HashSet::new(),
            filter_only: filter_only,
        }
    }

    fn up(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == 0 { limit } else { self.index - 1 };
    }

    fn down(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == limit { 0 } else { self.index + 1 }
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.index = 0;
        self.matches = self.choices.to_vec();
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
        self.matches = self.choices.to_vec();
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.index = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.matches = self.choices.to_vec();
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(&self.matches, &self.query, self.filter_only);
            self.stale = false;
        }
    }

    fn toggle_selection(&mut self) {
        self.recompute_matches();
        let selection = self.matches.get(self.index).unwrap_or(&"").to_string();
        if self.selections.contains(&selection) {
            self.selections.remove(&selection);
        } else {
            self.selections.insert(selection);
        }
    }

    fn get_selections(&mut self) -> String {
        let mut ret = String::new();
        if self.state != Canceled {
            for selection in &self.selections {
                ret.push_str(selection);
                ret.push_str(NEWLINE);
            }
            if ret.is_empty() {
                self.recompute_matches();
                let selection = self.matches.get(self.index).unwrap_or(&"").to_string();
                ret.push_str(&selection);
            }
        }
        ret
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut dyn Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(&format!("> {} ({}/{} choices){}", search.query, search.matches.len(), search.choices.len(), NEWLINE));

    print_matches(screen, &search.matches, &search.query, search.index, &search.selections);

    let query_str: &str = &search.query;
    screen.move_cursor_to_prompt_line(2 + UnicodeWidthStr::width(query_str) as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut dyn Screen, matches: &[&str], query: &str, index: usize, selections: &HashSet<String>) {
    let mut i = 1;
    for choice in matches.iter() {
        let indices = matching::visual_score(choice, query);
        let max_width = screen.width();
        let mut annotated_choice = choice.to_string();
        if selections.contains(&annotated_choice) {
            annotated_choice.push(' ');
            if cfg!(windows) {
                annotated_choice.push('√');
            } else {
                annotated_choice.push('✓');
            }
        }
        print_match(&annotated_choice, &indices, max_width, &mut |s, highlight| {
            if i == index + 1 {
                if highlight { screen.write_red_inverted(s); } else { screen.write_inverted(s); }
            } else if highlight {
                screen.write_red(s);
            } else { screen.write(s); }
        });
        if i >= screen.visible_choices() as usize {
            return;
        }
        screen.write(NEWLINE);
        i += 1;
    }
}

fn print_match(choice: &str, indices: &[usize], max_width: u16, writer: &mut dyn FnMut(&str, bool)) {
    #[cfg(windows)] const MARGIN: u16 = 1;
    #[cfg(not(windows))] const MARGIN: u16 = 0;
    let max_width = max_width - MARGIN;
    let chars_in_choice = choice.chars().count();
    let mut chars_to_draw = min(chars_in_choice, max_width as usize);
    while UnicodeWidthStr::width(slice_chars(choice, 0, chars_to_draw)) > max_width as usize {
        chars_to_draw -= 1;
    }
    let mut last_idx = 0;
    for &idx in indices {
        let idx = min(idx, chars_to_draw);
        if last_idx >= chars_to_draw {
            return;
        }
        writer(slice_chars(choice, last_idx, idx), false);
        if idx == chars_to_draw { return }
        writer(slice_chars(choice, idx, idx + 1), true);
        last_idx = idx + 1;
    }
    writer(slice_chars(choice, last_idx, chars_to_draw), false);
}

fn read_choices() -> Vec<String> {
    let stdin = stdin();
    let mut lines = Vec::new();

    let mut stdin = stdin.lock();
    let mut first_error = None;
    let mut suppressed = 0;
    loop {
        let mut s = String::new();
        match stdin.read_line(&mut s) {
            Ok(_) => {
                if s.is_empty() { break; }
                trim(&mut s);
                lines.push(s);
            },
            Err(e) => {
                if first_error.is_some() {
                    suppressed = suppressed + 1;
                } else {
                    first_error = Some(e);
                }
            }
        }
    }
    if first_error.is_some() {
        eprintln!("Warning: Failed to parse one or more lines (\"{}\"); {} additional error(s) suppressed", first_error.unwrap(), suppressed);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' && x != '\r' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

// This is technically a standard library function, but it's plastered with
// stability warnings and therefore only available on the nightly channel.
fn slice_chars(s: &str, begin: usize, end: usize) -> &str {
    assert!(begin <= end);
    let mut count = 0;
    let mut begin_byte = None;
    let mut end_byte = None;

    // This could be even more efficient by not decoding,
    // only finding the char boundaries
    for (idx, _) in s.char_indices() {
        if count == begin { begin_byte = Some(idx); }
        if count == end { end_byte = Some(idx); break; }
        count += 1;
    }
    if begin_byte.is_none() && count == begin { begin_byte = Some(s.len()) }
    if end_byte.is_none() && count == end { end_byte = Some(s.len()) }

    match (begin_byte, end_byte) {
        (None, _) => panic!("slice_chars: `begin` is beyond end of string"),
        (_, None) => panic!("slice_chars: `end` is beyond end of string"),
        (Some(a), Some(b)) => unsafe { s.get_unchecked(a..b) }
    }
}

#[cfg(test)]
mod tests {
    use super::{trim, delete_last_word};

    #[test]
    fn trim_test() {
        fn should_become(before: &str, after: &str) {
            let mut x = before.to_string();
            trim(&mut x);
            assert_eq!(after.to_string(), x);
        }
        should_become("", "");
        should_become("\n", "");
        should_become("\n\n", "");
        should_become("asdf", "asdf");
        should_become("asdf\n", "asdf");
        should_become("asdf\nasdf\n", "asdf\nasdf");
        should_become("asdf\r\n", "asdf");
    }

    #[test]
    fn delete_word_test() {
        fn should_become(before: &str, after: &str) {
            let mut x = before.to_string();
            delete_last_word(&mut x);
            assert_eq!(after.to_string(), x);
        }
        should_become("", "");
        should_become("a", "");
        should_become("asdf", "");
        should_become("asdf asdf asdf", "asdf asdf ");
        should_become("asdf asdf asdf ", "asdf asdf ");
        should_become("asdf asdf asdf  ", "asdf asdf ");
    }
}
<commit_msg>Rename variable<commit_after>extern crate unicode_width;

mod args;
mod matching;
mod screen;
#[cfg(not(windows))] mod ansi;

use std::collections::HashSet;
use std::process;
use std::io::{stdin, BufRead};
use std::cmp::min;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;
use unicode_width::UnicodeWidthStr;

#[cfg(windows)] pub const NEWLINE: &'static str = "\r\n";
#[cfg(not(windows))] pub const NEWLINE: &'static str = "\n";

const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/version.txt"));
const TIMESTAMP: &'static str = include_str!(concat!(env!("OUT_DIR"), "/timestamp.txt"));
const TARGET: &'static str = include_str!(concat!(env!("OUT_DIR"), "/target.txt"));
const COMMIT: &'static str = include_str!(concat!(env!("OUT_DIR"), "/commit.txt"));

fn main() {
    let args = match args::parse_args() {
        Some(args) => args,
        None => {
            process::exit(1);
        },
    };

    if args.help { return; }

    if args.version {
        if COMMIT == "" {
            println!("heatseeker {} (built {} for {})", VERSION, TIMESTAMP, TARGET);
        } else {
            println!("heatseeker {}-{} (built {} for {})", VERSION, COMMIT, TIMESTAMP, TARGET);
        }
        return;
    }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();
    if args.use_first {
        let matches = matching::compute_matches(&choices, &initial_search, args.filter_only);
        println!("{}", matches.get(0).unwrap_or(&""));
        return;
    } else {
        let desired_rows = if args.full_screen { 999 } else { 20 };
        let selections = event_loop(desired_rows, &choices, &initial_search, args.filter_only);
        print!("{}", selections);
    }
}

fn event_loop(desired_rows: u16, choices: &[&str], initial_search: &str, filter_only: bool) -> String {
    let mut search = Search::new(choices, initial_search.to_string(), filter_only);
    let mut screen = screen::new(desired_rows);

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(screen.as_mut(), &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in &keys {
            handle_key(&mut search, key, screen.visible_choices());
        }
    }

    screen.blank_screen();
    search.get_selections()
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace |
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('c') |
        Control('g') => search.cancel(),
        Control('t') => { search.toggle_selection(); search.down(visible_choices); },
        Control('p') |
        Up |
        ShiftTab => search.up(visible_choices),
        Control('n') |
        Down |
        Tab => search.down(visible_choices),
        Enter => search.done(),
        _ => {}
    }
}

struct Search<'a> {
    choices: &'a [&'a str],
    query: String,
    matches: Vec<&'a str>,
    stale: bool,
    cursor_index: usize,
    state: SearchState,
    selections: HashSet<String>,
    filter_only: bool,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a [&'a str], initial_search: String, filter_only: bool) -> Search<'a> {
        let matches = choices.to_vec();
        Search {
            choices: choices,
            query: initial_search,
            matches: matches,
            stale: true,
            cursor_index: 0,
            state: InProgress,
            selections: HashSet::new(),
            filter_only: filter_only,
        }
    }

    fn up(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.cursor_index = if self.cursor_index == 0 { limit } else { self.cursor_index - 1 };
    }

    fn down(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.cursor_index = if self.cursor_index == limit { 0 } else { self.cursor_index + 1 }
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.cursor_index = 0;
        self.matches = self.choices.to_vec();
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
        self.matches = self.choices.to_vec();
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.cursor_index = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.matches = self.choices.to_vec();
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(&self.matches, &self.query, self.filter_only);
            self.stale = false;
        }
    }

    fn toggle_selection(&mut self) {
        self.recompute_matches();
        let selection = self.matches.get(self.cursor_index).unwrap_or(&"").to_string();
        if self.selections.contains(&selection) {
            self.selections.remove(&selection);
        } else {
            self.selections.insert(selection);
        }
    }

    fn get_selections(&mut self) -> String {
        let mut ret = String::new();
        if self.state != Canceled {
            for selection in &self.selections {
                ret.push_str(selection);
                ret.push_str(NEWLINE);
            }
            if ret.is_empty() {
                self.recompute_matches();
                let selection = self.matches.get(self.cursor_index).unwrap_or(&"").to_string();
                ret.push_str(&selection);
            }
        }
        ret
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut dyn Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(&format!("> {} ({}/{} choices){}", search.query, search.matches.len(), search.choices.len(), NEWLINE));

    print_matches(screen, &search.matches, &search.query, search.cursor_index, &search.selections);

    let query_str: &str = &search.query;
    screen.move_cursor_to_prompt_line(2 + UnicodeWidthStr::width(query_str) as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut dyn Screen, matches: &[&str], query: &str, cursor_index: usize, selections: &HashSet<String>) {
    let mut i = 1;
    for choice in matches.iter() {
        let indices = matching::visual_score(choice, query);
        let max_width = screen.width();
        let mut annotated_choice = choice.to_string();
        if selections.contains(&annotated_choice) {
            annotated_choice.push(' ');
            if cfg!(windows) {
                annotated_choice.push('√');
            } else {
                annotated_choice.push('✓');
            }
        }
        print_match(&annotated_choice, &indices, max_width, &mut |s, highlight| {
            if i == cursor_index + 1 {
                if highlight { screen.write_red_inverted(s); } else { screen.write_inverted(s); }
            } else if highlight {
                screen.write_red(s);
            } else { screen.write(s); }
        });
        if i >= screen.visible_choices() as usize {
            return;
        }
        screen.write(NEWLINE);
        i += 1;
    }
}

fn print_match(choice: &str, indices: &[usize], max_width: u16, writer: &mut dyn FnMut(&str, bool)) {
    #[cfg(windows)] const MARGIN: u16 = 1;
    #[cfg(not(windows))] const MARGIN: u16 = 0;
    let max_width = max_width - MARGIN;
    let chars_in_choice = choice.chars().count();
    let mut chars_to_draw = min(chars_in_choice, max_width as usize);
    while UnicodeWidthStr::width(slice_chars(choice, 0, chars_to_draw)) > max_width as usize {
        chars_to_draw -= 1;
    }
    let mut last_idx = 0;
    for &idx in indices {
        let idx = min(idx, chars_to_draw);
        if last_idx >= chars_to_draw {
            return;
        }
        writer(slice_chars(choice, last_idx, idx), false);
        if idx == chars_to_draw { return }
        writer(slice_chars(choice, idx, idx + 1), true);
        last_idx = idx + 1;
    }
    writer(slice_chars(choice, last_idx, chars_to_draw), false);
}

fn read_choices() -> Vec<String> {
    let stdin = stdin();
    let mut lines = Vec::new();

    let mut stdin = stdin.lock();
    let mut first_error = None;
    let mut suppressed = 0;
    loop {
        let mut s = String::new();
        match stdin.read_line(&mut s) {
            Ok(_) => {
                if s.is_empty() { break; }
                trim(&mut s);
                lines.push(s);
            },
            Err(e) => {
                if first_error.is_some() {
                    suppressed = suppressed + 1;
                } else {
                    first_error = Some(e);
                }
            }
        }
    }
    if first_error.is_some() {
        eprintln!("Warning: Failed to parse one or more lines (\"{}\"); {} additional error(s) suppressed", first_error.unwrap(), suppressed);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' && x != '\r' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

// This is technically a standard library function, but it's plastered with
// stability warnings and therefore only available on the nightly channel.
fn slice_chars(s: &str, begin: usize, end: usize) -> &str {
    assert!(begin <= end);
    let mut count = 0;
    let mut begin_byte = None;
    let mut end_byte = None;

    // This could be even more efficient by not decoding,
    // only finding the char boundaries
    for (idx, _) in s.char_indices() {
        if count == begin { begin_byte = Some(idx); }
        if count == end { end_byte = Some(idx); break; }
        count += 1;
    }
    if begin_byte.is_none() && count == begin { begin_byte = Some(s.len()) }
    if end_byte.is_none() && count == end { end_byte = Some(s.len()) }

    match (begin_byte, end_byte) {
        (None, _) => panic!("slice_chars: `begin` is beyond end of string"),
        (_, None) => panic!("slice_chars: `end` is beyond end of string"),
        (Some(a), Some(b)) => unsafe { s.get_unchecked(a..b) }
    }
}

#[cfg(test)]
mod tests {
    use super::{trim, delete_last_word};

    #[test]
    fn trim_test() {
        fn should_become(before: &str, after: &str) {
            let mut x = before.to_string();
            trim(&mut x);
            assert_eq!(after.to_string(), x);
        }
        should_become("", "");
        should_become("\n", "");
        should_become("\n\n", "");
        should_become("asdf", "asdf");
        should_become("asdf\n", "asdf");
        should_become("asdf\nasdf\n", "asdf\nasdf");
        should_become("asdf\r\n", "asdf");
    }

    #[test]
    fn delete_word_test() {
        fn should_become(before: &str, after: &str) {
            let mut x = before.to_string();
            delete_last_word(&mut x);
            assert_eq!(after.to_string(), x);
        }
        should_become("", "");
        should_become("a", "");
        should_become("asdf", "");
        should_become("asdf asdf asdf", "asdf asdf ");
        should_become("asdf asdf asdf ", "asdf asdf ");
        should_become("asdf asdf asdf  ", "asdf asdf ");
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::rc::Rc;

type Error = &'static str;

#[derive(Debug,Clone)]
enum Code {
    Integer(i64),
    Rational(i64, i64),
    String(String),
    Add(Rc<Code>, Rc<Code>),
    Sub(Rc<Code>, Rc<Code>),
    Div(Rc<Code>, Rc<Code>),
    Mul(Rc<Code>, Rc<Code>),
    Neg(Rc<Code>),
}

impl fmt::Display for Code {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Code::Integer(n) => write!(f, "{}", n),
            &Code::Rational(n, d) => write!(f, "{}/{}", n, d),
            &Code::String(ref s) => write!(f, "'{}'", s),
            &Code::Add(ref left, ref right) => write!(f, "({} + {})", left, right),
            &Code::Sub(ref left, ref right) => write!(f, "({} - {})", left, right),
            &Code::Div(ref left, ref right) => write!(f, "({} / {})", left, right),
            &Code::Mul(ref left, ref right) => write!(f, "({} * {})", left, right),
            &Code::Neg(ref num) => write!(f, "-({})", num),
        }
    }
}

// Calculate the greatest common divisor using Euclid's algorithm.
fn gcd(mut a: i64, mut b: i64) -> i64 {
    let mut c;
    while a != 0 {
        c = a;
        a = b % a;
        b = c;
    }
    b
}

// This normalizes inputs and sometimes even converts to integers.
fn new_rational(mut n: i64, mut d: i64) -> Code {
    let g = gcd(n, d);
    n = n / g;
    d = d / g;
    if d < 0 {
      n = -n;
      d = -d;
    }
    if d == 1 {
        Code::Integer(n)
    }
    else {
        Code::Rational(n, d)
    }
}

fn rational_add(n1: i64, d1: i64, n2: i64, d2: i64) -> Code {
  if d1 == d2 {
    // Fast path for common divisors.
    new_rational(n1 + n2, d1)
  }
  else {
    let g = gcd(d1, d2);
    new_rational(n1 * (d2 / g) + n2 * (d1 / g), d1 / g * d2)
  }

}

fn add(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Integer(a + b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(rational_add(n, d, b, 1)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(rational_add(a, 1, n, d)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(rational_add(n1, d1, n2, d2)),
        _ => Err("Add requires two numbers"),
    }
}

fn subtract(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Integer(a - b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(rational_add(n, d, -b, 1)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(rational_add(a, 1, -n, d)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(rational_add(n1, d1, -n2, d2)),
        _ => Err("Subtract requires two numbers"),
    }
}

fn multiply(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Integer(a * b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(new_rational(n * b, d)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(new_rational(a * n, d)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(new_rational(n1 * n2, d1 * d2)),
        _ => Err("Multiply requires two numbers"),
    }
}

fn divide(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(new_rational(a, b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(new_rational(n, d * b)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(new_rational(a * d, n)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(new_rational(n1 * d2, d1 * n2)),
        _ => Err("Divide requires two numbers"),
    }
}

fn negate(num: &Code) -> Result<Code, Error> {
    match num {
        &Code::Integer(a) => Ok(Code::Integer(-a)),
        &Code::Rational(n, d) => Ok(Code::Rational(-n, d)),
        _ => Err("Negate requires a number"),
    }
}

fn eval(expr: &Code) -> Result<Code, Error> {
    match expr {
        &Code::Add(ref left, ref right) => add(left, right),
        &Code::Sub(ref left, ref right) => subtract(left, right),
        &Code::Mul(ref left, ref right) => multiply(left, right),
        &Code::Div(ref left, ref right) => divide(left, right),
        &Code::Neg(ref num) => negate(num),
        n @ &Code::Integer(_) => Ok(n.clone()),
        n @ &Code::Rational(_, _) => Ok(n.clone()),
        n @ &Code::String(_) => Ok(n.clone()),
    }
}

fn test(left: Code, right: Code) {
    let a = Rc::new(left);
    let b = Rc::new(right);
    let mut e = Rc::new(Code::Add(a.clone(), b.clone()));
    println!("{} = {:?}", e, eval(&e));
    e = Rc::new(Code::Sub(a.clone(), b.clone()));
    println!("{} = {:?}", e, eval(&e));
    e = Rc::new(Code::Mul(a.clone(), b.clone()));
    println!("{} = {:?}", e, eval(&e));
    e = Rc::new(Code::Div(a.clone(), b.clone()));
    println!("{} = {:?}", e, eval(&e));
    e = Rc::new(Code::Neg(a.clone()));
    println!("{} = {:?}", e, eval(&e));
}

fn main() {
    test(Code::Integer(44), Code::Integer(14));
    test(Code::Integer(14), Code::Integer(44));
    test(Code::Integer(44), Code::Integer(-14));
    test(Code::Integer(14), Code::Integer(-44));
    test(Code::Integer(-44), Code::Integer(14));
    test(Code::Integer(-14), Code::Integer(44));
    test(Code::Integer(-44), Code::Integer(-14));
    test(Code::Integer(-14), Code::Integer(-44));
    test(new_rational(1, 2), new_rational(2, 1));
    test(new_rational(2, 1), new_rational(1, 2));
    test(new_rational(1, 2), new_rational(1, 2));
    test(new_rational(2, 2), new_rational(2, 1));
    test(Code::String(str::to_string("Input string")),
         new_rational(2, 1));
    let e = Code::Div(
        Rc::new(Code::Add(
            Rc::new(Code::Rational(1, 2)),
            Rc::new(Code::Rational(1, 3)),
        )),
        Rc::new(Code::Sub(
            Rc::new(Code::Integer(2)),
            Rc::new(Code::Integer(5)),
        )),
    );
    println!("{} = {:?}", e, eval(&e));

}
<commit_msg>Make Op own subtype and improve pretty printer<commit_after>use std::fmt;
use std::rc::Rc;

type Error = &'static str;

#[derive(Debug,Clone)]
enum Op {
    Add(Rc<Code>, Rc<Code>),
    Sub(Rc<Code>, Rc<Code>),
    Div(Rc<Code>, Rc<Code>),
    Mul(Rc<Code>, Rc<Code>),
    Neg(Rc<Code>),
}

#[derive(Debug,Clone)]
enum Code {
    Integer(i64),
    Rational(i64, i64),
    String(String),
    Op(Op),
}

fn binopfmt(f: &mut fmt::Formatter, left: &Code, right: &Code, op: &'static str) -> fmt::Result {
    try!(match left {
        &Code::Op(_) => write!(f, "({})", left),
        _ => write!(f, "{}", left)
    });
    try!(write!(f, " {} ", op));
    match right {
        &Code::Op(_) => write!(f, "({})", right),
        _ => write!(f, "{}", right)
    }
}

fn negfmt(f: &mut fmt::Formatter, num: &Code) -> fmt::Result {
    match num {
        &Code::Integer(n) if n < 0 => write!(f, "-({})", num),
        &Code::Rational(n, _) if n < 0 => write!(f, "-({})", num),
        &Code::Op(_) => write!(f, "-({})", num),
        _ => write!(f, "-{}", num),
    }
}

impl fmt::Display for Code {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Code::Integer(n) => write!(f, "{}", n),
            &Code::Rational(n, d) => write!(f, "{}/{}", n, d),
            &Code::String(ref s) => write!(f, "'{}'", s),
            &Code::Op(ref op) => match op {
                &Op::Add(ref left, ref right) => binopfmt(f, left, right, "+"),
                &Op::Sub(ref left, ref right) => binopfmt(f, left, right, "-"),
                &Op::Div(ref left, ref right) => binopfmt(f, left, right, "/"),
                &Op::Mul(ref left, ref right) => binopfmt(f, left, right, "*"),
                &Op::Neg(ref num) => negfmt(f, num),

            }
        }
    }
}

// Calculate the greatest common divisor using Euclid's algorithm.
fn gcd(mut a: i64, mut b: i64) -> i64 {
    let mut c;
    while a != 0 {
        c = a;
        a = b % a;
        b = c;
    }
    b
}

// This normalizes inputs and sometimes even converts to integers.
fn new_rational(mut n: i64, mut d: i64) -> Code {
    let g = gcd(n, d);
    n = n / g;
    d = d / g;
    if d < 0 {
      n = -n;
      d = -d;
    }
    if d == 1 {
        Code::Integer(n)
    }
    else {
        Code::Rational(n, d)
    }
}

fn rational_add(n1: i64, d1: i64, n2: i64, d2: i64) -> Code {
  if d1 == d2 {
    // Fast path for common divisors.
    new_rational(n1 + n2, d1)
  }
  else {
    let g = gcd(d1, d2);
    new_rational(n1 * (d2 / g) + n2 * (d1 / g), d1 / g * d2)
  }

}

fn add(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Integer(a + b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(rational_add(n, d, b, 1)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(rational_add(a, 1, n, d)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(rational_add(n1, d1, n2, d2)),
        _ => Err("Add requires two numbers"),
    }
}

fn subtract(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Integer(a - b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(rational_add(n, d, -b, 1)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(rational_add(a, 1, -n, d)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(rational_add(n1, d1, -n2, d2)),
        _ => Err("Subtract requires two numbers"),
    }
}

fn multiply(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(Code::Integer(a * b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(new_rational(n * b, d)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(new_rational(a * n, d)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(new_rational(n1 * n2, d1 * d2)),
        _ => Err("Multiply requires two numbers"),
    }
}

fn divide(left: &Code, right: &Code) -> Result<Code, Error> {
    match (try!(eval(left)), try!(eval(right))) {
        (Code::Integer(a), Code::Integer(b)) => Ok(new_rational(a, b)),
        (Code::Rational(n, d), Code::Integer(b)) => Ok(new_rational(n, d * b)),
        (Code::Integer(a), Code::Rational(n, d)) => Ok(new_rational(a * d, n)),
        (Code::Rational(n1, d1), Code::Rational(n2, d2)) => Ok(new_rational(n1 * d2, d1 * n2)),
        _ => Err("Divide requires two numbers"),
    }
}

fn negate(num: &Code) -> Result<Code, Error> {
    match num {
        &Code::Integer(a) => Ok(Code::Integer(-a)),
        &Code::Rational(n, d) => Ok(Code::Rational(-n, d)),
        _ => Err("Negate requires a number"),
    }
}

fn eval(expr: &Code) -> Result<Code, Error> {
    match expr {
        &Code::Op(ref op) => match op {
            &Op::Add(ref left, ref right) => add(left, right),
            &Op::Sub(ref left, ref right) => subtract(left, right),
            &Op::Mul(ref left, ref right) => multiply(left, right),
            &Op::Div(ref left, ref right) => divide(left, right),
            &Op::Neg(ref num) => negate(num),
        },
        n => Ok(n.clone()),
    }
}

fn test(left: Code, right: Code) {
    let a = Rc::new(left);
    let b = Rc::new(right);
    let mut e = Rc::new(Code::Op(Op::Add(a.clone(), b.clone())));
    println!("{} = {:?}", e, eval(&e));
    e = Rc::new(Code::Op(Op::Sub(a.clone(), b.clone())));
    println!("{} = {:?}", e, eval(&e));
    e = Rc::new(Code::Op(Op::Mul(a.clone(), b.clone())));
    println!("{} = {:?}", e, eval(&e));
    e = Rc::new(Code::Op(Op::Div(a.clone(), b.clone())));
    println!("{} = {:?}", e, eval(&e));
    e = Rc::new(Code::Op(Op::Neg(a.clone())));
    println!("{} = {:?}", e, eval(&e));
}

fn main() {
    test(Code::Integer(44), Code::Integer(14));
    test(Code::Integer(14), Code::Integer(44));
    test(Code::Integer(44), Code::Integer(-14));
    test(Code::Integer(14), Code::Integer(-44));
    test(Code::Integer(-44), Code::Integer(14));
    test(Code::Integer(-14), Code::Integer(44));
    test(Code::Integer(-44), Code::Integer(-14));
    test(Code::Integer(-14), Code::Integer(-44));
    test(new_rational(1, 2), new_rational(2, 1));
    test(new_rational(2, 1), new_rational(1, 2));
    test(new_rational(1, 2), new_rational(1, 2));
    test(new_rational(2, 2), new_rational(2, 1));
    test(Code::String(str::to_string("Input string")),
         new_rational(2, 1));
    let e = Code::Op(Op::Div(
        Rc::new(Code::Op(Op::Add(
            Rc::new(Code::Rational(1, 2)),
            Rc::new(Code::Rational(1, 3)),
        ))),
        Rc::new(Code::Op(Op::Sub(
            Rc::new(Code::Integer(2)),
            Rc::new(Code::Integer(5)),
        ))),
    ));
    println!("{} = {:?}", e, eval(&e));

}
<|endoftext|>


<commit_before>
mod curses;

use std::ffi::CString;

use curses::*;

enum DrawMe {
    Rec { width : i32, height : i32, x : i32, y : i32 },
    /*
    Character
    */
    // TODO add color
    // TODO add background color
    // TODO can probably have a struct represent common items (x,y, color, etc)
}

unsafe fn render_shape( s : &DrawMe ) {
    match s {
        &DrawMe::Rec { width, height, x, y } => render_rec( width, height, x, y ),
        _ => (),
    }
}

unsafe fn render_rec( w : i32, h : i32, x : i32, y : i32 ) {
    let dot = CString::new( "." ).unwrap();
    let h_line = CString::new( "-" ).unwrap();
    let v_line = CString::new( "|" ).unwrap();
    let plus = CString::new( "+" ).unwrap();

    let mut h2 = h + 2;
    while h2 > 0 {
        let d : &CString = if h2 == h + 2 {
            &h_line
        } else if h2 == 1 {
            &h_line
        } else {
            &dot
        };
        let mut w2 = w;

        let other_d : &CString = if h2 == h + 2 {
            &plus
        } else if h2 == 1 {
            &plus
        } else {
            &v_line
        };
        mvprintw( y + h2 + UserY, x + w2 + 1 + UserX, other_d.as_ptr() );
        while w2 > 0 {
            mvprintw( y + h2 + UserY, x + w2 + UserX, d.as_ptr() );
            w2 -= 1;
        }
        mvprintw( y + h2 + UserY, x + UserX, other_d.as_ptr() );
        h2 -= 1;
    }
}

static mut UserX : i32 = 0;
static mut UserY : i32 = 0;

fn main() {
    let mut x = 0;
    let mut y = 0;
    unsafe {
        initscr();
        cbreak();
        noecho();

        let mut c = getch();
        let mut shapes : Vec<DrawMe> = vec! [];

        // TODO need mode for entering rech, etc
        // TODO need to handle negative (some boxes disappear when off screen, followed by failure to enter new rects)

        while c as u8 as char != 'q' {
            let c2 = c as u8 as char;

            match c2 {
                /*'c' => {
                    shapes.push( DrawMe::Circle { radius: 5, x: x, y: y } );
                },*/
                'r' => {
                    shapes.push( DrawMe::Rec { width: 0, height: 0, x: x - UserX, y: y - UserY } );
                },
                'j' => {
                    y+=1;
                },
                'k' => {
                    y-=1;
                },
                'l' => {
                    x+=1;
                },
                'h' => {
                    x-=1;
                },
                'J' => {
                    UserY += 1;
                },
                'K' => {
                    UserY -= 1;
                },
                'L' => {
                    UserX += 1;
                },
                'H' => {
                    UserX -= 1;
                },
                _ => (),
            }

            clear();

            for s in &shapes {
                render_shape( s );
            }

            cursor_move( y, x );
            refresh();

            c = getch();
        }

        endwin();
    }
}

<commit_msg>remote negative todo<commit_after>
mod curses;

use std::ffi::CString;

use curses::*;

enum DrawMe {
    Rec { width : i32, height : i32, x : i32, y : i32 },
    /*
    Character
    */
    // TODO add color
    // TODO add background color
    // TODO can probably have a struct represent common items (x,y, color, etc)
}

unsafe fn render_shape( s : &DrawMe ) {
    match s {
        &DrawMe::Rec { width, height, x, y } => render_rec( width, height, x, y ),
        _ => (),
    }
}

unsafe fn render_rec( w : i32, h : i32, x : i32, y : i32 ) {
    let dot = CString::new( "." ).unwrap();
    let h_line = CString::new( "-" ).unwrap();
    let v_line = CString::new( "|" ).unwrap();
    let plus = CString::new( "+" ).unwrap();

    let mut h2 = h + 2;
    while h2 > 0 {
        let d : &CString = if h2 == h + 2 {
            &h_line
        } else if h2 == 1 {
            &h_line
        } else {
            &dot
        };
        let mut w2 = w;

        let other_d : &CString = if h2 == h + 2 {
            &plus
        } else if h2 == 1 {
            &plus
        } else {
            &v_line
        };
        mvprintw( y + h2 + UserY, x + w2 + 1 + UserX, other_d.as_ptr() );
        while w2 > 0 {
            mvprintw( y + h2 + UserY, x + w2 + UserX, d.as_ptr() );
            w2 -= 1;
        }
        mvprintw( y + h2 + UserY, x + UserX, other_d.as_ptr() );
        h2 -= 1;
    }
}

static mut UserX : i32 = 0;
static mut UserY : i32 = 0;

fn main() {
    let mut x = 0;
    let mut y = 0;
    unsafe {
        initscr();
        cbreak();
        noecho();

        let mut c = getch();
        let mut shapes : Vec<DrawMe> = vec! [];

        // TODO need mode for entering rech, etc

        while c as u8 as char != 'q' {
            let c2 = c as u8 as char;

            match c2 {
                /*'c' => {
                    shapes.push( DrawMe::Circle { radius: 5, x: x, y: y } );
                },*/
                'r' => {
                    shapes.push( DrawMe::Rec { width: 0, height: 0, x: x - UserX, y: y - UserY } );
                },
                'j' => {
                    y+=1;
                },
                'k' => {
                    y-=1;
                },
                'l' => {
                    x+=1;
                },
                'h' => {
                    x-=1;
                },
                'J' => {
                    UserY += 1;
                },
                'K' => {
                    UserY -= 1;
                },
                'L' => {
                    UserX += 1;
                },
                'H' => {
                    UserX -= 1;
                },
                _ => (),
            }

            clear();

            for s in &shapes {
                render_shape( s );
            }

            cursor_move( y, x );
            refresh();

            c = getch();
        }

        endwin();
    }
}

<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

extern crate clap;
use clap::{Arg, App, SubCommand};
extern crate toml;

extern crate reqwest;
use reqwest::header::Authorization;

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

extern crate glob;
use glob::glob;

#[macro_use]
extern crate lazy_static;

extern crate libc;
#[derive(Deserialize, Debug)]
struct Config {
    token: String,
    org: String,
    team: String,
    #[serde(default="default_endpoint")]
    endpoint: String,
    #[serde(default="default_home")]
    home: String,
    #[serde(default="default_gid")]
    gid: u64,
    #[serde(default="default_sh")]
    sh: String,
    group: Option<String>,
    #[serde(default="default_cache_duration")]
    cache_duration: u64,
    #[serde(default="default_cert_path")]
    cert_path: String
}

fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_gid() -> u64 { 2000 }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }

#[derive(Serialize, Deserialize, Debug, Clone)]
struct Team {
    id: u64,
    name: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct Member {
    id: u64,
    login: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct PublicKey {
    id: u64,
    key: String,
}

#[derive(Debug)]
enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error),
}

impl From<serde_json::Error> for CliError {fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }}
impl From<reqwest::Error> for CliError {fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }}
impl From<std::io::Error> for CliError {fn from(err: std::io::Error) -> CliError { CliError::Io(err) }}

lazy_static! {
    static ref CLIENT:GithubClient = create_github_client(
        std::env::var("GHTEAMAUTH_CONFIG")
                 .unwrap_or(String::from("/etc/ghteam-auth.conf"))
                 .as_str()
    ).unwrap();
}

fn main() {

    let matches = App::new("ghteam-auth")
                      .version("0.1")
                      .author("Yasuyuki YAMADA <yasuyuki.ymd@gmail.com>")
                      .about("")
                      .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                      .subcommand(SubCommand::with_name("key")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(true)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("pam")
                                             .about("execute pam check")
                                             .arg(Arg::with_name("USER")
                                                      .required(false)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("passwd")
                                             .about("get passwd"))
                      .subcommand(SubCommand::with_name("shadow")
                                             .about("get shadow"))
                      .subcommand(SubCommand::with_name("group")
                                             .about("get group"))
                      .subcommand(SubCommand::with_name("refresh")
                                             .about("refresh cache"))
                      .get_matches();

    if let Some(matches) = matches.subcommand_matches("key") {
        CLIENT.print_user_public_key(matches.value_of("USER").unwrap()).unwrap();
    } else if let Some(_) = matches.subcommand_matches("passwd") {
        CLIENT.print_passwd().unwrap();
    } else if let Some(_) = matches.subcommand_matches("shadow") {
        CLIENT.print_shadow().unwrap();
    } else if let Some(_) = matches.subcommand_matches("group") {
        CLIENT.print_group().unwrap();
    } else if let Some(_) = matches.subcommand_matches("refresh") {
        CLIENT.clear_all_caches().unwrap();
    } else if let Some(_) = matches.subcommand_matches("pam") {
        match std::env::var("PAM_USER") {
            Ok(user) => {
                if CLIENT.check_pam(&user).unwrap() { std::process::exit(0); }
                else { std::process::exit(1) }
            }
            Err(e) => println!("couldn't interpret PAM_USER: {}", e),
        }
    }

}

fn create_github_client( configpath: &str ) -> Result<GithubClient, CliError> {
    let mut file = File::open(configpath)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let config = toml::from_str::<Config>(contents.as_str()).unwrap();
    match std::env::var("SSL_CERT_FILE") {
        Ok(_) => (),
        Err(_) => std::env::set_var("SSL_CERT_FILE", config.cert_path.clone()),
    }
    let client = reqwest::Client::new()?;
    Ok(GithubClient::new(client, config))
}

struct GithubClient {
    client: reqwest::Client,
    conf: Config
}

impl GithubClient {
    fn new(client:reqwest::Client, conf:Config) -> GithubClient {
        GithubClient {client:client, conf:conf}
    }

    fn load_content_from_cache(&self, url:&String) -> Result<(std::fs::Metadata,String),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata,content))
    }

    fn store_content_to_cache(&self, url:&String, content:&String) -> Result<(),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let mut dirpath = path.clone(); dirpath.pop();
        std::fs::create_dir_all(dirpath)?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url:&String) -> Result<String,CliError> {
        let token = String::from("token ") + self.conf.token.clone().as_str();
        let res = self.client.get(url.as_str()).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        self.store_content_to_cache(url,&content)?;
        Ok(content)
    }

    fn get_content(&self, url:&String) -> Result<String,CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata,cache_content)) => {
                match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                    Ok(caching_duration) => {
                        if caching_duration.as_secs() > self.conf.cache_duration {
                            match self.get_content_from_url(url) {
                                Ok(content_from_url) => Ok(content_from_url),
                                Err(_) => Ok(cache_content)
                            }
                        } else {
                            Ok(cache_content)
                        }
                    },
                    Err(_) => {
                        Ok(cache_content)
                    }
                }
            },
            Err(_) => self.get_content_from_url(url)
        }
    }

    fn print_user_public_key(&self, user:&str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user:&str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url);
        let keys = serde_json::from_str::<Vec<PublicKey>>(content?.as_str())?;
        Ok(keys.iter().map(|k|{k.key.clone()}).collect::<Vec<String>>().join("\n"))
    }

    fn check_pam(&self, user:&String) -> Result<bool, CliError> {
        let (_,members) = self.get_team_members()?;
        Ok(members.contains_key(user))
    }

    fn print_passwd(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_passwd_line(&member));
        }
        Ok(())
    }

    fn print_shadow(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_shadow_line(&member));
        }
        Ok(())
    }

    fn print_group(&self) -> Result<(), CliError> {
        let (team,members) = self.get_team_members()?;
        println!("{}", self.create_group_line(&team.name, self.conf.gid, &members));
        Ok(())
    }

    fn create_passwd_line(&self, member:&Member) -> String {
        format!("{login}:x:{uid}:{gid}:user@{org}:{home}:{sh}",
                login=member.login,
                uid=member.id,
                gid=self.conf.gid,
                org=self.conf.org,
                home=self.conf.home.replace("{}",member.login.as_str()),
                sh=self.conf.sh,
                )
    }

    fn create_shadow_line(&self, member:&Member) -> String {
        format!("{login}:!!:::::::", login=member.login )
    }

    fn create_group_line(&self, name:&String, id:u64, members:&HashMap<String,Member>) -> String {
        format!("{name}:x:{id}:{members}", name=name, id=id,
                members=members.values().map(|m|{m.login.clone()}).collect::<Vec<String>>().join(","))
    }

    fn get_team_members(&self) -> Result<(Team,HashMap<String,Member>),CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            Ok((team.clone(),self.get_members(team.id)?))
        } else {
            Err(CliError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "Team not found")))
        }
    }

    fn get_teams(&self) -> Result<HashMap<String,Team>, CliError> {
        let url = format!("{}/orgs/{}/teams",self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(content.as_str())?;
        let mut team_map = HashMap::new();
        for team in teams { team_map.insert(team.name.clone(), team); }
        Ok(team_map)
    }

    fn get_members(&self, mid:u64) -> Result<HashMap<String,Member>, CliError> {
        let url = format!("{}/teams/{}/members",self.conf.endpoint.clone(), mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(content.as_str())?;
        let mut member_map = HashMap::new();
        for member in members { member_map.insert(member.login.clone(), member); }
        Ok(member_map)
    }

    fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push("**");
        path.push("*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {if path.is_file() {std::fs::remove_file(path)?}},
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }

}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct Passwd {
    name:   *mut libc::c_char,
    pass:   *mut libc::c_char,
    uid:    libc::uid_t,
    gid:    libc::gid_t,
    gecos:  *mut libc::c_char,
    home:   *mut libc::c_char,
    sh:     *mut libc::c_char,
}
<commit_msg>Remove an unused arg<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

extern crate clap;
use clap::{Arg, App, SubCommand};
extern crate toml;

extern crate reqwest;
use reqwest::header::Authorization;

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

extern crate glob;
use glob::glob;

#[macro_use]
extern crate lazy_static;

extern crate libc;
#[derive(Deserialize, Debug)]
struct Config {
    token: String,
    org: String,
    team: String,
    #[serde(default="default_endpoint")]
    endpoint: String,
    #[serde(default="default_home")]
    home: String,
    #[serde(default="default_gid")]
    gid: u64,
    #[serde(default="default_sh")]
    sh: String,
    group: Option<String>,
    #[serde(default="default_cache_duration")]
    cache_duration: u64,
    #[serde(default="default_cert_path")]
    cert_path: String
}

fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_gid() -> u64 { 2000 }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cache_duration() -> u64 { 3600 }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }

#[derive(Serialize, Deserialize, Debug, Clone)]
struct Team {
    id: u64,
    name: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct Member {
    id: u64,
    login: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct PublicKey {
    id: u64,
    key: String,
}

#[derive(Debug)]
enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error),
}

impl From<serde_json::Error> for CliError {fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }}
impl From<reqwest::Error> for CliError {fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }}
impl From<std::io::Error> for CliError {fn from(err: std::io::Error) -> CliError { CliError::Io(err) }}

lazy_static! {
    static ref CLIENT:GithubClient = create_github_client(
        std::env::var("GHTEAMAUTH_CONFIG")
                 .unwrap_or(String::from("/etc/ghteam-auth.conf"))
                 .as_str()
    ).unwrap();
}

fn main() {

    let matches = App::new("ghteam-auth")
                      .version("0.1")
                      .author("Yasuyuki YAMADA <yasuyuki.ymd@gmail.com>")
                      .about("")
                      .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                      .subcommand(SubCommand::with_name("key")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(true)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("pam")
                                             .about("execute pam check"))
                      .subcommand(SubCommand::with_name("passwd")
                                             .about("get passwd"))
                      .subcommand(SubCommand::with_name("shadow")
                                             .about("get shadow"))
                      .subcommand(SubCommand::with_name("group")
                                             .about("get group"))
                      .subcommand(SubCommand::with_name("refresh")
                                             .about("refresh cache"))
                      .get_matches();

    if let Some(matches) = matches.subcommand_matches("key") {
        CLIENT.print_user_public_key(matches.value_of("USER").unwrap()).unwrap();
    } else if let Some(_) = matches.subcommand_matches("passwd") {
        CLIENT.print_passwd().unwrap();
    } else if let Some(_) = matches.subcommand_matches("shadow") {
        CLIENT.print_shadow().unwrap();
    } else if let Some(_) = matches.subcommand_matches("group") {
        CLIENT.print_group().unwrap();
    } else if let Some(_) = matches.subcommand_matches("refresh") {
        CLIENT.clear_all_caches().unwrap();
    } else if let Some(_) = matches.subcommand_matches("pam") {
        match std::env::var("PAM_USER") {
            Ok(user) => {
                if CLIENT.check_pam(&user).unwrap() { std::process::exit(0); }
                else { std::process::exit(1) }
            }
            Err(e) => println!("couldn't interpret PAM_USER: {}", e),
        }
    }

}

fn create_github_client( configpath: &str ) -> Result<GithubClient, CliError> {
    let mut file = File::open(configpath)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let config = toml::from_str::<Config>(contents.as_str()).unwrap();
    match std::env::var("SSL_CERT_FILE") {
        Ok(_) => (),
        Err(_) => std::env::set_var("SSL_CERT_FILE", config.cert_path.clone()),
    }
    let client = reqwest::Client::new()?;
    Ok(GithubClient::new(client, config))
}

struct GithubClient {
    client: reqwest::Client,
    conf: Config
}

impl GithubClient {
    fn new(client:reqwest::Client, conf:Config) -> GithubClient {
        GithubClient {client:client, conf:conf}
    }

    fn load_content_from_cache(&self, url:&String) -> Result<(std::fs::Metadata,String),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata,content))
    }

    fn store_content_to_cache(&self, url:&String, content:&String) -> Result<(),CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url.as_str());
        let mut dirpath = path.clone(); dirpath.pop();
        std::fs::create_dir_all(dirpath)?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url:&String) -> Result<String,CliError> {
        let token = String::from("token ") + self.conf.token.clone().as_str();
        let res = self.client.get(url.as_str()).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        self.store_content_to_cache(url,&content)?;
        Ok(content)
    }

    fn get_content(&self, url:&String) -> Result<String,CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata,cache_content)) => {
                match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                    Ok(caching_duration) => {
                        if caching_duration.as_secs() > self.conf.cache_duration {
                            match self.get_content_from_url(url) {
                                Ok(content_from_url) => Ok(content_from_url),
                                Err(_) => Ok(cache_content)
                            }
                        } else {
                            Ok(cache_content)
                        }
                    },
                    Err(_) => {
                        Ok(cache_content)
                    }
                }
            },
            Err(_) => self.get_content_from_url(url)
        }
    }

    fn print_user_public_key(&self, user:&str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user:&str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url);
        let keys = serde_json::from_str::<Vec<PublicKey>>(content?.as_str())?;
        Ok(keys.iter().map(|k|{k.key.clone()}).collect::<Vec<String>>().join("\n"))
    }

    fn check_pam(&self, user:&String) -> Result<bool, CliError> {
        let (_,members) = self.get_team_members()?;
        Ok(members.contains_key(user))
    }

    fn print_passwd(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_passwd_line(&member));
        }
        Ok(())
    }

    fn print_shadow(&self) -> Result<(), CliError> {
        let (_,members) = self.get_team_members()?;
        for member in members.values() {
            println!("{}", self.create_shadow_line(&member));
        }
        Ok(())
    }

    fn print_group(&self) -> Result<(), CliError> {
        let (team,members) = self.get_team_members()?;
        println!("{}", self.create_group_line(&team.name, self.conf.gid, &members));
        Ok(())
    }

    fn create_passwd_line(&self, member:&Member) -> String {
        format!("{login}:x:{uid}:{gid}:user@{org}:{home}:{sh}",
                login=member.login,
                uid=member.id,
                gid=self.conf.gid,
                org=self.conf.org,
                home=self.conf.home.replace("{}",member.login.as_str()),
                sh=self.conf.sh,
                )
    }

    fn create_shadow_line(&self, member:&Member) -> String {
        format!("{login}:!!:::::::", login=member.login )
    }

    fn create_group_line(&self, name:&String, id:u64, members:&HashMap<String,Member>) -> String {
        format!("{name}:x:{id}:{members}", name=name, id=id,
                members=members.values().map(|m|{m.login.clone()}).collect::<Vec<String>>().join(","))
    }

    fn get_team_members(&self) -> Result<(Team,HashMap<String,Member>),CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            Ok((team.clone(),self.get_members(team.id)?))
        } else {
            Err(CliError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "Team not found")))
        }
    }

    fn get_teams(&self) -> Result<HashMap<String,Team>, CliError> {
        let url = format!("{}/orgs/{}/teams",self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(content.as_str())?;
        let mut team_map = HashMap::new();
        for team in teams { team_map.insert(team.name.clone(), team); }
        Ok(team_map)
    }

    fn get_members(&self, mid:u64) -> Result<HashMap<String,Member>, CliError> {
        let url = format!("{}/teams/{}/members",self.conf.endpoint.clone(), mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(content.as_str())?;
        let mut member_map = HashMap::new();
        for member in members { member_map.insert(member.login.clone(), member); }
        Ok(member_map)
    }

    fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push("**");
        path.push("*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {if path.is_file() {std::fs::remove_file(path)?}},
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }

}

#[repr(C)]
#[allow(missing_copy_implementations)]
pub struct Passwd {
    name:   *mut libc::c_char,
    pass:   *mut libc::c_char,
    uid:    libc::uid_t,
    gid:    libc::gid_t,
    gecos:  *mut libc::c_char,
    home:   *mut libc::c_char,
    sh:     *mut libc::c_char,
}
<|endoftext|>


<commit_before>#![feature(io, os)]

extern crate docopt;
extern crate iron;
extern crate "rustc-serialize" as rustc_serialize;

use std::old_io::net::ip::Ipv4Addr;

use docopt::Docopt;

use iron::mime::Mime;
use iron::prelude::*;
use iron::status;

static USAGE: &'static str = "
Usage: rustwarsaw [options]

Options:
    -p <port>, --port <port>  Port number [default: 3000]
    -h, --help  Show help and exit
";

#[derive(RustcDecodable)]
struct Args {
    flag_port: u16,
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.help(true).argv(std::os::args().into_iter()).decode())
        .unwrap_or_else(|e| e.exit());
    println!("Starting server on port {}", args.flag_port);
    Iron::new(|&: _: &mut Request| {
        let content_type: Mime = "text/html".parse().unwrap();
        Ok(Response::with((status::Ok, "Hello Rust Warsaw!")).set(content_type))
    }).listen((Ipv4Addr(127, 0, 0, 1), args.flag_port)).unwrap();
}
<commit_msg>Rely on docopt defaults for argv input.<commit_after>#![feature(io)]

extern crate docopt;
extern crate iron;
extern crate "rustc-serialize" as rustc_serialize;

use std::old_io::net::ip::Ipv4Addr;

use docopt::Docopt;

use iron::mime::Mime;
use iron::prelude::*;
use iron::status;

static USAGE: &'static str = "
Usage: rustwarsaw [options]

Options:
    -p <port>, --port <port>  Port number [default: 3000]
    -h, --help  Show help and exit
";

#[derive(RustcDecodable)]
struct Args {
    flag_port: u16,
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.help(true).decode())
        .unwrap_or_else(|e| e.exit());
    println!("Starting server on port {}", args.flag_port);
    Iron::new(|&: _: &mut Request| {
        let content_type: Mime = "text/html".parse().unwrap();
        Ok(Response::with((status::Ok, "Hello Rust Warsaw!")).set(content_type))
    }).listen((Ipv4Addr(127, 0, 0, 1), args.flag_port)).unwrap();
}
<|endoftext|>


<commit_before>extern crate rustyprefetch;
extern crate clap;
use rustyprefetch::librp;
use clap::{App, Arg};

fn main() {
    let prefetch_arg = Arg::with_name("prefetch")
        .short("p")
        .long("prefetch")
        .value_name("FILE")
        .help("The Prefetch file to decode")
        .required(true)
        .takes_value(true);

    let options = App::new("RustyPrefetch")
        .version("0.0.0")
        .author("Matthew Seyer <matthew.seyer@gmail.com>")
        .about("Parse prefetch.")
        .arg(prefetch_arg)
        .get_matches();

    let filename = options.value_of("prefetch").unwrap();

    // Check if file is a prefetch file
    let mut prefetch_file = match librp::prefetch::PrefetchFile::new(filename){
        Ok(prefetch_file) => prefetch_file,
        Err(error) => panic!(error)
    };

    let prefetch_header = match prefetch_file.get_file_header() {
        Ok(prefetch_header) => prefetch_header,
        Err(error) => panic!(error)
    };

    println!("prefetch_header: {:?}",prefetch_header);

    println!("Finished");
}
<commit_msg>updated for new changes<commit_after>extern crate rustyprefetch;
extern crate clap;
use rustyprefetch::librp;
use clap::{App, Arg};

fn main() {
    let prefetch_arg = Arg::with_name("prefetch")
        .short("p")
        .long("prefetch")
        .value_name("FILE")
        .help("The Prefetch file to decode")
        .required(true)
        .takes_value(true);

    let options = App::new("RustyPrefetch")
        .version("0.0.0")
        .author("Matthew Seyer <matthew.seyer@gmail.com>")
        .about("Parse prefetch.")
        .arg(prefetch_arg)
        .get_matches();

    let filename = options.value_of("prefetch").unwrap();

    // Check if file is a prefetch file
    let mut prefetch_file = match librp::prefetch::PrefetchHandle::new(filename){
        Ok(prefetch_file) => prefetch_file,
        Err(error) => panic!(error)
    };

    let mut prefetch = match prefetch_file.get_prefetch(){
        Ok(prefetch) => prefetch,
        Err(error) => panic!(error)
    };

    println!("prefetch: {:?}",prefetch);

    println!("Finished");
}
<|endoftext|>