<commit_before>// Copyright (C) 2016 Mark Lee

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

extern crate env_logger;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate redis;
extern crate regex;
extern crate rquery;
extern crate semver;
extern crate serde;
extern crate serde_json;

use hyper::{Client as HTTPClient, Get, Server};
use hyper::header::{Accept, ContentType};
use hyper::server::{Request, Response};
use hyper::uri::RequestUri::AbsolutePath;
use redis::{Commands, RedisError, RedisResult};
use regex::Regex;
use rquery::Document;
use semver::Version;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;

const DEFAULT_SERVER_PORT: u32 = 9000;
const MIN_BUNDLER_VERSION: &'static str = "1.11.0";
const RUBY_LANGPACK_RELEASES_URL: &'static str = "https://github.\
                                                  com/heroku/heroku-buildpack-ruby/releases.atom";

fn download_url(http: &HTTPClient, url: &str) -> io::Result<String> {
    let mut resp = http.get(url).send().expect("Could not send HTTP request");
    let mut body = String::new();
    match resp.read_to_string(&mut body) {
        Err(error) => Err(error),
        _ => Ok(body),
    }
}

fn download_latest_buildpack_release(http: &HTTPClient) -> String {
    let xml = download_url(&http, RUBY_LANGPACK_RELEASES_URL)
                  .expect("Could not download Atom releases!");
    let atom_doc = Document::new_from_xml_string(&xml[..]).expect("Could not parse Atom releases!");
    let latest_tag = atom_doc.select("entry title")
                             .expect("Could not find latest buildpack tag!");
    latest_tag.text().clone()
}

fn redis_cache_value(redis: &redis::Connection, key: &str, value: &str) -> String {
    let set_result: RedisResult<()> = redis.set_ex(key, value, 3600);
    if let Err(_) = set_result {
        warn!("Cannot set {} in Redis, ignoring", key);
    }
    value.to_owned()
}

fn redis_cache_hash_value(redis: &redis::Connection, hash_name: &str, key: &str, value: &str) {
    let set_result: RedisResult<()> = redis.hset_nx(hash_name, key, value);
    if let Err(_) = set_result {
        warn!("Cannot set {} in Redis, ignoring", key);
    }
}

fn latest_buildpack_release(http: &HTTPClient, redis_result: &RedisResult<redis::Connection>) -> String {
    if let Ok(ref redis) = *redis_result {
        if let Ok(buildpack_release) = redis.get("latest_buildpack_release") {
            buildpack_release
        } else {
            let buildpack_release = download_latest_buildpack_release(http);
            redis_cache_value(redis, "latest_buildpack_release", &buildpack_release[..])
        }
    } else {
        download_latest_buildpack_release(http)
    }
}

fn cached_version_from_buildpack_release(buildpack_release: &str,
                                         redis_result: &RedisResult<redis::Connection>)
                                         -> Option<String> {
    if let Ok(ref redis) = *redis_result {
        if let Ok(version) = redis.hget("bundler_version", buildpack_release) {
            Some(version)
        } else {
            None
        }
    } else {
        None
    }
}

fn bundler_version_from_ruby_buildpack(http: &HTTPClient,
                                       redis_result: &RedisResult<redis::Connection>)
                                       -> Option<String> {
    let buildpack_release = latest_buildpack_release(http, redis_result);
    if let Some(cached_version) = cached_version_from_buildpack_release(&buildpack_release[..],
                                                                        redis_result) {
        return Some(cached_version);
    }
    let ruby_langpack_url = format!("https://raw.githubusercontent.com/\
                                     heroku/heroku-buildpack-ruby/{}/lib/language_pack/\
                                     ruby.rb",
                                    buildpack_release);
    let ruby_file = &download_url(&http, &ruby_langpack_url[..])
                         .expect("Could not download ruby.rb!")[..];
    let regex = Regex::new(r#"BUNDLER_VERSION += "(.+?)""#).expect("Invalid regular expression!");
    if regex.is_match(ruby_file) {
        let captures = regex.captures(ruby_file).expect("Could not match?!");
        let version = captures.at(1).expect("Capture not found?!");
        if let Ok(ref redis) = *redis_result {
            redis_cache_hash_value(&redis, "bundler_version", &buildpack_release[..], version);
        }
        Some(version.to_owned())
    } else {
        None
    }
}

fn is_bundler_upgraded(http: &HTTPClient,
                       redis_result: RedisResult<redis::Connection>)
                       -> bool {
    let bundler_version_result = bundler_version_from_ruby_buildpack(&http, &redis_result);
    if let Some(buildpack_bundler_version_str) = bundler_version_result {
        let min_version = Version::parse(MIN_BUNDLER_VERSION)
                              .expect("Could not parse min version!");
        let new_version = Version::parse(&buildpack_bundler_version_str[..])
                              .expect("Could not parse new version!");
        min_version < new_version
    } else {
        false
    }
}

fn determine_content_type(req: &Request) -> ContentType {
    if let Some(accept) = req.headers.get::<Accept>() {
        let mut use_json = false;
        for qitem in &accept.0 {
            let qitem_str = &format!("{}", qitem)[..];
            if qitem_str == "application/json" {
                use_json = true;
                break;
            }
        }

        if use_json {
            ContentType::json()
        } else {
            ContentType::html()
        }
    } else {
        ContentType::html()
    }
}

fn result_to_json(result: bool) -> String {
    let mut map = HashMap::new();
    map.insert("result".to_owned(), result);
    serde_json::to_string(&map).expect("Could not serialize result!")
}

fn result_to_html(result: bool) -> String {
    let result_str = if result {
        r#"
Yes
"#
    } else {
        r#"
No
"#
    };
    match File::open("index.html") {
        Ok(mut html_file) => {
            let mut html = String::new();
            html_file.read_to_string(&mut html).expect("Could not read HTML file!");
            html.replace("{{ is_bundler_upgraded }}", result_str)
                .replace("{{ MIN_BUNDLER_VERSION }}", MIN_BUNDLER_VERSION)
        }
        Err(error) => format!("HTML NOT FOUND: {}", error),
    }
}

fn server_port() -> String {
    match env::var("PORT") {
        Ok(port) => port,
        _ => format!("{}", DEFAULT_SERVER_PORT),
    }
}

fn connect_to_redis() -> RedisResult<redis::Connection> {
    if let Ok(redis_url) = env::var("REDIS_URL") {
        let client = redis::Client::open(&redis_url[..]).expect("Cannot connect to Redis");
        client.get_connection()
    } else {
        Err(RedisError::from(io::Error::new(io::ErrorKind::Other, "REDIS_URL not found")))
    }
}

fn request_handler(req: Request, mut res: Response) {
    if let AbsolutePath(ref path) = req.uri {
        if *path == "/" {
            if req.method == Get {
                let http = HTTPClient::new();
                let redis = connect_to_redis();
                let result = is_bundler_upgraded(&http, redis);
                let content_type = determine_content_type(&req);
                let data = match &format!("{}", content_type)[..] {
                    "application/json; charset=utf-8" => result_to_json(result),
                    _ => result_to_html(result),
                };
                res.headers_mut().set(content_type);
                res.send(data.as_bytes()).expect("Could not set response body!");
            } else {
                *res.status_mut() = hyper::status::StatusCode::MethodNotAllowed;
            }
        } else {
            *res.status_mut() = hyper::NotFound;
        }
    } else {
        *res.status_mut() = hyper::BadRequest;
    }
}

fn main() {
    env_logger::init().expect("Could not initialize env_logger!");
    let server = Server::http(&format!("0.0.0.0:{}", server_port())[..])
                     .expect("Could not create server!");
    server.handle(request_handler).expect("Could not set up HTTP request handler!");
}
<commit_msg>Bump bundler version<commit_after>// Copyright (C) 2016 Mark Lee

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.

// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

extern crate env_logger;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate redis;
extern crate regex;
extern crate rquery;
extern crate semver;
extern crate serde;
extern crate serde_json;

use hyper::{Client as HTTPClient, Get, Server};
use hyper::header::{Accept, ContentType};
use hyper::server::{Request, Response};
use hyper::uri::RequestUri::AbsolutePath;
use redis::{Commands, RedisError, RedisResult};
use regex::Regex;
use rquery::Document;
use semver::Version;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;

const DEFAULT_SERVER_PORT: u32 = 9000;
const MIN_BUNDLER_VERSION: &'static str = "1.12.0";
const RUBY_LANGPACK_RELEASES_URL: &'static str = "https://github.\
                                                  com/heroku/heroku-buildpack-ruby/releases.atom";

fn download_url(http: &HTTPClient, url: &str) -> io::Result<String> {
    let mut resp = http.get(url).send().expect("Could not send HTTP request");
    let mut body = String::new();
    match resp.read_to_string(&mut body) {
        Err(error) => Err(error),
        _ => Ok(body),
    }
}

fn download_latest_buildpack_release(http: &HTTPClient) -> String {
    let xml = download_url(&http, RUBY_LANGPACK_RELEASES_URL)
                  .expect("Could not download Atom releases!");
    let atom_doc = Document::new_from_xml_string(&xml[..]).expect("Could not parse Atom releases!");
    let latest_tag = atom_doc.select("entry title")
                             .expect("Could not find latest buildpack tag!");
    latest_tag.text().clone()
}

fn redis_cache_value(redis: &redis::Connection, key: &str, value: &str) -> String {
    let set_result: RedisResult<()> = redis.set_ex(key, value, 3600);
    if let Err(_) = set_result {
        warn!("Cannot set {} in Redis, ignoring", key);
    }
    value.to_owned()
}

fn redis_cache_hash_value(redis: &redis::Connection, hash_name: &str, key: &str, value: &str) {
    let set_result: RedisResult<()> = redis.hset_nx(hash_name, key, value);
    if let Err(_) = set_result {
        warn!("Cannot set {} in Redis, ignoring", key);
    }
}

fn latest_buildpack_release(http: &HTTPClient, redis_result: &RedisResult<redis::Connection>) -> String {
    if let Ok(ref redis) = *redis_result {
        if let Ok(buildpack_release) = redis.get("latest_buildpack_release") {
            buildpack_release
        } else {
            let buildpack_release = download_latest_buildpack_release(http);
            redis_cache_value(redis, "latest_buildpack_release", &buildpack_release[..])
        }
    } else {
        download_latest_buildpack_release(http)
    }
}

fn cached_version_from_buildpack_release(buildpack_release: &str,
                                         redis_result: &RedisResult<redis::Connection>)
                                         -> Option<String> {
    if let Ok(ref redis) = *redis_result {
        if let Ok(version) = redis.hget("bundler_version", buildpack_release) {
            Some(version)
        } else {
            None
        }
    } else {
        None
    }
}

fn bundler_version_from_ruby_buildpack(http: &HTTPClient,
                                       redis_result: &RedisResult<redis::Connection>)
                                       -> Option<String> {
    let buildpack_release = latest_buildpack_release(http, redis_result);
    if let Some(cached_version) = cached_version_from_buildpack_release(&buildpack_release[..],
                                                                        redis_result) {
        return Some(cached_version);
    }
    let ruby_langpack_url = format!("https://raw.githubusercontent.com/\
                                     heroku/heroku-buildpack-ruby/{}/lib/language_pack/\
                                     ruby.rb",
                                    buildpack_release);
    let ruby_file = &download_url(&http, &ruby_langpack_url[..])
                         .expect("Could not download ruby.rb!")[..];
    let regex = Regex::new(r#"BUNDLER_VERSION += "(.+?)""#).expect("Invalid regular expression!");
    if regex.is_match(ruby_file) {
        let captures = regex.captures(ruby_file).expect("Could not match?!");
        let version = captures.at(1).expect("Capture not found?!");
        if let Ok(ref redis) = *redis_result {
            redis_cache_hash_value(&redis, "bundler_version", &buildpack_release[..], version);
        }
        Some(version.to_owned())
    } else {
        None
    }
}

fn is_bundler_upgraded(http: &HTTPClient,
                       redis_result: RedisResult<redis::Connection>)
                       -> bool {
    let bundler_version_result = bundler_version_from_ruby_buildpack(&http, &redis_result);
    if let Some(buildpack_bundler_version_str) = bundler_version_result {
        let min_version = Version::parse(MIN_BUNDLER_VERSION)
                              .expect("Could not parse min version!");
        let new_version = Version::parse(&buildpack_bundler_version_str[..])
                              .expect("Could not parse new version!");
        min_version < new_version
    } else {
        false
    }
}

fn determine_content_type(req: &Request) -> ContentType {
    if let Some(accept) = req.headers.get::<Accept>() {
        let mut use_json = false;
        for qitem in &accept.0 {
            let qitem_str = &format!("{}", qitem)[..];
            if qitem_str == "application/json" {
                use_json = true;
                break;
            }
        }

        if use_json {
            ContentType::json()
        } else {
            ContentType::html()
        }
    } else {
        ContentType::html()
    }
}

fn result_to_json(result: bool) -> String {
    let mut map = HashMap::new();
    map.insert("result".to_owned(), result);
    serde_json::to_string(&map).expect("Could not serialize result!")
}

fn result_to_html(result: bool) -> String {
    let result_str = if result {
        r#"
Yes
"#
    } else {
        r#"
No
"#
    };
    match File::open("index.html") {
        Ok(mut html_file) => {
            let mut html = String::new();
            html_file.read_to_string(&mut html).expect("Could not read HTML file!");
            html.replace("{{ is_bundler_upgraded }}", result_str)
                .replace("{{ MIN_BUNDLER_VERSION }}", MIN_BUNDLER_VERSION)
        }
        Err(error) => format!("HTML NOT FOUND: {}", error),
    }
}

fn server_port() -> String {
    match env::var("PORT") {
        Ok(port) => port,
        _ => format!("{}", DEFAULT_SERVER_PORT),
    }
}

fn connect_to_redis() -> RedisResult<redis::Connection> {
    if let Ok(redis_url) = env::var("REDIS_URL") {
        let client = redis::Client::open(&redis_url[..]).expect("Cannot connect to Redis");
        client.get_connection()
    } else {
        Err(RedisError::from(io::Error::new(io::ErrorKind::Other, "REDIS_URL not found")))
    }
}

fn request_handler(req: Request, mut res: Response) {
    if let AbsolutePath(ref path) = req.uri {
        if *path == "/" {
            if req.method == Get {
                let http = HTTPClient::new();
                let redis = connect_to_redis();
                let result = is_bundler_upgraded(&http, redis);
                let content_type = determine_content_type(&req);
                let data = match &format!("{}", content_type)[..] {
                    "application/json; charset=utf-8" => result_to_json(result),
                    _ => result_to_html(result),
                };
                res.headers_mut().set(content_type);
                res.send(data.as_bytes()).expect("Could not set response body!");
            } else {
                *res.status_mut() = hyper::status::StatusCode::MethodNotAllowed;
            }
        } else {
            *res.status_mut() = hyper::NotFound;
        }
    } else {
        *res.status_mut() = hyper::BadRequest;
    }
}

fn main() {
    env_logger::init().expect("Could not initialize env_logger!");
    let server = Server::http(&format!("0.0.0.0:{}", server_port())[..])
                     .expect("Could not create server!");
    server.handle(request_handler).expect("Could not set up HTTP request handler!");
}
<|endoftext|>


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            all_pathes.par_iter().enumerate().for_each(|(index, elem)| {
                let collection_data_in_iterator = collection_protected.clone();
                let system_messages_in_iterator = tx_sys.clone();
                task::block_on(async move {
                    search_in_path(
                        has_ui,
                        has_tui,
                        collection_data_in_iterator,
                        system_messages_in_iterator,
                        index,
                        elem,
                    )
                    .await;
                });
            });
            if !has_ui {
                collection_protected.lock().await.print_stats();
                // todo: send terminate to net runner depending if it should continue or not
            }
            Ok::<(), Error>(())
        };

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

    Eof,
}

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

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}
impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

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

    fn advance(&mut self) -> char {
        let result = self.source.chars().nth(self.current).unwrap();
        self.current += 1;
        result
    }

    fn add_simple_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: None,
            line: self.line,
        }
    }

    fn scan_next(&mut self) -> TokenWithContext {
        match self.advance() {
            '(' => self.add_simple_context(Token::LeftParen),
            ')' => self.add_simple_context(Token::RightParen),
            '{' => self.add_simple_context(Token::LeftBrace),
            '}' => self.add_simple_context(Token::RightBrace),
            _ => unimplemented!(),
        }
    }
}

fn tokenize(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        tokens.push(scanner.scan_next());
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: None,
        line: scanner.line,
    });
    Ok(tokens)
}

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

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

fn run_prompt() -> Result<(), String> {
    loop {
        print!("> ");
        io::stdout().flush();
        let mut source = String::new();
        io::stdin().read_line(&mut source);
        try!(run(&source));
        ()
    }
}

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

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

    Eof,
}

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

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}
impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

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

    fn advance(&mut self) -> char {
        let result = self.source.chars().nth(self.current).unwrap();
        self.current += 1;
        result
    }

    fn add_simple_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: None,
            line: self.line,
        }
    }

    fn scan_next(&mut self) -> TokenWithContext {
        match self.advance() {
            '(' => self.add_simple_context(Token::LeftParen),
            ')' => self.add_simple_context(Token::RightParen),
            '{' => self.add_simple_context(Token::LeftBrace),
            '}' => self.add_simple_context(Token::RightBrace),
            ',' => self.add_simple_context(Token::Comma),
            '.' => self.add_simple_context(Token::Dot),
            '-' => self.add_simple_context(Token::Minus),
            '+' => self.add_simple_context(Token::Plus),
            ';' => self.add_simple_context(Token::Semicolon),
            '*' => self.add_simple_context(Token::Star),
            _ => unimplemented!(),
        }
    }
}

fn tokenize(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        tokens.push(scanner.scan_next());
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: None,
        line: scanner.line,
    });
    Ok(tokens)
}

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

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

fn run_prompt() -> Result<(), String> {
    loop {
        print!("> ");
        io::stdout().flush();
        let mut source = String::new();
        io::stdin().read_line(&mut source);
        try!(run(&source));
        ()
    }
}

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


<commit_before>extern crate base64;
extern crate hyper;
extern crate hyper_native_tls;
extern crate pem;
#[macro_use]
extern crate prettytable;
extern crate ring;
extern crate serde_json;
extern crate url;

extern crate ct_submitter;

use std::{env, process};
use std::fs::File;
use std::io::Read;

use ring::digest;

use ct_submitter::{fetch_trusted_ct_logs, submit_cert_to_logs, AddChainRequest};

fn pems_to_chain(data: &str) -> Vec<Vec<u8>> {
    return pem::parse_many(data)
               .into_iter()
               .filter(|p| p.tag == "CERTIFICATE")
               .map(|p| p.contents)
               .collect();
}

fn build_chain(http_client: &hyper::Client, cert: &[u8]) -> Vec<Vec<u8>> {
    let body = url::form_urlencoded::Serializer::new(String::new())
        .append_pair("b64cert", &base64::encode(cert))
        .finish();
    let body_bytes = body.as_bytes();
    let response = http_client
        .post("https://crt.sh/gen-add-chain")
        .header(hyper::header::ContentType::form_url_encoded())
        .body(hyper::client::Body::BufBody(body_bytes, body_bytes.len()))
        .send()
        .unwrap();

    let add_chain_request: AddChainRequest = serde_json::from_reader(response).unwrap();
    return add_chain_request
               .chain
               .iter()
               .map(|c| base64::decode(c).unwrap())
               .collect();
}

fn main() {
    if env::args().len() == 1 {
        println!("Usage: {} <cert-or-chain.pem ...>",
                 env::args().next().unwrap());
        process::exit(1);
    }

    let http_client = hyper::Client::with_connector(
        hyper::net::HttpsConnector::new(hyper_native_tls::NativeTlsClient::new().unwrap())
    );
    // TODO: timeout on the http_client, but for submit_cert_to_logs only, not build_chain
    let logs = fetch_trusted_ct_logs(&http_client);

    for path in env::args().skip(1) {
        println!("Submitting {} ...", path);

        let mut contents = String::new();
        File::open(path)
            .unwrap()
            .read_to_string(&mut contents)
            .unwrap();

        let mut chain = pems_to_chain(&contents);
        if chain.len() == 1 {
            // TODO: There's got to be some way to do this ourselves, instead of using crt.sh as a
            // glorified AIA chaser.
            println!("Only one certificate in chain, using crt.sh to build a full chain ...");
            chain = build_chain(&http_client, &chain[0]);
        }
        let scts = submit_cert_to_logs(&http_client, &logs, &chain);

        // TODO: is there a better way to do this hex-encoding?
        println!("Find the cert on crt.sh: https://crt.sh?q={}",
                 digest::digest(&digest::SHA256, &chain[0])
                     .as_ref()
                     .iter()
                     .map(|b| format!("{:02X}", b))
                     .collect::<String>());

        let mut table = prettytable::Table::new();
        table.add_row(row!["Log", "SCT"]);
        for (log, sct) in scts {
            table.add_row(row![log.description, base64::encode(&sct.to_raw_bytes())]);
        }
        table.printstd();
        println!();
        println!();
    }

}
<commit_msg>factor out function<commit_after>extern crate base64;
extern crate hyper;
extern crate hyper_native_tls;
extern crate pem;
#[macro_use]
extern crate prettytable;
extern crate ring;
extern crate serde_json;
extern crate url;

extern crate ct_submitter;

use std::{env, process};
use std::fs::File;
use std::io::Read;

use ring::digest;

use ct_submitter::{fetch_trusted_ct_logs, submit_cert_to_logs, AddChainRequest};

fn pems_to_chain(data: &str) -> Vec<Vec<u8>> {
    return pem::parse_many(data)
               .into_iter()
               .filter(|p| p.tag == "CERTIFICATE")
               .map(|p| p.contents)
               .collect();
}

fn build_chain_for_cert(http_client: &hyper::Client, cert: &[u8]) -> Vec<Vec<u8>> {
    let body = url::form_urlencoded::Serializer::new(String::new())
        .append_pair("b64cert", &base64::encode(cert))
        .finish();
    let body_bytes = body.as_bytes();
    let response = http_client
        .post("https://crt.sh/gen-add-chain")
        .header(hyper::header::ContentType::form_url_encoded())
        .body(hyper::client::Body::BufBody(body_bytes, body_bytes.len()))
        .send()
        .unwrap();

    let add_chain_request: AddChainRequest = serde_json::from_reader(response).unwrap();
    return add_chain_request
               .chain
               .iter()
               .map(|c| base64::decode(c).unwrap())
               .collect();
}

fn crtsh_url_for_cert(cert: &[u8]) -> String {
    return format!("https://crt.sh?q={}",
             digest::digest(&digest::SHA256, &cert)
                 .as_ref()
                 .iter()
                 .map(|b| format!("{:02X}", b))
                 .collect::<String>());
}

fn main() {
    if env::args().len() == 1 {
        println!("Usage: {} <cert-or-chain.pem ...>",
                 env::args().next().unwrap());
        process::exit(1);
    }

    let http_client = hyper::Client::with_connector(
        hyper::net::HttpsConnector::new(hyper_native_tls::NativeTlsClient::new().unwrap())
    );
    // TODO: timeout on the http_client, but for submit_cert_to_logs only, not build_chain
    let logs = fetch_trusted_ct_logs(&http_client);

    for path in env::args().skip(1) {
        println!("Submitting {} ...", path);

        let mut contents = String::new();
        File::open(path)
            .unwrap()
            .read_to_string(&mut contents)
            .unwrap();

        let mut chain = pems_to_chain(&contents);
        if chain.len() == 1 {
            // TODO: There's got to be some way to do this ourselves, instead of using crt.sh as a
            // glorified AIA chaser.
            println!("Only one certificate in chain, using crt.sh to build a full chain ...");
            chain = build_chain_for_cert(&http_client, &chain[0]);
        }
        let scts = submit_cert_to_logs(&http_client, &logs, &chain);

        // TODO: is there a better way to do this hex-encoding?
        println!("Find the cert on crt.sh: {}", crtsh_url_for_cert(&chain[0]));

        let mut table = prettytable::Table::new();
        table.add_row(row!["Log", "SCT"]);
        for (log, sct) in scts {
            table.add_row(row![log.description, base64::encode(&sct.to_raw_bytes())]);
        }
        table.printstd();
        println!();
        println!();
    }

}
<|endoftext|>


<commit_before>mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2_commit;
extern crate git2;
extern crate clog;

use docopt::Docopt;
use commit_analyzer::CommitType;
use std::process;
use semver::Version;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w, --write            Run with writing the changes afterwards.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: bool,
    flag_version: bool,
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

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

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let is_dry_run = !args.flag_write;

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let repository_path = &args.flag_path;

    match git2::Repository::open(repository_path) {
        Ok(_) => { },
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    let version = match toml_file::read_from_file(repository_path) {
        Ok(toml) => toml,
        Err(e) => {
            logger::stderr(format!("Reading `Cargo.toml` failed: {:?}", e));
            process::exit(1);
        }
    };

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(repository_path);
    if is_dry_run {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    else {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if is_dry_run {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version.to_string()) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    }
    else {
        logger::stdout(format!("New version: {}", new_version));

        match toml_file::write_new_version(repository_path, &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Writing `Cargo.toml` failed: {:?}", err));
                process::exit(1);
            }
        }

        logger::stdout(format!("Writing Changelog"));
        match changelog::write(repository_path, &version.to_string(), &new_version.to_string()) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Writing Changelog failed: {:?}", err));
                process::exit(1);
            }
        }

        match git::commit_files(repository_path, &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Committing `Cargo.toml` and `Changelog.md` failed: {:?}", err));
                process::exit(1);
            }
        }

        logger::stdout("Creating annotated git tag");
        let tag_message = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(msg) => msg,
            Err(err) => {
                logger::stderr(format!("Can't generate changelog: {:?}", err));
                process::exit(1);
            }
        };

        let tag_name = format!("v{}", new_version);
        match git::tag(repository_path, &tag_name, &tag_message) {
            Ok(_) => { },
            Err(err) => {
                logger::stderr(format!("Failed to create git tag: {:?}", err));
                process::exit(1);
            }
        }
    }
}
<commit_msg>feat: Disable dry-run by setting CI environment variable<commit_after>mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2_commit;
extern crate git2;
extern crate clog;

use docopt::Docopt;
use commit_analyzer::CommitType;
use std::process;
use semver::Version;
use std::env;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w, --write            Run with writing the changes afterwards.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: bool,
    flag_version: bool,
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

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

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let is_dry_run = if ci_env_set() {
        false
    }
    else {
        !args.flag_write
    };

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let repository_path = &args.flag_path;

    match git2::Repository::open(repository_path) {
        Ok(_) => { },
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    let version = match toml_file::read_from_file(repository_path) {
        Ok(toml) => toml,
        Err(e) => {
            logger::stderr(format!("Reading `Cargo.toml` failed: {:?}", e));
            process::exit(1);
        }
    };

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(repository_path);
    if is_dry_run {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    else {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if is_dry_run {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version.to_string()) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    }
    else {
        logger::stdout(format!("New version: {}", new_version));

        match toml_file::write_new_version(repository_path, &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Writing `Cargo.toml` failed: {:?}", err));
                process::exit(1);
            }
        }

        logger::stdout(format!("Writing Changelog"));
        match changelog::write(repository_path, &version.to_string(), &new_version.to_string()) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Writing Changelog failed: {:?}", err));
                process::exit(1);
            }
        }

        match git::commit_files(repository_path, &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Committing `Cargo.toml` and `Changelog.md` failed: {:?}", err));
                process::exit(1);
            }
        }

        logger::stdout("Creating annotated git tag");
        let tag_message = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(msg) => msg,
            Err(err) => {
                logger::stderr(format!("Can't generate changelog: {:?}", err));
                process::exit(1);
            }
        };

        let tag_name = format!("v{}", new_version);
        match git::tag(repository_path, &tag_name, &tag_message) {
            Ok(_) => { },
            Err(err) => {
                logger::stderr(format!("Failed to create git tag: {:?}", err));
                process::exit(1);
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate jsrs_parser;

mod value;
mod eval;

use std::io::{self, Write};
use std::collections::HashMap;
use eval::eval_string;

fn main() {
    let mut state = HashMap::new();
    let stdin = io::stdin();
    let mut stdout = io::stdout();

    loop {
        // prompt
        print!(">> ");
        stdout.flush().unwrap();

        // read input
        let mut input = String::new();
        stdin.read_line(&mut input).unwrap();

        // exit if no input (eg. ^D)
        if input.len() == 0 {
            println!("");
            break;
        }

        input = String::from(input.trim());

        // ignore if only whitespace
        if input.len() == 0 {
            continue;
        }

        // insert semicolon if necessary
        if !input.ends_with(";") && !input.ends_with("}") {
            input.push_str(";");
        }

        // eval
        println!("=> {:?}", eval_string(&input, &mut state));
        println!("** {:?}", state);
    }
}
<commit_msg>Read from file functionality.<commit_after>extern crate jsrs_parser;

mod value;
mod eval;

use std::env;
use std::io;
//use std::io::{self, Write};
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;
use std::fs::File;

use std::collections::HashMap;

use eval::eval_string;

fn eval_file(filename: String, debug: bool) {
    let mut state = HashMap::new();

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

    for line in file_buffer.lines() {
        let mut input = String::from(line.unwrap().trim());

        if debug {
            println!(">> {:?}", input);
        }

        // insert semicolon if necessary
        if !input.ends_with(";") && !input.ends_with("}") {
            input.push_str(";");
        }

        if debug {
            println!("=> {:?}", eval_string(&input, &mut state));
            println!("** {:?}", state);
        }
    }
}

fn repl() {
    let mut state = HashMap::new();
    let stdin = io::stdin();
    let mut stdout = io::stdout();

    loop {
        // prompt
        print!(">> ");
        stdout.flush().unwrap();

        // read input
        let mut input = String::new();
        stdin.read_line(&mut input).unwrap();

        // exit if no input (eg. ^D)
        if input.len() == 0 {
            println!("");
            break;
        }

        input = String::from(input.trim());

        // ignore if only whitespace
        if input.len() == 0 {
            continue;
        }

        // insert semicolon if necessary
        if !input.ends_with(";") && !input.ends_with("}") {
            input.push_str(";");
        }

        // eval
        println!("=> {:?}", eval_string(&input, &mut state));
        println!("** {:?}", state);
    }
}

fn main() {
    let args = env::args();
    if args.len() > 1 {
        for file in args.skip(1) {
            eval_file(file, true);
        }
    } else {
        repl();
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate meval;
extern crate itertools;

use std::io::{self, BufRead};
use std::io::prelude::*;
use std::io::BufReader;
use std::io::BufWriter;
use std::path::Path;
use std::fs::File;
use itertools::Itertools;

#[cfg(feature = "yaml")]
fn main()
{
    use clap::App;

    let yml = load_yaml!("cli.yaml");
    let m = App::from_yaml(yml).get_matches();

    match m.subcommand_name()
    {
        Some("generate") => generate(m),
        Some("lookup") => lookup(m),
        None => println!("show help here. TODO: me!"),
           _ => println!("Some other subcommand was used"),
    }
}

#[cfg(feature = "yaml")]
fn generate(m_opts: clap::ArgMatches)
{
    if let Some(ref m_opts) = m_opts.subcommand_matches("generate")
    {
        let lib = m_opts.value_of("lib").unwrap();
        let gb_ratios = m_opts.value_of("gb_ratios").unwrap();
        let m: f64 = m_opts.value_of("gear_m").unwrap().parse::<f64>().unwrap();  // TODO dafuq?
        let n: f64 = m_opts.value_of("gear_n").unwrap().parse::<f64>().unwrap();  // TODO same
        let pitch: f64 = m_opts.value_of("pitch").unwrap().parse::<f64>().unwrap(); // TODO and me

        // file output (w/buffering)
        let out_str = m_opts.value_of("out").unwrap();
        let mut out_file = match File::create(Path::new(out_str))
        {
            Ok(out_file) => out_file,
            Err(e) => panic!("Couldn't open output file: {:?}", e),
        };
        let mut out = BufWriter::new(&out_file);

        let gears: Vec<i32> = gears_from_file(lib);
        let ratios: Vec<f64> = ratios_from_file(gb_ratios);

        let mnp: f64 = (m/n)*pitch;

        // iterate every ab combination
        for combos in gears.clone().into_iter().combinations(2)
        {
            let a = combos[0] as f64;
            let b = combos[1] as f64;

            // generate a list with 2 gears
            // a/b
            // b/a
            for r in &ratios
            {
                let mut ans = (mnp * (a/b) * r);
                out.write_fmt(format_args!("({:03}/{:03}) * ({:03} / {:03}) * {:2.5} * {:?} = {:2.5}mm ({:2.5} TPI)\n", m, n, a, b, r, pitch, ans, 25.4/ans ));
                ans = (mnp * (b/a) * r);
                out.write_fmt(format_args!("({:03}/{:03}) * ({:03} / {:03}) * {:2.5} * {:?} = {:2.5}mm ({:2.5} TPI)\n", m, n, a, b, r, pitch, ans, 25.4/ans ));
            }
        }

        // iterate every abcd combination
        for combos in gears.clone().into_iter().combinations(4)
        {
            let a = combos[0] as f64;
            let b = combos[1] as f64;
            let c = combos[3] as f64;
            let d = combos[2] as f64;

            // generate a list with 4 gears
            // 2 on top = 4C2 = 6 sub-combinations:
            // ac / bd
            // ab / cd
            // ad / bc
            // bc / ad
            // bd / ac
            // cd / ab
            for r in &ratios
            {
                let mut ans;
                ans = (mnp * ((a*c)/(b*d)) * r);
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:2.5}mm ({:2.5} TPI)\n", m, n, a, c, b, d, r, pitch, ans, 25.4/ans) );
                ans = (mnp * ((a*b)/(c*d)) * r);
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:2.5}mm ({:2.5} TPI)\n", m, n, a, b, c, d, r, pitch, ans, 25.4/ans) );
                ans = (mnp * ((a*d)/(b*c)) * r);
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:2.5}mm ({:2.5} TPI)\n", m, n, a, d, b, c, r, pitch, ans, 25.4/ans) );
                ans = (mnp * ((b*c)/(a*d)) * r);
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:2.5}mm ({:2.5} TPI)\n", m, n, b, c, a, d, r, pitch, ans, 25.4/ans) );
                ans = (mnp * ((b*d)/(a*c)) * r);
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:2.5}mm ({:2.5} TPI)\n", m, n, b, d, a, c, r, pitch, ans, 25.4/ans) );
                ans = (mnp * ((c*d)/(a*b)) * r);
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:2.5}mm ({:2.5} TPI)\n", m, n, c, d, a, b, r, pitch, ans, 25.4/ans) );
                /*
                print_abcd(&m, &n, &a, &c, &b, &d, &r, &pitch, mnp * ( (a*c) / (b*d)) * r );
                print_abcd(&m, &n, &a, &b, &c, &d, &r, &pitch, mnp * ( (a*b) / (c*d)) * r );
                print_abcd(&m, &n, &a, &d, &b, &c, &r, &pitch, mnp * ( (a*d) / (b*c)) * r );
                print_abcd(&m, &n, &b, &c, &a, &d, &r, &pitch, mnp * ( (b*c) / (a*d)) * r );
                print_abcd(&m, &n, &b, &d, &a, &c, &r, &pitch, mnp * ( (b*d) / (a*c)) * r );
                print_abcd(&m, &n, &c, &d, &a, &b, &r, &pitch, mnp * ( (c*d) / (a*b)) * r );
                */
            }
        }
    }
}

//fn print_abcd(m: &f64, n: &f64, a: &f64, b: &f64, c: &f64, d: &f64, r: &f64, pitch: &f64, ans: f64)
//{
    //println!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:2.5}", m, n, a, c, b, d, r, pitch, ans );
//}

fn lookup(m: clap::ArgMatches)
{
    if let Some(ref m) = m.subcommand_matches("generate")
    {
        let db = m.value_of("db").unwrap();
        let pitch = m.value_of("pitch").unwrap();
        for line in lines_from_file(db).unwrap()
        {
            let line = match line
            {
                Ok(line) => line,
                Err(err) => panic!("failed to read line: {}", err)
            };
            assert_eq!(line.trim(), pitch);
        }
    }
}

fn gears_from_file<P>(filename: P) -> Vec<i32>
where
    P: AsRef<Path>,
{
    let file = File::open(filename).expect("No such file.");
    let gears = BufReader::new(file);
    gears.lines()
        .map(|l| l.expect("could not parse line"))
        .map(|l| l.parse::<i32>().unwrap())
        .collect()
}

fn ratios_from_file<P>(filename: P) -> Vec<f64>
where
    P: AsRef<Path>,
{
    use meval::eval_str;
    let file = File::open(filename).expect("No such file.");
    let ratios = BufReader::new(file);
    ratios.lines()
        .map(|l| l.expect("could not parse line"))
        .map(|l| match eval_str(l)
        {
            Ok(v) => v,
            Err(e) => panic!("{:?}", e)
        })
        //.map(|l| l.parse::<f64>().unwrap())
        .collect()
}

// https://stackoverflow.com/a/30801708
// returns BufReader s of strings
fn lines_from_file<P>(filename: P) -> Result<io::Lines<io::BufReader<File>>, io::Error>
where
    P: AsRef<Path>,
{
    let file = try!(File::open(filename));
    Ok(io::BufReader::new(file).lines())
}

#[cfg(not(feature = "yaml"))]
fn main() {
    // As stated above, if clap is not compiled with the YAML feature, it is disabled.
    println!("YAML feature is disabled.");
    println!("Pass --features yaml to cargo when trying this example.");
}
<commit_msg>minor tidies and TODOs<commit_after>#[macro_use]
extern crate clap;
extern crate meval;
extern crate itertools;

use std::io::{self, BufRead};
use std::io::prelude::*;
use std::io::BufReader;
use std::io::BufWriter;
use std::path::Path;
use std::fs::File;
use itertools::Itertools;

#[cfg(feature = "yaml")]
fn main()
{
    use clap::App;

    let yml = load_yaml!("cli.yaml");
    let m = App::from_yaml(yml).get_matches();

    match m.subcommand_name()
    {
        Some("generate") => generate(m),
        Some("lookup") => lookup(m),
        None => println!("show help here. TODO: me!"),
           _ => println!("Some other subcommand was used"),
    }
}

#[cfg(feature = "yaml")]
fn generate(m_opts: clap::ArgMatches)
{
    if let Some(ref m_opts) = m_opts.subcommand_matches("generate")
    {
        let lib = m_opts.value_of("lib").unwrap();
        let gb_ratios = m_opts.value_of("gb_ratios").unwrap();
        let m: f64 = m_opts.value_of("gear_m").unwrap().parse::<f64>().unwrap();  // TODO dafuq?
        let n: f64 = m_opts.value_of("gear_n").unwrap().parse::<f64>().unwrap();  // TODO same
        let pitch: f64 = m_opts.value_of("pitch").unwrap().parse::<f64>().unwrap(); // TODO and me

        // file output (w/buffering)
        let out_str = m_opts.value_of("out").unwrap();
        let out_file = match File::create(Path::new(out_str))
        {
            Ok(out_file) => out_file,
            Err(e) => panic!("Couldn't open output file: {:?}", e),
        };
        let mut out = BufWriter::new(&out_file);

        let gears: Vec<i32> = gears_from_file(lib);
        let ratios: Vec<f64> = ratios_from_file(gb_ratios);

        let mnp: f64 = (m/n)*pitch;

        // iterate every ab combination
        for combos in gears.clone().into_iter().combinations(2)
        {
            let a = combos[0] as f64;
            let b = combos[1] as f64;

            // generate a list with 2 gears
            // a/b
            // b/a
            for r in &ratios
            {
                // TODO: turn the out.write_fmt() calls into functions, or use a string for the
                // format string. FUGLY!
                let mut ans = mnp * (a/b) * r;
                out.write_fmt(format_args!("({:03}/{:03}) * ({:03} / {:03}) * {:2.5} * {:?} = {:?}mm ({:?} TPI)\n", m, n, a, b, r, pitch, ans, 25.4/ans )).unwrap();
                ans = mnp * (b/a) * r;
                out.write_fmt(format_args!("({:03}/{:03}) * ({:03} / {:03}) * {:2.5} * {:?} = {:?}mm ({:?} TPI)\n", m, n, a, b, r, pitch, ans, 25.4/ans )).unwrap();
            }
        }

        // iterate every abcd combination
        for combos in gears.clone().into_iter().combinations(4)
        {
            let a = combos[0] as f64;
            let b = combos[1] as f64;
            let c = combos[3] as f64;
            let d = combos[2] as f64;

            // generate a list with 4 gears
            // 2 on top = 4C2 = 6 sub-combinations:
            // ac / bd
            // ab / cd
            // ad / bc
            // bc / ad
            // bd / ac
            // cd / ab
            for r in &ratios
            {
                // TODO: turn the out.write_fmt() calls into functions, or use a string for the
                // format string. FUGLY!
                let mut ans;
                ans = mnp * ((a*c)/(b*d)) * r;
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:?}mm ({:?} TPI)\n", m, n, a, c, b, d, r, pitch, ans, 25.4/ans) ).unwrap();
                ans = mnp * ((a*b)/(c*d)) * r;
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:?}mm ({:?} TPI)\n", m, n, a, b, c, d, r, pitch, ans, 25.4/ans) ).unwrap();
                ans = mnp * ((a*d)/(b*c)) * r;
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:?}mm ({:?} TPI)\n", m, n, a, d, b, c, r, pitch, ans, 25.4/ans) ).unwrap();
                ans = mnp * ((b*c)/(a*d)) * r;
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:?}mm ({:?} TPI)\n", m, n, b, c, a, d, r, pitch, ans, 25.4/ans) ).unwrap();
                ans = mnp * ((b*d)/(a*c)) * r;
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:?}mm ({:?} TPI)\n", m, n, b, d, a, c, r, pitch, ans, 25.4/ans) ).unwrap();
                ans = mnp * ((c*d)/(a*b)) * r;
                out.write_fmt(format_args!("({:03}/{:03}) * [({:03} * {:03}) / ({:03} * {:03})] * {:2.5} * {:?} = {:?}mm ({:?} TPI)\n", m, n, c, d, a, b, r, pitch, ans, 25.4/ans) ).unwrap();
            }
        }
    }
}

// TODO: fixme.
/*
fn write2gear(buf :BufWriter, mnp:f64, m:f64, n:f64, p:f64, r:f64, top:f64, bottom:f64, ans:f64)
{
    buf.write_fmt(format_args!("({:03}/{:03}) * ({:03} / {:03}) * {:2.5} * {:?} = {:?}mm ({:?} TPI)\n", m, n, top, bottom, r, p, ans, 25.4/ans )).unwrap();
}
*/

fn lookup(m: clap::ArgMatches)
{
    if let Some(ref m) = m.subcommand_matches("generate")
    {
        let db = m.value_of("db").unwrap();
        let pitch = m.value_of("pitch").unwrap();
        for line in lines_from_file(db).unwrap()
        {
            let line = match line
            {
                Ok(line) => line,
                Err(err) => panic!("failed to read line: {}", err)
            };
            assert_eq!(line.trim(), pitch);
        }
    }
}

fn gears_from_file<P>(filename: P) -> Vec<i32>
where
    P: AsRef<Path>,
{
    let file = File::open(filename).expect("No such file.");
    let gears = BufReader::new(file);
    gears.lines()
        .map(|l| l.expect("could not parse line"))
        .map(|l| l.parse::<i32>().unwrap())
        .collect()
}

fn ratios_from_file<P>(filename: P) -> Vec<f64>
where
    P: AsRef<Path>,
{
    use meval::eval_str;
    let file = File::open(filename).expect("No such file.");
    let ratios = BufReader::new(file);
    ratios.lines()
        .map(|l| l.expect("could not parse line"))
        .map(|l| match eval_str(l)
        {
            Ok(v) => v,
            Err(e) => panic!("{:?}", e)
        })
        .collect()
}

// https://stackoverflow.com/a/30801708
// returns BufReader s of strings
fn lines_from_file<P>(filename: P) -> Result<io::Lines<io::BufReader<File>>, io::Error>
where
    P: AsRef<Path>,
{
    let file = try!(File::open(filename));
    Ok(io::BufReader::new(file).lines())
}

#[cfg(not(feature = "yaml"))]
fn main() {
    // As stated above, if clap is not compiled with the YAML feature, it is disabled.
    println!("YAML feature is disabled.");
    println!("Pass --features yaml to cargo when trying this example.");
}
<|endoftext|>


<commit_before>extern crate rsdocs2docset;
extern crate html5ever;
#[macro_use]
extern crate quick_error;
extern crate clap;

use clap::{Arg, App};
use html5ever::driver::{ParseOpts, parse_document};
use html5ever::rcdom::RcDom;
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;
use html5ever::tree_builder::TreeBuilderOpts;

use std::fs::{self, DirBuilder, DirEntry, File};
use std::io::{self,Write};
use std::path::Path;
use std::string::String;

use rsdocs2docset::dom::{manipulator, parser};

type Result<T> = std::result::Result<T, RsDoc2DocsetError>;

// #### read https://thesquareplanet.com/blog/rust-tips-and-tricks/ Cloning iterators
// #### read https://thesquareplanet.com/blog/rust-tips-and-tricks/ Partial matching

quick_error! {
    #[derive(Debug)]
    pub enum RsDoc2DocsetError {
        Io(err: std::io::Error) {
            from()
            cause(err)
            description(err.description())
        }
        Utf8(err: std::string::FromUtf8Error) {
            from()
            cause(err)
            description(err.description())
        }
    }
}

fn main() {
    let args = App::new("RsDoc2Docset")
                      .version("0.0.1")
                      .about("A tool that converts rust docs to Dash docset files")
                      .arg(Arg::with_name("indir")
                               .short("i")
                               .long("rsdoc")
                               .value_name("INDIR")
                               .help("directory that contains rustdoc files")
                               .required(true)
                               .takes_value(true))
                      .arg(Arg::with_name("outfile")
                               .short("o")
                               .long("outfile")
                               .value_name("OUTFILE")
                               .help("out file")
                               .required(true)
                               .takes_value(true))
                      .get_matches();

    if let Err(e) = docset_from_rs_doc_tree(Path::new(args.value_of("indir").unwrap()),
                                                 &args.value_of("outfile").unwrap(),
                                                 &annotate_file) {
        println!{"error: {}", e}

    }
}

fn docset_from_rs_doc_tree(source_dir: &Path, out_dir: &str,
                                annotate_file: &Fn(&DirEntry, &str) -> Result<()>)
                                -> Result<()> {
    if !source_dir.exists() {
        return Err(io::Error::new(io::ErrorKind::NotFound, format!("{} does not exist", source_dir.to_str().unwrap())).into());
    }

    if source_dir.is_dir() {
        for entry in fs::read_dir(source_dir)? {
            let entry = entry?;
            if entry.path().is_dir() {
                try!(docset_from_rs_doc_tree(&source_dir, &out_dir, annotate_file));
            } else {
                try!(annotate_file(&entry, &out_dir));
            }
        }
    }
    Ok(())
}

fn annotate_file(input: &DirEntry, output_prefix: &str) -> Result<()> {
    let out_dir = Path::new(output_prefix).join(input.path());
    let output = out_dir.join(input.file_name());

    try!(DirBuilder::new()
             .recursive(true)
             .create(out_dir));

    let opts = ParseOpts {
        tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() },
        ..Default::default()
    };

    let mut dom = parse_document(RcDom::default(), opts)
                           .from_utf8()
                           .read_from( &mut File::open(&input.path())? )?;

    let entries = parser::find_entry_elements(&mut dom);
    manipulator::add_dash_links(&mut dom, &entries);

    let mut bytes = vec![];
    try!(serialize(&mut bytes, &dom.document, SerializeOpts::default()));

    let result = String::from_utf8(bytes)?;

    File::create(&output)?.write_all(result.as_ref())?;

    println!("Successfully added docset links from {} -> {}",
             input.path().display(),
             output.display());
    Ok(())
}
<commit_msg>Copy non-html file instead of parsing them<commit_after>extern crate rsdocs2docset;
extern crate html5ever;
#[macro_use]
extern crate quick_error;
extern crate clap;

use clap::{Arg, App};
use html5ever::driver::{ParseOpts, parse_document};
use html5ever::rcdom::RcDom;
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;
use html5ever::tree_builder::TreeBuilderOpts;

use std::ffi::OsStr;
use std::fs::{self, DirBuilder, DirEntry, File};
use std::io::{self,Write};
use std::path::Path;
use std::string::String;

use rsdocs2docset::dom::{manipulator, parser};

type Result<T> = std::result::Result<T, RsDoc2DocsetError>;

// #### read https://thesquareplanet.com/blog/rust-tips-and-tricks/ Cloning iterators
// #### read https://thesquareplanet.com/blog/rust-tips-and-tricks/ Partial matching

quick_error! {
    #[derive(Debug)]
    pub enum RsDoc2DocsetError {
        Io(err: std::io::Error) {
            from()
                cause(err)
                description(err.description())
        }
        Utf8(err: std::string::FromUtf8Error) {
            from()
                cause(err)
                description(err.description())
        }
    }
}

fn main() {
    let args = App::new("RsDoc2Docset")
        .version("0.0.1")
        .about("A tool that converts rust docs to Dash docset files")
        .arg(Arg::with_name("indir")
             .short("i")
             .long("rsdoc")
             .value_name("INDIR")
             .help("directory that contains rustdoc files")
             .required(true)
             .takes_value(true))
        .arg(Arg::with_name("outdir")
             .short("o")
             .long("outdir")
             .value_name("OUTFILE")
             .help("out file")
             .required(true)
             .takes_value(true))
        .get_matches();

    if let Err(e) = docset_from_rs_doc_tree(Path::new(args.value_of("indir").unwrap()),
    &args.value_of("outdir").unwrap(),
    &annotate_file) {
        println!{"error: {}", e}

    }
}

fn docset_from_rs_doc_tree(source_dir: &Path, out_dir: &str,
                           annotate_file: &Fn(&DirEntry, &str) -> Result<()>)
    -> Result<()> {
        if !source_dir.exists() {
            return Err(io::Error::new(io::ErrorKind::NotFound, format!("{} does not exist", source_dir.to_str().unwrap())).into());
        }

        if source_dir.is_dir() {
            for entry in fs::read_dir(source_dir)? {
                let entry = entry?;
                if entry.path().is_dir() {
                    try!(docset_from_rs_doc_tree(&entry.path(), &out_dir, annotate_file));
                } else {
                    try!(annotate_file(&entry, &out_dir));
                }
            }
        }
        Ok(())
    }

fn annotate_file(in_file: &DirEntry, output_prefix: &str) -> Result<()> {
    let out_dir = Path::new(output_prefix).join(in_file.path().parent().unwrap());
    let out_file = out_dir.join(in_file.file_name());

    try!(DirBuilder::new()
         .recursive(true)
         .create(out_dir));

    if in_file.path().extension() != Some(OsStr::new("html")) {
        try!(fs::copy(in_file.path(), &out_file));
        println!("copied {} -> {}",
                 in_file.path().display(),
                 out_file.display());
    } else {
        let opts = ParseOpts {
            tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() },
            ..Default::default()
        };

        let mut dom = parse_document(RcDom::default(), opts)
            .from_utf8()
            .read_from( &mut File::open(&in_file.path())? )?;

        let entries = parser::find_entry_elements(&mut dom);
        manipulator::add_dash_links(&mut dom, &entries);

        let mut bytes = vec![];
        try!(serialize(&mut bytes, &dom.document, SerializeOpts::default()));

        let result = String::from_utf8(bytes)?;

        File::create(&out_file)?.write_all(result.as_ref())?;

        println!("added docset links from {} to {}",
                 in_file.path().display(),
                 out_file.display());
    }

    Ok(())
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
extern crate rand;
extern crate rustty;
use rustty::{Event, Terminal};
use std::io::Write;
use std::time::Duration;

mod addr;
mod byte;
mod dungeon;
mod geometry;
mod grid;
mod item;
mod monster;
mod player;
mod spell;
mod sprite;
mod tile;
mod timer;
mod util;
mod view;

use geometry::Point;
use player::Player;

fn main() {
    let mut dungeon = dungeon::build().unwrap_or_else(|e| {
        writeln!(std::io::stderr(), "{}", e).unwrap();
        std::process::exit(1)
    });

    let mut player = Player::new(&dungeon);

    let mut terminal = Terminal::new().unwrap();

    loop {
        view::draw_level(&mut terminal, &dungeon, &player);
        terminal.swap_buffers().unwrap();
        if let Some(Event::Key(key)) = terminal.get_event(Duration::from_secs(99999)).unwrap() {
            match key {
                'q' => break,
                '<' => player.try_stairs_up(&dungeon),
                '>' => player.try_stairs_down(&dungeon),
                _ => {
                    // try movement commands
                    if let Some(step_direction) = key_to_direction(key) {
                        player.step(&mut dungeon, step_direction)
                    }
                }
            }
        }
    }
}

fn key_to_direction(key: char) -> Option<Point> {
    match key {
        'y' | '7' => Some(Point(-1, -1)),
        'k' | '8' => Some(Point(0, -1)),
        'u' | '9' => Some(Point(1, -1)),
        'h' | '4' => Some(Point(-1, 0)),
        '.' | '5' | 's' => Some(Point(0, 0)),
        'l' | '6' => Some(Point(1, 0)),
        'b' | '1' => Some(Point(-1, 1)),
        'j' | '2' => Some(Point(0, 1)),
        'n' | '3' => Some(Point(1, 1)),
        _ => None
    }
}
<commit_msg>eat escape sequences<commit_after>#![allow(dead_code)]
extern crate rand;
extern crate rustty;
use rustty::{Event, Terminal};
use std::io::Write;
use std::time::Duration;

mod addr;
mod byte;
mod dungeon;
mod geometry;
mod grid;
mod item;
mod monster;
mod player;
mod spell;
mod sprite;
mod tile;
mod timer;
mod util;
mod view;

use geometry::Point;
use player::Player;

fn main() {
    let mut dungeon = dungeon::build().unwrap_or_else(|e| {
        writeln!(std::io::stderr(), "{}", e).unwrap();
        std::process::exit(1)
    });

    let mut player = Player::new(&dungeon);

    let mut terminal = Terminal::new().unwrap();

    loop {
        view::draw_level(&mut terminal, &dungeon, &player);
        terminal.swap_buffers().unwrap();
        if let Some(Event::Key(key)) = terminal.get_event(Duration::from_secs(99999)).unwrap() {
            match key {
                'q' => break,
                '<' => player.try_stairs_up(&dungeon),
                '>' => player.try_stairs_down(&dungeon),
                '\x1b' => {
                    // eat escape sequences
                    while terminal.get_event(Duration::from_millis(1)).unwrap().is_some() {
                    }
                }
                _ => {
                    // try movement commands
                    if let Some(step_direction) = key_to_direction(key) {
                        player.step(&mut dungeon, step_direction)
                    }
                }
            }
        }
    }
}

fn key_to_direction(key: char) -> Option<Point> {
    match key {
        'y' | '7' => Some(Point(-1, -1)),
        'k' | '8' => Some(Point(0, -1)),
        'u' | '9' => Some(Point(1, -1)),
        'h' | '4' => Some(Point(-1, 0)),
        '.' | '5' | 's' => Some(Point(0, 0)),
        'l' | '6' => Some(Point(1, 0)),
        'b' | '1' => Some(Point(-1, 1)),
        'j' | '2' => Some(Point(0, 1)),
        'n' | '3' => Some(Point(1, 1)),
        _ => None
    }
}
<|endoftext|>