<commit_before>mod uci;
mod board;
mod tests;
mod monte_carlo;
mod alpha_beta;

extern crate time;

use Score::*;

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

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

#[macro_use]
extern crate lazy_static;

fn main() {
    let reader = io::stdin();
    let mut input = "".to_string();
    loop {
        // println!("Enter input");
        reader.read_line(&mut input).unwrap();
        match &input[..] {
            "uci\n" => {
                let mut log_writer = Arc::new(Mutex::new(None));
                match uci::connect_engine(&log_writer) {
                    Ok(_) => (),
                    Err(e) => {
                        uci::to_log(&e, &mut log_writer);
                        panic!(e);
                    },
                }
            },

            "mem usage\n" => {
                use std::mem;
                println!("Standard board: {}", mem::size_of::<board::std_board::ChessBoard>());
                println!("Standard move: {}", mem::size_of::<board::std_move::ChessMove>());
                println!("Standard piece: {}", mem::size_of::<board::std_board::Piece>());
                println!("Board score: {}", mem::size_of::<Score>());

            },
            //"play_self\n" => play_game(&board::START_BOARD.clone()),
            //"play\n" => play_human(),
            s => println!("Unrecognized command \"{}\".", s),
        }
        input.clear();
    }

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

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

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

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

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

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

            }

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

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

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

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

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Score {
    Val(f32),
    Draw(u8),
    MateW(u8),
    MateB(u8),
}

impl Display for Score {
    fn fmt(&self, fmt : &mut Formatter) -> Result<(), Error> {
        let _ = match self {
            &Val(f) => fmt.write_str(&format!("cp {}", f).to_string()),
            &MateW(moves) => fmt.write_str(&format!("mate {}", moves)),
            &MateB(moves) => fmt.write_str(&format!("mate -{}", moves)),
            &Draw(_) => fmt.write_str("0.0 (forced draw)"),
        };
        Ok(())
    }
}

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

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

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

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

        }
    }
}
<commit_msg>Removed assumptions about newlines chars from stdin being \\n<commit_after>mod uci;
mod board;
mod tests;
mod monte_carlo;
mod alpha_beta;

extern crate time;

use Score::*;

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

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

#[macro_use]
extern crate lazy_static;

fn main() {
    let reader = io::stdin();
    let mut input = "".to_string();
    loop {
        // println!("Enter input");
        reader.read_line(&mut input).unwrap();
	input = input.trim().to_string();
        match &input[..] {
            "uci" => {
                let mut log_writer = Arc::new(Mutex::new(None));
                match uci::connect_engine(&log_writer) {
                    Ok(_) => (),
                    Err(e) => {
                        uci::to_log(&e, &mut log_writer);
                        panic!(e);
                    },
                }
            },

            "mem usage" => {
                use std::mem;
                println!("Standard board: {}", mem::size_of::<board::std_board::ChessBoard>());
                println!("Standard move: {}", mem::size_of::<board::std_move::ChessMove>());
                println!("Standard piece: {}", mem::size_of::<board::std_board::Piece>());
                println!("Board score: {}", mem::size_of::<Score>());

            },
            //"play_self" => play_game(&board::START_BOARD.clone()),
            //"play" => play_human(),
            s => println!("Unrecognized command \"{}\".", s),
        }
        input.clear();
    }

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

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

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

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

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

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

            }

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

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

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

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

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Score {
    Val(f32),
    Draw(u8),
    MateW(u8),
    MateB(u8),
}

impl Display for Score {
    fn fmt(&self, fmt : &mut Formatter) -> Result<(), Error> {
        let _ = match self {
            &Val(f) => fmt.write_str(&format!("cp {}", f).to_string()),
            &MateW(moves) => fmt.write_str(&format!("mate {}", moves)),
            &MateB(moves) => fmt.write_str(&format!("mate -{}", moves)),
            &Draw(_) => fmt.write_str("0.0 (forced draw)"),
        };
        Ok(())
    }
}

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

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

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

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

        }
    }
}
<|endoftext|>


<commit_before>//! A simple first program in rust, actually I plan to do something even
//! useful. Trying to find all my audiobooks on many machines, identify them,
//! find duplicates (later trying to solve the problem, also including their permissions, different names, but same albums, etc),
//! get all stats about it).
//! It acts as a wrapper around adbflib, which holds major parts of the implemention.
extern crate adbflib;
extern crate clap;
extern crate rayon;

use adbflib::{
    ctrl::{Alive, Ctrl, ReceiveDialog, Status, SystemMsg},
    data::{self, Collection},
    logit,
    net::{key_keeper, Net},
};
use async_std::{
    sync::{Arc, Mutex},
    task,
};
use futures::try_join;
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::{
    io::{self, Error, ErrorKind},
    path::Path, // path, clear
    sync::{
        self,
        mpsc::{channel, Sender},
    },
};

static INPUT_FOLDERS: &str = "folders";
static APP_TITLE: &str = concat!("The audiobook finder (", env!("CARGO_PKG_NAME"), ")");
static ARG_NET: &str = "net";
static ARG_TUI: &str = "tui";
static ARG_WEBUI: &str = "webui";

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");
const HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE");
const DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION");

fn main() -> io::Result<()> {
    let parse_args = clap::App::new(APP_TITLE)
        .version(VERSION)
        .author(AUTHORS)
        .about(DESCRIPTION)
        .long_about::<&str>(
            &[
                &DESCRIPTION,
                "\n\
                 It reads data from possibly multiple given path(s). Via local network it searches \
                 for other instances of the program, and will later exchange data securely.\n\
                 All information gathered will be used to find duplicates, versions of \
                 different quality, different tags for same content (spelling, \
                 incompleteness).\n\
                 For documentation see: ",
                &HOMEPAGE,
                "\n \
                 A program to learn, embrace, and love Rust! \n\
                 Have fun!",
            ]
            .concat(),
        )
        .arg(
            clap::Arg::with_name("config")
                .short("c")
                .long("config")
                .value_name("FILE")
                .help("Sets custom config file (not implemented yet)")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name(ARG_TUI)
                .short("t")
                .long(ARG_TUI)
                .help("Run with TUI")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_WEBUI)
                .short("w")
                .long(ARG_WEBUI)
                .help("Run with-in a webui.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_NET)
                .short("n")
                .long(ARG_NET)
                .help("With net search for other audiobookfinders running in local network.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(INPUT_FOLDERS)
                .help("Sets multiple input folder(s) to be searched for audio files.")
                .multiple(true)
                .required(false),
        )
        .get_matches();
    //
    let max_threads = rayon::current_num_threads();

    // tricky thing, but I really like that
    let all_pathes = if let Some(correct_input) = parse_args.values_of(INPUT_FOLDERS) {
        correct_input.collect()
    } else {
        vec!["."]
    };

    //
    // check argments if tui and net search is needed
    //
    let has_arg = |x: &str| parse_args.is_present(x);

    let has_tui = has_arg(ARG_TUI);
    let has_webui = has_arg(ARG_WEBUI);
    let has_net = has_arg(ARG_NET);

    // either one will have a ui, representing data and error messages
    let has_ui = has_tui || has_webui;

    //
    // prepare the message system
    //
    let (tx, rx) = channel::<SystemMsg>();

    // these will be taken directly
    let tx_net = tx.clone();
    let tx_net_alive = tx.clone();

    // for now this will stay wrapped
    let tx_sys = Arc::new(Mutex::new(tx.clone()));

    //
    // pathes: copy to vec<&str>
    //
    let tui_pathes = all_pathes.iter().map(|s| s.to_string()).collect();

    // start the logging
    logit::Logit::init(logit::Log::File);

    // start the main operators
    //
    task::block_on(async move {
        // all optional components are wrapped into a future which can result in an empty future,
        // but collector future will not be empty
        // 1 - UI         ui_future   (optional)
        // 2 - Net        net_future  (optional)
        // 3 - Collector  collector_future

        let ui_future = async move {
            if has_ui {
                match Ctrl::new(
                    key_keeper::get_p2p_server_id(),
                    &tui_pathes,
                    rx,
                    tx.clone(),
                    has_net,
                ) {
                    Ok(mut controller) => {
                        if has_webui {
                            controller.run_webui().await?;
                        }
                        if has_tui {
                            // do finally the necessary
                            controller.run_tui();
                            // and send startup animation
                            // todo: maybe a timer spawn task? but then if a tui gets
                            //       terminated to fast, send will panic!!
                            tx_net_alive
                                .send(SystemMsg::StartAnimation(Alive::HostSearch, Status::ON))
                                .unwrap();
                        }
                        Ok::<(), Error>(())
                    }
                    Err(error_text) => {
                        println!("{:?}", error_text);
                        Err(Error::new(ErrorKind::Other, error_text))
                    }
                }
            } else {
                println!("no ui was created!");
                Ok::<(), Error>(())
            }
        };

        // 2 - Net Future
        let net_future = async move {
            // This thread will not end itself
            // - can be terminated by ui message
            // - collector finished (depending on definition)
            if has_net {
                println!("Net Started!!");
                let net_system_messages = tx_net;
                let mut network = Net::new(
                    key_keeper::get_p2p_server_id(),
                    has_tui,
                    net_system_messages,
                );
                network.lookup().await;
                println!("Net finished!!");
                Ok::<(), Error>(())
            } else {
                println!("no net!");
                Ok::<(), Error>(())
            }
        };

        // start the parallel search threads with rayon, each path its own
        let collector_future = async move {
            // initialize the data collection for all
            let init_collection = Collection::new(&key_keeper::get_p2p_server_id(), max_threads);
            let collection_protected = sync::Arc::new(sync::Mutex::new(init_collection));

            // todo: is this right? async lock to create a sync message mutex?
            let synced_sys_messages = tx_sys.lock().await.clone();
            let tx_sync_sys = sync::Arc::new(sync::Mutex::new(synced_sys_messages));

            all_pathes.par_iter().enumerate().for_each(|(index, elem)| {
                let collection_data_in_iterator = collection_protected.clone();
                let system_messages_in_iterator = tx_sync_sys.clone();
                search_in_single_path(
                    has_ui,
                    has_tui,
                    collection_data_in_iterator,
                    system_messages_in_iterator,
                    index,
                    elem,
                );
            });
            if !has_ui {
                collection_protected
                    .lock()
                    .and_then(|locked_collection| Ok(locked_collection.print_stats()))
                    .unwrap_or(())
                // todo: send terminate to net runner depending if it should continue or not
            }
            Ok::<(), Error>(())
        };

        // Compose all futures that possible threads inside are running
        try_join!(ui_future, net_future, collector_future)
    })
    .and_then(|(_, _, _)| {
        // shrink the 3 ok results to one
        println!("done here");
        Ok(())
    })
}

fn search_in_single_path(
    has_ui: bool,
    has_tui: bool,
    collection_protected: sync::Arc<sync::Mutex<Collection>>,
    mutex_system_msg: sync::Arc<sync::Mutex<Sender<SystemMsg>>>,
    index: usize,
    elem: &str,
) {
    if !has_ui {
        println!("[{:?}] looking into path {:?}", index, elem);
    } else {
        // start animation .... timer and so on
        if has_tui {
            mutex_system_msg
                .lock()
                .and_then(|locked_start_message| {
                    locked_start_message
                        .send(SystemMsg::StartAnimation(
                            Alive::BusyPath(index),
                            Status::ON,
                        ))
                        .unwrap();
                    Ok(())
                })
                .unwrap_or_else(|_| {
                    println!("... find a more graceful program termination, or consume it")
                });
        }
    }

    let locked_collection = &mut *collection_protected.lock().unwrap();
    match locked_collection.visit_dirs(Path::new(elem), &data::Collection::visit_files) {
        Ok(local_stats) => {
            if has_ui {
                // stop animation
                if has_tui {
                    mutex_system_msg
                        .lock()
                        .and_then(|locked_stop_message| {
                            locked_stop_message
                                .send(SystemMsg::StartAnimation(
                                    Alive::BusyPath(index),
                                    Status::OFF,
                                ))
                                .unwrap();
                            Ok(())
                        })
                        .unwrap_or_else(|_| {
                            println!("... find a more graceful program termination, or consume it")
                        });
                }
            } else {
                let text = format!(
                    "\n\
                             analyzed: {an:>width$}, faulty: {fa:>width$}\n\
                             searched: {se:>width$}, other: {ot:>width$}",
                    an = local_stats.analyzed,
                    fa = local_stats.faulty,
                    se = local_stats.searched,
                    ot = local_stats.other,
                    width = 3
                );
                println!("[{:?}] done {}", index, text);
            }
        }
        Err(_e) => {
            let text = format!("An error has occurred in search path [{}]!!", index);
            if has_ui {
                if has_tui {
                    let debug_message_id = ReceiveDialog::Debug;
                    let text = text.to_string();
                    mutex_system_msg
                        .lock()
                        .and_then(|locked_update_message| {
                            locked_update_message
                                .send(SystemMsg::Update(debug_message_id, text))
                                .unwrap();
                            Ok(())
                        })
                        .unwrap();
                }
            } else {
                println!("{:?}", text);
            }
        }
    }
}
<commit_msg>[libp2p migration] protect channel message from panicking<commit_after>//! A simple first program in rust, actually I plan to do something even
//! useful. Trying to find all my audiobooks on many machines, identify them,
//! find duplicates (later trying to solve the problem, also including their permissions, different names, but same albums, etc),
//! get all stats about it).
//! It acts as a wrapper around adbflib, which holds major parts of the implemention.
extern crate adbflib;
extern crate clap;
extern crate rayon;

use adbflib::{
    ctrl::{Alive, Ctrl, ReceiveDialog, Status, SystemMsg},
    data::{self, Collection},
    logit,
    net::{key_keeper, Net},
};
use async_std::{
    sync::{Arc, Mutex},
    task,
};
use futures::try_join;
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::{
    io::{self, Error, ErrorKind},
    path::Path, // path, clear
    sync::{
        self,
        mpsc::{channel, Sender},
    },
};

static INPUT_FOLDERS: &str = "folders";
static APP_TITLE: &str = concat!("The audiobook finder (", env!("CARGO_PKG_NAME"), ")");
static ARG_NET: &str = "net";
static ARG_TUI: &str = "tui";
static ARG_WEBUI: &str = "webui";

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS");
const HOMEPAGE: &'static str = env!("CARGO_PKG_HOMEPAGE");
const DESCRIPTION: &'static str = env!("CARGO_PKG_DESCRIPTION");

fn main() -> io::Result<()> {
    let parse_args = clap::App::new(APP_TITLE)
        .version(VERSION)
        .author(AUTHORS)
        .about(DESCRIPTION)
        .long_about::<&str>(
            &[
                &DESCRIPTION,
                "\n\
                 It reads data from possibly multiple given path(s). Via local network it searches \
                 for other instances of the program, and will later exchange data securely.\n\
                 All information gathered will be used to find duplicates, versions of \
                 different quality, different tags for same content (spelling, \
                 incompleteness).\n\
                 For documentation see: ",
                &HOMEPAGE,
                "\n \
                 A program to learn, embrace, and love Rust! \n\
                 Have fun!",
            ]
            .concat(),
        )
        .arg(
            clap::Arg::with_name("config")
                .short("c")
                .long("config")
                .value_name("FILE")
                .help("Sets custom config file (not implemented yet)")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name(ARG_TUI)
                .short("t")
                .long(ARG_TUI)
                .help("Run with TUI")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_WEBUI)
                .short("w")
                .long(ARG_WEBUI)
                .help("Run with-in a webui.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(ARG_NET)
                .short("n")
                .long(ARG_NET)
                .help("With net search for other audiobookfinders running in local network.")
                .takes_value(false),
        )
        .arg(
            clap::Arg::with_name(INPUT_FOLDERS)
                .help("Sets multiple input folder(s) to be searched for audio files.")
                .multiple(true)
                .required(false),
        )
        .get_matches();
    //
    let max_threads = rayon::current_num_threads();

    // tricky thing, but I really like that
    let all_pathes = if let Some(correct_input) = parse_args.values_of(INPUT_FOLDERS) {
        correct_input.collect()
    } else {
        vec!["."]
    };

    //
    // check argments if tui and net search is needed
    //
    let has_arg = |x: &str| parse_args.is_present(x);

    let has_tui = has_arg(ARG_TUI);
    let has_webui = has_arg(ARG_WEBUI);
    let has_net = has_arg(ARG_NET);

    // either one will have a ui, representing data and error messages
    let has_ui = has_tui || has_webui;

    //
    // prepare the message system
    //
    let (tx, rx) = channel::<SystemMsg>();

    // these will be taken directly
    let tx_net = tx.clone();
    let tx_net_alive = tx.clone();

    // for now this will stay wrapped
    let tx_sys = Arc::new(Mutex::new(tx.clone()));

    //
    // pathes: copy to vec<&str>
    //
    let ui_pathes = all_pathes.iter().map(|s| s.to_string()).collect();

    // start the logging
    logit::Logit::init(logit::Log::File);

    // start the main operators
    //
    task::block_on(async move {
        // all optional components are wrapped into a future which can result in an empty future,
        // but collector future will not be empty
        // 1 - UI         ui_future   (optional)
        // 2 - Net        net_future  (optional)
        // 3 - Collector  collector_future

        let ui_future = async move {
            if has_ui {
                match Ctrl::new(
                    key_keeper::get_p2p_server_id(),
                    &ui_pathes,
                    rx,
                    tx.clone(),
                    has_net,
                ) {
                    Ok(mut controller) => {
                        if has_webui {
                            controller.run_webui().await?;
                        }
                        if has_tui {
                            // do finally the necessary
                            controller.run_tui();
                            // and send startup animation
                            // todo: maybe a timer spawn task? but then if a tui gets
                            //       terminated to fast, send will panic!!
                            tx_net_alive
                                .send(SystemMsg::StartAnimation(Alive::HostSearch, Status::ON))
                                .unwrap();
                        }
                        Ok::<(), Error>(())
                    }
                    Err(error_text) => {
                        println!("{:?}", error_text);
                        Err(Error::new(ErrorKind::Other, error_text))
                    }
                }
            } else {
                println!("no ui was created!");
                Ok::<(), Error>(())
            }
        };

        // 2 - Net Future
        let net_future = async move {
            // This thread will not end itself
            // - can be terminated by ui message
            // - collector finished (depending on definition)
            if has_net {
                println!("Net Started!!");
                let net_system_messages = tx_net;
                let mut network = Net::new(
                    key_keeper::get_p2p_server_id(),
                    has_tui,
                    net_system_messages,
                );
                network.lookup().await;
                println!("Net finished!!");
                Ok::<(), Error>(())
            } else {
                println!("no net!");
                Ok::<(), Error>(())
            }
        };

        // start the parallel search threads with rayon, each path its own
        let collector_future = async move {
            // initialize the data collection for all
            let init_collection = Collection::new(&key_keeper::get_p2p_server_id(), max_threads);
            let collection_protected = sync::Arc::new(sync::Mutex::new(init_collection));

            // todo: is this right? async lock to create a sync message mutex?
            let synced_sys_messages = tx_sys.lock().await.clone();
            let tx_sync_sys = sync::Arc::new(sync::Mutex::new(synced_sys_messages));

            all_pathes.par_iter().enumerate().for_each(|(index, elem)| {
                let collection_data_in_iterator = collection_protected.clone();
                let system_messages_in_iterator = tx_sync_sys.clone();
                search_in_single_path(
                    has_ui,
                    has_tui,
                    collection_data_in_iterator,
                    system_messages_in_iterator,
                    index,
                    elem,
                );
            });
            if !has_ui {
                collection_protected
                    .lock()
                    .and_then(|locked_collection| Ok(locked_collection.print_stats()))
                    .unwrap_or(())
                // todo: send terminate to net runner depending if it should continue or not
            }
            Ok::<(), Error>(())
        };

        // Compose all futures that possible threads inside are running
        try_join!(ui_future, net_future, collector_future)
    })
    .and_then(|(_, _, _)| {
        // shrink the 3 ok results to one
        println!("done here");
        Ok(())
    })
}

fn search_in_single_path(
    has_ui: bool,
    has_tui: bool,
    collection_protected: sync::Arc<sync::Mutex<Collection>>,
    mutex_system_msg: sync::Arc<sync::Mutex<Sender<SystemMsg>>>,
    index: usize,
    elem: &str,
) {
    if !has_ui {
        println!("[{:?}] looking into path {:?}", index, elem);
    } else {
        // start animation .... timer and so on
        if has_tui {
            mutex_system_msg
                .lock()
                .and_then(|locked_start_message| {
                    locked_start_message
                        .send(SystemMsg::StartAnimation(
                            Alive::BusyPath(index),
                            Status::ON,
                        ))
                        .unwrap_or_else(|_| println!("... lost start animation"));
                    Ok(())
                })
                .unwrap_or_else(|_| println!("... that should not happen here at start"));
        }
    }

    let locked_collection = &mut *collection_protected.lock().unwrap();
    match locked_collection.visit_dirs(Path::new(elem), &data::Collection::visit_files) {
        Ok(local_stats) => {
            if has_ui {
                // stop animation
                if has_tui {
                    mutex_system_msg
                        .lock()
                        .and_then(|locked_stop_message| {
                            locked_stop_message
                                .send(SystemMsg::StartAnimation(
                                    Alive::BusyPath(index),
                                    Status::OFF,
                                ))
                                .unwrap_or_else(|_| println!("... lost stop animation"));
                            Ok(())
                        })
                        .unwrap_or_else(|_| println!("... that should not happen here at stop"));
                }
            } else {
                let text = format!(
                    "\n\
                             analyzed: {an:>width$}, faulty: {fa:>width$}\n\
                             searched: {se:>width$}, other: {ot:>width$}",
                    an = local_stats.analyzed,
                    fa = local_stats.faulty,
                    se = local_stats.searched,
                    ot = local_stats.other,
                    width = 3
                );
                println!("[{:?}] done {}", index, text);
            }
        }
        Err(_e) => {
            let text = format!("An error has occurred in search path [{}]!!", index);
            if has_ui {
                if has_tui {
                    let debug_message_id = ReceiveDialog::Debug;
                    let text = text.to_string();
                    mutex_system_msg
                        .lock()
                        .and_then(|locked_update_message| {
                            locked_update_message
                                .send(SystemMsg::Update(debug_message_id, text))
                                .unwrap();
                            Ok(())
                        })
                        .unwrap();
                }
            } else {
                println!("{:?}", text);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::LinkedList;

struct Operator {
    operators: LinkedList<Operator>,
    operands: LinkedList<Operand>,
    runner: Box<Fn(&Operand) -> Operand>
}

impl Operator {
    fn new(runner: Box<Fn(&Operand) -> Operand>) -> Operator {
        Operator { operators: LinkedList::new(),
                   operands: LinkedList::new(),
                   runner: runner }
    }
    fn feed(&mut self, operand: Operand) {
        self.operands.push_back(operand);
    }
    fn run(&mut self) {
        let mut iter = self.operands.iter();
        let ref runner = self.runner;
        loop {
               match iter.next() {
                   Some(x) => { runner(x); },
                   None => { break; }
            }
        }
    }
}

enum Operand {
    Number(f64),
    Symbol(String),
    Operator(Operator),
    Sentinel
}

fn main() {
    let mut a = LinkedList::new();
    let mut b = LinkedList::new();
    a.push_back(1);
    a.push_back(2);
    b.push_back(3);
    b.push_back(4);

    a.append(&mut b);

    for e in &a {
        println!("{}", e); // prints 1, then 2, then 3, then 4
    }
    println!("{}", b.len()); // prints 0

    let mut op: Operator = Operator::new(Box::new(|x: &Operand| -> Operand {
        match *x {
            Operand::Number(y) => {
                println!("{:?}", y);
            },
            _ => { println!("fail!"); }
        }
        Operand::Sentinel
    }));
    op.feed(Operand::Number(777f64));
    op.feed(Operand::Symbol(String::from("hello")));

    op.run();
}
<commit_msg>Makes more sense to do it like this.<commit_after>use std::collections::LinkedList;

struct Operator {
    operators: LinkedList<Operator>,
    operands: LinkedList<Operand>,  // should be used as "return" to the next operator in line
    runner: Box<Fn(&Operand) -> Operand>  // should work on the stack directly, instead of on per operand basis
                                          // in which case should receive &mut self and return nothing
}

impl Operator { // make "Incremental Operator" type with all of the same traits for convenience
    fn new(runner: Box<Fn(&Operand) -> Operand>) -> Operator {
        Operator { operators: LinkedList::new(),
                   operands: LinkedList::new(),
                   runner: runner }
    }
    fn feed(&mut self, operand: Operand) {
        self.operands.push_back(operand);
    }
    fn run(&mut self) {
        let mut iter = self.operands.iter();
        let ref runner = self.runner;
        loop {
               match iter.next() {
                   Some(x) => { runner(x); },
                   None => { break; }
            }
        }
    }
}

enum Operand {
    Number(f64),
    Symbol(String),
    Operator(Operator),
    Sentinel
}

fn main() {
    let mut a = LinkedList::new();
    let mut b = LinkedList::new();
    a.push_back(1);
    a.push_back(2);
    b.push_back(3);
    b.push_back(4);

    a.append(&mut b);

    for e in &a {
        println!("{}", e); // prints 1, then 2, then 3, then 4
    }
    println!("{}", b.len()); // prints 0

    let mut op: Operator = Operator::new(Box::new(|x: &Operand| -> Operand {
        match *x {
            Operand::Number(y) => {
                println!("{:?}", y);
            },
            _ => { println!("fail!"); }
        }
        Operand::Sentinel
    }));
    op.feed(Operand::Number(777f64));
    op.feed(Operand::Symbol(String::from("hello")));

    op.run();
}
<|endoftext|>


<commit_before>use std::fs;
use std::env;
use std::fs::File;
use std::io::Read;
use std::hash::Hasher;
use std::path::PathBuf;
use std::collections::HashMap;
//use std::os::unix::fs::MetadataExt;
use std::collections::hash_map::DefaultHasher;

#[macro_use]
extern crate log;
extern crate env_logger;

fn collect_files(dir: &String, h: &mut HashMap<u64, Vec<String>>) {

    let entries = match fs::read_dir(dir) {
        Ok(x) => x,
        _ => {
            warn!("{}: invalid directory", dir);
            return
        },
    };

    for path in  entries.filter( |x| x.is_ok() ).map( |x| x.unwrap().path() )
                .collect::<Vec<PathBuf>>() {
        if let (Some(path_str), Ok(metadata)) =
                (path.to_str(), path.symlink_metadata()) {
            let ft = metadata.file_type();
            if ft.is_symlink() {
                continue;
            } else if ft.is_file() {
                match metadata.len() {
                    0 => continue, //Ignore empty files.
                    i => h.entry(i).or_insert_with(Vec::new)
                          .push(String::from(path_str)),
                };
            } else if ft.is_dir() {
                collect_files(&(String::from(path_str)), h);
            }
        } else {
            warn!("Failed to retrieve metadata for {}", path.to_str().unwrap());
        }
    }
}

fn find_duplicates(duplicates: &mut HashMap<u64, Vec<String>>,
                   vec: &Vec<String>, thorough: bool ) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(file_entry) {
            let mut hash: u64 = 0;
            let mut hasher = DefaultHasher::new();
            if thorough {
                let mut contents: Vec<u8> = Vec::new();
                if let Ok(_) = file.read_to_end(&mut contents) {
                    hasher.write(&contents[..]);
                    hash = hasher.finish();
                } else {
                    warn!("Failed to read {}", file_entry);
                }
            } else {
                let mut buf: [u8; 1024] = [0; 1024];
                if let Ok(nbytes) = file.read(&mut buf) {
                    hasher.write(&buf[..nbytes as usize]);
                    hash = hasher.finish();
                } else {
                    warn!("Failed to read {} chunk", file_entry);
                }
            }
            if hash != 0 {
                duplicates.entry(hash).or_insert_with(Vec::new)
                    .push(file_entry.to_string());
            }
        } else {
            warn!("Failed to open file {}", file_entry);
        }
    }
}

fn print_duplicates(vec: &Vec<String>, cluster: i32, key: u64, hash: u64) {
    println!("{} files in cluster {} (size: {}, digest: {})",
             vec.len(), cluster, key, hash);
    // for_each becomes stable v1.22.0 onwards. Should uncomment then.
    //vec.iter().for_each(|f| println!("{}", f.path));
    for file_entry in vec {
        println!("{}", file_entry);
    }
}

fn exhaustive_search(emap: &mut HashMap<Vec<u8>, Vec<String>>,
                     vec: &Vec<String>) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(file_entry) {
            let mut contents: Vec<u8> = Vec::new();
            if let Ok(_) = file.read_to_end(&mut contents) {
                emap.entry(contents).or_insert_with(Vec::new)
                    .push(file_entry.to_string());
            }
        }
    }
}

fn print_usage_and_exit(binary_name: &String) {
        println!("Usage: {} [-t] [-e] <dir1> [dir2 [dir3 ...]]", binary_name);
        ::std::process::exit(0);
}

fn main() {
    env_logger::init().unwrap();

    let args: Vec<String> = env::args().collect();

    let binary_name = &args[0];

    // Print out usage if no directories are given.
    if args.len() == 1 {
        print_usage_and_exit(binary_name);
    }

    let mut hmap: HashMap<u64, Vec<String> > = HashMap::new();

    let mut thorough = false;
    let mut exhaustive = false;
    let mut print_usage = true;

    for dir in &args[1..] {
        if dir == "-t" {
            thorough = true;
            continue;
        } else if dir == "-e" {
            thorough = false;
            exhaustive = false;
            continue;
        }
        print_usage = false;
        collect_files(dir, &mut hmap);
    }

    if print_usage {
        print_usage_and_exit(binary_name);
    }

    // Get rid of all the single entries.
    hmap.retain(|_, v| v.len() >= 2);

    let mut cluster = 1;

    for (key, val) in hmap {
        let mut duplicates: HashMap<u64, Vec<String> > = HashMap::new();
        find_duplicates(&mut duplicates, &val, thorough);

        for (hash, vec) in duplicates {
            if vec.len() >= 2 {
                if exhaustive {
                    let mut emap: HashMap<Vec<u8>, Vec<String>> = HashMap::new();
                    exhaustive_search(&mut emap, &vec);
                    for (_, v) in emap {
                        if v.len() >= 2 {
                            print_duplicates(&v, cluster, key, hash);
                        }
                        cluster += 1;
                    }
                } else {
                    print_duplicates(&vec, cluster, key, hash);
                    cluster += 1;
                }
            }
        }
    }
}
<commit_msg>Minor fixes<commit_after>use std::fs;
use std::env;
use std::fs::File;
use std::io::Read;
use std::hash::Hasher;
use std::path::PathBuf;
use std::collections::HashMap;
//use std::os::unix::fs::MetadataExt;
use std::collections::hash_map::DefaultHasher;

#[macro_use]
extern crate log;
extern crate env_logger;

fn collect_files(dir: &String, h: &mut HashMap<u64, Vec<String>>) {

    let entries = match fs::read_dir(dir) {
        Ok(x) => x,
        _ => {
            warn!("{}: invalid directory", dir);
            return
        },
    };

    for path in  entries.filter( |x| x.is_ok() ).map( |x| x.unwrap().path() )
                .collect::<Vec<PathBuf>>() {
        if let (Some(path_str), Ok(metadata)) =
                (path.to_str(), path.symlink_metadata()) {
            let ft = metadata.file_type();
            if ft.is_symlink() {
                continue;
            } else if ft.is_file() {
                match metadata.len() {
                    0 => continue, //Ignore empty files.
                    i => h.entry(i).or_insert_with(Vec::new)
                          .push(String::from(path_str)),
                };
            } else if ft.is_dir() {
                collect_files(&(String::from(path_str)), h);
            }
        } else {
            warn!("Failed to retrieve metadata for {}", path.to_str().unwrap());
        }
    }
}

fn find_duplicates(duplicates: &mut HashMap<u64, Vec<String>>,
                   vec: &Vec<String>, thorough: bool ) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(file_entry) {
            let mut hash: u64 = 0;
            let mut hasher = DefaultHasher::new();
            if thorough {
                let mut contents: Vec<u8> = Vec::new();
                if let Ok(_) = file.read_to_end(&mut contents) {
                    hasher.write(&contents[..]);
                    hash = hasher.finish();
                } else {
                    warn!("Failed to read {}", file_entry);
                }
            } else {
                let mut buf: [u8; 1024] = [0; 1024];
                if let Ok(nbytes) = file.read(&mut buf) {
                    hasher.write(&buf[..nbytes as usize]);
                    hash = hasher.finish();
                } else {
                    warn!("Failed to read {} chunk", file_entry);
                }
            }
            if hash != 0 {
                duplicates.entry(hash).or_insert_with(Vec::new)
                    .push(file_entry.to_string());
            }
        } else {
            warn!("Failed to open file {}", file_entry);
        }
    }
}

fn print_duplicates(vec: &Vec<String>, cluster: i32, key: u64, hash: u64) {
    println!("{} files in cluster {} (size: {}, digest: {})",
             vec.len(), cluster, key, hash);
    // for_each becomes stable v1.22.0 onwards. Should uncomment then.
    //vec.iter().for_each(|f| println!("{}", f.path));
    for file_entry in vec {
        println!("{}", file_entry);
    }
}

fn exhaustive_search(emap: &mut HashMap<Vec<u8>, Vec<String>>,
                     vec: &Vec<String>) {
    for file_entry in vec {
        if let Ok(mut file) = File::open(file_entry) {
            let mut contents: Vec<u8> = Vec::new();
            if let Ok(_) = file.read_to_end(&mut contents) {
                emap.entry(contents).or_insert_with(Vec::new)
                    .push(file_entry.to_string());
            } else {
                warn!("Failed to read {}", file_entry);
            }
        } else {
            warn!("Failed to open {}", file_entry);
        }
    }
}

fn print_usage_and_exit(binary_name: &String) {
        println!("Usage: {} [-t] [-e] <dir1> [dir2 [dir3 ...]]", binary_name);
        ::std::process::exit(0);
}

fn main() {
    env_logger::init().unwrap();

    let args: Vec<String> = env::args().collect();

    let binary_name = &args[0];

    // Print out usage if no directories are given.
    if args.len() == 1 {
        print_usage_and_exit(binary_name);
    }

    let mut hmap: HashMap<u64, Vec<String> > = HashMap::new();

    let mut thorough = false;
    let mut exhaustive = false;
    let mut print_usage = true;

    for dir in &args[1..] {
        if dir == "-t" {
            thorough = true;
            continue;
        } else if dir == "-e" {
            thorough = false;
            exhaustive = false;
            continue;
        }
        print_usage = false;
        collect_files(dir, &mut hmap);
    }

    if print_usage {
        print_usage_and_exit(binary_name);
    }

    // Get rid of all the single entries.
    hmap.retain(|_, v| v.len() >= 2);

    let mut cluster = 1;

    for (key, val) in hmap {
        let mut duplicates: HashMap<u64, Vec<String> > = HashMap::new();
        find_duplicates(&mut duplicates, &val, thorough);

        for (hash, vec) in duplicates {
            if vec.len() >= 2 {
                if exhaustive {
                    let mut emap: HashMap<Vec<u8>, Vec<String>> = HashMap::new();
                    exhaustive_search(&mut emap, &vec);
                    for (_, v) in emap {
                        if v.len() >= 2 {
                            print_duplicates(&v, cluster, key, hash);
                            cluster += 1;
                        }
                    }
                } else {
                    print_duplicates(&vec, cluster, key, hash);
                    cluster += 1;
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use rand::Rng;

const SEA: u8 = 0;
const LAND: u8 = 1;

const VISITED: u8 = 1;

const SIZE: usize = 7;

fn main() {
    // TODO: make the array initializations better, not literals
    // probably some vectors

    let mut map: [[u8; SIZE]; SIZE] = [[0; SIZE]; SIZE];

    map = initalize_map(map);

    let mut visited: [[u8; SIZE]; SIZE] = [[0; SIZE]; SIZE];

    let mut islands_count = 0;

    for (i, &row) in map.iter().enumerate() {
        for (j, &el) in row.iter().enumerate() {
            if el == LAND && visited[i][j] != VISITED {
                islands_count += 1;
                traverse_figure(&map, &mut visited, i, j);
            }
        }
    }

    println!("There are {} islands in the field", islands_count);
}

fn initalize_map(mut map: [[u8; SIZE]; SIZE]) -> [[u8; SIZE]; SIZE] {
    for row in map.iter_mut() {
        for el in row.iter_mut() {
            *el = rand::thread_rng().gen_range(0, 2);
        }
    }
    map
}

fn traverse_figure(field: &[[u8; SIZE]], visited: &mut [[u8; SIZE]], r: usize, c: usize) {
    if visited[r][c] == VISITED || field[r][c] == SEA {
        return;
    }
    visited[r][c] = VISITED;

    if r > 0 {
        traverse_figure(field, visited, r - 1, c);
    }
    if r + 1 < SIZE {
        traverse_figure(field, visited, r + 1, c);
    }
    if c > 0 {
        traverse_figure(field, visited, r, c - 1);
    }
    if c + 1 < SIZE {
        traverse_figure(field, visited, r, c + 1);
    }
}
<commit_msg>Simple moving_average<commit_after>extern crate rand;

const N: usize = 3;

struct MetricHolder {
    // TODO: make N a part of the metric holder and use a vector
    // also improve by persisting the sum instead of doing avg every time
    history: [u32; N],
    counter: usize
}

impl MetricHolder {
    fn moving_average(&mut self, new_val: u32) -> f64 {
        self.history[self.counter] = new_val;
        self.counter = (self.counter + 1) % N;
        let mut sum: u32 = 0;
        let mut count: u32 = 0;
        for &el in self.history.iter() {
            sum += el;
            if el != 0 {
                count += 1;
            }
        }
        sum as f64 / count as f64
    }
}

fn main() {

    // TODO: implement default
    let mut metric_holder = MetricHolder {
        history: [0; N],
        counter: 0
    };

    println!("{}", metric_holder.moving_average(1));
    println!("{}", metric_holder.moving_average(1));
    println!("{}", metric_holder.moving_average(1));
    println!("{}", metric_holder.moving_average(2));
    println!("{}", metric_holder.moving_average(3));
}
<|endoftext|>


<commit_before>use std::error::Error;

fn try_main() -> Result<(), Box<Error>> {
    Ok(())
}

fn main() {
    if let Err(err) = try_main() {
        panic!("{}", err.description());
    }
}
<commit_msg>ruskey: add basic command line argument parsing<commit_after>#[macro_use]
extern crate clap;
use clap::{Arg, App};

use std::error::Error;

fn try_main() -> Result<(), Box<Error>> {
    let matches = App::new("ruskey")
        .version(&crate_version!()[..])
        .author("Ben Boeckel <mathstuf@gmail.com>")
        .about("Implement the SecretService API for keepass databases")
        .arg(Arg::with_name("CONFIG")
                .short("c")
                .long("config")
                .help("Path to the configuration file")
                .takes_value(true))
        .arg(Arg::with_name("DATABASE_NAME")
                .short("n")
                .long("name")
                .help("Name of the database")
                .takes_value(true))
        .arg(Arg::with_name("DATABASE")
                .short("d")
                .long("database")
                .help("Path to the database")
                .takes_value(true))
        .get_matches();

    Ok(())
}

fn main() {
    if let Err(err) = try_main() {
        panic!("{}", err.description());
    }
}
<|endoftext|>


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

#[derive(Debug)]
enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    // These are not described in the book but they simplify the parser
    Comment,
    Whitespace,
}

#[derive(Debug)]
struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn char_at(&self, index: usize) -> char {
        // TODO: there must be a better way
        self.source.chars().nth(index).unwrap()
    }

    fn substring(&self, start: usize, end: usize) -> String {
        self.source.chars().skip(start).take(end - start).collect()
    }

    fn peek(&self) -> char() {
        if self.is_at_end() {
            '\0'
        } else {
            self.char_at(self.current)
        }
    }

    fn peek_next(&self) -> char() {
        if self.current + 1 >= self.source.len() {
            '\0'
        } else {
            self.char_at(self.current + 1)
        }
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        self.char_at(self.current - 1)
    }

    fn is_match(&mut self, expected: char) -> bool {
        if self.is_at_end() {
            return false;
        }
        if self.char_at(self.current) != expected {
            return false;
        }
        self.current += 1;
        return true;
    }

    fn add_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.substring(self.start, self.current),
            line: self.line,
        }
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.line;
        while self.peek() != '"' && !self.is_at_end() {
            if self.peek() == '\n' {
                self.line += 1
            }
            self.advance();
        }
        if self.is_at_end() {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        self.advance();
        Ok(Token::StringLiteral(self.substring(self.start + 1, self.current - 1)))
    }

    fn number(&mut self) -> Token {
        while is_digit(self.peek()) {
            self.advance();
        }
        if self.peek() == '.' && is_digit(self.peek_next()) {
            self.advance(); // Consume the .
            while is_digit(self.peek()) {
                self.advance();
            }
        }
        let literal = self.substring(self.start, self.current);
        let value = literal.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        while is_alphanumeric(self.peek()) {
            self.advance();
        }
        // TODO: take a ref in the first place
        match self.substring(self.start, self.current).as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        let token = match self.advance() {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.is_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.is_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.is_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.is_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.is_match('/') {
                    // Comments go on till the end of the line
                    while self.peek() != '\n' && !self.is_at_end() {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            ' ' => Token::Whitespace,
            '\r' => Token::Whitespace,
            '\t' => Token::Whitespace,
            '\n' => {
                self.line += 1;
                Token::Whitespace
            }
            '"' => try!(self.string()),
            c => {
                if is_digit(c) {
                    self.number()
                } else if is_alpha(c) {
                    self.identifier()
                } else {
                    let position = self.current - 1;
                    return Err(format!("Unexpected character {} at line {}, pos {}",
                                       self.char_at(position),
                                       self.line,
                                       position));
                }
            }
        };
        Ok(self.add_context(token))
    }
}

fn tokenize(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            _ => tokens.push(token_with_context),
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

fn run(source: &String) -> Result<(), String> {
    let tokens = try!(tokenize(source));
    for token in tokens {
        println!("{:?}", token);
    }
    Ok(())
}

fn run_file(file_name: &String) -> Result<(), String> {
    match File::open(file_name) {
        Err(_) => {
            Err("Error opening file".into()) // TODO: add context
        }
        Ok(mut file) => {
            let mut source = String::new();
            match file.read_to_string(&mut source) {
                Err(_) => {
                    Err("Error reading file".into()) // TODO: add context
                }
                Ok(_) => run(&source),
            }
        }
    }
}

fn run_prompt() -> Result<(), String> {
    println!("Rulox - A lox interpreter written in Rust");
    let _ = io::stdout().flush(); //TODO: is this okay?
    loop {
        print!("> ");
        let _ = io::stdout().flush();
        let mut source = String::new();
        let _ = io::stdin().read_line(&mut source);
        //TODO: add a way to exit
        try!(run(&source))
        //TODO: report syntax errors to the user
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let result = match args.len() {
        // The first argument is the program name
        1 => run_prompt(),
        2 => run_file(&args[1]),
        _ => {
            println!("Usage: rulox [script]");
            Ok(())
        }
    };
    let exit_code = match result {
        Ok(_) => 0,
        Err(e) => {
            println!("{}", e);
            1
        }
    };
    std::process::exit(exit_code)
}
<commit_msg>Using conditions in pattern matching<commit_after>use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;

#[derive(Debug)]
enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(f64),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    // These are not described in the book but they simplify the parser
    Comment,
    Whitespace,
}

#[derive(Debug)]
struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}

fn is_digit(c: char) -> bool {
    c >= '0' && c <= '9'
}

fn is_alpha(c: char) -> bool {
    (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
}

fn is_alphanumeric(c: char) -> bool {
    is_digit(c) || is_alpha(c)
}

impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn char_at(&self, index: usize) -> char {
        // TODO: there must be a better way
        self.source.chars().nth(index).unwrap()
    }

    fn substring(&self, start: usize, end: usize) -> String {
        self.source.chars().skip(start).take(end - start).collect()
    }

    fn peek(&self) -> char() {
        if self.is_at_end() {
            '\0'
        } else {
            self.char_at(self.current)
        }
    }

    fn peek_next(&self) -> char() {
        if self.current + 1 >= self.source.len() {
            '\0'
        } else {
            self.char_at(self.current + 1)
        }
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        self.char_at(self.current - 1)
    }

    fn is_match(&mut self, expected: char) -> bool {
        if self.is_at_end() {
            return false;
        }
        if self.char_at(self.current) != expected {
            return false;
        }
        self.current += 1;
        return true;
    }

    fn add_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.substring(self.start, self.current),
            line: self.line,
        }
    }

    fn string(&mut self) -> Result<Token, String> {
        let initial_line = self.line;
        while self.peek() != '"' && !self.is_at_end() {
            if self.peek() == '\n' {
                self.line += 1
            }
            self.advance();
        }
        if self.is_at_end() {
            return Err(format!("Unterminated string at line {}", initial_line));
        }
        self.advance();
        Ok(Token::StringLiteral(self.substring(self.start + 1, self.current - 1)))
    }

    fn number(&mut self) -> Token {
        while is_digit(self.peek()) {
            self.advance();
        }
        if self.peek() == '.' && is_digit(self.peek_next()) {
            self.advance(); // Consume the .
            while is_digit(self.peek()) {
                self.advance();
            }
        }
        let literal = self.substring(self.start, self.current);
        let value = literal.parse::<f64>().unwrap();
        Token::NumberLiteral(value)
    }

    fn identifier(&mut self) -> Token {
        while is_alphanumeric(self.peek()) {
            self.advance();
        }
        // TODO: take a ref in the first place
        match self.substring(self.start, self.current).as_ref() {
            "and" => Token::And,
            "class" => Token::Class,
            "else" => Token::Else,
            "false" => Token::False,
            "for" => Token::For,
            "fun" => Token::Fun,
            "if" => Token::If,
            "nil" => Token::Nil,
            "or" => Token::Or,
            "print" => Token::Print,
            "return" => Token::Return,
            "super" => Token::Super,
            "this" => Token::This,
            "true" => Token::True,
            "var" => Token::Var,
            "while" => Token::While,
            identifier => Token::Identifier(identifier.into()),
        }

    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        let token = match self.advance() {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.is_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.is_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.is_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.is_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.is_match('/') {
                    // Comments go on till the end of the line
                    while self.peek() != '\n' && !self.is_at_end() {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            ' ' => Token::Whitespace,
            '\r' => Token::Whitespace,
            '\t' => Token::Whitespace,
            '\n' => {
                self.line += 1;
                Token::Whitespace
            }
            '"' => try!(self.string()),
            c if is_digit(c) => self.number(),
            c if is_alpha(c) => self.identifier(),
            c => {
                return Err(format!("Unexpected character {} at line {}, pos {}",
                                   c,
                                   self.line,
                                   self.current - 1));
            }
        };
        Ok(self.add_context(token))
    }
}

fn tokenize(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        let token_with_context = try!(scanner.scan_next());
        match token_with_context.token {
            // Ignoring tokens we don't care about
            Token::Comment => {}
            Token::Whitespace => {}
            _ => tokens.push(token_with_context),
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

fn run(source: &String) -> Result<(), String> {
    let tokens = try!(tokenize(source));
    for token in tokens {
        println!("{:?}", token);
    }
    Ok(())
}

fn run_file(file_name: &String) -> Result<(), String> {
    match File::open(file_name) {
        Err(_) => {
            Err("Error opening file".into()) // TODO: add context
        }
        Ok(mut file) => {
            let mut source = String::new();
            match file.read_to_string(&mut source) {
                Err(_) => {
                    Err("Error reading file".into()) // TODO: add context
                }
                Ok(_) => run(&source),
            }
        }
    }
}

fn run_prompt() -> Result<(), String> {
    println!("Rulox - A lox interpreter written in Rust");
    let _ = io::stdout().flush(); //TODO: is this okay?
    loop {
        print!("> ");
        let _ = io::stdout().flush();
        let mut source = String::new();
        let _ = io::stdin().read_line(&mut source);
        // TODO: add a way to exit
        try!(run(&source))
        // TODO: report syntax errors to the user
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let result = match args.len() {
        // The first argument is the program name
        1 => run_prompt(),
        2 => run_file(&args[1]),
        _ => {
            println!("Usage: rulox [script]");
            Ok(())
        }
    };
    let exit_code = match result {
        Ok(_) => 0,
        Err(e) => {
            println!("{}", e);
            1
        }
    };
    std::process::exit(exit_code)
}
<|endoftext|>


<commit_before>
extern crate rustc_serialize;
extern crate docopt;
extern crate tiny_http;

use docopt::Docopt;

use std::thread;

use std::net::{TcpListener, TcpStream};
use std::net::SocketAddr;

use std::io::Read;
use std::io::Cursor;

use std::str::FromStr;

use tiny_http::{Server, Request, Response, StatusCode, Method, Header};

const USAGE: &'static str = "
Naval Fate.

Usage:
  rust_redis [--port=<p>]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --port=<p>    Port to listen on [default: 5001]
";

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

fn handle_client(mut stream: TcpStream) {
    let mut buffer = Vec::<u8>::new();
    let _ = stream.read_to_end(&mut buffer).unwrap();
    println!("{}", String::from_utf8(buffer).unwrap());
}

fn handle_request(mut request: Request) {
    //println!("Headers: {}", request.headers());
    let mut content = String::new();
    request.as_reader().read_to_string(&mut content).unwrap();
    println!("Body: {}", content);

    let response = match request.method() {
        &Method::Get => handle_get(&request),
        &Method::Post => handle_post(&request),
        &Method::Put => handle_put(&request),
        &Method::Delete => handle_delete(&request),
        _ => handle_nonstandard_request(&request)
    };

    request.respond(response);
}

//static headers: Vec<tiny_http::Header> = Vec::new();
//static header: Header = tiny_http::Header::from_bytes(&b"Content-Type", &b"text/plain").unwrap();

fn http_response<S>(status: StatusCode, data: S) -> Response<Cursor<Vec<u8>>> where S: Into<String> {

        let data = data.into();
        let data_len = data.len();

        Response::new(
            status,
            vec![
                Header::from_bytes(&b"Content-Type"[..], &b"text/plain; charset=UTF-8"[..]).unwrap()
                    ],
            Cursor::new(data.into_bytes()),
            Some(data_len),
            None,
            )
}

fn handle_get(request: &Request) -> Response<Cursor<Vec<u8>>> {
    println!("GET");
    http_response(StatusCode(200), "COOL")
    //Response::new(StatusCode(200), headers, "COOL", Option::None, Option::None)
}
fn handle_post(request: &Request) -> Response<Cursor<Vec<u8>>> {
    println!("POST");
    http_response(StatusCode(200), "COOL")
}
fn handle_put(request: &Request) -> Response<Cursor<Vec<u8>>> {
    println!("PUT");
    http_response(StatusCode(200), "COOL")
}
fn handle_delete(request: &Request) -> Response<Cursor<Vec<u8>>> {
    println!("DELETE");
    http_response(StatusCode(200), "COOL")
}
fn handle_nonstandard_request(request: &Request) -> Response<Cursor<Vec<u8>>> {
    println!("WTF!!!");
    http_response(StatusCode(200), "COOL")
}

fn main() {

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

    println!("Listening on port: {:?}", args.flag_port);

    let addr: SocketAddr = SocketAddr::from_str(&format!("127.0.0.1:{}", args.flag_port)).unwrap();

    let server = Server::http(addr).unwrap();

    loop {
        // blocks until the next request is received
        let request = match server.recv() {
            Ok(rq) => handle_request(rq),
            Err(e) => { println!("error: {}", e); break }
        };

        // do something with the request
        // ...
        }

/*
    let listener = TcpListener::bind(addr).unwrap();

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                thread::spawn(move|| {
                    handle_client(stream)
                });
            }
            Err(e) => {
                println!("ERROR: {:}", e);
            }
        }
    }
*/
}
<commit_msg>Basic parsing of request, starting to handle json<commit_after>
extern crate rustc_serialize;
extern crate docopt;
extern crate tiny_http;

use docopt::Docopt;

use std::thread;

use std::net::{TcpListener, TcpStream};
use std::net::SocketAddr;

use std::io::Read;
use std::io::Cursor;

use std::str::FromStr;

use rustc_serialize::json::Json;

use tiny_http::{Server, Request, Response, StatusCode, Method, Header};

const USAGE: &'static str = "
Naval Fate.

Usage:
  rust_redis [--port=<p>]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --port=<p>    Port to listen on [default: 5001]
";

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

fn handle_client(mut stream: TcpStream) {
    let mut buffer = Vec::<u8>::new();
    let _ = stream.read_to_end(&mut buffer).unwrap();
    println!("{}", String::from_utf8(buffer).unwrap());
}

enum ParsedRequest {
    Unknown,
    GetRequest(String),
    PostJson(Json),
    PutJson(Json),
    DeleteRequest(String)
}

fn parse_request(request: &mut Request) -> ParsedRequest {
    let response = match request.method() {
        &Method::Get => ParsedRequest::GetRequest(request.url().to_string()),
        &Method::Post => ParsedRequest::PostJson(get_json(request)),
        &Method::Put => ParsedRequest::PutJson(get_json(request)),
        &Method::Delete => ParsedRequest::DeleteRequest(request.url().to_string()),
        _ => ParsedRequest::Unknown
    };
    response
}

fn handle_request(mut request: Request) {

    let mut content = String::new();
    request.as_reader().read_to_string(&mut content).unwrap();
    println!("Body: {}", content);

    let response = match parse_request(&mut request) {
        ParsedRequest::GetRequest(url) => handle_get(url), //http_response(StatusCode(500), "Unsupported"),
        ParsedRequest::PostJson(json) => handle_post(json),
        ParsedRequest::PutJson(json) => handle_put(json),
        ParsedRequest::DeleteRequest(url) => handle_delete(url),
        ParsedRequest::Unknown => http_response(StatusCode(400), "Send me JSON, fuck face")
    };
    request.respond(response);
}

fn get_json(request: &mut Request) -> Json {
    println!("Getting Json");
    let mut content = String::new();
    request.as_reader().read_to_string(&mut content).unwrap();
    let json: Json = content.parse().unwrap();
    println!("GOT Json");
    json
}

fn http_response<S>(status: StatusCode, data: S) -> Response<Cursor<Vec<u8>>> where S: Into<String> {

        let data = data.into();
        let data_len = data.len();

        Response::new(
            status,
            vec![
                Header::from_bytes(&b"Content-Type"[..], &b"text/plain; charset=UTF-8"[..]).unwrap()
                    ],
            Cursor::new(data.into_bytes()),
            Some(data_len),
            None,
            )
}

fn handle_get(url: String) -> Response<Cursor<Vec<u8>>> {
    println!("GET");
    http_response(StatusCode(200), "COOL")
}
fn handle_post(request: Json) -> Response<Cursor<Vec<u8>>> {
    println!("POST");
    http_response(StatusCode(200), "COOL")
}
fn handle_put(request: Json) -> Response<Cursor<Vec<u8>>> {
    println!("PUT");
    http_response(StatusCode(200), "COOL")
}
fn handle_delete(url: String) -> Response<Cursor<Vec<u8>>> {
    println!("DELETE");
    http_response(StatusCode(200), "COOL")
}
fn handle_nonstandard_request(request: Json) -> Response<Cursor<Vec<u8>>> {
    println!("WTF!!!");
    http_response(StatusCode(200), "COOL")
}

fn main() {

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

    println!("Listening on port: {:?}", args.flag_port);

    let addr: SocketAddr = SocketAddr::from_str(&format!("127.0.0.1:{}", args.flag_port)).unwrap();

    let server = Server::http(addr).unwrap();

    loop {
        // blocks until the next request is received
        let request = match server.recv() {
            Ok(rq) => handle_request(rq),
            Err(e) => { println!("error: {}", e); break }
        };

        // do something with the request
        // ...
        }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate rusthub;
extern crate notify_rust;
extern crate rustc_serialize;
extern crate env_logger;
mod io;
mod notify;
//use notify_rust::{Notification, NotificationHint, NotificationUrgency};
use rusthub::notifications;
use rusthub::notifications::{NotificationResponse, Notifications, Notification};
use std::thread;
use std::time::Duration;

fn main() {
    // INIT
    const TIMEOUT: i32 = 120;
    const DEFAULT_POLL_INTERVAL: u64 = 60; // In seconds
    env_logger::init().unwrap();
    let token: String = io::retrieve_token(TIMEOUT).unwrap();
    debug!("Token: {}", &token);
    // TODO: Check if this token is stale before using it in the main loop
    //
    // MAIN LOOP
    loop {
        // GET NOTIFICATIONS
        let response: NotificationResponse = notifications::get_notifications_oauth(&token).unwrap();
        let notifications: Notifications = match response.notifications {
            Some(result) => {
                match result {
                    Ok(notifications) => notifications,
                    Err(err) => {
                        error!("While retrieving notifications: {}", err);
                        Notifications {list: vec!()}
                    }
                }
            },
            None => Notifications {list: vec!()}
        };
        //
        // Filter unread notifications that haven't been updated since last check
        let display_notifications: Vec<Notification> = match io::get_saved_notifications() {
            Ok(saves) => notifications.list
                .clone()
                .into_iter()
                .filter(|n| !saves.list.iter().any(|sn| n.updated_at == sn.updated_at && n.subject.title == sn.subject.title))
                .collect(),
            Err(err) => {
                error!("{}", err);
                notifications.list.clone()
            }
        };
        debug!("\nUnread Notifications: {} out of {}", display_notifications.len(), notifications.list.len());
        //
        // Display notifications
        for notification in display_notifications {
            notify::show_notification(&notification);
            thread::sleep(Duration::new(0, 500000000)); // Sleep for a half-second to give notify api a chance
        }
        //
        // Cache Previous Notifications
        io::write_notifications(&notifications)
            .unwrap_or_else(|err| error!("While writing notifications: {}", err));
        //
        // Sleep for requested time by GitHub
        let sleep_time: u64 = match response.poll_interval {
            Some(time) => {
                // Safe to assume that if rate_limit was returned, the other values were as well.
                debug!("\nPoll Interval: {}\nRate Limit: {}\nRate Limit Remaining: {}",
                       time, response.rate_limit.unwrap(), response.rate_limit_remaining.unwrap());
                time as u64
            },
            None => DEFAULT_POLL_INTERVAL
        };
        thread::sleep(Duration::new(sleep_time, 0));
        //
    }
}<commit_msg>un-vague-ified main function<commit_after>#[macro_use] extern crate log;
extern crate rusthub;
extern crate notify_rust;
extern crate rustc_serialize;
extern crate env_logger;
mod io;
mod notify;
use rusthub::notifications;
use rusthub::notifications::{NotificationResponse, Notifications};
use std::thread;
use std::time::Duration;

const TIMEOUT: i32 = 120;
const DEFAULT_POLL_INTERVAL: usize = 60; // In seconds

fn parse_response(response: &NotificationResponse) -> Notifications {
    match response.notifications.clone() {
        Some(result) => {
            match result {
                Ok(notifications) => notifications,
                Err(err) => {
                    error!("While retrieving notifications: {}", err);
                    Notifications {list: vec!()}
                }
            }
        },
        None => Notifications {list: vec!()}
    }
}

fn filter_unseen(notifications: &Notifications) -> Notifications {
    let list = match io::get_saved_notifications() {
        Ok(saves) => notifications.list
            .clone()
            .into_iter()
            .filter(|n| !saves.list.iter().any(|sn| n.updated_at == sn.updated_at && n.subject.title == sn.subject.title))
            .collect(),
        Err(err) => {
            error!("{}", err);
            notifications.list.clone()
        }
    };

    Notifications {list: list}
}

fn main() {
    // INIT
    env_logger::init().unwrap();
    let token: String = io::retrieve_token(TIMEOUT).unwrap();
    debug!("Token: {}", &token);
    // TODO: Check if this token is stale before using it in the main loop

    // MAIN LOOP
    loop {
        // Get notifications
        let response: NotificationResponse = notifications::get_notifications_oauth(&token).unwrap();
        let notifications: Notifications = parse_response(&response);

        let unseen = filter_unseen(&notifications);
        debug!("Unseen Notifications: {} out of {}", unseen.list.len(), notifications.list.len());

        // Display notifications
        for notification in &unseen.list {
            notify::show_notification(&notification);
            thread::sleep(Duration::new(0, 500000000)); // Sleep for a half-second to give notify api a chance
        }

        // Cache Previous Notifications
        io::write_notifications(&notifications)
            .unwrap_or_else(|err| error!("While writing notifications: {}", err));

        // Sleep for requested time by GitHub
        let sleep_time: u64 = response.poll_interval.unwrap_or(DEFAULT_POLL_INTERVAL) as u64;
        thread::sleep(Duration::new(sleep_time, 0));
    }
}<|endoftext|>