<commit_before>#![feature(plugin)]
#![plugin(docopt_macros)]

#![feature(test)]

extern crate jsrs_common;
extern crate jsrs_parser;
extern crate french_press;

extern crate walkdir;
extern crate unescape;
extern crate rustyline;
extern crate rustc_serialize;
extern crate docopt;

extern crate test;

mod js_error;
mod var;

mod eval;
mod native;
mod number;
mod bench;
mod preprocess;

use std::cell::RefCell;
use std::fs::{File, metadata};
use std::io::{self, BufReader};
use std::io::prelude::*;
use std::path::Path;
use std::process::exit;
use std::rc::Rc;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use walkdir::WalkDir;

use jsrs_common::types::js_var::JsPtrEnum;
use french_press::{init_gc, ScopeManager};

use eval::eval_string;
use native::add_pervasives;
use preprocess::{clean_string, add_semicolon};
use js_error::JsError;

docopt!(Args derive Debug, "
js.rs - a javascript interpreter

Usage:
jsrs
jsrs <file>
jsrs --test
");

fn eval_file(filename: String, debug: bool, should_repl: bool,
             scope_manager: Rc<RefCell<ScopeManager>>) -> js_error::Result<()> {
    if debug {
        println!("Reading from \"{}\"", filename);
    }

    let path = Path::new(&filename);
    let file = File::open(&path)
        .expect(&format!("Cannot open \"{}\": no such file or directory", filename));
    let file_buffer = BufReader::new(file);

    let mut line_builder = String::new();
    let mut braces = Vec::new();

    let mut file_iter = file_buffer.lines();
    let mut negative_test = false;
    loop {
        if let Some(line) = file_iter.next() {
            let input = String::from(line.expect(&format!("Cannot read from {}", filename)));
            let input = clean_string(input);
            if input.contains("@negative") {
                negative_test = true;
            }

            let mut last = '\0';
            // Match braces to see if we should wait for more input
            for c in input.chars() {
                if c == '(' {
                    braces.push('(');
                } else if c == '{' {
                    braces.push('{');
                } else if c == '*' && last == '/' {
                    braces.push('/');
                } else if c == ')' {
                    if braces.pop() != Some('(') {
                        return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                    }
                } else if c == '}' {
                    if braces.pop() != Some('{') {
                        return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                    }
                } else if c == '/' && last == '*' {
                    if braces.pop() != Some('/') {
                        return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                    }
                }
                last = c;
            }

            line_builder.push_str(&input);

            if braces.len() == 0 {
                let js_string = clean_string(line_builder.clone());
                line_builder = String::new();
                if js_string == "" {
                    continue;
                }

                if debug {
                    println!("\n{}", js_string);
                }

                let ret;
                let eval_result = eval_string(&js_string, scope_manager.clone());
                if negative_test {
                    match eval_result {
                        Ok((var, ptr)) => ret = (var, ptr),
                        Err(e) => {
                            if !e.is_meta_error() {
                                continue;
                            } else {
                                return Err(e);
                            }
                        }
                    }
                } else {
                    ret = try!(eval_result);
                }

                if debug {
                    println!("=> {:?}", ret);
                }
            }
        } else {
            break;
        }
    }
    if should_repl {
        repl(scope_manager);
    }
    Ok(())
}

fn test_dir(dir_name: String) {
    for entry in WalkDir::new(dir_name) {
        let entry = entry.unwrap();
        if !entry.path().is_dir() {
            let entry_path = entry.path().display().to_string();
            let scope_manager = Rc::new(RefCell::new(init_gc()));
            add_pervasives(scope_manager.clone());
            match eval_file(entry_path.clone(), false, false, scope_manager.clone()) {
                Ok(_) => {
                    println!("{}: {}", entry_path, "OK");
                }
                Err(e) => {
                    println!("{}: {}", entry_path, e);
                }
            }
        }
    }
}

fn repl(scope_manager: Rc<RefCell<ScopeManager>>) -> i32 {
    let mut rl = Editor::new();
    let mut stderr = io::stderr();

    if metadata(".history").is_ok() && rl.load_history(".history").is_err() {
        writeln!(stderr, "Error: unable to load history on startup").unwrap();
    }

    loop {
        // prompt
        let readline = rl.readline(">> ");

        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                let input = add_semicolon(clean_string(String::from(line)));
                if input == "" {
                    continue;
                }
                rl.add_history_entry(&input);

                match eval_string(&input, scope_manager.clone()) {
                    Ok((var, ptr)) => {
                        match ptr {
                            Some(JsPtrEnum::JsSym(s)) => println!("=> Symbol({:?})", s),
                            Some(JsPtrEnum::JsStr(s)) => println!("=> {:?}", s.text),
                            _ => println!("=> {:?}", var.t),
                        }
                    },
                    Err(e) => {
                        println!("{:?}", e);
                    }
                }
            },
            Err(ReadlineError::Interrupted) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 1;
            },
            Err(ReadlineError::Eof) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);

                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 3;
            }
        }
    }
    0
}

fn main() {
    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
    if args.flag_test {
        let dir_name = "sputnik";

        test_dir(String::from(dir_name))
    } else {
        let scope_manager = Rc::new(RefCell::new(init_gc()));
        add_pervasives(scope_manager.clone());

        if args.arg_file == "" {
            let ret = repl(scope_manager.clone());
            exit(ret)
        } else {
            eval_file(args.arg_file, true, true, scope_manager.clone())
                .expect("Error evaluating file");
        }
    }
}
<commit_msg>make value printouts in repl better<commit_after>#![feature(plugin)]
#![plugin(docopt_macros)]

#![feature(test)]

extern crate jsrs_common;
extern crate jsrs_parser;
extern crate french_press;

extern crate walkdir;
extern crate unescape;
extern crate rustyline;
extern crate rustc_serialize;
extern crate docopt;

extern crate test;

mod js_error;
mod var;

mod eval;
mod native;
mod number;
mod bench;
mod preprocess;

use std::cell::RefCell;
use std::fs::{File, metadata};
use std::io::{self, BufReader};
use std::io::prelude::*;
use std::path::Path;
use std::process::exit;
use std::rc::Rc;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use walkdir::WalkDir;

use jsrs_common::types::coerce::AsString;
use jsrs_common::types::js_var::JsPtrEnum;
use french_press::{init_gc, ScopeManager};

use eval::eval_string;
use native::add_pervasives;
use preprocess::{clean_string, add_semicolon};
use js_error::JsError;

docopt!(Args derive Debug, "
js.rs - a javascript interpreter

Usage:
jsrs
jsrs <file>
jsrs --test
");

fn eval_file(filename: String, debug: bool, should_repl: bool,
             scope_manager: Rc<RefCell<ScopeManager>>) -> js_error::Result<()> {
    if debug {
        println!("Reading from \"{}\"", filename);
    }

    let path = Path::new(&filename);
    let file = File::open(&path)
        .expect(&format!("Cannot open \"{}\": no such file or directory", filename));
    let file_buffer = BufReader::new(file);

    let mut line_builder = String::new();
    let mut braces = Vec::new();

    let mut file_iter = file_buffer.lines();
    let mut negative_test = false;
    loop {
        if let Some(line) = file_iter.next() {
            let input = String::from(line.expect(&format!("Cannot read from {}", filename)));
            let input = clean_string(input);
            if input.contains("@negative") {
                negative_test = true;
            }

            let mut last = '\0';
            // Match braces to see if we should wait for more input
            for c in input.chars() {
                if c == '(' {
                    braces.push('(');
                } else if c == '{' {
                    braces.push('{');
                } else if c == '*' && last == '/' {
                    braces.push('/');
                } else if c == ')' {
                    if braces.pop() != Some('(') {
                        return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                    }
                } else if c == '}' {
                    if braces.pop() != Some('{') {
                        return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                    }
                } else if c == '/' && last == '*' {
                    if braces.pop() != Some('/') {
                        return Err(JsError::ParseError(format!("Unexpected token {}", c)));
                    }
                }
                last = c;
            }

            line_builder.push_str(&input);

            if braces.len() == 0 {
                let js_string = clean_string(line_builder.clone());
                line_builder = String::new();
                if js_string == "" {
                    continue;
                }

                if debug {
                    println!("\n{}", js_string);
                }

                let ret;
                let eval_result = eval_string(&js_string, scope_manager.clone());
                if negative_test {
                    match eval_result {
                        Ok((var, ptr)) => ret = (var, ptr),
                        Err(e) => {
                            if !e.is_meta_error() {
                                continue;
                            } else {
                                return Err(e);
                            }
                        }
                    }
                } else {
                    ret = try!(eval_result);
                }

                if debug {
                    println!("=> {:?}", ret);
                }
            }
        } else {
            break;
        }
    }
    if should_repl {
        repl(scope_manager);
    }
    Ok(())
}

fn test_dir(dir_name: String) {
    for entry in WalkDir::new(dir_name) {
        let entry = entry.unwrap();
        if !entry.path().is_dir() {
            let entry_path = entry.path().display().to_string();
            let scope_manager = Rc::new(RefCell::new(init_gc()));
            add_pervasives(scope_manager.clone());
            match eval_file(entry_path.clone(), false, false, scope_manager.clone()) {
                Ok(_) => {
                    println!("{}: {}", entry_path, "OK");
                }
                Err(e) => {
                    println!("{}: {}", entry_path, e);
                }
            }
        }
    }
}

fn repl(scope_manager: Rc<RefCell<ScopeManager>>) -> i32 {
    let mut rl = Editor::new();
    let mut stderr = io::stderr();

    if metadata(".history").is_ok() && rl.load_history(".history").is_err() {
        writeln!(stderr, "Error: unable to load history on startup").unwrap();
    }

    loop {
        // prompt
        let readline = rl.readline(">> ");

        match readline {
            Ok(line) => {
                rl.add_history_entry(&line);
                let input = add_semicolon(clean_string(String::from(line)));
                if input == "" {
                    continue;
                }
                rl.add_history_entry(&input);

                match eval_string(&input, scope_manager.clone()) {
                    Ok((var, ptr)) => println!("{}", ptr.map(|p| p.as_string()).unwrap_or(var.t.as_string())),
                    Err(e) => println!("{:?}", e),
                }
            },
            Err(ReadlineError::Interrupted) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 1;
            },
            Err(ReadlineError::Eof) => {
                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                break
            },
            Err(err) => {
                println!("Error: {:?}", err);

                if rl.save_history(".history").is_err() {
                    writeln!(stderr, "Error: unable to save history on exit").unwrap();
                    return 2;
                }
                return 3;
            }
        }
    }
    0
}

fn main() {
    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
    if args.flag_test {
        let dir_name = "sputnik";

        test_dir(String::from(dir_name))
    } else {
        let scope_manager = Rc::new(RefCell::new(init_gc()));
        add_pervasives(scope_manager.clone());

        if args.arg_file == "" {
            let ret = repl(scope_manager.clone());
            exit(ret)
        } else {
            eval_file(args.arg_file, true, true, scope_manager.clone())
                .expect("Error evaluating file");
        }
    }
}
<|endoftext|>


<commit_before>#![allow(unknown_lints)]
#![allow(unused_doc_comment)]
#![cfg_attr(feature="clippy", feature(plugin))]

extern crate amy;
extern crate byteorder;
extern crate rand;
extern crate sha1;
extern crate url;
#[macro_use]
extern crate lazy_static;
extern crate net2;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate bincode;
extern crate toml;
extern crate ctrlc;
#[macro_use]
extern crate error_chain;
extern crate c_ares;
extern crate httparse;
extern crate base64;
extern crate base32;
extern crate shellexpand;
extern crate chrono;
extern crate openssl;

// TODO: Get rid of this
extern crate num;

#[macro_use]
mod log;

mod handle;
mod bencode;
mod torrent;
mod util;
mod socket;
mod disk;
mod tracker;
mod control;
mod listener;
mod rpc;
mod throttle;
mod config;

use std::{thread, process};
use std::sync::{atomic, mpsc};
use std::io;

// We need to do this for the log macros
use log::LogLevel;
use control::acio;

pub const DHT_EXT: (usize, u8) = (7, 1);
pub const EXT_PROTO: (usize, u8) = (5, 0x10);
pub const UT_META_ID: u8 = 9;

/// Throttler max token amount
pub const THROT_TOKS: usize = 2 * 1024 * 1024;

lazy_static! {
    pub static ref SHUTDOWN: atomic::AtomicBool = {
        atomic::AtomicBool::new(false)
    };

    pub static ref CONFIG: config::Config = {
        if let Ok(cfg)  = config::ConfigFile::try_load() {
            info!("Loaded config file");
            config::Config::from_file(cfg)
        } else {
            info!("Using default config");
            Default::default()
        }
    };

    pub static ref PEER_ID: [u8; 20] = {
        use rand::{self, Rng};

        let mut pid = [0u8; 20];
        let prefix = b"-SY0001-";
        for i in 0..prefix.len() {
            pid[i] = prefix[i];
        }

        let mut rng = rand::thread_rng();
        for i in prefix.len()..20 {
            pid[i] = rng.gen::<u8>();
        }
        pid
    };
}

fn init() -> io::Result<Vec<thread::JoinHandle<()>>> {
    let cpoll = amy::Poller::new()?;
    let mut creg = cpoll.get_registrar()?;
    let (dh, dhj) = disk::start(&mut creg)?;
    let (lh, lhj) = listener::Listener::start(&mut creg)?;
    let (rh, rhj) = rpc::RPC::start(&mut creg)?;
    let (th, thj) = tracker::Tracker::start(&mut creg)?;
    let chans = acio::ACChans {
        disk_tx: dh.tx,
        disk_rx: dh.rx,
        rpc_tx: rh.tx,
        rpc_rx: rh.rx,
        trk_tx: th.tx,
        trk_rx: th.rx,
        lst_tx: lh.tx,
        lst_rx: lh.rx,
    };
    let (tx, rx) = mpsc::channel();
    let chj = thread::spawn(move || {
        let throttler = throttle::Throttler::new(None, None, THROT_TOKS, &creg);
        let acio = acio::ACIO::new(cpoll, creg, chans);
        match control::Control::new(acio, throttler) {
            Ok(mut c) => {
                tx.send(Ok(())).unwrap();
                c.run();
            }
            Err(e) => {
                tx.send(Err(e)).unwrap();
            }
        }
    });
    rx.recv().unwrap()?;

    ctrlc::set_handler(|| if SHUTDOWN.load(atomic::Ordering::SeqCst) {
        process::abort();
    } else {
        info!("Trigggering shutdown via signal!");
        SHUTDOWN.store(true, atomic::Ordering::SeqCst);
    }).map_err(|_| util::io_err_val("Signal installation failed!"))?;

    Ok(vec![chj, dhj, lhj, rhj, thj])
}

fn main() {
    if cfg!(debug_assertions) {
        log::log_init(log::LogLevel::Debug);
    } else {
        log::log_init(log::LogLevel::Info);
    }
    info!("Initializing");
    match init() {
        Ok(threads) => {
            info!("Initialized");
            for thread in threads {
                if let Err(_) = thread.join() {
                    error!("Unclean shutdown detected, terminating");
                    return;
                }
            }
            info!("Shutdown complete");
        }
        Err(e) => {
            error!("Couldn't initialize synapse: {}", e);
        }
    }
}
<commit_msg>Update SIGINT shutdown messages<commit_after>#![allow(unknown_lints)]
#![allow(unused_doc_comment)]
#![cfg_attr(feature="clippy", feature(plugin))]

extern crate amy;
extern crate byteorder;
extern crate rand;
extern crate sha1;
extern crate url;
#[macro_use]
extern crate lazy_static;
extern crate net2;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate bincode;
extern crate toml;
extern crate ctrlc;
#[macro_use]
extern crate error_chain;
extern crate c_ares;
extern crate httparse;
extern crate base64;
extern crate base32;
extern crate shellexpand;
extern crate chrono;
extern crate openssl;

// TODO: Get rid of this
extern crate num;

#[macro_use]
mod log;

mod handle;
mod bencode;
mod torrent;
mod util;
mod socket;
mod disk;
mod tracker;
mod control;
mod listener;
mod rpc;
mod throttle;
mod config;

use std::{thread, process};
use std::sync::{atomic, mpsc};
use std::io;

// We need to do this for the log macros
use log::LogLevel;
use control::acio;

pub const DHT_EXT: (usize, u8) = (7, 1);
pub const EXT_PROTO: (usize, u8) = (5, 0x10);
pub const UT_META_ID: u8 = 9;

/// Throttler max token amount
pub const THROT_TOKS: usize = 2 * 1024 * 1024;

lazy_static! {
    pub static ref SHUTDOWN: atomic::AtomicBool = {
        atomic::AtomicBool::new(false)
    };

    pub static ref CONFIG: config::Config = {
        if let Ok(cfg)  = config::ConfigFile::try_load() {
            info!("Loaded config file");
            config::Config::from_file(cfg)
        } else {
            info!("Using default config");
            Default::default()
        }
    };

    pub static ref PEER_ID: [u8; 20] = {
        use rand::{self, Rng};

        let mut pid = [0u8; 20];
        let prefix = b"-SY0001-";
        for i in 0..prefix.len() {
            pid[i] = prefix[i];
        }

        let mut rng = rand::thread_rng();
        for i in prefix.len()..20 {
            pid[i] = rng.gen::<u8>();
        }
        pid
    };
}

fn init() -> io::Result<Vec<thread::JoinHandle<()>>> {
    let cpoll = amy::Poller::new()?;
    let mut creg = cpoll.get_registrar()?;
    let (dh, dhj) = disk::start(&mut creg)?;
    let (lh, lhj) = listener::Listener::start(&mut creg)?;
    let (rh, rhj) = rpc::RPC::start(&mut creg)?;
    let (th, thj) = tracker::Tracker::start(&mut creg)?;
    let chans = acio::ACChans {
        disk_tx: dh.tx,
        disk_rx: dh.rx,
        rpc_tx: rh.tx,
        rpc_rx: rh.rx,
        trk_tx: th.tx,
        trk_rx: th.rx,
        lst_tx: lh.tx,
        lst_rx: lh.rx,
    };
    let (tx, rx) = mpsc::channel();
    let chj = thread::spawn(move || {
        let throttler = throttle::Throttler::new(None, None, THROT_TOKS, &creg);
        let acio = acio::ACIO::new(cpoll, creg, chans);
        match control::Control::new(acio, throttler) {
            Ok(mut c) => {
                tx.send(Ok(())).unwrap();
                c.run();
            }
            Err(e) => {
                tx.send(Err(e)).unwrap();
            }
        }
    });
    rx.recv().unwrap()?;

    ctrlc::set_handler(|| if SHUTDOWN.load(atomic::Ordering::SeqCst) {
        info!("Shutting down immediately!");
        process::abort();
    } else {
        info!("Caught SIGINT, shutting down cleanly. Interrupt again to shut down immediately.");
        SHUTDOWN.store(true, atomic::Ordering::SeqCst);
    }).map_err(|_| util::io_err_val("Signal installation failed!"))?;

    Ok(vec![chj, dhj, lhj, rhj, thj])
}

fn main() {
    if cfg!(debug_assertions) {
        log::log_init(log::LogLevel::Debug);
    } else {
        log::log_init(log::LogLevel::Info);
    }
    info!("Initializing");
    match init() {
        Ok(threads) => {
            info!("Initialized");
            for thread in threads {
                if let Err(_) = thread.join() {
                    error!("Unclean shutdown detected, terminating");
                    return;
                }
            }
            info!("Shutdown complete");
        }
        Err(e) => {
            error!("Couldn't initialize synapse: {}", e);
        }
    }
}
<|endoftext|>


<commit_before>pub mod block;
pub mod board;
pub mod coordinate;

use block::Block;
use block::Tetromino;
use board::Board;

fn main() {
    // TODO: make it a dynamic board
    let r = 4;
    let c = 7;

    let mut board = Board::new(r, c);

    let coordinate = coordinate::Coordinate::new(3, 4);

    // Test data
    let blocks: Vec<Block> = vec![
        Block::new(Tetromino::I),
        Block::new(Tetromino::I),
        Block::new(Tetromino::T),
        Block::new(Tetromino::T),
        Block::new(Tetromino::S),
        Block::new(Tetromino::Z),
        Block::new(Tetromino::L),
        Block::new(Tetromino::O),
        Block::new(Tetromino::J)
    ];

    let x = &blocks[6];

    // test matching
    match x {
        &Block { shape: Tetromino::I, .. } => println!("I shape"),
        &Block { shape: Tetromino::O, .. } => println!("O shape"),
        &Block { shape: Tetromino::T, .. } => println!("T shape"),
        &Block { shape: Tetromino::J, .. } => println!("J shape"),
        &Block { shape: Tetromino::L, .. } => println!("L shape"),
        &Block { shape: Tetromino::S, .. } => println!("S shape"),
        &Block { shape: Tetromino::Z, .. } => println!("Z shape"),
    }

    // TODO: put in a printing function
    for row in board.squares {
        for col in row {
            match col.filled {
                true => print!("[X]"),
                false => print!("[ ]")
            }
        }
        println!("");
    }

}

<commit_msg>Initialize board dynamically<commit_after>pub mod block;
pub mod board;
pub mod coordinate;

use block::Block;
use block::Tetromino;
use board::Board;
use std::io;

fn main() {

    let board = init_board();

    let coordinate = coordinate::Coordinate::new(3, 4);

    // Test data
    let blocks: Vec<Block> = vec![
        Block::new(Tetromino::I),
        Block::new(Tetromino::I),
        Block::new(Tetromino::T),
        Block::new(Tetromino::T),
        Block::new(Tetromino::S),
        Block::new(Tetromino::Z),
        Block::new(Tetromino::L),
        Block::new(Tetromino::O),
        Block::new(Tetromino::J)
    ];

    let x = &blocks[6];

    // test matching
    match x {
        &Block { shape: Tetromino::I, .. } => println!("I shape"),
        &Block { shape: Tetromino::O, .. } => println!("O shape"),
        &Block { shape: Tetromino::T, .. } => println!("T shape"),
        &Block { shape: Tetromino::J, .. } => println!("J shape"),
        &Block { shape: Tetromino::L, .. } => println!("L shape"),
        &Block { shape: Tetromino::S, .. } => println!("S shape"),
        &Block { shape: Tetromino::Z, .. } => println!("Z shape"),
    }

    // TODO: put in a printing function
    for row in board.squares {
        for col in row {
            match col.filled {
                true => print!("[X]"),
                false => print!("[ ]")
            }
        }
        println!("");
    }

}

fn init_board() -> Board {
    let mut row = String::new();
    let mut column = String::new();

    println!("Enter number of rows:");

    io::stdin().read_line(&mut row)
        .ok()
        .expect("Failed to read line!");

    let row: i32 = row.trim().parse()
        .ok()
        .expect("Please type a number!");

    println!("Enter number of columns:");

    io::stdin().read_line(&mut column)
        .ok()
        .expect("Failed to read line!");

    let column: i32 = column.trim().parse()
        .ok()
        .expect("Please type a number!");

    return Board::new(row, column);
}

<|endoftext|>


<commit_before>#![forbid(unsafe_code)]

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

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

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

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

#[derive(Parser)]
struct Opt {
    #[clap(long, default_value = "127.0.0.1:9000")]
    bind: SocketAddr,
    #[clap(long, default_value = "_db")]
    db: PathBuf,
    #[clap(long)]
    cors: bool,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

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

    let opt = Opt::parse();

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

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

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

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

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

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

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

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

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

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

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

async fn personal(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PersonalQuery>,
) -> Result<NdJson<impl Stream<Item = ExplorerResponse>>, Error> {
    let player = UserId::from(query.player);
    let indexing = indexer.index_player(&player).await;

    let variant = query.variant.into();

    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = KeyBuilder::personal(&player, query.color).with_zobrist(variant, pos.zobrist_hash());

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

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

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

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

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

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

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

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

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

async fn masters(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<MastersQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => {
            VariantPosition::from_setup(Variant::Chess, &Fen::from(fen), CastlingMode::Chess960)?
        }
        None => VariantPosition::new(Variant::Chess),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;
    let key = KeyBuilder::masters().with_zobrist(Variant::Chess, pos.zobrist_hash());
    let masters_db = db.masters();
    let mut entry = masters_db
        .read(key, query.since, query.until)
        .expect("get masters")
        .prepare();

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

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

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

async fn lichess(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<LichessQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let variant = Variant::from(query.variant);
    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = KeyBuilder::lichess().with_zobrist(variant, pos.zobrist_hash());
    let lichess_db = db.lichess();
    let mut filtered = lichess_db
        .read_lichess(&key, query.filter.since, query.filter.until)
        .expect("get lichess")
        .prepare(&dbg!(query.filter));

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

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

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

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

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

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

#[derive(Parser)]
struct Opt {
    #[clap(long, default_value = "127.0.0.1:9000")]
    bind: SocketAddr,
    #[clap(long, default_value = "_db")]
    db: PathBuf,
    #[clap(long)]
    cors: bool,
    #[clap(flatten)]
    indexer: IndexerOpt,
}

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

    let opt = Opt::parse();

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

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

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

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

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

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

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

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

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

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

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

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

async fn personal(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Extension(indexer): Extension<IndexerStub>,
    Query(query): Query<PersonalQuery>,
) -> Result<NdJson<impl Stream<Item = ExplorerResponse>>, Error> {
    let player = UserId::from(query.player);
    let indexing = indexer.index_player(&player).await;

    let variant = query.variant.into();

    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = KeyBuilder::personal(&player, query.color).with_zobrist(variant, pos.zobrist_hash());

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

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

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

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

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

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

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

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

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

async fn masters(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<MastersQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => {
            VariantPosition::from_setup(Variant::Chess, &Fen::from(fen), CastlingMode::Chess960)?
        }
        None => VariantPosition::new(Variant::Chess),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;
    let key = KeyBuilder::masters().with_zobrist(Variant::Chess, pos.zobrist_hash());
    let masters_db = db.masters();
    let mut entry = masters_db
        .read(key, query.since, query.until)
        .expect("get masters")
        .prepare();

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

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

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

async fn lichess(
    Extension(openings): Extension<&'static Openings>,
    Extension(db): Extension<Arc<Database>>,
    Query(query): Query<LichessQuery>,
) -> Result<Json<ExplorerResponse>, Error> {
    let variant = Variant::from(query.variant);
    let mut pos = Zobrist::new(match query.fen {
        Some(fen) => VariantPosition::from_setup(variant, &Fen::from(fen), CastlingMode::Chess960)?,
        None => VariantPosition::new(variant),
    });

    let opening = openings.classify_and_play(&mut pos, query.play)?;

    let key = KeyBuilder::lichess().with_zobrist(variant, pos.zobrist_hash());
    let lichess_db = db.lichess();
    let mut filtered = lichess_db
        .read_lichess(&key, query.filter.since, query.filter.until)
        .expect("get lichess")
        .prepare(&dbg!(query.filter));

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

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


<commit_before>#![feature(core)]
#![feature(path)]

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

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            let output = find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
            make_output(output);
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {let (old, new) = (&pair[1], &pair[0]);
			let output = find_moves(&repo, old.clone(), new.clone()).unwrap();
			// mangle the vectors here to make pretty output
			make_output(output);
		}
	}
}

fn make_output(output: Vec<Output>) {
	println!("make_output:");
    println!("\toutput.len()={}", output.len());
    for i in range(0, output.len()) {
            println!("\told_commit={}",         output[i].old_commit);
            println!("\tnew_commit={}",         output[i].new_commit);
            println!("\torigin_line={}",        output[i].origin_line);
            println!("\tdestintation_line={}",  output[i].destination_line);
            println!("\tnum_lines={}",          output[i].num_lines);
    }
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, Error> {

    println!("\nFIND MOVES---------------------");

	let old_tree = try!(old.tree());
	let new_tree = try!(new.tree());
	// Build up a diff of the two trees.
	let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));
	// State. TODO: Don't just use a u64.
	//let moved = 0;

    //let mut results: Vec<Output> = Vec::new();

    let mut has_addition = false;
    let mut has_deletion = false;

	//let mut current_hunk: Vec<String> = Vec::new();
	// Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
	// It's a bit weird, but I think it will provide the necessary information.
	diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {
		// Thinking:
		//  * If is not a hunk, keep going.
		//  * If it's a hunk, do regex magic.
		//  * Stick regex output into a hashmap as a hash.
		//  * Later, we will iterate through and output pased on the values.
		// Filter out all the boring context lines.
		// If we're not interested in this line just return since it will iterate to the next.
		if maybe_hunk.is_none() { return true }; // Return early.

        // 'origin' is wrapped in pipes to ease displaying space characters.
        print!("line: old={:?} new={:?} offset={} |origin|=|{}|\n      content={}",
                 line.old_lineno(), line.new_lineno(), line.content_offset(),
                 line.origin(), str::from_utf8(line.content()).unwrap());

        println!("delta: nfiles={} status={:?} old_file=(id={} path_bytes={:?} path={:?} tsize={}) new_file=(id={} path_bytes={:?} path={:?} tsize={})",
                delta.nfiles(), delta.status(),
                delta.old_file().id(), delta.old_file().path_bytes(), delta.old_file().path(), delta.old_file().size(),
                delta.new_file().id(), delta.new_file().path_bytes(), delta.new_file().path(), delta.new_file().size());

		match line.origin() {
			// Context
			' ' | '=' => {
                print!("= {}", str::from_utf8(line.content()).unwrap());
                true
            },
			// Headers
			'F' | 'H' => {
                print!("F {}", str::from_utf8(line.content()).unwrap());
                true
            },
			// Additions
			'+' | '>' => {
				print!("+ {}", str::from_utf8(line.content()).unwrap());
                has_addition = true;
				true
			},
			// Deletions
			'-' | '<' => {
				print!("- {}", str::from_utf8(line.content()).unwrap());
                has_deletion = true;
				true
			},
			// Other (We don't care about these.)
			_         => {
                print!("_ {}", str::from_utf8(line.content()).unwrap());
                true
            }
		}
			// Look at that match statement, what a hunk. So hunky.
			// match maybe_hunk {
			//     Some(hunk) => unimplemented!(),
			//     None => unimplemented!(),
			// }
	});

    println!("\nhas_addition={}, has_deletion={}", has_addition, has_deletion);

   // if has_addition && has_deletion {
/*
        results.push(Output {
		    old_commit: old.id(),
		    new_commit: new.id(),
		    origin_line: 0,
		    destination_line: 0,
		    num_lines: 0
        });
*/
    //}

    if !has_addition || !has_deletion {
        println!("Does not have addition or deletion.");
        Ok(vec![])  // Return empty vector.
    } else {

	    Ok(vec![Output {
		    old_commit: old.id(),
		    new_commit: new.id(),
		    origin_line: 0,
		    destination_line: 0,
		    num_lines: 0
	    }])
    }

   // Ok(results)
}

#[derive(Show)]
struct Output {
    old_commit: Oid,
    new_commit: Oid,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}
<commit_msg>Rough implementation of collecting groupings of additions and deletions.<commit_after>#![feature(core)]
#![feature(path)]

#![feature(plugin)]
#![plugin(regex_macros)]
extern crate regex;
extern crate git2;
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use git2::{Repository, Branch, BranchType, DiffLine,
    Commit, Diff, DiffFormat, DiffDelta, DiffHunk, Error, Oid};
use docopt::Docopt;
use std::old_io as io;
use std::old_io::BufferedReader;
use std::old_io::File;
use std::str;
use regex::Regex;

// Write the Docopt usage string.
static USAGE: &'static str = "
Usage: transit <repo> [<old> <new>]

If no commits are given, transit will revwalk from latest to oldest.
";

#[derive(RustcDecodable, Debug)]
struct Args {
    arg_repo: String,
    arg_old: Option<String>,
    arg_new: Option<String>,
}

fn main() {
    // Parse the args above or die.
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());
    // Pull up the revwalk.
    let path = Path::new(args.arg_repo);
    let repo = Repository::discover(&path)
        .ok().expect("Unable to find repo.");
    // If we can destructures the two optional args into real things.
    if let (Some(old_string), Some(new_string)) = (args.arg_old, args.arg_new) {
        // Compare a specific commit pair.
        // Fist, get the commits. (Error checked)
        let old = Oid::from_str(&old_string[]).and_then(|oid| repo.find_commit(oid));
        let new = Oid::from_str(&new_string[]).and_then(|oid| repo.find_commit(oid));
        if old.is_ok() && new.is_ok() {
            let output = find_moves(&repo, &old.unwrap(), &new.unwrap()).unwrap();
            make_output(output);
        } else {
            panic!("Commit ids were not valid.");
        }
    } else {
        // Revwalk.
        let mut revwalk = repo.revwalk()
            .ok().expect("Unable to get revwalk.");
        // Setup some options.
        revwalk.simplify_first_parent(); // TODO: Maybe remove?
        let mut flags = git2::Sort::empty();
        flags.insert(git2::SORT_TIME);
        flags.insert(git2::SORT_TOPOLOGICAL);
        revwalk.set_sorting(flags);
        // Push HEAD to the revwalk.
        revwalk.push_head()
            .ok().expect("Unable to push HEAD.");
        // We sadly must collect here to use `.windows()`
        let history = revwalk.filter_map(|id| repo.find_commit(id).ok())
            .collect::<Vec<Commit>>();
        // Walk through each pair of commits.
        for pair in history.windows(2) {let (old, new) = (&pair[1], &pair[0]);
			let output = find_moves(&repo, old.clone(), new.clone()).unwrap();
			// mangle the vectors here to make pretty output
			make_output(output);
		}
	}
}

fn make_output(output: Vec<Output>) {
	println!("make_output:");
    println!("\toutput.len()={}", output.len());
    for i in range(0, output.len()) {
            println!("\told_commit={}",         output[i].old_commit);
            println!("\tnew_commit={}",         output[i].new_commit);
            println!("\torigin_line={}",        output[i].origin_line);
            println!("\tdestintation_line={}",  output[i].destination_line);
            println!("\tnum_lines={}",          output[i].num_lines);
    }
}
/*
fn to_key(s: String) -> String {
    let re = regex!(r"\s{2,}"); // 2 or more whitespaces
    //let re = regex::Regex::new(r"\s{2,}");
    re.replace_all(s.as_slice(), "")
}
*/

#[derive(Debug)]
enum State {
    Other, Addition, Deletion
}

//fn transition_from_add_or_del(state: State, added: String, deleted: String) {

//}

fn dump_diffdelta(delta: DiffDelta) {
    println!("delta: nfiles={} status={:?} old_file=(id={} path_bytes={:?} path={:?} tsize={}) new_file=(id={} path_bytes={:?} path={:?} tsize={})",
            delta.nfiles(), delta.status(),
            delta.old_file().id(), delta.old_file().path_bytes(), delta.old_file().path(), delta.old_file().size(),
            delta.new_file().id(), delta.new_file().path_bytes(), delta.new_file().path(), delta.new_file().size());
}

fn find_moves(repo: &Repository, old: &Commit, new: &Commit) -> Result<Vec<Output>, Error> {

    println!("\nFIND MOVES---------------------");

	let old_tree = try!(old.tree());
	let new_tree = try!(new.tree());
	// Build up a diff of the two trees.
	let diff = try!(Diff::tree_to_tree(repo, Some(&old_tree), Some(&new_tree), None));

    //let mut results: Vec<Output> = Vec::new();

    let mut state = State::Other;
    let mut added = String::new();
    let mut deleted = String::new();
    let mut has_addition = false;
    let mut has_deletion = false;

    let mut keys: Vec<String> = Vec::new();  // TODO Will become hashmap.

	//let mut current_hunk: Vec<String> = Vec::new();
	// Read about this function in http://alexcrichton.com/git2-rs/git2/struct.Diff.html#method.print
	// It's a bit weird, but I think it will provide the necessary information.
	diff.print(DiffFormat::Patch, |delta, maybe_hunk, line| -> bool {

		// Thinking:
		//  * If is not a hunk, keep going.
		//  * If it's a hunk, do regex magic.
		//  * Stick regex output into a hashmap as a hash.
		//  * Later, we will iterate through and output pased on the values.
		// Filter out all the boring context lines.
		// If we're not interested in this line just return since it will iterate to the next.
		if maybe_hunk.is_none() { return true }; // Return early.

        println!("top of loop: keys={:?}", keys);
        println!("top of loop: added={:?} deleted={:?}", added, deleted);

        // 'origin' is wrapped in pipes to ease displaying space characters.
        print!("line: old={:?} new={:?} offset={} |origin|=|{}|\n      content={}",
                 line.old_lineno(), line.new_lineno(), line.content_offset(),
                 line.origin(), str::from_utf8(line.content()).unwrap());

        //dump_diffdelta(delta);

        let re = regex!(r"\s{2,}"); // 2 or more whitespaces    // TODO Removes whitespace from a string.

		match line.origin() {
            /*
            // Context
			' ' | '=' => {
                print!("= {}", str::from_utf8(line.content()).unwrap());
                offset += 1;
                true
            },
			// Headers
			'F' | 'H' => {
                print!("F {}", str::from_utf8(line.content()).unwrap());
                offset += 1;
                true
            },
            */
			// Additions
			'+' | '>' => {
				print!("+ {}", str::from_utf8(line.content()).unwrap());

                added.push_str(str::from_utf8(line.content()).unwrap());
                has_addition = true;

                println!("In additions. state={:?}", state);

                match state {
                    State::Deletion => {
                        keys.push(re.replace_all(added.as_slice(), ""));
                        deleted = String::new();
                    },
                    _ => {}
                }

                state = State::Addition;
				true
			},
			// Deletions
			'-' | '<' => {
				print!("- {}", str::from_utf8(line.content()).unwrap());

                deleted.push_str(str::from_utf8(line.content()).unwrap());
                has_deletion = true;

                println!("In deletion. state={:?}", state);

                match state {
                    State::Addition => {
                        keys.push(re.replace_all(added.as_slice(), ""));
                        added = String::new();
                    },
                    _ => {}
                }

                state = State::Deletion;
				true
			},
			// Other (We don't care about these.)
			_         => {
                print!("_ {}", str::from_utf8(line.content()).unwrap());

                println!("in _. state={:?}", state);

                match state {
                    State::Addition => {
                        keys.push(re.replace_all(added.as_slice(), ""));
                        added = String::new();
                    },
                    State::Deletion => {
                        keys.push(re.replace_all(deleted.as_slice(), ""));
                        deleted = String::new();
                    },
                    State::Other => {}
                }

                state = State::Other;
                true
            }
		}

			// Look at that match statement, what a hunk. So hunky.
			// match maybe_hunk {
			//     Some(hunk) => unimplemented!(),
			//     None => unimplemented!(),
			// }
	});

    println!("\nhas_addition={}, has_deletion={}", has_addition, has_deletion);
    println!("KEYS={:?}", keys);
   // if has_addition && has_deletion {
/*
        results.push(Output {
		    old_commit: old.id(),
		    new_commit: new.id(),
		    origin_line: 0,
		    destination_line: 0,
		    num_lines: 0
        });
*/
    //}

    if !has_addition || !has_deletion {
        println!("Does not have addition or deletion.");
        Ok(vec![])  // Return empty vector.
    } else {

	    Ok(vec![Output {
		    old_commit: old.id(),
		    new_commit: new.id(),
		    origin_line: 0,
		    destination_line: 0,
		    num_lines: 0
	    }])
    }

   // Ok(results)
}

#[derive(Debug)]
struct Output {
    old_commit: Oid,
    new_commit: Oid,
    origin_line: u32,
    destination_line: u32,
    num_lines: u32,
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

use std::fmt;
use std::slice::Iter;

mod arguments;
mod utils;
mod entry;
mod root;

use root::Root;

use arguments::Options;
use arguments::Mode;

trait DisplayableEntry : fmt::Display + Sized {
    type Child: DisplayableEntry;

    fn size(&self) -> u64;
    fn name(&self) -> &String;
    fn children_iter(&self) -> Iter<Self::Child>;

    fn is_hidden(&self) -> bool {
        self.name().chars().nth(0) == Some('.')
    }
}

fn print_tree<T: DisplayableEntry>(entry: &T, options: &Options) {
    print_indented_tree(entry, options, 0);
}

fn print_indented_tree<T: DisplayableEntry>(entry: &T, options: &Options, level: usize) {
    println!("{0:1$}{2}", "", level * 2, entry);
    if options.depth_accepts(level) {
        let mut shown_entries = 0;

        for child in entry.children_iter() {
            if !options.should_show_hidden() && child.is_hidden() {
                continue;
            }

            print_indented_tree(child, options, level + 1);
            shown_entries += 1;

            if options.limit_reached(shown_entries) {
                break;
            }
        }
    }
}

fn work(root: &Root, options: &Options) {
    match options.mode() {
        &Mode::Tree => print_tree(root, options),
        &Mode::Files => panic!("Not yet implemented")
    }
}

fn main() {
    let options = arguments::parse();
    for root_path in options.roots() {
        match Root::for_path(&root_path) {
            Ok(root) => work(&root, &options),
            Err(message) =>
                println!("{}: {}", root_path.to_string_lossy(), message)
        }
    }
}
<commit_msg>Give the root away to the mode handler<commit_after>#[macro_use]
extern crate clap;

use std::fmt;
use std::slice::Iter;

mod arguments;
mod utils;
mod entry;
mod root;

use root::Root;

use arguments::Options;
use arguments::Mode;

trait DisplayableEntry : fmt::Display + Sized {
    type Child: DisplayableEntry;

    fn size(&self) -> u64;
    fn name(&self) -> &String;
    fn children_iter(&self) -> Iter<Self::Child>;

    fn is_hidden(&self) -> bool {
        self.name().chars().nth(0) == Some('.')
    }
}

fn print_tree<T: DisplayableEntry>(entry: T, options: &Options) {
    print_indented_tree(&entry, options, 0);
}

fn print_indented_tree<T: DisplayableEntry>(entry: &T, options: &Options, level: usize) {
    println!("{0:1$}{2}", "", level * 2, entry);
    if options.depth_accepts(level) {
        let mut shown_entries = 0;

        for child in entry.children_iter() {
            if !options.should_show_hidden() && child.is_hidden() {
                continue;
            }

            print_indented_tree(child, options, level + 1);
            shown_entries += 1;

            if options.limit_reached(shown_entries) {
                break;
            }
        }
    }
}

fn work(root: Root, options: &Options) {
    match options.mode() {
        &Mode::Tree => print_tree(root, options),
        &Mode::Files => panic!("Not yet implemented")
    }
}

fn main() {
    let options = arguments::parse();
    for root_path in options.roots() {
        match Root::for_path(&root_path) {
            Ok(root) => work(root, &options),
            Err(message) =>
                println!("{}: {}", root_path.to_string_lossy(), message)
        }
    }
}
<|endoftext|>


<commit_before>extern crate "rustc-serialize" as serialize;
extern crate handlebars;

use handlebars::Handlebars;
use serialize::json::Json;
use std::old_io as io;
use std::os;

fn main() {
   let template = match os::args().as_slice() {
      [_, ref bars] =>
          io::File::open(&Path::new(&bars))
            .ok()
            .expect("invalid path")
            .read_to_string()
            .ok()
            .expect("invalid file"),
           _ => "{{.}}".to_string()
   };
   let input = if io::stdio::stdin_raw().isatty() {
     "{}".to_string()
   } else {
     io::stdin()
       .read_line()
       .ok()
       .expect("json expected")
   };
   let json = Json::from_str(&input)
     .ok()
     .expect("malformed json");
   let mut handlebars = Handlebars::new();
   handlebars.register_template_string("t", template)
     .ok()
     .expect("expected template");
   println!("{}", handlebars.render("t", &json)
                  .ok().unwrap());
}
<commit_msg>somewhat better style<commit_after>extern crate "rustc-serialize" as serialize;
extern crate handlebars;

use handlebars::Handlebars;
use serialize::json::Json;
use std::old_io as io;
use std::os;

fn main() {
   let template = match os::args().as_slice() {
      [_, ref bars] =>
        io::File::open(&Path::new(&bars))
          .and_then(|mut f| f.read_to_string())
          .ok()
          .expect("invalid path"),
       _ =>
         "{{.}}".to_string()
   };
   let input = if io::stdio::stdin_raw().isatty() {
     "{}".to_string()
   } else {
     io::stdin()
       .read_line()
       .ok()
       .expect("json expected")
   };
   let json = Json::from_str(&input)
     .ok()
     .expect("malformed json");
   let mut handlebars = Handlebars::new();
   handlebars.register_template_string("t", template)
     .ok()
     .expect("expected template");
   println!("{}", handlebars.render("t", &json)
                  .ok().unwrap());
}
<|endoftext|>


<commit_before>use std::io::{BufferedReader, File, stdin};
use std::num::FromPrimitive;
use std::os;

static BUFFER_SIZE: uint = 100;

#[allow(non_camel_case_types)]
#[deriving(PartialEq, FromPrimitive, Show)]
enum Foptypes{
    GF_FOP_NULL=0,
    GF_FOP_MKNOD=3,
    GF_FOP_MKDIR=4,
    GF_FOP_UNLINK=5,
    GF_FOP_RMDIR=6,
    GF_FOP_SYMLINK=7,
    GF_FOP_RENAME=8,
    GF_FOP_LINK=9,
    GF_FOP_SETXATTR=17,
    GF_FOP_REMOVEXATTR=19,
    GF_FOP_CREATE=23,
    GF_FOP_SETATTR=38,
}

impl Foptypes{
    fn get(&self) -> &str{
        match *self{
            GF_FOP_NULL => "NULL",
            GF_FOP_MKNOD => "MKNOD",
            GF_FOP_MKDIR => "MKDIR",
            GF_FOP_UNLINK => "UNLINK",
            GF_FOP_RMDIR => "RMDIR",
            GF_FOP_SYMLINK => "SYMLINK",
            GF_FOP_RENAME => "RENAME",
            GF_FOP_LINK => "LINK",
            GF_FOP_SETXATTR => "SETXATTR",
            GF_FOP_REMOVEXATTR => "REMOVEXATTR",
            GF_FOP_CREATE => "CREATE",
            GF_FOP_SETATTR => "SETATTR"
        }
    }

    fn num_fields(&self) -> uint{
        match *self{
            GF_FOP_NULL => 3,
            GF_FOP_MKNOD => 7,
            GF_FOP_MKDIR => 7,
            GF_FOP_UNLINK => 4,
            GF_FOP_RMDIR => 4,
            GF_FOP_SYMLINK => 4,
            GF_FOP_RENAME => 5,
            GF_FOP_LINK => 4,
            GF_FOP_SETXATTR => 3,
            GF_FOP_REMOVEXATTR => 3,
            GF_FOP_CREATE => 7,
            GF_FOP_SETATTR => 3
        }
    }
}

#[deriving(Show)]
struct Parser{
    record_complete: bool,
    fop_type: char,
    token: String,
    tokens: Vec<String>,
    sep: char
}

impl Parser{
    fn new() -> Parser{
        Parser{record_complete: true,
               fop_type: '.',
               token: "".to_string(),
               tokens: Vec::new(),
               sep: '\x00'}
    }

    fn get_num_tokens(&self) -> uint {
        match self.fop_type{
            'E' => 7,
            'M' => 3,
            'D' => 2,
            _ => 0
        }
    }

    fn get_fop_enum(&self, v: String) -> Option<Foptypes>{
        let key:int = from_str::<int>(v.as_slice()).unwrap();
        let ty: Option<Foptypes> = FromPrimitive::from_int(key);
        return ty;
    }

    fn process_record(&self){
        let mut op: String = String::new();
        for (i, v) in self.tokens.iter().enumerate(){
            if (self.fop_type == 'E' || self.fop_type == 'M') && i == 2 {
                let ty: Option<Foptypes> = self.get_fop_enum(v.clone());
                match ty{
                    Some(aa) => {
                        op.push_str(aa.get());
                        op.push_str(" ");
                    },
                    None => {}
                }
            }
            else{
                op.push_str(v.as_slice());
                op.push_str(" ");
            }
        }
        println!("{}", op.as_slice().trim_chars(' '));
    }

    fn if_record_complete(&mut self){
        if self.token.as_slice() != ""{
            self.tokens.push(self.token.clone());
            self.token = "".to_string();
        }

        let mut num_tokens: uint = self.get_num_tokens();
        if (self.fop_type == 'E' || self.fop_type == 'M') && self.tokens.len() >= 3{
            let ty: Option<Foptypes> = self.get_fop_enum(self.tokens.get(2).clone());
            num_tokens = match ty{
                Some(aa) => aa.num_fields(),
                None => 0
            }
        }

        if num_tokens == self.tokens.len(){
            self.record_complete = true;
            self.process_record();
            self.tokens = Vec::new();
        }
    }

    fn parse_chunk(&mut self, chunk: &str){
        for (_, c) in chunk.chars().enumerate(){
            if self.record_complete && (c == 'E' || c == 'M' || c == 'D'){
                self.fop_type = c;
                self.tokens.push(String::from_char(1, c));
                self.record_complete = false;
                continue;
            }

            if c == self.sep{
                self.if_record_complete();
            }
            else{
                self.token.push_str(String::from_char(1, c).as_slice());
            }
        }
    }

    fn parse<T:Reader>(&mut self, mut reader: BufferedReader<T>){
        let buf:&mut [u8] = [0, ..BUFFER_SIZE];
        // read and ignore header
        let _header = reader.read_line();

        loop{
            match reader.read(buf) {
                Ok(nread) => self.parse_chunk(std::str::from_utf8(buf.slice(0, nread)).unwrap()),
                Err(_e) => {
                    break;
                }
            }
        }
        // process last record
        self.if_record_complete();
    }
}

fn main() {
    let mut parser = Parser::new();
    let reader = stdin();
    if reader.get_ref().isatty(){
        let args = os::args();
        let file = File::open(&Path::new(args.get(1).clone()));
        let reader_file = BufferedReader::new(file);
        parser.parse(reader_file);
    }
    else{
        parser.parse(reader);
    }
}
<commit_msg>codefix: Removed enumerate since index is not used<commit_after>use std::io::{BufferedReader, File, stdin};
use std::num::FromPrimitive;
use std::os;

static BUFFER_SIZE: uint = 100;

#[allow(non_camel_case_types)]
#[deriving(PartialEq, FromPrimitive, Show)]
enum Foptypes{
    GF_FOP_NULL=0,
    GF_FOP_MKNOD=3,
    GF_FOP_MKDIR=4,
    GF_FOP_UNLINK=5,
    GF_FOP_RMDIR=6,
    GF_FOP_SYMLINK=7,
    GF_FOP_RENAME=8,
    GF_FOP_LINK=9,
    GF_FOP_SETXATTR=17,
    GF_FOP_REMOVEXATTR=19,
    GF_FOP_CREATE=23,
    GF_FOP_SETATTR=38,
}

impl Foptypes{
    fn get(&self) -> &str{
        match *self{
            GF_FOP_NULL => "NULL",
            GF_FOP_MKNOD => "MKNOD",
            GF_FOP_MKDIR => "MKDIR",
            GF_FOP_UNLINK => "UNLINK",
            GF_FOP_RMDIR => "RMDIR",
            GF_FOP_SYMLINK => "SYMLINK",
            GF_FOP_RENAME => "RENAME",
            GF_FOP_LINK => "LINK",
            GF_FOP_SETXATTR => "SETXATTR",
            GF_FOP_REMOVEXATTR => "REMOVEXATTR",
            GF_FOP_CREATE => "CREATE",
            GF_FOP_SETATTR => "SETATTR"
        }
    }

    fn num_fields(&self) -> uint{
        match *self{
            GF_FOP_NULL => 3,
            GF_FOP_MKNOD => 7,
            GF_FOP_MKDIR => 7,
            GF_FOP_UNLINK => 4,
            GF_FOP_RMDIR => 4,
            GF_FOP_SYMLINK => 4,
            GF_FOP_RENAME => 5,
            GF_FOP_LINK => 4,
            GF_FOP_SETXATTR => 3,
            GF_FOP_REMOVEXATTR => 3,
            GF_FOP_CREATE => 7,
            GF_FOP_SETATTR => 3
        }
    }
}

#[deriving(Show)]
struct Parser{
    record_complete: bool,
    fop_type: char,
    token: String,
    tokens: Vec<String>,
    sep: char
}

impl Parser{
    fn new() -> Parser{
        Parser{record_complete: true,
               fop_type: '.',
               token: "".to_string(),
               tokens: Vec::new(),
               sep: '\x00'}
    }

    fn get_num_tokens(&self) -> uint {
        match self.fop_type{
            'E' => 7,
            'M' => 3,
            'D' => 2,
            _ => 0
        }
    }

    fn get_fop_enum(&self, v: String) -> Option<Foptypes>{
        let key:int = from_str::<int>(v.as_slice()).unwrap();
        let ty: Option<Foptypes> = FromPrimitive::from_int(key);
        return ty;
    }

    fn process_record(&self){
        let mut op: String = String::new();
        for (i, v) in self.tokens.iter().enumerate(){
            if (self.fop_type == 'E' || self.fop_type == 'M') && i == 2 {
                let ty: Option<Foptypes> = self.get_fop_enum(v.clone());
                match ty{
                    Some(aa) => {
                        op.push_str(aa.get());
                        op.push_str(" ");
                    },
                    None => {}
                }
            }
            else{
                op.push_str(v.as_slice());
                op.push_str(" ");
            }
        }
        println!("{}", op.as_slice().trim_chars(' '));
    }

    fn if_record_complete(&mut self){
        if self.token.as_slice() != ""{
            self.tokens.push(self.token.clone());
            self.token = "".to_string();
        }

        let mut num_tokens: uint = self.get_num_tokens();
        if (self.fop_type == 'E' || self.fop_type == 'M') && self.tokens.len() >= 3{
            let ty: Option<Foptypes> = self.get_fop_enum(self.tokens[2].clone());
            num_tokens = match ty{
                Some(aa) => aa.num_fields(),
                None => 0
            }
        }

        if num_tokens == self.tokens.len(){
            self.record_complete = true;
            self.process_record();
            self.tokens = Vec::new();
        }
    }

    fn parse_chunk(&mut self, chunk: &str){
        for c in chunk.chars(){
            if self.record_complete && (c == 'E' || c == 'M' || c == 'D'){
                self.fop_type = c;
                self.tokens.push(String::from_char(1, c));
                self.record_complete = false;
                continue;
            }

            if c == self.sep{
                self.if_record_complete();
            }
            else{
                self.token.push_str(String::from_char(1, c).as_slice());
            }
        }
    }

    fn parse<T:Reader>(&mut self, mut reader: BufferedReader<T>){
        let buf:&mut [u8] = [0, ..BUFFER_SIZE];
        // read and ignore header
        let _header = reader.read_line();

        loop{
            match reader.read(buf) {
                Ok(nread) => self.parse_chunk(std::str::from_utf8(buf.slice(0, nread)).unwrap()),
                Err(_e) => {
                    break;
                }
            }
        }
        // process last record
        self.if_record_complete();
    }
}

fn main() {
    let mut parser = Parser::new();
    let reader = stdin();
    if reader.get_ref().isatty(){
        let args = os::args();
        let file = File::open(&Path::new(args[1].clone()));
        let reader_file = BufferedReader::new(file);
        parser.parse(reader_file);
    }
    else{
        parser.parse(reader);
    }
}
<|endoftext|>