<commit_before>#![recursion_limit = "1024"]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;
#[macro_use]
extern crate nom;

extern crate bip_bencode;
extern crate bip_metainfo;
extern crate bip_util;
extern crate bip_utracker;
extern crate chrono;
extern crate futures;
extern crate hyper;
extern crate pnet;
extern crate pnet_macros_support;
extern crate pretty_env_logger;
extern crate rustyline;
extern crate tokio_core;
extern crate url;

mod btclient;
mod packet;

mod errors {
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain!{}
}

use bip_bencode::Bencode;
use bip_metainfo::MetainfoFile;
use bip_utracker::contact::CompactPeersV4;
use btclient::*;
use chrono::{TimeZone, UTC};
use errors::*;
use hyper::Client;
use hyper::header::Connection;
use packet::peer_pkt::{MutablePeerHandshakePacket, MutablePeerMessagePacket};
use pnet::packet::Packet;
use rustyline::completion::FilenameCompleter;
use rustyline::error::ReadlineError;
use rustyline::{Config, CompletionType, Editor};
use tokio_core::io::{read, write_all};
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use url::Url;

use std::fs::File;
use std::io::prelude::*;
use std::net::SocketAddr;
use std::str;
use std::string::String;
use std::thread;

const HISTORY_FILE: &'static str = ".rustyline.history";
const PEER_HANDSHAKE_STRUCT_SZ: usize = 68;
const PEER_REQ_PKT_SZ: usize = 14;

/// Print file list from the torrent.
fn print_files(bytes: &[u8]) -> Result<()> {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();

    println!("File List:");
    println!("Size (bytes)\tPath");
    println!("------------\t----------------------------------------------");
    for file in info.files() {
        println!("{:12}\t{}",
                 file.length(),
                 file.paths().next().unwrap_or("<unknown>"));
    }
    Ok(())
}

/// Print general information about the torrent.
fn print_metainfo_overview(bytes: &[u8]) -> Result<()> {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();
    let info_hash_hex = metainfo.info_hash()
        .as_ref()
        .iter()
        .map(|b| format!("{:02x}", b))
        .fold(String::new(), |mut acc, nex| {
            acc.push_str(&nex);
            acc
        });
    let utc_creation_date = metainfo.creation_date().map(|c| UTC.timestamp(c, 0));

    println!("------Metainfo File Overview-----");

    println!("InfoHash: {}", info_hash_hex);
    println!("Main Tracker: {:?}", metainfo.main_tracker());
    println!("Comment: {:?}", metainfo.comment());
    println!("Creator: {:?}", metainfo.created_by());
    println!("Creation Date: {:?}", utc_creation_date);

    println!("Directory: {:?}", info.directory());
    println!("Piece Length: {:?}", info.piece_length());
    println!("Number Of Pieces: {}", info.pieces().count());
    println!("Number Of Files: {}", info.files().count());
    println!("Total File Size: {}\n",
             info.files().fold(0, |acc, nex| acc + nex.length()));

    print_files(bytes)?;
    Ok(())
}

fn connect_to_tracker(metainfo: MetainfoFile,
                      peer_id: String,
                      port: u16)
                      -> Result<(Vec<String>, String)> {
    debug!("connecting to tracker: {:?}", metainfo.main_tracker());

    let info_hash = metainfo.info_hash();
    // TODO figure can this conversion to url-encoded form be done safely?
    let info_hash_str = unsafe { str::from_utf8_unchecked(info_hash.as_ref()) };

    // TODO this needs to be calculated based on what we have
    let total_len = metainfo.info().files().fold(0, |acc, nex| acc + nex.length());

    let mut url = Url::parse(metainfo.main_tracker().unwrap()).unwrap();
    url.query_pairs_mut()
        .append_pair("info_hash", info_hash_str)
        .append_pair("peer_id", &peer_id)
        .append_pair("port", &(port.to_string()))
        // TODO parametrize this
        .append_pair("uploaded", "0")
        // TODO parametrize this
        .append_pair("downloaded", "0")
        // TODO see note on total_len above
        .append_pair("left", &(total_len.to_string()))
        .append_pair("compact", "1")
        .append_pair("event", "started")
        .append_pair("supportcrypto", "0");
    trace!("URL {:?}", url);

    let client = Client::new();
    let mut res = client.get(url).header(Connection::close()).send().unwrap();
    let mut buffer = Vec::new();
    res.read_to_end(&mut buffer).unwrap();
    debug!("{:?}", res);
    let bencode = Bencode::decode(&buffer).unwrap();
    trace!("{:?}", bencode);
    let (_, peers) = CompactPeersV4::from_bytes(bencode.dict()
            .unwrap()
            .lookup("peers")
            .unwrap()
            .bytes()
            .unwrap())
        .unwrap();
    trace!("{:?}", peers);
    let mut ip_ports: Vec<String> = Vec::new();
    debug!("Peer list received:");
    for peer in peers.iter() {
        debug!("{:?}", peer);
        ip_ports.push(peer.to_string());
    }
    Ok((ip_ports, info_hash_str.to_string()))
}

fn send_keep_alive(mut stream: &TcpStream) -> Result<()> {
    let buf = vec![0; 1];
    match stream.write(&buf) {
        Ok(_) => Ok(()),
        Err(e) => bail!("Writing to stream  failed! {:?}", e),
    }
}

fn peer_connections(peer_ip_ports: Vec<String>, info_hash: &str, peer_id: String) -> Result<()> {
    for ip_port in peer_ip_ports {
        let peer_ip_port = ip_port.clone();
        let info_hash_clone = info_hash.to_string().clone();
        let peer_id_clone = peer_id.clone();
        thread::spawn(move || {
            fn run(pip: String, ih: String, pid: String) -> Result<()> {
                let mut l = Core::new().unwrap();
                // TODO setup timeout before handshake
                // let dur = Duration::from_secs(100);
                // let timeout = Timeout::new(dur, &l.handle()).unwrap();

                if let Ok(client) = handshake_peer(&mut l, pip, ih, pid)
                    .chain_err(|| "handshake failed") {
                    // debug!("Communicating with peer {:?}", peer_ip_port_cl);
                    // debug!("Starting thread timer...");
                    if let Ok((_, buf, amt)) =
                        l.run(read(client, vec![0; PEER_HANDSHAKE_STRUCT_SZ]))
                            .chain_err(|| "didn't receive handshake response from peer") {
                        if buf[0] == 19 &&
                           String::from_utf8_lossy(&buf[1..20]) == "BitTorrent protocol" &&
                           amt == 68 {
                            info!("handshake successful!");
                        } else {
                            info!("not a useful peer")
                        }
                    } else {
                        bail!("");
                    }
                } else {
                    bail!("");
                }
                Ok(())
            }
            if let Err(ref e) = run(peer_ip_port, info_hash_clone, peer_id_clone) {
                println!("error: {}", e);

                for e in e.iter().skip(1) {
                    println!("caused by: {}", e);
                }

                if let Some(backtrace) = e.backtrace() {
                    println!("backtrace: {:?}", backtrace);
                }
            }

            // TODO determine the below three parameters logically
            // let index = 0;
            // let begin = 0;
            // let length = 10;
            // match request_piece(&stream, index, begin, length) {
            //     Ok(buf_read) => {
            //         trace!("buf_read: {:?}", buf_read);
            //     }
            //     Err(_) => error!("Requesting piece failed!"),
            // }
            // send_keep_alive(&stream);
        });
    }
    Ok(())
}

fn request_piece(mut stream: &TcpStream, index: u32, begin: u32, length: u32) -> Result<Vec<u8>> {
    let mut buf = vec![0u8; PEER_REQ_PKT_SZ];
    let mut peer_msg_pkt = MutablePeerMessagePacket::new(&mut buf).unwrap();
    peer_msg_pkt.set_len(13);
    peer_msg_pkt.set_id(6);
    let mut index_begin_length: Vec<u32> = Vec::new();
    index_begin_length.push(index);
    index_begin_length.push(begin);
    index_begin_length.push(length);
    let byte_arr = unsafe { std::mem::transmute::<Vec<u32>, Vec<u8>>(index_begin_length) };

    peer_msg_pkt.set_payload(&byte_arr);

    match stream.write(peer_msg_pkt.packet()) {
        Ok(_) => {
            let mut buf_read = vec![0; 2048];
            match stream.read(&mut buf_read) {
                Ok(bytes_read) => {
                    debug!("Bytes read: {:?}", bytes_read);
                    Ok(buf_read[..bytes_read].to_vec())
                }
                Err(e) => {
                    // error!("Read failed! {:?}", e);
                    bail!("Read failed! {:?}", e)
                }
            }
        }
        Err(e) => {
            bail!("Write to stream failed! {:?}", e)
            // Err("Write to stream failed!".to_owned())
        }
    }
}

fn handshake_peer(core: &mut Core,
                  peer_ip_port: String,
                  info_hash: String,
                  peer_id: String)
                  -> Result<TcpStream> {
    let mut buf = vec![0u8; PEER_HANDSHAKE_STRUCT_SZ];
    let mut ph = MutablePeerHandshakePacket::new(&mut buf).unwrap();
    ph.set_pstrlen("BitTorrent protocol".len() as u8);
    ph.set_pstr(&String::from("BitTorrent protocol").into_bytes());
    ph.set_reserved(&[0; 8]);
    ph.set_info_hash(info_hash.as_bytes());
    ph.set_peer_id(peer_id.as_bytes());
    let handle = core.handle();
    let client = TcpStream::connect(&peer_ip_port.parse::<SocketAddr>().unwrap(), &handle);
    let client = core.run(client).chain_err(|| "unable to connect to peer")?;
    let (client, _) = core.run(write_all(client, ph.packet())).unwrap();
    trace!("{:?}", client);
    Ok(client)
}

fn main() {
    pretty_env_logger::init();

    // See: https://brson.github.io/2016/11/30/starting-with-error-chain for explanation
    if let Err(ref e) = cli_run() {
        println!("error: {}", e);

        for e in e.iter().skip(1) {
            println!("caused by: {}", e);
        }

        if let Some(backtrace) = e.backtrace() {
            println!("backtrace: {:?}", backtrace);
        }

        ::std::process::exit(1);
    }
}

#[allow(cyclomatic_complexity)]
fn cli_run() -> Result<()> {
    let mut btclient = BTClient::new();

    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .build();
    let c = FilenameCompleter::new();
    let mut rl = Editor::with_config(config);
    rl.set_completer(Some(c));
    if rl.load_history(HISTORY_FILE).is_err() {
        info!("No previous history!");
    }

    loop {
        let readline = rl.readline("> ");
        match readline {
            Ok(line) => {
                rl.add_history_entry(line.as_ref());

                let line = line.trim().split(' ').collect::<Vec<&str>>();

                match line[0] {
                    "help" | "h" => {
                        println!("Commands:
parse/p <torrent file path>      - show Metainfo File Overview
add/a <torrent file path>        - add a new torrent file for tracking
remove/r <torrent id>            - remove given torrent from tracking list
list/l                           - list torrents being tracked
showfiles/sf <torrent file path> - show files in the torrent
help/h                           - show this help");
                    }
                    "parse" | "p" => {
                        if line.len() != 2 {
                            error!("usage: parse <torrent file>");
                        } else {
                            let path = line[1];
                            debug!("MetainfoFile: {:?}", path);

                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();
                                    print_metainfo_overview(&bytes)?;
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "add" | "a" => {
                        if line.len() != 2 {
                            error!("usage: add <torrent file>");
                        } else if let Ok(file) = File::open(line[1]) {
                            btclient.add(file)?;
                        } else {
                            error!("Unable to open file, are you sure it exists?");
                        }
                    }
                    "remove" | "r" => {
                        if line.len() != 2 {
                            error!("usage: remove <torrent number>");
                        } else {
                            let id = line[1].parse::<usize>().unwrap();
                            btclient.remove(id)?;
                        }
                    }
                    "list" | "l" => {
                        if line.len() != 1 {
                            error!("usage: list");
                        } else {
                            let t_list = btclient.list();
                            println!("ID  Torrent");
                            println!("--  ----------------------------------------------");
                            for t in t_list {
                                println!("{:2}  {}", t.0, t.1);
                            }
                        }
                    }
                    "showfiles" | "sf" => {
                        if line.len() != 2 {
                            error!("usage: showfiles <torrent file>");
                        } else {
                            let path = line[1];
                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();
                                    print_files(&bytes)?;
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "" => {}
                    _ => {
                        println!("invalid command, see \"help\"");
                    }
                }
            }
            Err(ReadlineError::Interrupted) => {
                info!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                info!("CTRL-D");
                break;
            }
            Err(err) => {
                error!("Error: {:?}", err);
                break;
            }
        }
    }
    rl.save_history(HISTORY_FILE).unwrap();
    Ok(())
}
<commit_msg>Add stubs for download/seed commands<commit_after>#![recursion_limit = "1024"]

#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;
#[macro_use]
extern crate nom;

extern crate bip_bencode;
extern crate bip_metainfo;
extern crate bip_util;
extern crate bip_utracker;
extern crate chrono;
extern crate futures;
extern crate hyper;
extern crate pnet;
extern crate pnet_macros_support;
extern crate pretty_env_logger;
extern crate rustyline;
extern crate tokio_core;
extern crate url;

mod btclient;
mod packet;

mod errors {
    // Create the Error, ErrorKind, ResultExt, and Result types
    error_chain!{}
}

use bip_bencode::Bencode;
use bip_metainfo::MetainfoFile;
use bip_utracker::contact::CompactPeersV4;
use btclient::*;
use chrono::{TimeZone, UTC};
use errors::*;
use hyper::Client;
use hyper::header::Connection;
use packet::peer_pkt::{MutablePeerHandshakePacket, MutablePeerMessagePacket};
use pnet::packet::Packet;
use rustyline::completion::FilenameCompleter;
use rustyline::error::ReadlineError;
use rustyline::{Config, CompletionType, Editor};
use tokio_core::io::{read, write_all};
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use url::Url;

use std::fs::File;
use std::io::prelude::*;
use std::net::SocketAddr;
use std::str;
use std::string::String;
use std::thread;

const HISTORY_FILE: &'static str = ".rustyline.history";
const PEER_HANDSHAKE_STRUCT_SZ: usize = 68;
const PEER_REQ_PKT_SZ: usize = 14;

/// Print file list from the torrent.
fn print_files(bytes: &[u8]) -> Result<()> {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();

    println!("File List:");
    println!("Size (bytes)\tPath");
    println!("------------\t----------------------------------------------");
    for file in info.files() {
        println!("{:12}\t{}",
                 file.length(),
                 file.paths().next().unwrap_or("<unknown>"));
    }
    Ok(())
}

/// Print general information about the torrent.
fn print_metainfo_overview(bytes: &[u8]) -> Result<()> {
    let metainfo = MetainfoFile::from_bytes(bytes).unwrap();
    let info = metainfo.info();
    let info_hash_hex = metainfo.info_hash()
        .as_ref()
        .iter()
        .map(|b| format!("{:02x}", b))
        .fold(String::new(), |mut acc, nex| {
            acc.push_str(&nex);
            acc
        });
    let utc_creation_date = metainfo.creation_date().map(|c| UTC.timestamp(c, 0));

    println!("------Metainfo File Overview-----");

    println!("InfoHash: {}", info_hash_hex);
    println!("Main Tracker: {:?}", metainfo.main_tracker());
    println!("Comment: {:?}", metainfo.comment());
    println!("Creator: {:?}", metainfo.created_by());
    println!("Creation Date: {:?}", utc_creation_date);

    println!("Directory: {:?}", info.directory());
    println!("Piece Length: {:?}", info.piece_length());
    println!("Number Of Pieces: {}", info.pieces().count());
    println!("Number Of Files: {}", info.files().count());
    println!("Total File Size: {}\n",
             info.files().fold(0, |acc, nex| acc + nex.length()));

    print_files(bytes)?;
    Ok(())
}

fn connect_to_tracker(metainfo: MetainfoFile,
                      peer_id: String,
                      port: u16)
                      -> Result<(Vec<String>, String)> {
    debug!("connecting to tracker: {:?}", metainfo.main_tracker());

    let info_hash = metainfo.info_hash();
    // TODO figure can this conversion to url-encoded form be done safely?
    let info_hash_str = unsafe { str::from_utf8_unchecked(info_hash.as_ref()) };

    // TODO this needs to be calculated based on what we have
    let total_len = metainfo.info().files().fold(0, |acc, nex| acc + nex.length());

    let mut url = Url::parse(metainfo.main_tracker().unwrap()).unwrap();
    url.query_pairs_mut()
        .append_pair("info_hash", info_hash_str)
        .append_pair("peer_id", &peer_id)
        .append_pair("port", &(port.to_string()))
        // TODO parametrize this
        .append_pair("uploaded", "0")
        // TODO parametrize this
        .append_pair("downloaded", "0")
        // TODO see note on total_len above
        .append_pair("left", &(total_len.to_string()))
        .append_pair("compact", "1")
        .append_pair("event", "started")
        .append_pair("supportcrypto", "0");
    trace!("URL {:?}", url);

    let client = Client::new();
    let mut res = client.get(url).header(Connection::close()).send().unwrap();
    let mut buffer = Vec::new();
    res.read_to_end(&mut buffer).unwrap();
    debug!("{:?}", res);
    let bencode = Bencode::decode(&buffer).unwrap();
    trace!("{:?}", bencode);
    let (_, peers) = CompactPeersV4::from_bytes(bencode.dict()
            .unwrap()
            .lookup("peers")
            .unwrap()
            .bytes()
            .unwrap())
        .unwrap();
    trace!("{:?}", peers);
    let mut ip_ports: Vec<String> = Vec::new();
    debug!("Peer list received:");
    for peer in peers.iter() {
        debug!("{:?}", peer);
        ip_ports.push(peer.to_string());
    }
    Ok((ip_ports, info_hash_str.to_string()))
}

fn send_keep_alive(mut stream: &TcpStream) -> Result<()> {
    let buf = vec![0; 1];
    match stream.write(&buf) {
        Ok(_) => Ok(()),
        Err(e) => bail!("Writing to stream  failed! {:?}", e),
    }
}

fn peer_connections(peer_ip_ports: Vec<String>, info_hash: &str, peer_id: String) -> Result<()> {
    for ip_port in peer_ip_ports {
        let peer_ip_port = ip_port.clone();
        let info_hash_clone = info_hash.to_string().clone();
        let peer_id_clone = peer_id.clone();
        thread::spawn(move || {
            fn run(pip: String, ih: String, pid: String) -> Result<()> {
                let mut l = Core::new().unwrap();
                // TODO setup timeout before handshake
                // let dur = Duration::from_secs(100);
                // let timeout = Timeout::new(dur, &l.handle()).unwrap();

                if let Ok(client) = handshake_peer(&mut l, pip, ih, pid)
                    .chain_err(|| "handshake failed") {
                    // debug!("Communicating with peer {:?}", peer_ip_port_cl);
                    // debug!("Starting thread timer...");
                    if let Ok((_, buf, amt)) =
                        l.run(read(client, vec![0; PEER_HANDSHAKE_STRUCT_SZ]))
                            .chain_err(|| "didn't receive handshake response from peer") {
                        if buf[0] == 19 &&
                           String::from_utf8_lossy(&buf[1..20]) == "BitTorrent protocol" &&
                           amt == 68 {
                            info!("handshake successful!");
                        } else {
                            info!("not a useful peer")
                        }
                    } else {
                        bail!("");
                    }
                } else {
                    bail!("");
                }
                Ok(())
            }
            if let Err(ref e) = run(peer_ip_port, info_hash_clone, peer_id_clone) {
                println!("error: {}", e);

                for e in e.iter().skip(1) {
                    println!("caused by: {}", e);
                }

                if let Some(backtrace) = e.backtrace() {
                    println!("backtrace: {:?}", backtrace);
                }
            }

            // TODO determine the below three parameters logically
            // let index = 0;
            // let begin = 0;
            // let length = 10;
            // match request_piece(&stream, index, begin, length) {
            //     Ok(buf_read) => {
            //         trace!("buf_read: {:?}", buf_read);
            //     }
            //     Err(_) => error!("Requesting piece failed!"),
            // }
            // send_keep_alive(&stream);
        });
    }
    Ok(())
}

fn request_piece(mut stream: &TcpStream, index: u32, begin: u32, length: u32) -> Result<Vec<u8>> {
    let mut buf = vec![0u8; PEER_REQ_PKT_SZ];
    let mut peer_msg_pkt = MutablePeerMessagePacket::new(&mut buf).unwrap();
    peer_msg_pkt.set_len(13);
    peer_msg_pkt.set_id(6);
    let mut index_begin_length: Vec<u32> = Vec::new();
    index_begin_length.push(index);
    index_begin_length.push(begin);
    index_begin_length.push(length);
    let byte_arr = unsafe { std::mem::transmute::<Vec<u32>, Vec<u8>>(index_begin_length) };

    peer_msg_pkt.set_payload(&byte_arr);

    match stream.write(peer_msg_pkt.packet()) {
        Ok(_) => {
            let mut buf_read = vec![0; 2048];
            match stream.read(&mut buf_read) {
                Ok(bytes_read) => {
                    debug!("Bytes read: {:?}", bytes_read);
                    Ok(buf_read[..bytes_read].to_vec())
                }
                Err(e) => {
                    // error!("Read failed! {:?}", e);
                    bail!("Read failed! {:?}", e)
                }
            }
        }
        Err(e) => {
            bail!("Write to stream failed! {:?}", e)
            // Err("Write to stream failed!".to_owned())
        }
    }
}

fn handshake_peer(core: &mut Core,
                  peer_ip_port: String,
                  info_hash: String,
                  peer_id: String)
                  -> Result<TcpStream> {
    let mut buf = vec![0u8; PEER_HANDSHAKE_STRUCT_SZ];
    let mut ph = MutablePeerHandshakePacket::new(&mut buf).unwrap();
    ph.set_pstrlen("BitTorrent protocol".len() as u8);
    ph.set_pstr(&String::from("BitTorrent protocol").into_bytes());
    ph.set_reserved(&[0; 8]);
    ph.set_info_hash(info_hash.as_bytes());
    ph.set_peer_id(peer_id.as_bytes());
    let handle = core.handle();
    let client = TcpStream::connect(&peer_ip_port.parse::<SocketAddr>().unwrap(), &handle);
    let client = core.run(client).chain_err(|| "unable to connect to peer")?;
    let (client, _) = core.run(write_all(client, ph.packet())).unwrap();
    trace!("{:?}", client);
    Ok(client)
}

fn main() {
    pretty_env_logger::init();

    // See: https://brson.github.io/2016/11/30/starting-with-error-chain for explanation
    if let Err(ref e) = cli_run() {
        println!("error: {}", e);

        for e in e.iter().skip(1) {
            println!("caused by: {}", e);
        }

        if let Some(backtrace) = e.backtrace() {
            println!("backtrace: {:?}", backtrace);
        }

        ::std::process::exit(1);
    }
}

#[allow(cyclomatic_complexity)]
fn cli_run() -> Result<()> {
    let mut btclient = BTClient::new();

    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .build();
    let c = FilenameCompleter::new();
    let mut rl = Editor::with_config(config);
    rl.set_completer(Some(c));
    if rl.load_history(HISTORY_FILE).is_err() {
        info!("No previous history!");
    }

    loop {
        let readline = rl.readline("> ");
        match readline {
            Ok(line) => {
                rl.add_history_entry(line.as_ref());

                let line = line.trim().split(' ').collect::<Vec<&str>>();

                match line[0] {
                    "help" | "h" => {
                        println!("Commands:
parse/p <torrent file path>      - show Metainfo File Overview
add/a <torrent file path>        - add a new torrent file for tracking
remove/r <torrent id>            - remove given torrent from tracking list
list/l                           - list torrents being tracked
showfiles/sf <torrent id>        - show files in the torrent
download/d <torrent id>          - download this torrent
seed/s <torrent id>              - seed this torrent
help/h                           - show this help");
                    }
                    "parse" | "p" => {
                        if line.len() != 2 {
                            error!("usage: parse <torrent file>");
                        } else {
                            let path = line[1];
                            debug!("MetainfoFile: {:?}", path);

                            match File::open(path) {
                                Ok(mut f) => {
                                    let mut bytes: Vec<u8> = Vec::new();
                                    f.read_to_end(&mut bytes).unwrap();
                                    print_metainfo_overview(&bytes)?;
                                }
                                Err(e) => error!("{:?}", e),
                            }
                        }
                    }
                    "add" | "a" => {
                        if line.len() != 2 {
                            error!("usage: add <torrent file>");
                        } else if let Ok(file) = File::open(line[1]) {
                            btclient.add(file)?;
                        } else {
                            error!("Unable to open file, are you sure it exists?");
                        }
                    }
                    "remove" | "r" => {
                        if line.len() != 2 {
                            error!("usage: remove <torrent number>");
                        } else {
                            let id = line[1].parse::<usize>().unwrap();
                            btclient.remove(id)?;
                        }
                    }
                    "list" | "l" => {
                        if line.len() != 1 {
                            error!("usage: list");
                        } else {
                            let t_list = btclient.list();
                            println!("ID  Torrent");
                            println!("--  ----------------------------------------------");
                            for t in t_list {
                                println!("{:2}  {}", t.0, t.1);
                            }
                        }
                    }
                    "showfiles" | "sf" => {
                        if line.len() != 2 {
                            error!("usage: showfiles <torrent id>");
                        } else {
                            // TODO call a btclient fn
                        }
                    }
                    "download" | "d" => {
                        if line.len() != 2 {
                            error!("usage: download <torrent id>");
                        } else {

                        }
                    }
                    "" => {}
                    _ => {
                        println!("invalid command, see \"help\"");
                    }
                }
            }
            Err(ReadlineError::Interrupted) => {
                info!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                info!("CTRL-D");
                break;
            }
            Err(err) => {
                error!("Error: {:?}", err);
                break;
            }
        }
    }
    rl.save_history(HISTORY_FILE).unwrap();
    Ok(())
}
<|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::ForwardNetMessage;
use adbflib::{
    ctrl::{CollectionPathAlive, Ctrl, NetMessages, Status, UiUpdateMsg},
    data::{self, Collection},
    logit,
    net::{key_keeper, Net},
};
use async_std::task;
use crossbeam::sync::WaitGroup;
use log::{error, info, trace, warn};
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::{
    io::{self, Error},
    path::Path, // path, clear
    sync::{
        self,
        mpsc::{channel, Sender},
        Arc, Mutex,
    },
};

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";
static ARG_KEEP_ALIVE: &str = "keep";

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(ARG_KEEP_ALIVE)
                .short("k")
                .long(ARG_KEEP_ALIVE)
                .help("With keep alive process will continue even after search has been performed.")
                .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!["."]
    };

    // for synced start
    let wait_collector = WaitGroup::new();
    let wait_ui = wait_collector.clone();
    let wait_net = wait_collector.clone();

    //
    // 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);
    let keep_alive = has_arg(ARG_KEEP_ALIVE);

    // 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::<UiUpdateMsg>();

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

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

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

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

    // all optional components are wrapped into threads
    // 1 - UI         ui_thread   (optional)
    // 2 - Net        net_thread  (optional)
    // 3 - Collector  no thread yet (uses multiple rayon worker threads)
    let ui_thread = std::thread::Builder::new()
        .name("ui".into())
        .spawn(move || {
            if has_ui {
                Ctrl::run(
                    key_keeper::get_p2p_server_id(),
                    &ui_paths,
                    rx,
                    has_net,
                    wait_ui,
                    has_webui,
                    has_tui,
                )
            } else {
                info!("no ui was created!");
                drop(wait_ui);
                Ok::<(), Error>(())
            }
        });

    // 2 - Net Future
    let net_thread = std::thread::Builder::new()
        .name("net".into())
        .spawn(move || {
            if has_net {
                task::block_on(async move {
                    // This thread will not end itself
                    // - can be terminated by ui message
                    // - collector finished (depending on definition)

                    info!("net started!!");
                    let net_system_messages = tx_net;
                    let mut network = Net::new(
                        key_keeper::get_p2p_server_id(),
                        has_tui,
                        net_system_messages,
                    );

                    // startup net synchronization
                    wait_net.wait();

                    network.lookup().await;
                    info!("net finished!!");
                    Ok::<(), Error>(())
                })
            } else {
                info!("no net!");
                drop(wait_net);
                Ok::<(), Error>(())
            }
        });

    // the collector ... still a problem with threading and parse_args
    // borrowing?? but since rayon is used, using a separate thread is not really
    // important
    // but yet this simple bracket to enclose this a little
    {
        trace!("syncing with 2 other threads");
        wait_collector.wait();
        trace!("sync with net and ui done ... collector can start");

        let synced_to_ui_messages = tx_from_collector_to_ui.clone();

        // start the parallel search threads with rayon, each path its own
        let init_collection = Collection::new(&key_keeper::get_p2p_server_id(), max_threads);
        let collection_protected = sync::Arc::new(sync::Mutex::new(init_collection));

        all_pathes.par_iter().enumerate().for_each(|(index, elem)| {
            let sender_loop = synced_to_ui_messages.clone();
            let collection_data_in_iterator = collection_protected.clone();
            search_in_single_path(
                has_ui,
                has_tui,
                collection_data_in_iterator,
                sender_loop,
                index,
                elem,
            );
        });
        if !has_ui {
            collection_protected
                .lock()
                .and_then(|locked_collection| Ok(locked_collection.print_stats()))
                .unwrap_or(())
        }
        info!("collector finished!!");
    }

    // look for keeping alive argument if that was chosen
    if keep_alive {
        ui_thread
            .and_then(|running_thread| Ok(running_thread.join()))
            .unwrap_or_else(|_| {
                // it doesn't matter because we will terminate anyway
                error!("this should be result of ui thread!!");
                Ok(Ok(()))
            })
            .unwrap_or_else(|_| {
                // it doesn't matter because we will terminate anyway
                error!("is this normal when joining ui thread???");
                Ok(())
            })
            .unwrap();
        if has_ui {
            info!("Stopped ui thread");
        }
        // net thread
        if has_ui {
            // if had a ui , net_thread will stop also after ui quit
            drop(net_thread);
        } else {
            // if didn't have ui, net_thread will continue running
            net_thread
                .and_then(|running_thread| Ok(running_thread.join()))
                .unwrap_or_else(|_| {
                    // it doesn't matter because we will terminate anyway
                    error!("is this normal when joining net thread???");
                    Ok(Ok(()))
                })
                // todo: honestly ... fix this here!!!!!! unwrap TWICE!!!
                .unwrap()
                .unwrap();
            info!("Stopped net thread");
        }
    } else {
        drop(net_thread);
        drop(ui_thread);
    }
    Ok(())
}

fn search_in_single_path(
    has_ui: bool,
    has_tui: bool,
    collection_protected: sync::Arc<sync::Mutex<Collection>>,
    mutex_to_ui_msg: sync::Arc<sync::Mutex<Sender<UiUpdateMsg>>>,
    index: usize,
    elem: &str,
) {
    if !has_ui {
        println!("[{:?}] looking into path {:?}", index, elem);
    } else {
        // send start animation for that path
        if has_tui {
            mutex_to_ui_msg
                .lock()
                .and_then(|locked_to_start_ui_message| {
                    trace!("send startAnimation for path {:?}", index);
                    locked_to_start_ui_message
                        .send(UiUpdateMsg::CollectionUpdate(
                            CollectionPathAlive::BusyPath(index),
                            Status::ON,
                        ))
                        .unwrap_or_else(|_| {
                            warn!("... lost start animation for index {:?}", index)
                        });
                    trace!("start busy animation");
                    Ok(())
                })
                .unwrap_or_else(|_| error!("... that should not happen here at start"));
        }
    }
    let locked_collection = &mut *collection_protected.lock().unwrap();

    // do it: main task here is to visit and dive deep
    //        into the subfolders of this folder
    match locked_collection.visit_path(Path::new(elem), &data::Collection::visit_files) {
        Ok(local_stats) => {
            if has_ui {
                // send stop animation for that path
                if has_tui {
                    trace!("send stopAnimation for path {:?}", index);
                    mutex_to_ui_msg
                        .lock()
                        .and_then(|locked_to_stop_ui_message| {
                            locked_to_stop_ui_message
                                .send(UiUpdateMsg::CollectionUpdate(
                                    CollectionPathAlive::BusyPath(index),
                                    Status::OFF,
                                ))
                                .unwrap_or_else(|_| {
                                    warn!("... lost stop animation for {:?}", index)
                                });
                            trace!("stop busy animation");
                            Ok(())
                        })
                        .unwrap_or_else(|_| {
                            error!("... 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 = NetMessages::Debug;
                    mutex_to_ui_msg
                        .lock()
                        .and_then(|locked_update_message| {
                            locked_update_message
                                .send(UiUpdateMsg::NetUpdate(ForwardNetMessage::new(
                                    debug_message_id,
                                    text,
                                )))
                                .unwrap();
                            Ok(())
                        })
                        .unwrap();
                }
            } else {
                println!("{:?}", text);
            }
        }
    }
}
<commit_msg>[webui] fixing obsolete differentiation between tui and ui for net<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::ForwardNetMessage;
use adbflib::{
    ctrl::{CollectionPathAlive, Ctrl, NetMessages, Status, UiUpdateMsg},
    data::{self, Collection},
    logit,
    net::{key_keeper, Net},
};
use async_std::task;
use crossbeam::sync::WaitGroup;
use log::{error, info, trace, warn};
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::{
    io::{self, Error},
    path::Path, // path, clear
    sync::{
        self,
        mpsc::{channel, Sender},
        Arc, Mutex,
    },
};

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";
static ARG_KEEP_ALIVE: &str = "keep";

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(ARG_KEEP_ALIVE)
                .short("k")
                .long(ARG_KEEP_ALIVE)
                .help("With keep alive process will continue even after search has been performed.")
                .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!["."]
    };

    // for synced start
    let wait_collector = WaitGroup::new();
    let wait_ui = wait_collector.clone();
    let wait_net = wait_collector.clone();

    //
    // 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);
    let keep_alive = has_arg(ARG_KEEP_ALIVE);

    // 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::<UiUpdateMsg>();

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

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

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

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

    // all optional components are wrapped into threads
    // 1 - UI         ui_thread   (optional)
    // 2 - Net        net_thread  (optional)
    // 3 - Collector  no thread yet (uses multiple rayon worker threads)
    let ui_thread = std::thread::Builder::new()
        .name("ui".into())
        .spawn(move || {
            if has_ui {
                Ctrl::run(
                    key_keeper::get_p2p_server_id(),
                    &ui_paths,
                    rx,
                    has_net,
                    wait_ui,
                    has_webui,
                    has_tui,
                )
            } else {
                info!("no ui was created!");
                drop(wait_ui);
                Ok::<(), Error>(())
            }
        });

    // 2 - Net Future
    let net_thread = std::thread::Builder::new()
        .name("net".into())
        .spawn(move || {
            if has_net {
                task::block_on(async move {
                    // This thread will not end itself
                    // - can be terminated by ui message
                    // - collector finished (depending on definition)

                    info!("net started!!");
                    let net_system_messages = tx_net;
                    let mut network =
                        Net::new(key_keeper::get_p2p_server_id(), has_ui, net_system_messages);

                    // startup net synchronization
                    wait_net.wait();

                    network.lookup().await;
                    info!("net finished!!");
                    Ok::<(), Error>(())
                })
            } else {
                info!("no net!");
                drop(wait_net);
                Ok::<(), Error>(())
            }
        });

    // the collector ... still a problem with threading and parse_args
    // borrowing?? but since rayon is used, using a separate thread is not really
    // important
    // but yet this simple bracket to enclose this a little
    {
        trace!("syncing with 2 other threads");
        wait_collector.wait();
        trace!("sync with net and ui done ... collector can start");

        let synced_to_ui_messages = tx_from_collector_to_ui.clone();

        // start the parallel search threads with rayon, each path its own
        let init_collection = Collection::new(&key_keeper::get_p2p_server_id(), max_threads);
        let collection_protected = sync::Arc::new(sync::Mutex::new(init_collection));

        all_pathes.par_iter().enumerate().for_each(|(index, elem)| {
            let sender_loop = synced_to_ui_messages.clone();
            let collection_data_in_iterator = collection_protected.clone();
            search_in_single_path(
                has_ui,
                collection_data_in_iterator,
                sender_loop,
                index,
                elem,
            );
        });
        if !has_ui {
            collection_protected
                .lock()
                .and_then(|locked_collection| Ok(locked_collection.print_stats()))
                .unwrap_or(())
        }
        info!("collector finished!!");
    }

    // look for keeping alive argument if that was chosen
    if keep_alive {
        ui_thread
            .and_then(|running_thread| Ok(running_thread.join()))
            .unwrap_or_else(|_| {
                // it doesn't matter because we will terminate anyway
                error!("this should be result of ui thread!!");
                Ok(Ok(()))
            })
            .unwrap_or_else(|_| {
                // it doesn't matter because we will terminate anyway
                error!("is this normal when joining ui thread???");
                Ok(())
            })
            .unwrap();
        if has_ui {
            info!("Stopped ui thread");
        }
        // net thread
        if has_ui {
            // if had a ui , net_thread will stop also after ui quit
            drop(net_thread);
        } else {
            // if didn't have ui, net_thread will continue running
            net_thread
                .and_then(|running_thread| Ok(running_thread.join()))
                .unwrap_or_else(|_| {
                    // it doesn't matter because we will terminate anyway
                    error!("is this normal when joining net thread???");
                    Ok(Ok(()))
                })
                // todo: honestly ... fix this here!!!!!! unwrap TWICE!!!
                .unwrap()
                .unwrap();
            info!("Stopped net thread");
        }
    } else {
        drop(net_thread);
        drop(ui_thread);
    }
    Ok(())
}

fn search_in_single_path(
    has_ui: bool,
    collection_protected: sync::Arc<sync::Mutex<Collection>>,
    mutex_to_ui_msg: sync::Arc<sync::Mutex<Sender<UiUpdateMsg>>>,
    index: usize,
    elem: &str,
) {
    if !has_ui {
        println!("[{:?}] looking into path {:?}", index, elem);
    } else {
        // send start animation for that path
        mutex_to_ui_msg
            .lock()
            .and_then(|locked_to_start_ui_message| {
                trace!("send startAnimation for path {:?}", index);
                locked_to_start_ui_message
                    .send(UiUpdateMsg::CollectionUpdate(
                        CollectionPathAlive::BusyPath(index),
                        Status::ON,
                    ))
                    .unwrap_or_else(|_| warn!("... lost start animation for index {:?}", index));
                trace!("start busy animation");
                Ok(())
            })
            .unwrap_or_else(|_| error!("... that should not happen here at start"));
    }
    let locked_collection = &mut *collection_protected.lock().unwrap();

    // do it: main task here is to visit and dive deep
    //        into the subfolders of this folder
    match locked_collection.visit_path(Path::new(elem), &data::Collection::visit_files) {
        Ok(local_stats) => {
            if has_ui {
                // send stop animation for that path
                trace!("send stopAnimation for path {:?}", index);
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_to_stop_ui_message| {
                        locked_to_stop_ui_message
                            .send(UiUpdateMsg::CollectionUpdate(
                                CollectionPathAlive::BusyPath(index),
                                Status::OFF,
                            ))
                            .unwrap_or_else(|_| warn!("... lost stop animation for {:?}", index));
                        trace!("stop busy animation");
                        Ok(())
                    })
                    .unwrap_or_else(|_| {
                        error!("... 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 {
                let debug_message_id = NetMessages::Debug;
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_update_message| {
                        locked_update_message
                            .send(UiUpdateMsg::NetUpdate(ForwardNetMessage::new(
                                debug_message_id,
                                text,
                            )))
                            .unwrap();
                        Ok(())
                    })
                    .unwrap();
            } else {
                println!("{:?}", text);
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate cask;
extern crate env_logger;
extern crate rand;

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

use cask::Cask;
use rand::{Rng, SeedableRng, XorShiftRng};

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

    let cask = Cask::open("test.db", false);

    let seed = [1, 2, 3, 4];

    const N_THREADS: usize = 8;
    const WRITE_PROBABILITY: f64 = 0.1;
    const DURATION_SECS: u64 = 10;

    let base_value = rand::thread_rng().gen::<usize>();

    let mut threads = Vec::new();
    let mut txs = Vec::new();
    for id in 1..N_THREADS + 1 {
        let (tx, rx) = mpsc::channel();
        let cask = cask.clone();
        let vec = vec![1u8; 4096];
        let mut rng: XorShiftRng = SeedableRng::from_seed(seed);

        let t = thread::spawn(move || {
            let mut i = 0;
            loop {
                if let Ok(_) = rx.try_recv() {
                    break;
                }

                let r = rng.next_f64();
                if r < WRITE_PROBABILITY {
                    let key = (id * i).to_string().as_bytes().to_vec();
                    cask.put(key, &vec);
                } else {
                    let key = ((base_value + (id * i)) * r as usize).to_string();
                    cask.get(key.as_bytes());
                }

                i += 1
            }
        });
        threads.push(t);
        txs.push(tx);
    }

    thread::sleep(Duration::from_secs(DURATION_SECS));

    for tx in txs {
        tx.send(()).unwrap();
    }

    for thread in threads {
        thread.join().unwrap();
    }
}
<commit_msg>implement custom logger with timestamps<commit_after>extern crate cask;
extern crate env_logger;
extern crate log;
extern crate rand;
extern crate time;

use std::env;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

use env_logger::LogBuilder;
use rand::{Rng, SeedableRng, XorShiftRng};
use log::LogRecord;

use cask::Cask;

fn init_logger() {
    let format = |record: &LogRecord| {
        let ts = time::strftime("%Y-%m-%d %H:%M:%S,%f", &time::now()).unwrap();
        format!("{} {} {} {}",
                &ts[..ts.len() - 6],
                record.level(),
                record.location().module_path(),
                record.args())
    };

    let mut builder = LogBuilder::new();
    builder.format(format);

    if let Ok(s) = env::var("RUST_LOG") {
        builder.parse(&s);
    }

    builder.init().unwrap();
}

fn main() {
    init_logger();

    let cask = Cask::open("test.db", false);

    let seed = [1, 2, 3, 4];

    const N_THREADS: usize = 8;
    const WRITE_PROBABILITY: f64 = 0.1;
    const DURATION_SECS: u64 = 10;

    let base_value = rand::thread_rng().gen::<usize>();

    let mut threads = Vec::new();
    let mut txs = Vec::new();
    for id in 1..N_THREADS + 1 {
        let (tx, rx) = mpsc::channel();
        let cask = cask.clone();
        let vec = vec![1u8; 4096];
        let mut rng: XorShiftRng = SeedableRng::from_seed(seed);

        let t = thread::spawn(move || {
            let mut i = 0;
            loop {
                if let Ok(_) = rx.try_recv() {
                    break;
                }

                let r = rng.next_f64();
                if r < WRITE_PROBABILITY {
                    let key = (id * i).to_string().as_bytes().to_vec();
                    cask.put(key, &vec);
                } else {
                    let key = ((base_value + (id * i)) * r as usize).to_string();
                    cask.get(key.as_bytes());
                }

                i += 1
            }
        });
        threads.push(t);
        txs.push(tx);
    }

    thread::sleep(Duration::from_secs(DURATION_SECS));

    for tx in txs {
        tx.send(()).unwrap();
    }

    for thread in threads {
        thread.join().unwrap();
    }
}
<|endoftext|>


<commit_before>//! # Basic Sample
//!
//! This sample demonstrates how to create a toplevel `window`, set its title, size and position, how to add a `button` to this `window` and how to connect signals with actions.

extern crate gtk;

use gtk::prelude::*;
use gtk::{Button, Window, Value, WindowType, FileChooserDialog, TreeView,
         TreeStore, CellRendererText, CellRendererToggle, TreeViewColumn, TreePath};

extern crate svd_parser as svd;
use svd::Register;

use std::path::PathBuf;
use std::path::Path;
use std::fs::File;
use std::io::{Read, Write};

const FILE: &str = "registers.txt";

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let window = Window::new(WindowType::Toplevel);
    let view = TreeView::new();
    //let open_button = Button::new_with_label("Open");
    let ok_button = Button::new_with_label("Ok");
    let apply_button = Button::new_with_label("Apply");
    let cancel_button = Button::new_with_label("Cancel");

    let mut svd_filename:     Option<String> = None;
    let mut store:        Option<TreeStore> = None;

    window.set_title("SVD");
    window.set_border_width(10);

    let cell_name = CellRendererText::new();
    let column_name = TreeViewColumn::new();
    column_name.pack_start(&cell_name, true);
    column_name.add_attribute(&cell_name, "text", 0);
    column_name.set_title("Name");
    view.append_column(&column_name);

    let cell_in_out = CellRendererToggle::new();
    let column_in_out = TreeViewColumn::new();
    column_in_out.pack_start(&cell_in_out, true);
    column_in_out.add_attribute(&cell_in_out, "active", 1);
    column_in_out.set_title("Out?");
    view.append_column(&column_in_out);

    let cell_address = CellRendererText::new();
    let column_address = TreeViewColumn::new();
    column_address.pack_start(&cell_address, true);
    column_address.add_attribute(&cell_address, "text", 2);
    column_address.set_title("Address");
    view.append_column(&column_address);

    let cell_description = CellRendererText::new();
    let column_description = TreeViewColumn::new();
    column_description.pack_start(&cell_description, true);
    column_description.add_attribute(&cell_description, "text", 3);
    column_description.set_title("Description");
    view.append_column(&column_description);

    let scrolled_window = gtk::ScrolledWindow::new(None, None);
    scrolled_window.set_policy(
        gtk::PolicyType::Always, gtk::PolicyType::Always);
    scrolled_window.add_with_viewport(&view);

    scrolled_window.set_size_request(500, 600);

    let grid = gtk::Grid::new();
    grid.set_row_spacing(5);

    //grid.attach(&open_button,     0, 0, 1, 1);
    grid.attach(&scrolled_window,   0, 1, 5, 1);
    grid.attach(&ok_button,       2, 2, 1, 1);
    grid.attach(&apply_button,    3, 2, 1, 1);
    grid.attach(&cancel_button,   4, 2, 1, 1);

    window.add(&grid);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });
    window.show_all();

    let mut fflag = false;
    let text = &mut String::new();
    if let Ok(mut f) = File::open(FILE) {
        f.read_to_string(text).expect("Unable to read file");
        let mut lines = text.lines().map(|l| l.trim());
        if let Some(filename) = lines.next() {
            println!("SVD File {}", filename);
            let mut regs = Vec::new();
            for l in lines {
                regs.push(l.split_whitespace().collect::<Vec<_>>()[0]);
            }
            println!("{:?}", regs);
            store = load_svd(Path::new(&filename), regs);
            svd_filename = Some(filename.to_string());
            if let Some(ref st) = store {
                fflag = true;
                view.set_model(st);
            }
        }
    }
    if !fflag {
        if let Some(pathbuf) = open_file(&window) {
            println!("Open SVD File {:?}", pathbuf);
            store = load_svd(pathbuf.as_path(), vec![]);
            if let Some(ref st) = store {
                svd_filename = pathbuf.into_os_string().into_string().ok();
                view.set_model(st);
            }
        }
    }
    {
        let store = store.clone();
        cell_in_out.connect_toggled(move |_,path| {
            if let Some(ref st) = store {
                on_toggle(st, &path)
            }
        });
    }
    {
        let store = store.clone();
        let svd_filename = svd_filename.clone();
        ok_button.connect_clicked(move |_| {
            if let Some(ref st) = store {
                if let Some(ref svd_file) = svd_filename {
                    save_data(st, svd_file).expect("Unable to save file");
                }
            }
            gtk::main_quit();
        });
    }
    {
        let store = store.clone();
        let svd_filename = svd_filename.clone();
        apply_button.connect_clicked(move |_| {
            if let Some(ref st) = store {
                if let Some(ref svd_file) = svd_filename {
                    save_data(st, svd_file).expect("Unable to save file");
                }
            }
        });
    }
    cancel_button.connect_clicked(|_| { gtk::main_quit();  });
    /*
    {
        let window = window.clone();
        open_button.connect_clicked(move |_| {
            if let Some(pathbuf) = open_file(&window) {
            println!("Open SVD File {:?}", pathbuf);
            store = load_svd(pathbuf.as_path(), vec![]);
            if let Some(ref st) = store {
                svd_filename = pathbuf.into_os_string().into_string().ok();
                view.set_model(st);
            }
        }

        });
    }*/
    gtk::main();
}

fn open_file(window: &Window) -> Option<PathBuf> {
    let dialog = FileChooserDialog::new(Some("Please choose a file"), Some(window),
        gtk::FileChooserAction::Open);
    dialog.add_button("Cancel", 0);//gtk::ResponseType::Cancel);
    dialog.add_button("Open", 1);//gtk::ResponseType::Ok);
    let response = dialog.run();
    let pathbuf = match response {
        1 => dialog.get_filename(),
        _ =>  None
    };
    dialog.destroy();
    pathbuf
}

fn load_svd (svd_path: &Path, regs: Vec<&str>) -> Option<TreeStore> {
    let xml = &mut String::new();
    File::open(&svd_path).unwrap().read_to_string(xml).expect("Unable to read file");
    let device = svd::parse(xml);
    let periphs = device.peripherals;

    let store = TreeStore::new(&[String::static_type(), gtk::Type::Bool, String::static_type(), String::static_type()]);
    for p in &periphs {
        let paddr = p.base_address;
        let piter = store.append(None);
        let pdesc = match p.description {
            Some(ref s) => s.replace('\n', " "),
            None => "".to_string()
        };
        let pbase = match p.derived_from { // need correct
            Some(ref s) => {
                let mut pb = p;
                let mut k = 1000;
                for i in 0..periphs.len() {
                    if *s == periphs[i].name {
                        k = i;
                        break;
                    }
                }
                if k != 1000 {
                    pb = &periphs[k];
                }
                pb
            },
            None => p
        };
        store.set(&piter, &[0, 1, 2, 3], &[&p.name, &false, &format!("0x{:08x}", paddr), &pdesc]);
        if let Some(ref rs) = pbase.registers {
            for reg in rs {
                match reg {
                    &Register::Single(ref r) | &Register::Array(ref r, _) => {
                        let raddr = paddr + r.address_offset;
                        let rname = format!("{}.{}", p.name, r.name);
                        let rdesc = r.description.replace('\n', " ");

                        let mut enabled = false;
                        for rn in &regs {
                            if *rn == rname {
                                enabled = true;
                                break;
                            }
                        }
                        if enabled { println!("{}", rname); }
                        let riter = store.append(&piter);
                        store.set(&riter, &[0, 1, 2, 3], &[&r.name, &enabled, &format!("0x{:08x}", raddr), &rdesc]);
                    }
                }

            }
        }
        set_piter_selected(&store, &piter);
    }
    Some(store)
}

fn save_data (store: &TreeStore, svd_file: &String) -> Result<(), std::io::Error> {
    let mut s = svd_file.to_owned() + "\n";
    if let Some(ref piter) = store.get_iter_first() {
        loop {
            if let Some(ref citer) = store.iter_children(piter) {
                loop {
                    if store.get_value(citer, 1).get::<bool>().unwrap() {
                        s += &format!("{}.{} {}\n", store.get_value(&piter, 0).get::<String>().unwrap(),
                                                    store.get_value(&citer, 0).get::<String>().unwrap(),
                                                    store.get_value(&citer, 2).get::<String>().unwrap());
                    }
                    if !store.iter_next(citer) { break; }
                }
            }
            if !store.iter_next(piter)  { break; }
        }
    }
    println!("{}", s);
    let mut f = File::create(FILE)?;
    f.write_fmt(format_args!("{}", s))?;
    f.flush()?;
    Ok(())
}

fn on_toggle(st: &TreeStore, path: &TreePath) {
    if let Some(iter) = st.get_iter(path) {
        let current_value = !st.get_value(&iter, 1).get::<bool>().unwrap();
        st.set_value(&iter, 1, &Value::from(&current_value));

        if path.get_depth() == 1 {
            let piter = iter;
            if let Some(ref citer) = st.iter_children(&piter) {
                loop {
                    st.set_value(citer, 1, &Value::from(&current_value));
                    if !st.iter_next(citer) { break; }
                }
            }
            println!("{} {}", st.get_value(&piter, 0).get::<String>().unwrap(),
                              if current_value == true {"enabled"} else {"disabled"});
        }
        else {
            let citer = iter;
            if let Some(ref piter) = st.iter_parent(&citer) {
                let all_selected = set_piter_selected (st, &piter);
                if all_selected {
                    println!("{} {}", st.get_value(&piter, 0).get::<String>().unwrap(),
                                      if current_value == true {"enabled"} else {"disabled"});
                }
                else {
                    println!("{}.{} {}", st.get_value(&piter, 0).get::<String>().unwrap(),
                                         st.get_value(&citer, 0).get::<String>().unwrap(),
                                         if current_value == true {"enabled"} else {"disabled"});
                }
            }
        }
    }
}

fn set_piter_selected (store: &TreeStore, piter: &gtk::TreeIter) -> bool {
    let mut all_selected = true;
    if let Some(ref citer) = store.iter_children(piter) {
        loop {
            all_selected &= store.get_value(citer, 1).get::<bool>().unwrap();
            if !all_selected || !store.iter_next(citer) { break; }
        }
    }
    store.set_value(piter, 1, &Value::from(&all_selected));
    all_selected
}
<commit_msg>default name "_"<commit_after>//! # Basic Sample
//!
//! This sample demonstrates how to create a toplevel `window`, set its title, size and position, how to add a `button` to this `window` and how to connect signals with actions.

extern crate gtk;

use gtk::prelude::*;
use gtk::{Button, Window, Value, WindowType, FileChooserDialog, TreeView,
         TreeStore, CellRendererText, CellRendererToggle, TreeViewColumn, TreePath};

extern crate svd_parser as svd;
use svd::Register;

use std::path::PathBuf;
use std::path::Path;
use std::fs::File;
use std::io::{Read, Write};

const FILE: &str = "registers.txt";

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let window = Window::new(WindowType::Toplevel);
    let view = TreeView::new();
    //let open_button = Button::new_with_label("Open");
    let ok_button = Button::new_with_label("Ok");
    let apply_button = Button::new_with_label("Apply");
    let cancel_button = Button::new_with_label("Cancel");

    let mut svd_filename:     Option<String> = None;
    let mut store:        Option<TreeStore> = None;

    window.set_title("SVD");
    window.set_border_width(10);

    let cell_name = CellRendererText::new();
    let column_name = TreeViewColumn::new();
    column_name.pack_start(&cell_name, true);
    column_name.add_attribute(&cell_name, "text", 0);
    column_name.set_title("Name");
    view.append_column(&column_name);

    let cell_in_out = CellRendererToggle::new();
    let column_in_out = TreeViewColumn::new();
    column_in_out.pack_start(&cell_in_out, true);
    column_in_out.add_attribute(&cell_in_out, "active", 1);
    column_in_out.set_title("Out?");
    view.append_column(&column_in_out);

    let cell_address = CellRendererText::new();
    let column_address = TreeViewColumn::new();
    column_address.pack_start(&cell_address, true);
    column_address.add_attribute(&cell_address, "text", 2);
    column_address.set_title("Address");
    view.append_column(&column_address);

    let cell_description = CellRendererText::new();
    let column_description = TreeViewColumn::new();
    column_description.pack_start(&cell_description, true);
    column_description.add_attribute(&cell_description, "text", 3);
    column_description.set_title("Description");
    view.append_column(&column_description);

    let scrolled_window = gtk::ScrolledWindow::new(None, None);
    scrolled_window.set_policy(
        gtk::PolicyType::Always, gtk::PolicyType::Always);
    scrolled_window.add_with_viewport(&view);

    scrolled_window.set_size_request(500, 600);

    let grid = gtk::Grid::new();
    grid.set_row_spacing(5);

    //grid.attach(&open_button,     0, 0, 1, 1);
    grid.attach(&scrolled_window,   0, 1, 5, 1);
    grid.attach(&ok_button,       2, 2, 1, 1);
    grid.attach(&apply_button,    3, 2, 1, 1);
    grid.attach(&cancel_button,   4, 2, 1, 1);

    window.add(&grid);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });
    window.show_all();

    let mut fflag = false;
    let text = &mut String::new();
    if let Ok(mut f) = File::open(FILE) {
        f.read_to_string(text).expect("Unable to read file");
        let mut lines = text.lines().map(|l| l.trim());
        if let Some(filename) = lines.next() {
            println!("SVD File {}", filename);
            let mut regs = Vec::new();
            for l in lines {
                regs.push(l.split_whitespace().nth(0).unwrap());
            }
            println!("{:?}", regs);
            store = load_svd(Path::new(&filename), regs);
            svd_filename = Some(filename.to_string());
            if let Some(ref st) = store {
                fflag = true;
                view.set_model(st);
            }
        }
    }
    if !fflag {
        if let Some(pathbuf) = open_file(&window) {
            println!("Open SVD File {:?}", pathbuf);
            store = load_svd(pathbuf.as_path(), vec![]);
            if let Some(ref st) = store {
                svd_filename = pathbuf.into_os_string().into_string().ok();
                view.set_model(st);
            }
        }
    }
    {
        let store = store.clone();
        cell_in_out.connect_toggled(move |_,path| {
            if let Some(ref st) = store {
                on_toggle(st, &path)
            }
        });
    }
    {
        let store = store.clone();
        let svd_filename = svd_filename.clone();
        ok_button.connect_clicked(move |_| {
            if let Some(ref st) = store {
                if let Some(ref svd_file) = svd_filename {
                    save_data(st, svd_file).expect("Unable to save file");
                }
            }
            gtk::main_quit();
        });
    }
    {
        let store = store.clone();
        let svd_filename = svd_filename.clone();
        apply_button.connect_clicked(move |_| {
            if let Some(ref st) = store {
                if let Some(ref svd_file) = svd_filename {
                    save_data(st, svd_file).expect("Unable to save file");
                }
            }
        });
    }
    cancel_button.connect_clicked(|_| { gtk::main_quit();  });
    /*
    {
        let window = window.clone();
        open_button.connect_clicked(move |_| {
            if let Some(pathbuf) = open_file(&window) {
            println!("Open SVD File {:?}", pathbuf);
            store = load_svd(pathbuf.as_path(), vec![]);
            if let Some(ref st) = store {
                svd_filename = pathbuf.into_os_string().into_string().ok();
                view.set_model(st);
            }
        }

        });
    }*/
    gtk::main();
}

fn open_file(window: &Window) -> Option<PathBuf> {
    let dialog = FileChooserDialog::new(Some("Please choose a file"), Some(window),
        gtk::FileChooserAction::Open);
    dialog.add_button("Cancel", 0);//gtk::ResponseType::Cancel);
    dialog.add_button("Open", 1);//gtk::ResponseType::Ok);
    let response = dialog.run();
    let pathbuf = match response {
        1 => dialog.get_filename(),
        _ =>  None
    };
    dialog.destroy();
    pathbuf
}

fn load_svd (svd_path: &Path, regs: Vec<&str>) -> Option<TreeStore> {
    let xml = &mut String::new();
    File::open(&svd_path).unwrap().read_to_string(xml).expect("Unable to read file");
    let device = svd::parse(xml);
    let periphs = device.peripherals;

    let store = TreeStore::new(&[String::static_type(), gtk::Type::Bool, String::static_type(), String::static_type()]);
    for p in &periphs {
        let paddr = p.base_address;
        let piter = store.append(None);
        let pdesc = p.description.to_owned().unwrap_or_default()
                                 .replace("\n", " ");
        let pbase = match p.derived_from { // need correct
            Some(ref s) => {
                let mut pb = p;
                let mut k = 1000;
                for i in 0..periphs.len() {
                    if *s == periphs[i].name {
                        k = i;
                        break;
                    }
                }
                if k != 1000 {
                    pb = &periphs[k];
                }
                pb
            },
            None => p
        };
        store.set(&piter, &[0, 1, 2, 3], &[&p.name, &false, &format!("0x{:08x}", paddr), &pdesc]);
        if let Some(ref rs) = pbase.registers {
            for reg in rs {
                match reg {
                    &Register::Single(ref r) | &Register::Array(ref r, _) => {
                        let raddr = paddr + r.address_offset;
                        let rname = format!("{}.{}", p.name, r.name);
                        let rdesc = r.description.replace("\n", " ");

                        let mut enabled = false;
                        for rn in &regs {
                            if *rn == rname {
                                enabled = true;
                                break;
                            }
                        }
                        if enabled { println!("{}", rname); }
                        let riter = store.append(&piter);
                        store.set(&riter, &[0, 1, 2, 3], &[&r.name, &enabled, &format!("0x{:08x}", raddr), &rdesc]);
                    }
                }

            }
        }
        set_piter_selected(&store, &piter);
    }
    Some(store)
}

fn save_data (store: &TreeStore, svd_file: &String) -> Result<(), std::io::Error> {
    let mut s = svd_file.to_owned() + "\n";
    if let Some(ref piter) = store.get_iter_first() {
        loop {
            if let Some(ref citer) = store.iter_children(piter) {
                loop {
                    if store.get_value(citer, 1).get::<bool>().unwrap() {
                        s += &format!("{}.{} _ {}\n", store.get_value(&piter, 0).get::<String>().unwrap(),
                                                    store.get_value(&citer, 0).get::<String>().unwrap(),
                                                    store.get_value(&citer, 2).get::<String>().unwrap());
                    }
                    if !store.iter_next(citer) { break; }
                }
            }
            if !store.iter_next(piter)  { break; }
        }
    }
    println!("{}", s);
    let mut f = File::create(FILE)?;
    f.write_fmt(format_args!("{}", s))?;
    f.flush()?;
    Ok(())
}

fn on_toggle(st: &TreeStore, path: &TreePath) {
    if let Some(iter) = st.get_iter(path) {
        let current_value = !st.get_value(&iter, 1).get::<bool>().unwrap();
        st.set_value(&iter, 1, &Value::from(&current_value));

        if path.get_depth() == 1 {
            let piter = iter;
            if let Some(ref citer) = st.iter_children(&piter) {
                loop {
                    st.set_value(citer, 1, &Value::from(&current_value));
                    if !st.iter_next(citer) { break; }
                }
            }
            println!("{} {}", st.get_value(&piter, 0).get::<String>().unwrap(),
                              if current_value == true {"enabled"} else {"disabled"});
        }
        else {
            let citer = iter;
            if let Some(ref piter) = st.iter_parent(&citer) {
                let all_selected = set_piter_selected (st, &piter);
                if all_selected {
                    println!("{} {}", st.get_value(&piter, 0).get::<String>().unwrap(),
                                      if current_value == true {"enabled"} else {"disabled"});
                }
                else {
                    println!("{}.{} {}", st.get_value(&piter, 0).get::<String>().unwrap(),
                                         st.get_value(&citer, 0).get::<String>().unwrap(),
                                         if current_value == true {"enabled"} else {"disabled"});
                }
            }
        }
    }
}

fn set_piter_selected (store: &TreeStore, piter: &gtk::TreeIter) -> bool {
    let mut all_selected = true;
    if let Some(ref citer) = store.iter_children(piter) {
        loop {
            all_selected &= store.get_value(citer, 1).get::<bool>().unwrap();
            if !all_selected || !store.iter_next(citer) { break; }
        }
    }
    store.set_value(piter, 1, &Value::from(&all_selected));
    all_selected
}
<|endoftext|>


<commit_before>#![feature(io, slice_patterns)]
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::Path;

const MEMSIZE: usize = 4096;

enum ParserResult {
    Something(Op),
    Nothing,
    EOF
}

use ParserResult::*;

#[derive(Debug)]
enum Op {
    Add(u8),
    Mov(usize),
    In,
    Out,
    Loop(OpStream)
}

use Op::*;

#[derive(Debug)]
struct OpStream {
    ops: Vec<Op>
}

impl OpStream {
    fn add(&mut self, op: Op) {
        self.ops.push(op);
    }

    fn optimize(&mut self) {
        let mut i = 0;
        while i < self.ops.len() {
            match &self.ops[i..] {
                [Add(a), Add(b), ..] => {
                    self.ops[i] = Add(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Mov(a), Mov(b), ..] => {
                    self.ops[i] = Mov(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Add(0), ..] | [Mov(0), ..] => {
                    self.ops.remove(i);
                }
                [Loop(_), ..] => {
                    if let &mut Loop(ref mut stream) = &mut self.ops[i] {
                        stream.optimize();
                    }
                    i += 1
                }
                _ => i += 1
            }
        }
    }

    fn get(&self) -> &[Op] {
        &self.ops[..]
    }

    fn new() -> OpStream {
        OpStream { ops: Vec::new() }
    }
}

struct State {
    index: usize,
    memory: [u8; MEMSIZE]
}

impl State {
    fn new() -> State {
        State { index: 0, memory: [0; MEMSIZE] }
    }

    fn peek(&self) -> u8 {
        self.memory[self.index % MEMSIZE]
    }

    fn poke(&mut self, value: u8) {
        self.memory[self.index % MEMSIZE] = value;
    }

    fn step(&mut self, op: &Op) -> bool {
        match op {
            &Add(i) => {
                // XXX: Ugly expression due to lexical scoping of borrow,
                //      cf. https://github.com/rust-lang/rust/issues/6393
                let x = self.peek();
                self.poke(x.wrapping_add(i));
            }
            &Mov(n) => {
                self.index = self.index.wrapping_add(n);
            }
            &In => {
                let mut c = vec![0u8];
                if io::stdin().read(&mut c).unwrap() == 0 {
                    return false;
                }
                self.poke(c[0]);
            }
            &Out => {
                io::stdout().write(&vec![self.peek()]).unwrap();
            }
            &Loop(ref ops) => {
                while self.peek() != 0 {
                    if !self.run(ops.get()) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    fn run(&mut self, ops: &[Op]) -> bool {
        for op in ops {
            if !self.step(op) {
                return false;
            }
        }
        return true;
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        match reader(&Path::new(filename)) {
            Ok(reader) => {
                let mut chars = reader.chars().peekable();

                let mut opstream = OpStream::new();
                loop {
                    match parse(&mut chars) {
                        Something(op) => opstream.add(op),
                        EOF => break,
                        _ => {}
                    }
                }

                opstream.optimize();

                State::new().run(opstream.get());
            }
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while processing {}: {}", filename, e).unwrap();
            }
        }
    }
}

fn parse<T: io::Read>(mut chars: &mut std::iter::Peekable<io::Chars<T>>) -> ParserResult {
    match chars.next() {
        Some(Ok(c)) => match c {
            '+' => Something(Add(1)),
            '-' => Something(Add(-1)),
            '<' => Something(Mov(-1)),
            '>' => Something(Mov(1)),
            ',' => Something(In),
            '.' => Something(Out),
            '[' => {
                let mut childstream = OpStream::new();
                while match chars.peek() {
                    Some(&Ok(c)) if c != ']' => true,
                    _ => false
                } {
                    match parse(&mut chars) {
                        Something(op) => childstream.add(op),
                        Nothing => {},
                        EOF => panic!()
                    }
                }
                chars.next();
                Something(Loop(childstream))
            },
            _ => Nothing  // other characters
        },
        _ => EOF  // XXX: really?
    }
}

fn reader(path: &Path) -> Result<io::BufReader<fs::File>, io::Error> {
    Ok(io::BufReader::new(try!(fs::File::open(path))))
}

#[cfg(test)]
mod tests {
    use super::{State, OpStream};
    use super::Op::*;

    macro_rules! assert_let(
        ($test:pat, $value:expr, $then:block) => (
            if let $test = $value {
                $then
            } else {
                panic!("{:?} doesn't match {}", $value, stringify!($test))
            }
        );
        ($test:pat, $value:expr) => (
            assert_let!($test, $value, {})
        );
    );

    #[test]
    fn test_state_peek() {
        let mut state = State::new();
        state.memory[state.index] = 23;
        assert_eq!(23, state.peek());
        state.index = 5;
        state.memory[state.index] = 42;
        assert_eq!(42, state.peek());
    }

    #[test]
    fn test_state_poke() {
        let mut state = State::new();
        state.poke(23);
        assert_eq!(23, state.memory[state.index]);
        state.index = 5;
        state.poke(42);
        assert_eq!(42, state.memory[state.index]);
    }

    #[test]
    fn test_state_step_add() {
        let mut state = State::new();
        state.step(&Add(23));
        assert_eq!(23, state.peek());
        state.step(&Add(42));
        assert_eq!(65, state.peek());
        state.step(&Add(-66));
        assert_eq!(-1, state.peek());
    }

    #[test]
    fn test_state_step_mov() {
        let mut state = State::new();
        state.step(&Mov(1));
        assert_eq!(1, state.index);
        state.step(&Mov(42));
        assert_eq!(43, state.index);
        state.step(&Mov(-1));
        assert_eq!(42, state.index);
    }

    #[test]
    fn test_state_step_loop() {
        let mut state = State::new();
        state.poke(23);
        state.step(&Loop(OpStream { ops: vec![Add(1)] }));
        assert_eq!(0, state.peek());
    }

    #[test]
    fn test_opstream_new_empty() {
        let opstream = OpStream::new();
        assert_eq!(0, opstream.ops.len());
    }

    #[test]
    fn test_opstream_optimize() {
        let mut opstream = OpStream::new();
        opstream.add(Mov(1));
        opstream.add(Mov(1));
        opstream.add(Add(1));
        opstream.add(Add(-1));
        opstream.add(Add(-1));
        opstream.add(Mov(1));
        opstream.add(Mov(-1));
        let mut opstream2 = OpStream::new();
        opstream2.add(Mov(2));
        opstream2.add(Mov(3));
        opstream.add(Loop(opstream2));
        opstream.optimize();

        assert_let!([Mov(2), Add(-1), Loop(ref s2)], &opstream.ops[..], {
            assert_let!([Mov(5)], &s2.ops[..]);
        });
    }
}
<commit_msg>test: simple `parse` test<commit_after>#![feature(io, slice_patterns)]
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::Path;

const MEMSIZE: usize = 4096;

#[derive(Debug)]
enum ParserResult {
    Something(Op),
    Nothing,
    EOF
}

use ParserResult::*;

#[derive(Debug)]
enum Op {
    Add(u8),
    Mov(usize),
    In,
    Out,
    Loop(OpStream)
}

use Op::*;

#[derive(Debug)]
struct OpStream {
    ops: Vec<Op>
}

impl OpStream {
    fn add(&mut self, op: Op) {
        self.ops.push(op);
    }

    fn optimize(&mut self) {
        let mut i = 0;
        while i < self.ops.len() {
            match &self.ops[i..] {
                [Add(a), Add(b), ..] => {
                    self.ops[i] = Add(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Mov(a), Mov(b), ..] => {
                    self.ops[i] = Mov(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Add(0), ..] | [Mov(0), ..] => {
                    self.ops.remove(i);
                }
                [Loop(_), ..] => {
                    if let &mut Loop(ref mut stream) = &mut self.ops[i] {
                        stream.optimize();
                    }
                    i += 1
                }
                _ => i += 1
            }
        }
    }

    fn get(&self) -> &[Op] {
        &self.ops[..]
    }

    fn new() -> OpStream {
        OpStream { ops: Vec::new() }
    }
}

struct State {
    index: usize,
    memory: [u8; MEMSIZE]
}

impl State {
    fn new() -> State {
        State { index: 0, memory: [0; MEMSIZE] }
    }

    fn peek(&self) -> u8 {
        self.memory[self.index % MEMSIZE]
    }

    fn poke(&mut self, value: u8) {
        self.memory[self.index % MEMSIZE] = value;
    }

    fn step(&mut self, op: &Op) -> bool {
        match op {
            &Add(i) => {
                // XXX: Ugly expression due to lexical scoping of borrow,
                //      cf. https://github.com/rust-lang/rust/issues/6393
                let x = self.peek();
                self.poke(x.wrapping_add(i));
            }
            &Mov(n) => {
                self.index = self.index.wrapping_add(n);
            }
            &In => {
                let mut c = vec![0u8];
                if io::stdin().read(&mut c).unwrap() == 0 {
                    return false;
                }
                self.poke(c[0]);
            }
            &Out => {
                io::stdout().write(&vec![self.peek()]).unwrap();
            }
            &Loop(ref ops) => {
                while self.peek() != 0 {
                    if !self.run(ops.get()) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    fn run(&mut self, ops: &[Op]) -> bool {
        for op in ops {
            if !self.step(op) {
                return false;
            }
        }
        return true;
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        match reader(&Path::new(filename)) {
            Ok(reader) => {
                let mut chars = reader.chars().peekable();

                let mut opstream = OpStream::new();
                loop {
                    match parse(&mut chars) {
                        Something(op) => opstream.add(op),
                        EOF => break,
                        _ => {}
                    }
                }

                opstream.optimize();

                State::new().run(opstream.get());
            }
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while processing {}: {}", filename, e).unwrap();
            }
        }
    }
}

fn parse<T: io::Read>(mut chars: &mut std::iter::Peekable<io::Chars<T>>) -> ParserResult {
    match chars.next() {
        Some(Ok(c)) => match c {
            '+' => Something(Add(1)),
            '-' => Something(Add(-1)),
            '<' => Something(Mov(-1)),
            '>' => Something(Mov(1)),
            ',' => Something(In),
            '.' => Something(Out),
            '[' => {
                let mut childstream = OpStream::new();
                while match chars.peek() {
                    Some(&Ok(c)) if c != ']' => true,
                    _ => false
                } {
                    match parse(&mut chars) {
                        Something(op) => childstream.add(op),
                        Nothing => {},
                        EOF => panic!()
                    }
                }
                chars.next();
                Something(Loop(childstream))
            },
            _ => Nothing  // other characters
        },
        _ => EOF  // XXX: really?
    }
}

fn reader(path: &Path) -> Result<io::BufReader<fs::File>, io::Error> {
    Ok(io::BufReader::new(try!(fs::File::open(path))))
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use super::{parse, ParserResult, State, OpStream};
    use super::Op::*;

    macro_rules! assert_let(
        ($test:pat, $value:expr, $then:block) => (
            if let $test = $value {
                $then
            } else {
                panic!("{:?} doesn't match {}", $value, stringify!($test))
            }
        );
        ($test:pat, $value:expr) => (
            assert_let!($test, $value, {})
        );
    );

    #[test]
    fn test_state_peek() {
        let mut state = State::new();
        state.memory[state.index] = 23;
        assert_eq!(23, state.peek());
        state.index = 5;
        state.memory[state.index] = 42;
        assert_eq!(42, state.peek());
    }

    #[test]
    fn test_state_poke() {
        let mut state = State::new();
        state.poke(23);
        assert_eq!(23, state.memory[state.index]);
        state.index = 5;
        state.poke(42);
        assert_eq!(42, state.memory[state.index]);
    }

    #[test]
    fn test_state_step_add() {
        let mut state = State::new();
        state.step(&Add(23));
        assert_eq!(23, state.peek());
        state.step(&Add(42));
        assert_eq!(65, state.peek());
        state.step(&Add(-66));
        assert_eq!(-1, state.peek());
    }

    #[test]
    fn test_state_step_mov() {
        let mut state = State::new();
        state.step(&Mov(1));
        assert_eq!(1, state.index);
        state.step(&Mov(42));
        assert_eq!(43, state.index);
        state.step(&Mov(-1));
        assert_eq!(42, state.index);
    }

    #[test]
    fn test_state_step_loop() {
        let mut state = State::new();
        state.poke(23);
        state.step(&Loop(OpStream { ops: vec![Add(1)] }));
        assert_eq!(0, state.peek());
    }

    #[test]
    fn test_opstream_new_empty() {
        let opstream = OpStream::new();
        assert_eq!(0, opstream.ops.len());
    }

    #[test]
    fn test_opstream_optimize() {
        let mut opstream = OpStream::new();
        opstream.add(Mov(1));
        opstream.add(Mov(1));
        opstream.add(Add(1));
        opstream.add(Add(-1));
        opstream.add(Add(-1));
        opstream.add(Mov(1));
        opstream.add(Mov(-1));
        let mut opstream2 = OpStream::new();
        opstream2.add(Mov(2));
        opstream2.add(Mov(3));
        opstream.add(Loop(opstream2));
        opstream.optimize();

        assert_let!([Mov(2), Add(-1), Loop(ref s2)], &opstream.ops[..], {
            assert_let!([Mov(5)], &s2.ops[..]);
        });
    }

    #[test]
    fn test_parse() {
        let input = b"+-[+.,]+";
        let mut chars = io::BufReader::new(&input[..]).chars().peekable();
        assert_let!(ParserResult::Something(Add(1)), parse(&mut chars));
        assert_let!(ParserResult::Something(Add(-1)), parse(&mut chars));
        assert_let!(ParserResult::Something(Loop(loop_op)), parse(&mut chars), {
            assert_let!([Add(1), Out, In], &loop_op.ops[..]);
        });
        assert_let!(ParserResult::Something(Add(1)), parse(&mut chars));
    }
}
<|endoftext|>