<commit_before>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error};
use clap::{Arg, App};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            std::process::exit(E_NO_GIT_REPO);
        },
    };

    // 1. Add submodule as a remote
    let remote = add_remote(&repo, submodule_dir).unwrap();
    // 2. Fetch submodule's history
    // 3. Check out submodule's branch under some unique name (UUID?)
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    // 5. Run through main branch's history and note down commit IDs where submodule was touched,
    //    along with submodule's commit ID
    // 6. Run through old and new submodule's history (in lockstep) and note down new commit IDs of
    //    the commits that were referenced in the main repo
    // 7. Remove submodule's remote
    // 8. Run through master's history, doing two things:
    //      8.1 updating the tree to contain the relevant tree from submodule
    //      8.2 in commits that used to update the submodule, add a parent pointing to appropriate
    //          commit in new submodule history
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<commit_msg>Fetch submodule's history<commit_after>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error};
use clap::{Arg, App};

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            std::process::exit(E_NO_GIT_REPO);
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).unwrap();
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None);
    // 3. Check out submodule's branch under some unique name (UUID?)
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    // 5. Run through main branch's history and note down commit IDs where submodule was touched,
    //    along with submodule's commit ID
    // 6. Run through old and new submodule's history (in lockstep) and note down new commit IDs of
    //    the commits that were referenced in the main repo
    // 7. Remove submodule's remote
    // 8. Run through master's history, doing two things:
    //      8.1 updating the tree to contain the relevant tree from submodule
    //      8.2 in commits that used to update the submodule, add a parent pointing to appropriate
    //          commit in new submodule history
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<|endoftext|>


<commit_before>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate httparse;
extern crate hyper;
extern crate tokio_core;
extern crate farmhash;
extern crate futures;
extern crate futures_cpupool;
extern crate rand;
extern crate r2d2;
extern crate r2d2_redis;
extern crate redis;
extern crate unicase;

mod config;
mod header;
mod proxy;
mod cache;
mod control;
mod server;

use std::thread;
use std::ops::Deref;
use std::time::Duration;
use std::str::FromStr;

use clap::{App, Arg};
use log::LevelFilter;

use config::config::Config;
use config::logger::ConfigLogger;
use config::reader::ConfigReader;
use cache::store::{CacheStore, CacheStoreBuilder};
use control::listen::ControlListenBuilder;
use server::listen::ServerListenBuilder;

struct AppArgs {
    config: String,
}

pub static LINE_FEED: &'static str = "\r\n";

pub static THREAD_NAME_WORKER: &'static str = "bloom-worker";
pub static THREAD_NAME_CONTROL_MASTER: &'static str = "bloom-control-master";
pub static THREAD_NAME_CONTROL_CLIENT: &'static str = "bloom-control-client";

lazy_static! {
    static ref APP_ARGS: AppArgs = make_app_args();
    static ref APP_CONF: Config = ConfigReader::make();
    static ref APP_CACHE_STORE: CacheStore = CacheStoreBuilder::new();
}

fn make_app_args() -> AppArgs {
    let matches = App::new(crate_name!())
        .version(crate_version!())
        .author(crate_authors!("\n"))
        .about(crate_description!())
        .arg(
            Arg::with_name("config")
                .short("c")
                .long("config")
                .help("Path to configuration file")
                .default_value("./config.cfg")
                .takes_value(true),
        )
        .get_matches();

    // Generate owned app arguments
    AppArgs { config: String::from(matches.value_of("config").expect("invalid config value")) }
}

fn ensure_states() {
    // Ensure all statics are valid (a `deref` is enough to lazily initialize them)
    APP_ARGS.deref();
    APP_CONF.deref();
    APP_CACHE_STORE.deref();
}

fn spawn_worker() {
    let worker = thread::Builder::new()
        .name(THREAD_NAME_WORKER.to_string())
        .spawn(|| ServerListenBuilder::new().run());

    // Block on worker thread (join it)
    let has_error = if let Ok(worker_thread) = worker {
        worker_thread.join().is_err()
    } else {
        true
    };

    // Worker thread crashed?
    if has_error == true {
        error!("worker thread crashed, setting it up again");

        // Prevents thread start loop floods
        thread::sleep(Duration::from_secs(1));

        spawn_worker();
    }
}

fn main() {
    let _logger = ConfigLogger::init(LevelFilter::from_str(&APP_CONF.server.log_level).expect(
        "invalid log level",
    ));

    info!("starting up");

    // Ensure all states are bound
    ensure_states();

    // Run control interface (in its own thread)
    ControlListenBuilder::new().run();

    // Run server (from main thread, maintain thread active if down)
    spawn_worker();

    error!("could not start");
}
<commit_msg>Fix deref() warning in latest Rust<commit_after>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate httparse;
extern crate hyper;
extern crate tokio_core;
extern crate farmhash;
extern crate futures;
extern crate futures_cpupool;
extern crate rand;
extern crate r2d2;
extern crate r2d2_redis;
extern crate redis;
extern crate unicase;

mod config;
mod header;
mod proxy;
mod cache;
mod control;
mod server;

use std::thread;
use std::ops::Deref;
use std::time::Duration;
use std::str::FromStr;

use clap::{App, Arg};
use log::LevelFilter;

use config::config::Config;
use config::logger::ConfigLogger;
use config::reader::ConfigReader;
use cache::store::{CacheStore, CacheStoreBuilder};
use control::listen::ControlListenBuilder;
use server::listen::ServerListenBuilder;

struct AppArgs {
    config: String,
}

pub static LINE_FEED: &'static str = "\r\n";

pub static THREAD_NAME_WORKER: &'static str = "bloom-worker";
pub static THREAD_NAME_CONTROL_MASTER: &'static str = "bloom-control-master";
pub static THREAD_NAME_CONTROL_CLIENT: &'static str = "bloom-control-client";

lazy_static! {
    static ref APP_ARGS: AppArgs = make_app_args();
    static ref APP_CONF: Config = ConfigReader::make();
    static ref APP_CACHE_STORE: CacheStore = CacheStoreBuilder::new();
}

fn make_app_args() -> AppArgs {
    let matches = App::new(crate_name!())
        .version(crate_version!())
        .author(crate_authors!("\n"))
        .about(crate_description!())
        .arg(
            Arg::with_name("config")
                .short("c")
                .long("config")
                .help("Path to configuration file")
                .default_value("./config.cfg")
                .takes_value(true),
        )
        .get_matches();

    // Generate owned app arguments
    AppArgs { config: String::from(matches.value_of("config").expect("invalid config value")) }
}

fn ensure_states() {
    // Ensure all statics are valid (a `deref` is enough to lazily initialize them)
    let (_, _, _) = (APP_ARGS.deref(), APP_CONF.deref(), APP_CACHE_STORE.deref());
}

fn spawn_worker() {
    let worker = thread::Builder::new()
        .name(THREAD_NAME_WORKER.to_string())
        .spawn(|| ServerListenBuilder::new().run());

    // Block on worker thread (join it)
    let has_error = if let Ok(worker_thread) = worker {
        worker_thread.join().is_err()
    } else {
        true
    };

    // Worker thread crashed?
    if has_error == true {
        error!("worker thread crashed, setting it up again");

        // Prevents thread start loop floods
        thread::sleep(Duration::from_secs(1));

        spawn_worker();
    }
}

fn main() {
    let _logger = ConfigLogger::init(LevelFilter::from_str(&APP_CONF.server.log_level).expect(
        "invalid log level",
    ));

    info!("starting up");

    // Ensure all states are bound
    ensure_states();

    // Run control interface (in its own thread)
    ControlListenBuilder::new().run();

    // Run server (from main thread, maintain thread active if down)
    spawn_worker();

    error!("could not start");
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::str::FromStr;

fn print_arg_error() {
    println!("incorrect arguments passed");
}

struct CSV_descriptor<'a> {
    file_path: &'a  Path,
    delimiter:      char,
    quote:          Option<char>,
}

impl<'a> std::fmt::Display for CSV_descriptor<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{} {} {:?}", self.file_path.display(), self.delimiter, self.quote)
    }
}

fn parse_args<'a>(path_arg:         &'a String,
                  delimiter_arg:    &'a String,
                  quote_arg:        &'a String) -> Result<CSV_descriptor<'a>, &'static str>
{

    let csv_file_path = Path::new(path_arg);

    let csv_delimiter = match delimiter_arg.chars().next() {
                            Some(result) => result,
                            None         => return Err("error parsing delimiter"),
                        };

    let csv_quote     = quote_arg.chars().next();

    Ok(CSV_descriptor {file_path: &csv_file_path, delimiter: csv_delimiter, quote: csv_quote})
}

fn main() {

/*

1. Parse arguments
2. Open CSV files
3. Get columns (cols_N)
4. Get intersection of those two sets of columns(cols_to_compare)
5. Create {column name : column index in cols_N} dicts
6. Create {CSV_col_value : CSV row index in file} dicts, where CSV_col_value is a unique key made of the value of several CSV columns.
   For example, {Alex38 : 76}. In this example a name and age form a unique key for the 76th CSV row.
7. Get intersection of key sets of dicts from step 6 (row_keys_to_compare)
8. Loop through row_keys_to_compare, use dicts from step 6 to get line numbers for CSV files
    8.1 Loop through cols_to_compare, use dicts from step 5 to extract column values from CSV rows(step 8)
    8.2 Compare values

Input parameters: CSV paths, delimiters, quotes

For example, ./main file_1.csv "," "'" file_2.csv " " ""

*/

    let args: Vec<String> = env::args().collect();
    if args.len() != 7 {
        print_arg_error();
        return;
    };

    let csv_desc: CSV_descriptor = match parse_args(&args[1], &args[2], &args[3]) {
        Err(why)    => panic!("error parsing arguments: {}", why),
        Ok(result)  => result,
    };
    println!("{}", csv_desc);

    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

}
<commit_msg>Get CSV columns (not tested)<commit_after>use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::str::FromStr;

fn print_arg_error() {
    println!("incorrect arguments passed");
}

struct CSV_descriptor<'a> {
    file_path: &'a  Path,
    delimiter:      char,
    quote:          Option<char>,
}

impl<'a> std::fmt::Display for CSV_descriptor<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{} {} {:?}", self.file_path.display(), self.delimiter, self.quote)
    }
}

fn parse_args<'a>(path_arg:         &'a String,
                  delimiter_arg:    &'a String,
                  quote_arg:        &'a String) -> Result<CSV_descriptor<'a>, &'static str>
{

    let csv_file_path = Path::new(path_arg);

    let csv_delimiter = match delimiter_arg.chars().next() {
                            Some(result) => result,
                            None         => return Err("incorrect delimiter"),
                        };

    let csv_quote     = quote_arg.chars().next();

    Ok(CSV_descriptor {file_path: &csv_file_path, delimiter: csv_delimiter, quote: csv_quote})
}

fn get_csv_cols(csv_desc: CSV_descriptor) -> Result<Vec<String>, String> {

    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    let csv_reader = BufReader::new(csv_file);

    let mut csv_line_iter = csv_reader.lines();

    let csv_header: String = match csv_line_iter.next() {
        Some(result) => match result {
                            Err(why) => return Err(format!("error getting csv header: {}", why)),
                            Ok(header) => header,
                        },
        None         => return Err("csv header reading failed".to_string()),
    };

    let csv_cols: Vec<String> = {
        let cols_iter = csv_header.split(csv_desc.delimiter);
        match csv_desc.quote {
            Some(q) => cols_iter.map(|s| {s.trim_matches(q).to_string()}).collect(),
            None    => cols_iter.map(|s| {s.to_string()}).collect(),
        }
    };

    Ok(csv_cols)
}

fn main() {

/*

1. Parse arguments
2. Open CSV files
3. Get columns (cols_N)
4. Get intersection of those two sets of columns(cols_to_compare)
5. Create {column name : column index in cols_N} dicts
6. Create {CSV_col_value : CSV row index in file} dicts, where CSV_col_value is a unique key made of the value of several CSV columns.
   For example, {Alex38 : 76}. In this example a name and age form a unique key for the 76th CSV row.
7. Get intersection of key sets of dicts from step 6 (row_keys_to_compare)
8. Loop through row_keys_to_compare, use dicts from step 6 to get line numbers for CSV files
    8.1 Loop through cols_to_compare, use dicts from step 5 to extract column values from CSV rows(step 8)
    8.2 Compare values

Input parameters: CSV paths, delimiters, quotes

For example, ./main file_1.csv "," "'" file_2.csv " " ""

*/

    /*** 1 ***/
    let args: Vec<String> = env::args().collect();
    if args.len() != 7 {
        print_arg_error();
        return;
    };

    let csv_desc: CSV_descriptor = match parse_args(&args[1], &args[2], &args[3]) {
        Err(why)    => panic!("error parsing arguments: {}", why),
        Ok(result)  => result,
    };
    println!("{}", csv_desc);

    let csv_file = match File::open(csv_desc.file_path) {
        Err(why) => panic!("couldn't open csv @ {}: {}", csv_desc.file_path.display(), why),
        Ok(file) => file,
    };

    /*** 2 ***/
    //let csv_cols: Vec<String> = get_csv_cols(csv_desc);

}
<|endoftext|>


<commit_before>// Copyright © 2016 Bart Massey
// [This program is licensed under the "MIT License"]
// Please see the file COPYING in the source
// distribution of this software for license terms.

// Rocket pastebin demo

#![feature(plugin)]
#![feature(plugin, custom_derive)]
#![feature(proc_macro)]
#![plugin(rocket_codegen)]

extern crate rocket;
extern crate rocket_contrib;
extern crate rand;
extern crate serde;
#[macro_use] extern crate serde_derive;

use std::io;
use std::io::{Read, BufRead, Write};
use std::path::Path;
use std::collections::HashMap;
use std::fs::File;

use rocket::Data;
use rocket::response::NamedFile;
use rocket_contrib::Template;
use rocket::request::Form;
use rocket::response::Redirect;

mod paste_id;
use paste_id::PasteID;

#[derive(Serialize)]
struct PasteInfo {
    paste_id: String,
    snippet: String,
    ellipsis: bool
}

#[get("/")]
fn index() -> io::Result<Template> {
    let dirents = std::fs::read_dir("upload/")
                  .expect("could not open upload directory");
    let pastes = dirents.map(|d| {
        let paste_id = d.unwrap().file_name().into_string().unwrap();
        let mut paste_filename = "upload/".to_string();
        paste_filename.push_str(&paste_id);
        let paste_file = NamedFile::open(&paste_filename)
                         .expect("could not open paste file");
        let mut paste_reader = io::BufReader::new(paste_file);
        let mut paste_line = String::new();
        let paste_len = paste_reader.read_line(&mut paste_line)
                        .expect("could not read first line of paste");
        let (ellipsis, snippet) =
            if paste_len < 24 {
                (false, paste_line)
            } else {
                let result = (&paste_line)[0..24].to_string();
                (true, result)
            };
        PasteInfo { paste_id: paste_id, snippet: snippet, ellipsis: ellipsis }
    }).collect::<Vec<PasteInfo>>();
    let mut context: HashMap<&str, _> = HashMap::new();
    context.insert("pastes", &pastes);
    Ok(Template::render("index", &context))
}

#[post("/", data = "<paste>")]
fn upload(paste: Data) -> io::Result<String> {
    let paste_id = PasteID::new(8);
    let path = Path::new("upload/").join(paste_id.to_string());
    paste.stream_to_file(path)?;
    Ok(format!("http://localhost:8000/{}\n", paste_id))
}

fn open_paste(id: &PasteID) -> io::Result<NamedFile> {
    let filename = format!("upload/{}", id);
    NamedFile::open(&filename)
}

fn create_paste(id: &PasteID) -> io::Result<File> {
    let filename = format!("upload/{}", id);
    File::create(&filename)
}

#[get("/<id>")]
fn retrieve(id: PasteID) -> Option<NamedFile> {
    open_paste(&id).ok()
}

#[get("/edit/<id>")]
fn make_edit(id: PasteID) -> io::Result<Template> {
    let paste = open_paste(&id)?;
    let mut paste_reader = io::BufReader::new(paste);
    let mut paste_contents = String::new();
    paste_reader.read_to_string(&mut paste_contents)?;
    let paste_id = id.to_string();
    let paste_rows = format!("{}", 100);
    let mut context: HashMap<&str, _> = HashMap::new();
    context.insert("contents", &paste_contents);
    context.insert("paste_id", &paste_id);
    context.insert("paste_rows", &paste_rows);
    Ok(Template::render("edit", &context))
}

#[derive(FromForm)]
struct EditForm {
    paste: String
}

#[post("/edit/<id>", data = "<form>")]
fn accept_edit(id: PasteID, form: Form<EditForm>) -> io::Result<Redirect> {
    let edit = form.get();
    let paste = create_paste(&id)?;
    let mut paste_writer = io::BufWriter::new(paste);
    let paste_contents =
        edit.paste.chars().map(|c|{c as u8}).collect::<Vec<u8>>();
    paste_writer.write_all(paste_contents.as_slice())?;
    paste_writer.flush()?;
    Ok(Redirect::to("/"))
}

fn main() {
    let rocket = rocket::ignite();
    let rocket = rocket.mount("/", routes![index, upload, retrieve,
                                           make_edit, accept_edit]);
    rocket.launch()
}
<commit_msg>cleaned up upload directory handling<commit_after>// Copyright © 2016 Bart Massey
// [This program is licensed under the "MIT License"]
// Please see the file COPYING in the source
// distribution of this software for license terms.

// Rocket pastebin demo

#![feature(plugin)]
#![feature(plugin, custom_derive)]
#![feature(proc_macro)]
#![plugin(rocket_codegen)]

static UPLOAD: &'static str = "upload";

extern crate rocket;
extern crate rocket_contrib;
extern crate rand;
extern crate serde;
#[macro_use] extern crate serde_derive;

use std::io;
use std::io::{Read, BufRead, Write};
use std::path::Path;
use std::collections::HashMap;
use std::fs::{File, create_dir, metadata, Metadata};

use rocket::Data;
use rocket::response::NamedFile;
use rocket_contrib::Template;
use rocket::request::Form;
use rocket::response::Redirect;

mod paste_id;
use paste_id::PasteID;

#[derive(Serialize)]
struct PasteInfo {
    paste_id: String,
    snippet: String,
    ellipsis: bool
}

#[get("/")]
fn index() -> io::Result<Template> {
    let dirents = std::fs::read_dir(UPLOAD)
                  .expect("could not open upload directory");
    let pastes = dirents.map(|d| {
        let paste_id = d.unwrap().file_name().into_string().unwrap();
        let paste_filename = Path::new(UPLOAD).join(&paste_id);
        let paste_file = NamedFile::open(&paste_filename)
                         .expect("could not open paste file");
        let mut paste_reader = io::BufReader::new(paste_file);
        let mut paste_line = String::new();
        let paste_len = paste_reader.read_line(&mut paste_line)
                        .expect("could not read first line of paste");
        let (ellipsis, snippet) =
            if paste_len < 24 {
                (false, paste_line)
            } else {
                let result = (&paste_line)[0..24].to_string();
                (true, result)
            };
        PasteInfo { paste_id: paste_id, snippet: snippet, ellipsis: ellipsis }
    }).collect::<Vec<PasteInfo>>();
    let mut context: HashMap<&str, _> = HashMap::new();
    context.insert("pastes", &pastes);
    Ok(Template::render("index", &context))
}

#[post("/", data = "<paste>")]
fn upload(paste: Data) -> io::Result<String> {
    let paste_id = PasteID::new(8);
    let path = Path::new(UPLOAD).join(paste_id.to_string());
    paste.stream_to_file(path)?;
    Ok(format!("http://localhost:8000/{}\n", paste_id))
}

fn open_paste(id: &PasteID) -> io::Result<NamedFile> {
    let filename = Path::new(UPLOAD).join(id.to_string());
    NamedFile::open(&filename)
}

fn create_paste(id: &PasteID) -> io::Result<File> {
    let filename = Path::new(UPLOAD).join(id.to_string());
    File::create(&filename)
}

#[get("/<id>")]
fn retrieve(id: PasteID) -> Option<NamedFile> {
    open_paste(&id).ok()
}

#[get("/edit/<id>")]
fn make_edit(id: PasteID) -> io::Result<Template> {
    let paste = open_paste(&id)?;
    let mut paste_reader = io::BufReader::new(paste);
    let mut paste_contents = String::new();
    paste_reader.read_to_string(&mut paste_contents)?;
    let paste_id = id.to_string();
    let paste_rows = format!("{}", 100);
    let mut context: HashMap<&str, _> = HashMap::new();
    context.insert("contents", &paste_contents);
    context.insert("paste_id", &paste_id);
    context.insert("paste_rows", &paste_rows);
    Ok(Template::render("edit", &context))
}

#[derive(FromForm)]
struct EditForm {
    paste: String
}

#[post("/edit/<id>", data = "<form>")]
fn accept_edit(id: PasteID, form: Form<EditForm>) -> io::Result<Redirect> {
    let edit = form.get();
    let paste = create_paste(&id)?;
    let mut paste_writer = io::BufWriter::new(paste);
    let paste_contents =
        edit.paste.chars().map(|c|{c as u8}).collect::<Vec<u8>>();
    paste_writer.write_all(paste_contents.as_slice())?;
    paste_writer.flush()?;
    Ok(Redirect::to("/"))
}

fn ok_dir<P: AsRef<Path>>(path: P) -> bool {
    match metadata(path.as_ref()) {
        Ok(md) => md.is_dir(),
        _ => false
    }
}

fn check_upload_dir() {
    if !ok_dir(UPLOAD) {
        create_dir(UPLOAD).expect("could not create upload directory");
    }
}

fn main() {
    check_upload_dir();
    let rocket = rocket::ignite();
    let rocket = rocket.mount("/", routes![index, upload, retrieve,
                                           make_edit, accept_edit]);
    rocket.launch()
}
<|endoftext|>


<commit_before>#![feature(async_closure)]

extern crate dirs;
extern crate hyper;
extern crate hyper_rustls;
extern crate net2;
extern crate pem;
#[macro_use]
extern crate prettytable;
extern crate rustls;
extern crate structopt;
extern crate structopt_derive;
#[macro_use]
extern crate tera;
extern crate tokio_io;
extern crate tokio_process;
extern crate tokio_rustls;
extern crate tokio_service;

extern crate ct_tools;

use ct_tools::common::{sha256_hex, Log};
use ct_tools::ct::submit_cert_to_logs;
use ct_tools::google::{fetch_all_ct_logs, fetch_trusted_ct_logs};
use ct_tools::{crtsh, letsencrypt};
use futures::stream::{StreamExt, TryStreamExt};
use net2::unix::UnixTcpBuilderExt;
use rustls::Session;
use std::fs::{self, File};
use std::io::Read;
use std::net::SocketAddr;
use std::process::Stdio;
use std::sync::Arc;
use std::time::Duration;
use structopt::StructOpt;
use tokio_io::AsyncWriteExt;
use tokio_net::driver::Handle;
use tokio_process::Command;

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

fn new_http_client(
) -> hyper::Client<hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>> {
    hyper::Client::builder().build(hyper_rustls::HttpsConnector::new(4))
}

fn compute_paths(paths: &[String]) -> Vec<String> {
    paths
        .iter()
        .flat_map(|p| {
            if fs::metadata(p).unwrap().is_dir() {
                fs::read_dir(p)
                    .unwrap()
                    .map(|d| d.unwrap().path().to_str().unwrap().to_string())
                    .collect()
            } else {
                vec![p.clone()]
            }
        })
        .collect()
}

async fn submit(paths: &[String], all_logs: bool) {
    let http_client = new_http_client();

    let logs = if all_logs {
        fetch_all_ct_logs(&http_client).await
    } else {
        fetch_trusted_ct_logs(&http_client).await
    };

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let logs = &logs;
            let mut chain = pems_to_chain(&contents);
            async move {
                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 ...",
                    &path
                );
                    let new_chain = crtsh::build_chain_for_cert(&http_client, &chain[0]).await;
                    chain = match new_chain {
                        Ok(c) => c,
                        Err(()) => {
                            println!("[{}] Unable to build a chain", path);
                            return futures::future::ready(());
                        }
                    }
                }
                println!("[{}] Submitting ...", &path);
                let timeout = Duration::from_secs(30);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;

                if !scts.is_empty() {
                    println!(
                        "[{}] Find the cert on crt.sh: {}",
                        path,
                        crtsh::url_for_cert(&chain[0])
                    );
                    let mut table = prettytable::Table::new();
                    table.add_row(row!["Log"]);
                    for (log_idx, _) in scts {
                        let log = &logs[log_idx];
                        table.add_row(row![log.description]);
                    }
                    table.printstd();
                    println!();
                    println!();
                } else {
                    println!("[{}] No SCTs obtained", &path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(4)
        .for_each(async move |()| ());
    work.await;
}

async fn check(paths: &[String]) {
    let http_client = new_http_client();

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let chain = pems_to_chain(&contents);
            async move {
                if !chain.is_empty() && crtsh::is_cert_logged(&http_client, &chain[0]).await {
                    println!("{} was already logged", path);
                } else {
                    println!("{} has not been logged", path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(16)
        .for_each(async move |()| ());
    work.await;
}

async fn handle_request<C: hyper::client::connect::Connect + 'static>(
    request: hyper::Request<hyper::Body>,
    templates: Arc<tera::Tera>,
    http_client: Arc<hyper::Client<C>>,
    logs: Arc<Vec<Log>>,
    client_certs: Option<Vec<rustls::Certificate>>,
) -> Result<hyper::Response<hyper::Body>, hyper::Error> {
    let mut crtsh_url = None;
    let mut rendered_cert = None;
    if let Some(peer_chain) = client_certs {
        if request.method() == hyper::Method::POST {
            if let Ok(chain) = crtsh::build_chain_for_cert(&http_client, &peer_chain[0].0).await {
                let timeout = Duration::from_secs(5);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;
                if !scts.is_empty() {
                    crtsh_url = Some(crtsh::url_for_cert(&chain[0]));
                    println!("Successfully submitted: {}", sha256_hex(&chain[0]));
                }
            }
        }

        let mut process = Command::new("openssl")
            .arg("x509")
            .arg("-text")
            .arg("-noout")
            .arg("-inform")
            .arg("der")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .unwrap();
        let cert_bytes = peer_chain[0].0.clone();
        let mut stdin = process.stdin().take().unwrap();
        AsyncWriteExt::write_all(&mut stdin, &cert_bytes)
            .await
            .unwrap();
        let out = process.wait_with_output().await.unwrap();
        rendered_cert = Some(String::from_utf8_lossy(&out.stdout).into_owned());
    }

    let mut context = tera::Context::new();
    context.insert("cert", &rendered_cert);
    context.insert("crtsh_url", &crtsh_url);
    let body = templates.render("home.html", &context).unwrap();
    Ok(hyper::Response::builder()
        .status(hyper::StatusCode::OK)
        .body(body.into())
        .unwrap())
}

struct NoVerificationCertificateVerifier;
impl rustls::ClientCertVerifier for NoVerificationCertificateVerifier {
    fn client_auth_mandatory(&self) -> bool {
        false
    }

    fn verify_client_cert(
        &self,
        _: &[rustls::Certificate],
    ) -> Result<rustls::ClientCertVerified, rustls::TLSError> {
        Ok(rustls::ClientCertVerified::assertion())
    }

    fn client_auth_root_subjects(&self) -> rustls::DistinguishedNames {
        rustls::DistinguishedNames::new()
    }
}

fn server(local_dev: bool, domain: Option<&str>, letsencrypt_env: Option<&str>) {
    match (local_dev, domain, letsencrypt_env) {
        (true, Some(_), _) | (true, _, Some(_)) => {
            panic!("Can't use both --local-dev and --letsencrypt-env or --domain")
        }
        (_, Some(_), None) | (_, None, Some(_)) => {
            panic!("When using Let's Encrypt, must set both --letsencrypt-env and --domain")
        }
        (false, _, None) => panic!("Must set at least one of --local-dev or --letsencrypt-env"),
        _ => {}
    };

    // Disable certificate verification. In any normal context, this would be horribly insecure!
    // However, all we're doing is taking the certs and then turning around and submitting them to
    // CT logs, so it doesn't matter if they're verified.
    let mut tls_config = rustls::ServerConfig::new(Arc::new(NoVerificationCertificateVerifier));
    if local_dev {
        // TODO: not all the details on the cert are perfect, but it's fine.
        let (cert, pkey) = letsencrypt::generate_temporary_cert("localhost");
        tls_config
            .set_single_cert(
                vec![letsencrypt::openssl_cert_to_rustls(&cert)],
                letsencrypt::openssl_pkey_to_rustls(&pkey),
            )
            .unwrap();
    } else {
        let letsencrypt_url = match letsencrypt_env.unwrap() {
            "prod" => "https://acme-v01.api.letsencrypt.org/directory",
            "dev" => "https://acme-staging.api.letsencrypt.org/directory",
            _ => unreachable!(),
        };
        let cert_cache = letsencrypt::DiskCache::new(
            dirs::home_dir()
                .unwrap()
                .join(".ct-tools")
                .join("certificates"),
        );
        tls_config.cert_resolver = Arc::new(letsencrypt::AutomaticCertResolver::new(
            letsencrypt_url,
            vec![domain.unwrap().to_string()],
            cert_cache,
        ));
    }
    tls_config.set_persistence(rustls::ServerSessionMemoryCache::new(1024));
    tls_config.ticketer = rustls::Ticketer::new();

    let rt = tokio::runtime::Runtime::new().unwrap();
    let http_client = Arc::new(new_http_client());
    let logs = Arc::new(rt.block_on(fetch_trusted_ct_logs(&http_client)));
    let templates = Arc::new(compile_templates!("templates/*"));

    let addr = if local_dev {
        "0.0.0.0:8000"
    } else {
        "0.0.0.0:443"
    }
    .parse()
    .unwrap();

    // If there aren't at least two threads, the Let's Encrypt integration will deadlock.
    println!("Listening on https://{} ...", addr);
    let listener = match addr {
        SocketAddr::V4(_) => net2::TcpBuilder::new_v4().unwrap(),
        SocketAddr::V6(_) => net2::TcpBuilder::new_v6().unwrap(),
    };
    listener.reuse_port(true).unwrap();
    listener.reuse_address(true).unwrap();
    listener.bind(addr).unwrap();
    let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(tls_config));
    let connections =
        tokio::net::TcpListener::from_std(listener.listen(1024).unwrap(), &Handle::default())
            .unwrap()
            .incoming()
            .and_then(move |sock| tls_acceptor.accept(sock))
            .boxed();
    let server = hyper::Server::builder(connections).serve(hyper::service::make_service_fn(
        move |conn: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
            let http_client = Arc::clone(&http_client);
            let templates = Arc::clone(&templates);
            let logs = Arc::clone(&logs);
            let client_certs = conn.get_ref().1.get_peer_certificates();

            async {
                Ok::<_, hyper::Error>(hyper::service::service_fn(
                    move |r: hyper::Request<hyper::Body>| {
                        handle_request(
                            r,
                            Arc::clone(&templates),
                            Arc::clone(&http_client),
                            Arc::clone(&logs),
                            client_certs.clone(),
                        )
                    },
                ))
            }
        },
    ));

    rt.block_on(server).unwrap();
}

#[derive(StructOpt)]
#[structopt(name = "ct-tools")]
enum Opt {
    #[structopt(name = "submit", about = "Directly submits certificates to CT logs")]
    Submit {
        #[structopt(
            long = "all-logs",
            help = "Submit to all logs, instead of just ones trusted by Chrome"
        )]
        all_logs: bool,
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "check",
        about = "Checks whether a certificate exists in CT logs"
    )]
    Check {
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "server",
        about = "Run an HTTPS server that submits client certificates to CT logs"
    )]
    Server {
        #[structopt(
            long = "--local-dev",
            help = "Local development, do not obtain a certificate"
        )]
        local_dev: bool,
        #[structopt(long = "domain", help = "Domain this is running as")]
        domain: Option<String>,
        #[structopt(
            long = "letsencrypt-env",
            raw(possible_values = "&[\"dev\", \"prod\"]"),
            help = "Let's Encrypt environment to use"
        )]
        letsencrypt_env: Option<String>,
    },
}

#[tokio::main]
async fn main() {
    match Opt::from_args() {
        Opt::Submit { paths, all_logs } => {
            submit(&paths, all_logs).await;
        }
        Opt::Check { paths } => {
            check(&paths).await;
        }
        Opt::Server {
            local_dev,
            domain,
            letsencrypt_env,
        } => {
            server(
                local_dev,
                domain.as_ref().map(|s| s.as_ref()),
                letsencrypt_env.as_ref().map(|s| s.as_ref()),
            );
        }
    }
}
<commit_msg>Fix up handling of TLS alerts and simplify code<commit_after>#![feature(async_closure)]

extern crate dirs;
extern crate hyper;
extern crate hyper_rustls;
extern crate net2;
extern crate pem;
#[macro_use]
extern crate prettytable;
extern crate rustls;
extern crate structopt;
extern crate structopt_derive;
#[macro_use]
extern crate tera;
extern crate tokio_io;
extern crate tokio_process;
extern crate tokio_rustls;
extern crate tokio_service;

extern crate ct_tools;

use ct_tools::common::{sha256_hex, Log};
use ct_tools::ct::submit_cert_to_logs;
use ct_tools::google::{fetch_all_ct_logs, fetch_trusted_ct_logs};
use ct_tools::{crtsh, letsencrypt};
use futures::stream::{StreamExt, TryStreamExt};
use net2::unix::UnixTcpBuilderExt;
use rustls::Session;
use std::fs::{self, File};
use std::io::Read;
use std::net::SocketAddr;
use std::process::Stdio;
use std::sync::Arc;
use std::time::Duration;
use structopt::StructOpt;
use tokio_io::AsyncWriteExt;
use tokio_net::driver::Handle;
use tokio_process::Command;

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

fn new_http_client(
) -> hyper::Client<hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>> {
    hyper::Client::builder().build(hyper_rustls::HttpsConnector::new(4))
}

fn compute_paths(paths: &[String]) -> Vec<String> {
    paths
        .iter()
        .flat_map(|p| {
            if fs::metadata(p).unwrap().is_dir() {
                fs::read_dir(p)
                    .unwrap()
                    .map(|d| d.unwrap().path().to_str().unwrap().to_string())
                    .collect()
            } else {
                vec![p.clone()]
            }
        })
        .collect()
}

async fn submit(paths: &[String], all_logs: bool) {
    let http_client = new_http_client();

    let logs = if all_logs {
        fetch_all_ct_logs(&http_client).await
    } else {
        fetch_trusted_ct_logs(&http_client).await
    };

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let logs = &logs;
            let mut chain = pems_to_chain(&contents);
            async move {
                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 ...",
                    &path
                );
                    let new_chain = crtsh::build_chain_for_cert(&http_client, &chain[0]).await;
                    chain = match new_chain {
                        Ok(c) => c,
                        Err(()) => {
                            println!("[{}] Unable to build a chain", path);
                            return futures::future::ready(());
                        }
                    }
                }
                println!("[{}] Submitting ...", &path);
                let timeout = Duration::from_secs(30);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;

                if !scts.is_empty() {
                    println!(
                        "[{}] Find the cert on crt.sh: {}",
                        path,
                        crtsh::url_for_cert(&chain[0])
                    );
                    let mut table = prettytable::Table::new();
                    table.add_row(row!["Log"]);
                    for (log_idx, _) in scts {
                        let log = &logs[log_idx];
                        table.add_row(row![log.description]);
                    }
                    table.printstd();
                    println!();
                    println!();
                } else {
                    println!("[{}] No SCTs obtained", &path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(4)
        .for_each(async move |()| ());
    work.await;
}

async fn check(paths: &[String]) {
    let http_client = new_http_client();

    let all_paths = compute_paths(paths);

    let work = all_paths
        .iter()
        .map(|path| {
            let path = path.to_string();
            let mut contents = Vec::new();
            File::open(&path)
                .unwrap()
                .read_to_end(&mut contents)
                .unwrap();

            let http_client = &http_client;
            let chain = pems_to_chain(&contents);
            async move {
                if !chain.is_empty() && crtsh::is_cert_logged(&http_client, &chain[0]).await {
                    println!("{} was already logged", path);
                } else {
                    println!("{} has not been logged", path);
                }

                futures::future::ready(())
            }
        })
        .collect::<futures::stream::FuturesOrdered<_>>()
        .buffered(16)
        .for_each(async move |()| ());
    work.await;
}

async fn handle_request<C: hyper::client::connect::Connect + 'static>(
    request: hyper::Request<hyper::Body>,
    templates: Arc<tera::Tera>,
    http_client: Arc<hyper::Client<C>>,
    logs: Arc<Vec<Log>>,
    client_certs: Option<Vec<rustls::Certificate>>,
) -> Result<hyper::Response<hyper::Body>, hyper::Error> {
    let mut crtsh_url = None;
    let mut rendered_cert = None;
    if let Some(peer_chain) = client_certs {
        if request.method() == hyper::Method::POST {
            if let Ok(chain) = crtsh::build_chain_for_cert(&http_client, &peer_chain[0].0).await {
                let timeout = Duration::from_secs(5);
                let scts = submit_cert_to_logs(&http_client, &logs, &chain, timeout).await;
                if !scts.is_empty() {
                    crtsh_url = Some(crtsh::url_for_cert(&chain[0]));
                    println!("Successfully submitted: {}", sha256_hex(&chain[0]));
                }
            }
        }

        let mut process = Command::new("openssl")
            .arg("x509")
            .arg("-text")
            .arg("-noout")
            .arg("-inform")
            .arg("der")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .unwrap();
        let cert_bytes = peer_chain[0].0.clone();
        let mut stdin = process.stdin().take().unwrap();
        AsyncWriteExt::write_all(&mut stdin, &cert_bytes)
            .await
            .unwrap();
        let out = process.wait_with_output().await.unwrap();
        rendered_cert = Some(String::from_utf8_lossy(&out.stdout).into_owned());
    }

    let mut context = tera::Context::new();
    context.insert("cert", &rendered_cert);
    context.insert("crtsh_url", &crtsh_url);
    let body = templates.render("home.html", &context).unwrap();
    Ok(hyper::Response::builder()
        .status(hyper::StatusCode::OK)
        .body(body.into())
        .unwrap())
}

struct NoVerificationCertificateVerifier;
impl rustls::ClientCertVerifier for NoVerificationCertificateVerifier {
    fn client_auth_mandatory(&self) -> bool {
        false
    }

    fn verify_client_cert(
        &self,
        _: &[rustls::Certificate],
    ) -> Result<rustls::ClientCertVerified, rustls::TLSError> {
        Ok(rustls::ClientCertVerified::assertion())
    }

    fn client_auth_root_subjects(&self) -> rustls::DistinguishedNames {
        rustls::DistinguishedNames::new()
    }
}

async fn server(local_dev: bool, domain: Option<&str>, letsencrypt_env: Option<&str>) {
    match (local_dev, domain, letsencrypt_env) {
        (true, Some(_), _) | (true, _, Some(_)) => {
            panic!("Can't use both --local-dev and --letsencrypt-env or --domain")
        }
        (_, Some(_), None) | (_, None, Some(_)) => {
            panic!("When using Let's Encrypt, must set both --letsencrypt-env and --domain")
        }
        (false, _, None) => panic!("Must set at least one of --local-dev or --letsencrypt-env"),
        _ => {}
    };

    // Disable certificate verification. In any normal context, this would be horribly insecure!
    // However, all we're doing is taking the certs and then turning around and submitting them to
    // CT logs, so it doesn't matter if they're verified.
    let mut tls_config = rustls::ServerConfig::new(Arc::new(NoVerificationCertificateVerifier));
    if local_dev {
        // TODO: not all the details on the cert are perfect, but it's fine.
        let (cert, pkey) = letsencrypt::generate_temporary_cert("localhost");
        tls_config
            .set_single_cert(
                vec![letsencrypt::openssl_cert_to_rustls(&cert)],
                letsencrypt::openssl_pkey_to_rustls(&pkey),
            )
            .unwrap();
    } else {
        let letsencrypt_url = match letsencrypt_env.unwrap() {
            "prod" => "https://acme-v01.api.letsencrypt.org/directory",
            "dev" => "https://acme-staging.api.letsencrypt.org/directory",
            _ => unreachable!(),
        };
        let cert_cache = letsencrypt::DiskCache::new(
            dirs::home_dir()
                .unwrap()
                .join(".ct-tools")
                .join("certificates"),
        );
        tls_config.cert_resolver = Arc::new(letsencrypt::AutomaticCertResolver::new(
            letsencrypt_url,
            vec![domain.unwrap().to_string()],
            cert_cache,
        ));
    }
    tls_config.set_persistence(rustls::ServerSessionMemoryCache::new(1024));
    tls_config.ticketer = rustls::Ticketer::new();

    let http_client = Arc::new(new_http_client());
    let logs = Arc::new(fetch_trusted_ct_logs(&http_client).await);
    let templates = Arc::new(compile_templates!("templates/*"));

    let addr = if local_dev {
        "0.0.0.0:8000"
    } else {
        "0.0.0.0:443"
    }
    .parse()
    .unwrap();

    // If there aren't at least two threads, the Let's Encrypt integration will deadlock.
    println!("Listening on https://{} ...", addr);
    let listener = match addr {
        SocketAddr::V4(_) => net2::TcpBuilder::new_v4().unwrap(),
        SocketAddr::V6(_) => net2::TcpBuilder::new_v6().unwrap(),
    };
    listener.reuse_port(true).unwrap();
    listener.reuse_address(true).unwrap();
    listener.bind(addr).unwrap();
    let tls_acceptor = tokio_rustls::TlsAcceptor::from(Arc::new(tls_config));
    let connections =
        tokio::net::TcpListener::from_std(listener.listen(1024).unwrap(), &Handle::default())
            .unwrap()
            .incoming()
            .and_then(move |sock| tls_acceptor.accept(sock))
            .filter(move |s| futures::future::ready(s.is_ok()))
            .boxed();
    let server = hyper::Server::builder(connections).serve(hyper::service::make_service_fn(
        move |conn: &tokio_rustls::server::TlsStream<tokio::net::TcpStream>| {
            let http_client = Arc::clone(&http_client);
            let templates = Arc::clone(&templates);
            let logs = Arc::clone(&logs);
            let client_certs = conn.get_ref().1.get_peer_certificates();

            async {
                Ok::<_, hyper::Error>(hyper::service::service_fn(
                    move |r: hyper::Request<hyper::Body>| {
                        handle_request(
                            r,
                            Arc::clone(&templates),
                            Arc::clone(&http_client),
                            Arc::clone(&logs),
                            client_certs.clone(),
                        )
                    },
                ))
            }
        },
    ));

    server.await.unwrap()
}

#[derive(StructOpt)]
#[structopt(name = "ct-tools")]
enum Opt {
    #[structopt(name = "submit", about = "Directly submits certificates to CT logs")]
    Submit {
        #[structopt(
            long = "all-logs",
            help = "Submit to all logs, instead of just ones trusted by Chrome"
        )]
        all_logs: bool,
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "check",
        about = "Checks whether a certificate exists in CT logs"
    )]
    Check {
        #[structopt(help = "Path to certificate or chain")]
        paths: Vec<String>,
    },

    #[structopt(
        name = "server",
        about = "Run an HTTPS server that submits client certificates to CT logs"
    )]
    Server {
        #[structopt(
            long = "--local-dev",
            help = "Local development, do not obtain a certificate"
        )]
        local_dev: bool,
        #[structopt(long = "domain", help = "Domain this is running as")]
        domain: Option<String>,
        #[structopt(
            long = "letsencrypt-env",
            raw(possible_values = "&[\"dev\", \"prod\"]"),
            help = "Let's Encrypt environment to use"
        )]
        letsencrypt_env: Option<String>,
    },
}

#[tokio::main]
async fn main() {
    match Opt::from_args() {
        Opt::Submit { paths, all_logs } => {
            submit(&paths, all_logs).await;
        }
        Opt::Check { paths } => {
            check(&paths).await;
        }
        Opt::Server {
            local_dev,
            domain,
            letsencrypt_env,
        } => {
            server(
                local_dev,
                domain.as_ref().map(|s| s.as_ref()),
                letsencrypt_env.as_ref().map(|s| s.as_ref()),
            )
            .await;
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::OpenOptions;
use std::path::PathBuf;
use chrono::prelude::*;
use std::collections::HashMap;
use std::cmp::Ordering;

extern crate serde;
extern crate serde_json;
extern crate chrono;

#[macro_use]
extern crate serde_derive;

#[derive(Serialize, Deserialize)]
struct Exercise {
    desired: u32,
    current: u32,
    created: DateTime<Local>,
}

#[derive(Serialize, Deserialize)]
struct Regimen {
    exercises: HashMap<String, Exercise>,
    last_updated: DateTime<Local>
}

impl Regimen {
    fn add(&mut self, exer: &str, des: &str) {
        self.exercises.insert(String::from(exer), Exercise { desired: des.parse::<u32>().unwrap(), current: 0, created: Local::now()});
        println!("Added new exercise {}, with a goal for {} per day.", exer, des);
    }

    fn list(&self) {
        for (name, details) in &self.exercises {
            println!("{} {}/{}", name, details.current, details.desired);
        }
    }

    fn done(&mut self, exer: &str, des: &str) {
        let mut exercise = match self.exercises.get_mut(exer) {
            Some(x) => x,
            None => panic!("{} is not a valid exercise!", exer)
        };
        let exercises_done: u32 = match des.parse::<u32>() {
            Ok(x) => x,
            Err(e) => panic!("{}", e)
        };
        exercise.current += exercises_done;
        print!("{} of {} {} done.", exercise.current, exercise.desired, exer);
        match exercise.current.cmp(&exercise.desired) {
            Ordering::Less => println!("Still have {} to go.", exercise.desired - exercise.current),
            Ordering::Equal => println!("Exercise complete!"),
            Ordering::Greater => println!("That's {} more than needed!", exercise.current - exercise.desired)
        }
    }

    fn delete(&mut self, exer: &str) {
        if self.exercises.contains_key(exer)
        {
            self.exercises.remove(exer);
            println!("Deleted {}. Was it too hard for you?", exer);
        } else {
            panic!("No exercise {} to delete!", exer);
        }
    }

    fn goal(&mut self, exer: &str, new_goal: &str) {
        match self.exercises.get_mut(exer) {
            Some(exercise) => {
                exercise.desired = match new_goal.parse::<u32>() {
                    Ok(x) => x,
                    Err(err) => panic!("{}", err)
                };
                println!("Set goal of {} to {}", exer, new_goal);
            },
            None => panic!("{} is not a stored exercise!")
        }
    }

    fn reset_counts(&mut self) {
        for (_, details) in &mut self.exercises {
            details.current = 0;
        }
    }
}

fn main() {
    let file = get_file_read()
        .expect("Could not open file!");

    let mut reg: Regimen = serde_json::from_reader(file).unwrap();

    let mut args = env::args();
    args.next().expect("Only one argument???");

    // If it's not the same day, reset everything
    if reg.last_updated.date() != Local::now().date()
    {
        reg.reset_counts();
    }

    reg.last_updated = Local::now();

    match args.next().expect("TODO proper error").as_ref() {
        "add" => reg.add(args.next().expect("Need exercise name").as_ref(), args.next().expect("Need goal number of reps").as_ref()),
        "list" => reg.list(),
        "done" => reg.done(args.next().expect("Need exercise name").as_ref(), args.next().expect("Need completed number of reps").as_ref()),
        "delete" => reg.delete(args.next().expect("Need exercise name").as_ref()),
        "goal" => reg.goal(args.next().expect("Need exercise name").as_ref(), args.next().expect("Need new goal").as_ref()),
        _ => panic!("Not a valid command")
    };

    let file = get_file_write()
        .expect("Could not open or create file!");

    serde_json::to_writer(file, &reg)
        .expect("Could not write to file!");
}

fn get_file_read () -> std::result::Result<std::fs::File, std::io::Error> {
    let mut config = PathBuf::from(env::home_dir().unwrap());
    config.push(".swole.json");

    OpenOptions::new()
        .read(true)
        .open(config)
}

fn get_file_write () -> std::result::Result<std::fs::File, std::io::Error> {
    let mut config = PathBuf::from(env::home_dir().unwrap());
    config.push(".swole.json");

    OpenOptions::new()
        .write(true)
        .truncate(true)
        .open(config)
}
<commit_msg>Added history tracking and auto-creation of tracking files<commit_after>use std::env;
use std::fs::OpenOptions;
use std::path::PathBuf;
use chrono::prelude::*;
use std::collections::HashMap;
use std::cmp::Ordering;

extern crate serde;
extern crate serde_json;
extern crate chrono;

#[macro_use]
extern crate serde_derive;

const FILE_NAME: &'static str = ".swole.json";

#[derive(Serialize, Deserialize)]
struct Exercise {
    history: Vec<u32>,
    desired: u32,
    created: DateTime<Local>,
}

#[derive(Serialize, Deserialize)]
struct Regimen {
    exercises: HashMap<String, Exercise>,
    last_updated: DateTime<Local>
}

impl Regimen {
    pub fn new() -> Regimen {
        Regimen {
            exercises: HashMap::new(),
            last_updated: Local::now()
        }
    }

    fn add(&mut self, exer: &str, des: &str) {
        if self.exercises.contains_key(exer) {
            panic!("This exercise has already been added!");
        }
        self.exercises.insert(String::from(exer), Exercise { desired: des.parse::<u32>().unwrap(), history: vec![0], created: Local::now()});
        println!("Added new exercise {}, with a goal for {} per day.", exer, des);
    }

    fn list(&self) {
        for (name, details) in &self.exercises {
            println!("{} {}/{}", name, *details.history.last().unwrap(), details.desired);
        }
        if self.exercises.is_empty() {
            println!("No exercises yet!");
        }
    }

    fn done(&mut self, exer: &str, des: &str) {
        let mut exercise = match self.exercises.get_mut(exer) {
            Some(x) => x,
            None => panic!("{} is not a valid exercise!", exer)
        };
        let exercises_done: u32 = match des.parse::<u32>() {
            Ok(x) => x,
            Err(e) => panic!("{}", e)
        };
        *exercise.history.last_mut().unwrap() += exercises_done;
        print!("{} of {} {} done.", exercise.history.last().unwrap(), exercise.desired, exer);
        match exercise.history.last().unwrap().cmp(&exercise.desired) {
            Ordering::Less => println!("Still have {} to go.", exercise.desired - exercise.history.last().unwrap()),
            Ordering::Equal => println!("Exercise complete!"),
            Ordering::Greater => println!("That's {} more than needed!", exercise.history.last().unwrap() - exercise.desired)
        }
    }

    fn delete(&mut self, exer: &str) {
        if self.exercises.contains_key(exer)
        {
            self.exercises.remove(exer);
            println!("Deleted {}. Was it too hard for you?", exer);
        } else {
            panic!("No exercise {} to delete!", exer);
        }
    }

    fn goal(&mut self, exer: &str, new_goal: &str) {
        match self.exercises.get_mut(exer) {
            Some(exercise) => {
                exercise.desired = match new_goal.parse::<u32>() {
                    Ok(x) => x,
                    Err(err) => panic!("{}", err)
                };
                println!("Set goal of {} to {}", exer, new_goal);
            },
            None => panic!("{} is not a stored exercise!")
        }
    }

    fn update_history(&mut self) {
        self.last_updated = Local::now();

        for (_, details) in &mut self.exercises {
            let missed_days: i64 = (self.last_updated.date().signed_duration_since(details.created.date())).num_days() + 1 - details.history.len() as i64;
            for _ in 0..missed_days {
                details.history.push(0);
            }
        }
    }
}

fn main() {
    let mut reg: Regimen = match get_file_read() {
        Ok(f) => serde_json::from_reader(f).unwrap(),
        Err(_) => {
            println!("Can't get {}. Creating a new one.", FILE_NAME);
            Regimen::new()
        }
    };

    reg.update_history();

    let mut args = env::args();
    args.next().expect("Only one argument???");

    match args.next().expect("No command provided").as_ref() {
        "add" => reg.add(args.next().expect("Need exercise name").as_ref(), args.next().expect("Need goal number of reps").as_ref()),
        "list" => reg.list(),
        "done" => reg.done(args.next().expect("Need exercise name").as_ref(), args.next().expect("Need completed number of reps").as_ref()),
        "delete" => reg.delete(args.next().expect("Need exercise name").as_ref()),
        "goal" => reg.goal(args.next().expect("Need exercise name").as_ref(), args.next().expect("Need new goal").as_ref()),
        _ => panic!("Not a valid command")
    };

    let file = get_file_write()
        .expect("Could not open or create file!");

    serde_json::to_writer(file, &reg)
        .expect("Could not write to file!");
}

fn get_file_read () -> std::result::Result<std::fs::File, std::io::Error> {
    let mut config = PathBuf::from(env::home_dir().unwrap());
    config.push(FILE_NAME);

    OpenOptions::new()
        .read(true)
        .open(config)
}

fn get_file_write () -> std::result::Result<std::fs::File, std::io::Error> {
    let mut config = PathBuf::from(env::home_dir().unwrap());
    config.push(FILE_NAME);

    OpenOptions::new()
        .write(true)
        .truncate(true)
        .create(true)
        .open(config)
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate regex;
extern crate time;

mod error;
mod post;

use clap::{App, Arg, ArgMatches, AppSettings, SubCommand};
use error::{Error, Result};
use regex::Regex;
use std::{env, fs, path};
use std::io::Write;

fn get_open_option(force: bool) -> fs::OpenOptions {
    if force {
        fs::OpenOptions::new().write(true).create(true).truncate(true).clone()
    }
    else {
        fs::OpenOptions::new().write(true).create_new(true).clone()
    }
}

fn init_empty_site(m: &ArgMatches) -> Result<()> {
    let dir = path::Path::new(m.value_of("dir").unwrap_or("."));
    let opt = get_open_option(m.is_present("force"));

    opt.open(dir.join(".config"))
       .map_err(|e| format!("failed to create `.config`: {}", e))?;
    opt.open(dir.join(".nojekyll"))
       .map_err(|e| format!("failed to create `.nojekyll`: {}", e))?;

    fs::DirBuilder::new()
        .recursive(m.is_present("force"))
        .create(dir.join("files"))
        .map_err(|e| format!("failed to create `files`: {}", e))?;

    Ok(())
}

fn create_post(m: &ArgMatches) -> Result<()> {
    let link = m.value_of("link").expect("failed to get the link of post");
    if !Regex::new(r"^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$")?.is_match(link) {
        return Err(Error::from_string(format!("invalid link name `{}'", link)));
    }

    let now = time::now();

    let filename = format!("{}-{}.md", time::strftime("%Y-%m-%d", &now)?, link);
    let opt = get_open_option(m.is_present("force"));
    let mut file = opt.open(&filename)
                      .map_err(|e| format!("failed to create `{}': {}",
                                           filename, e))?;

    file.write(format!("%%\ntitle =\nlink = {}\ntimestamp = {}\n%%\n",
                       link, time::strftime("%Y-%m-%d %H:%M:%S", &now)?)
               .as_bytes())?;

    Ok(())
}

fn main() {
    let app = App::new(env::args().nth(0).unwrap())
        .settings(&[AppSettings::DisableVersion,
                    AppSettings::DeriveDisplayOrder,
                    AppSettings::UnifiedHelpMessage,
                    AppSettings::VersionlessSubcommands])
        .subcommand(SubCommand::with_name("init")
                    .about("Initialize an empty site")
                    .args(&[
                        Arg::from_usage("[dir] 'Directory for the site'"),
                        Arg::from_usage("-f, --force 'Overwrite existing \
                                        site metadata files'")
                    ]))
        .subcommand(SubCommand::with_name("post")
                    .about("Create a new post with the specified link name")
                    .args(&[
                        Arg::from_usage("<link> 'Link name of the post which'"),
                        Arg::from_usage("-f, --force 'Overwrite existing post'")
                    ]))
        .subcommand(SubCommand::with_name("gen")
                    .about("Generate the site"))
        .subcommand(SubCommand::with_name("view")
                    .about("View the site locally"))
        .get_matches();

    let ret = match app.subcommand() {
        ("init", Some(m)) => init_empty_site(m),
        ("gen",  Some(m)) => Ok(()),
        ("post", Some(m)) => create_post(m),
        ("view", Some(m)) => Ok(()),
        _ => Ok(())
    };

    if let Err(e) = ret {
        println!("{}", e)
    }
}
<commit_msg>Set up const strings for dir/file names<commit_after>extern crate clap;
extern crate regex;
extern crate time;

mod error;
mod post;

use clap::{App, Arg, ArgMatches, AppSettings, SubCommand};
use error::{Error, Result};
use regex::Regex;
use std::{env, fs, path};
use std::io::Write;

const CONFIG_FILENAME: &str = ".izzetconfig";
const NOJEKYLL_FILENAME: &str = ".nojekyll";
const FILES_DIRNAME: &str = "files";

fn get_open_option(force: bool) -> fs::OpenOptions {
    if force {
        fs::OpenOptions::new().write(true).create(true).truncate(true).clone()
    }
    else {
        fs::OpenOptions::new().write(true).create_new(true).clone()
    }
}

fn init_empty_site(m: &ArgMatches) -> Result<()> {
    let dir = path::Path::new(m.value_of("dir").unwrap_or("."));
    let opt = get_open_option(m.is_present("force"));

    opt.open(dir.join(CONFIG_FILENAME))
       .map_err(|e| format!("failed to create `{}`: {}", CONFIG_FILENAME, e))?;
    opt.open(dir.join(NOJEKYLL_FILENAME))
       .map_err(|e| format!("failed to create `{}`: {}", NOJEKYLL_FILENAME, e))?;

    fs::DirBuilder::new()
        .recursive(m.is_present("force"))
        .create(dir.join(FILES_DIRNAME))
        .map_err(|e| format!("failed to create `{}`: {}", FILES_DIRNAME, e))?;

    Ok(())
}

fn create_post(m: &ArgMatches) -> Result<()> {
    let link = m.value_of("link").expect("failed to get the link of post");
    if !Regex::new(r"^[A-Za-z0-9]+(-[A-Za-z0-9]+)*$")?.is_match(link) {
        return Err(Error::from_string(format!("invalid link name `{}'", link)));
    }

    let now = time::now();

    let filename = format!("{}-{}.md", time::strftime("%Y-%m-%d", &now)?, link);
    let opt = get_open_option(m.is_present("force"));
    let mut file = opt.open(&filename)
                      .map_err(|e| format!("failed to create `{}': {}",
                                           filename, e))?;

    file.write(format!("%%\ntitle =\nlink = {}\ntimestamp = {}\n%%\n",
                       link, time::strftime("%Y-%m-%d %H:%M:%S", &now)?)
               .as_bytes())?;

    Ok(())
}

fn main() {
    let app = App::new(env::args().nth(0).unwrap())
        .settings(&[AppSettings::DisableVersion,
                    AppSettings::DeriveDisplayOrder,
                    AppSettings::UnifiedHelpMessage,
                    AppSettings::VersionlessSubcommands])
        .subcommand(SubCommand::with_name("init")
                    .about("Initialize an empty site")
                    .args(&[
                        Arg::from_usage("[dir] 'Directory for the site'"),
                        Arg::from_usage("-f, --force 'Overwrite existing \
                                        site metadata files'")
                    ]))
        .subcommand(SubCommand::with_name("post")
                    .about("Create a new post with the specified link name")
                    .args(&[
                        Arg::from_usage("<link> 'Link name of the post which'"),
                        Arg::from_usage("-f, --force 'Overwrite existing post'")
                    ]))
        .subcommand(SubCommand::with_name("gen")
                    .about("Generate the site"))
        .subcommand(SubCommand::with_name("view")
                    .about("View the site locally"))
        .get_matches();

    let ret = match app.subcommand() {
        ("init", Some(m)) => init_empty_site(m),
        ("gen",  Some(m)) => Ok(()),
        ("post", Some(m)) => create_post(m),
        ("view", Some(m)) => Ok(()),
        _ => Ok(())
    };

    if let Err(e) = ret {
        println!("{}", e)
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;

use docopt::Docopt;

use std::env;
use std::path::PathBuf;
use std::error::Error;
use std::io;
use std::io::prelude::*;
use std::fs::File;
use rustc_serialize::json;

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
Banking for the command line.

Usage:
    teller list accounts
    teller show balance <account>
    teller -h | --help
    teller -V | --version

Commands:
    list accounts   List accounts.
    show balance    Show the balance of an account.

Options:
    -h --help       Show this screen.
    -V --version    Show version.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_list: bool,
    cmd_accounts: bool,
    cmd_show: bool,
    cmd_balance: bool,
    arg_account: String,
    flag_help: bool,
    flag_version: bool,
}

#[derive(RustcEncodable, RustcDecodable)]
struct Config {
    auth_token: String,
}

impl Config {
    pub fn new(auth_token: String) -> Config {
        Config {
            auth_token: auth_token,
        }
    }
}

// TODO: Work out when to use matching
//       and when to use try! and when to
//       use something else.

fn get_config_path() -> PathBuf {
    match env::home_dir() {
        Some(mut p) => {
            p.push(".tellerrc");
            p
        },
        None => panic!("Impossible to get your home directory!"),
    }
}

fn get_config_file() -> File {
    let config_path = get_config_path();

    match File::open(&config_path) {
        Err(why) => panic!("couldn't open {} to read: {}", config_path.display(), Error::description(&why)),
        Ok(file) => file,
    }
}

fn get_config_file_to_write() -> File {
    let config_path = get_config_path();

    match File::create(&config_path) {
        Err(why) => panic!("couldn't open {} to write: {}", config_path.display(), Error::description(&why)),
        Ok(file) => file,
    }
}

fn read_config() -> Config {
    let mut config_file = get_config_file();

    let mut content_str = String::new();
    match config_file.read_to_string(&mut content_str) {
        Err(why) => panic!("couldn't read: {}", Error::description(&why)),
        Ok(_) => (),
    }

    let config: Config = json::decode(&content_str).unwrap();
    return config;
}

fn write_config(config: &Config) {
    let mut config_file = get_config_file_to_write();

    let content_str = json::encode(&config).unwrap();

    config_file.write_all(content_str.as_bytes()).unwrap()
}

fn main() {
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| {
                                d.version(VERSION.map(|v| v.to_string()))
                                 .decode()
                            })
                            .unwrap_or_else(|e| e.exit());
    println!("{:?}", args);

    // TODO: If there is a config file then read it,
    //       otherwise ask a question and write to it.
    let config = read_config();
    println!("{}", config.auth_token);

    println!("What's the auth token?");
    let mut input = String::new();
    match io::stdin().read_line(&mut input) {
        Ok(_) => println!("{}", input),
        Err(error) => println!("error: {}", error),
    };

    let auth_token = String::new();
    let new_config = Config::new(auth_token);

    println!("fake token: {}", new_config.auth_token);

    write_config(&new_config);

    ()
}
<commit_msg>wip(account): account types are explicit.<commit_after>extern crate rustc_serialize;
extern crate docopt;

use docopt::Docopt;
use rustc_serialize::{Decodable, Decoder};

use std::env;
use std::path::PathBuf;
use std::error::Error;
use std::io;
use std::io::prelude::*;
use std::fs::File;
use rustc_serialize::json;

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
Banking for the command line.

Usage:
    teller list accounts
    teller show balance <account>
    teller -h | --help
    teller -V | --version

Commands:
    list accounts   List accounts.
    show balance    Show the balance of an account.

Options:
    -h --help       Show this screen.
    -V --version    Show version.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_list: bool,
    cmd_accounts: bool,
    cmd_show: bool,
    cmd_balance: bool,
    arg_account: AccountType,
    flag_help: bool,
    flag_version: bool,
}

#[derive(Debug)]
enum AccountType { Current, Savings, Business, Unknown(String), None }

impl Decodable for AccountType {
    fn decode<D: Decoder>(d: &mut D) -> Result<AccountType, D::Error> {
        let s = try!(d.read_str());
        Ok(match &*s {
            "" => AccountType::None,
            "current" => AccountType::Current,
            "savings" => AccountType::Savings,
            "business" => AccountType::Business,
            s => AccountType::Unknown(s.to_string()),
        })
    }
}

#[derive(RustcEncodable, RustcDecodable)]
struct Config {
    auth_token: String,
}

impl Config {
    pub fn new(auth_token: String) -> Config {
        Config {
            auth_token: auth_token,
        }
    }
}

// TODO: Work out when to use matching
//       and when to use try! and when to
//       use something else.

fn get_config_path() -> PathBuf {
    match env::home_dir() {
        Some(mut p) => {
            p.push(".tellerrc");
            p
        },
        None => panic!("Impossible to get your home directory!"),
    }
}

fn get_config_file() -> File {
    let config_path = get_config_path();

    match File::open(&config_path) {
        Err(why) => panic!("couldn't open {} to read: {}", config_path.display(), Error::description(&why)),
        Ok(file) => file,
    }
}

fn get_config_file_to_write() -> File {
    let config_path = get_config_path();

    match File::create(&config_path) {
        Err(why) => panic!("couldn't open {} to write: {}", config_path.display(), Error::description(&why)),
        Ok(file) => file,
    }
}

fn read_config() -> Config {
    let mut config_file = get_config_file();

    let mut content_str = String::new();
    match config_file.read_to_string(&mut content_str) {
        Err(why) => panic!("couldn't read: {}", Error::description(&why)),
        Ok(_) => (),
    }

    let config: Config = json::decode(&content_str).unwrap();
    return config;
}

fn write_config(config: &Config) {
    let mut config_file = get_config_file_to_write();

    let content_str = json::encode(&config).unwrap();

    config_file.write_all(content_str.as_bytes()).unwrap()
}

fn main() {
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| {
                                d.version(VERSION.map(|v| v.to_string()))
                                 .decode()
                            })
                            .unwrap_or_else(|e| e.exit());
    println!("{:?}", args);

    // TODO: If there is a config file then read it,
    //       otherwise ask a question and write to it.
    let config = read_config();
    println!("{}", config.auth_token);

    println!("What's the auth token?");
    let mut input = String::new();
    match io::stdin().read_line(&mut input) {
        Ok(_) => println!("{}", input),
        Err(error) => println!("error: {}", error),
    };

    let auth_token = String::new();
    let new_config = Config::new(auth_token);

    println!("fake token: {}", new_config.auth_token);

    write_config(&new_config);

    ()
}
<|endoftext|>


<commit_before>extern crate image;
extern crate chrono;
extern crate walkdir;

use std::env;
use std::error::Error;
use std::fs;
use std::io::prelude::*;
use std::path::Path;
use image::GenericImage;
use chrono::prelude::*;
use walkdir::WalkDir;

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

  if args.len() == 2 {
    println!("importing files from path {:?}", &args[1]);

    let mut category = String::from("uncategorised");

    for entry in WalkDir::new(&args[1]) {
      let entry = entry.unwrap();
      let file_name = entry.file_name().to_str().unwrap();

      // remove file extension
      let title = file_name.split(".").collect::<Vec<_>>()[0];

      if entry.file_type().is_dir() {
        category = file_name.to_owned();
      } else {
        let dimensions = image::open(&entry.path()).unwrap().dimensions();
        let date: DateTime<UTC> = UTC::now();

        // TODO: image_small, image_2x
        // TODO: width/height for each size

        let data = format!(
      "+++
title = \"{}\"
image = \"{}\"
width = {}
height = {}
date = \"{}\"
draft = false
photos = [\"{}\"]
+++\n",
          &title,
          &entry.path().display(),
          &dimensions.0,
          &dimensions.1,
          &date,
          &category
        );

        save_entry(&category, &title, &data);
      }
    }

    // TODO: error handling?

    // TODO: output path as arg?

    println!("DONE");
  } else {
    help();
  }
}

fn help() {
  println!("usage: uploader <path to files>");
}

fn save_entry(category: &str, title: &str, data: &str) {
  // TODO: base path?

  let category_dir = format!("./photos/{}", &category);

  // create category dir if it doesn't exist already
  match fs::metadata(&category_dir) {
    Err(msg) => {
      // TODO: handle this!
      fs::create_dir(&category_dir);
    },
    Ok(metadata) => {
      if !metadata.is_dir() {
        panic!("unable to create dir {}", &category_dir);
      }
    }
  }

  let filename = format!("./photos/{}/{}.md", &category, &title);
  let path = Path::new(&filename);
  let display = path.display();

  // TODO: don't create file if exists already - (in future update!)

  let mut file = match fs::File::create(&path) {
    Err(msg) => panic!("unable to create file {}: {}", display, msg.description()),
    Ok(file) => file,
  };

  match file.write_all(data.as_bytes()) {
    Err(msg) => panic!("unable to write to file {}: {}", display, msg.description()),
    Ok(file) => file,
  }

  // TODO: return value
}
<commit_msg>only save the details from the largest image<commit_after>extern crate image;
extern crate chrono;
extern crate walkdir;

use std::env;
use std::error::Error;
use std::fs;
use std::io::prelude::*;
use std::path::Path;
use image::GenericImage;
use chrono::prelude::*;
use walkdir::WalkDir;

// expected path /category/size/image.jpg
fn main() {
  let args: Vec<String> = env::args().collect();

  if args.len() == 2 {
    println!("importing files from path {:?}", &args[1]);

    let mut category = String::from("uncategorised");
    let mut width = String::from("0px");

    for entry in WalkDir::new(&args[1]) {
      let entry = entry.unwrap();
      let file_name = entry.file_name().to_str().unwrap();

      // remove file extension
      let title = file_name.split(".").collect::<Vec<_>>()[0];

      if entry.file_type().is_dir() {
        if file_name.contains("px") {
          width = file_name.to_owned();
        } else {
          category = file_name.to_owned();
        }
      } else if width == "3200px" {
        let dimensions = image::open(&entry.path()).unwrap().dimensions();
        let date: DateTime<UTC> = UTC::now();
        let data = format!(
      "+++
title = \"{}\"
image = \"{}\"
width = {}
height = {}
date = \"{}\"
draft = false
photos = [\"{}\"]
+++\n",
          &title,
          &file_name,
          &dimensions.0,
          &dimensions.1,
          &date,
          &category
        );

        save_entry(&category, &title, &data);
      }
    }

    // TODO: error handling?

    // TODO: output path as arg?

    println!("DONE");
  } else {
    help();
  }
}

fn help() {
  println!("usage: uploader <path to files>");
}

fn save_entry(category: &str, title: &str, data: &str) {
  // TODO: base path?

  let category_dir = format!("./photos/{}", &category);

  // create category dir if it doesn't exist already
  match fs::metadata(&category_dir) {
    Err(msg) => {
      // TODO: handle this!
      fs::create_dir(&category_dir);
    },
    Ok(metadata) => {
      if !metadata.is_dir() {
        panic!("unable to create dir {}", &category_dir);
      }
    }
  }

  let filename = format!("./photos/{}/{}.md", &category, &title);
  let path = Path::new(&filename);
  let display = path.display();

  // TODO: don't create file if exists already - (in future update!)

  let mut file = match fs::File::create(&path) {
    Err(msg) => panic!("unable to create file {}: {}", display, msg.description()),
    Ok(file) => file,
  };

  match file.write_all(data.as_bytes()) {
    Err(msg) => panic!("unable to write to file {}: {}", display, msg.description()),
    Ok(file) => file,
  }

  // TODO: return value
}
<|endoftext|>


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

extern crate rocket;
extern crate serde_json;
#[macro_use]
extern crate rocket_contrib;
#[macro_use]
extern crate serde_derive;

use rocket_contrib::JSON;

extern crate file;
extern crate rand;

use std::collections::HashSet;
use rand::distributions::{IndependentSample, Range};

#[derive(Serialize, Debug)]
struct Person {
    sex: bool,
    first_name: String,
    second_name: String,
    last_name: String
}

#[derive(Debug)]
struct NamePool {
    female: Vec<String>,
    male: Vec<String>,
    surname: Vec<String>,
}

impl NamePool {
    fn random_name(&self) -> Person {
        fn random_value(vec: &Vec<String>) -> &String {
            let between = Range::new(0, vec.len() - 1);
            let mut rng = rand::thread_rng();
            let index = between.ind_sample(&mut rng);
            &vec[index]
        }

        let sex: bool = rand::random();
        let first_name = if sex {
            random_value(&self.male)
        } else {
            random_value(&self.female)
        };

        let person = Person {
            sex: sex,
            first_name: first_name.to_string(),
            second_name: random_value(&self.male).to_string(),
            last_name: random_value(&self.surname).to_string()
        };

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

struct NameGen {
    name_pool: NamePool,
    female_range: Range<usize>,
    //    male_range: Range<usize>,
    //    surname_range: Range<usize>,
    rng: rand::ThreadRng
}

struct XPool<'a> {
    names: &'a Vec<String>,
    range: Range<usize>,
    rng: rand::ThreadRng,
}

#[get("/")]
fn index(pool: rocket::State<NamePool>) -> JSON<Person>/*String*/ {
    let person = pool.random_name();
    JSON(person)
    //    serde_json::to_string(&person).unwrap()
    //    let serialized = serde_json::to_string(&point).unwrap();
    //    JSON(person)
}

//fn x() -> XPool<'static> {
//    let female_names = &merge_names(&[
//        "src/names/female-names-v1-14376.txt",
//        "src/names/female-names-v2-16673.txt",
//    ]);
//    let len = female_names.len();
//
//    let vv = XPool {
//        names: female_names,
//        range: Range::new(0, len),
//        rng: rand::thread_rng()
//    };
//    //
//    //
//    //    let pool = &load_name_pool();
//    //
//    //    NameGen {
//    //        name_pool: *pool,
//    //        female_range: Range::new(0, pool.female.len()),
//    //        rng: rand::thread_rng()
//    //    }
//    vv
//}

fn load_name_pool() -> NamePool {
    let female_names = merge_names(&[
        "src/names/female-names-v1-14376.txt",
        "src/names/female-names-v2-16673.txt",
    ]);

    let male_names = merge_names(&[
        "src/names/male-names-v1-14999.txt",
        "src/names/male-names-v2-21904.txt",
    ]);

    let surnames = merge_names(&[
        "src/names/surnames-184624.txt",
    ]);

    NamePool { female: female_names, male: male_names, surname: surnames }
}

fn merge_names(filenames: &[&str]) -> Vec<String> {
    let mut distinct = HashSet::new();
    for filename in filenames {
        let names = load_from_file(filename);
        for item in names {
            distinct.insert(item);
        }
    }

    let mut result = vec![];
    for item in distinct {
        result.push(item);
    }

    result
}

fn load_from_file(filename: &str) -> HashSet<String> {
    let content = Box::new(file::get_text(filename).unwrap_or("".to_string()));
    let mut names = HashSet::new();
    for line in content.as_ref().split_whitespace() {
        names.insert(line.to_string());
    }
    names
}

fn main() {
    //    x();
    let pool = load_name_pool();
    rocket::ignite()
        .manage(pool)
        .mount("/", routes![index]).launch();
}
<commit_msg>Remove unused code.<commit_after>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;
extern crate rocket_contrib;

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

extern crate file;
extern crate rand;

use rocket_contrib::JSON;

use std::collections::HashSet;

use rand::distributions::{IndependentSample, Range};
use rand::Rng;

#[derive(Serialize, Debug, Clone, Copy)]
enum Sex {
    Male,
    Female
}

impl rand::Rand for Sex {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        let variants = [Sex::Male, Sex::Female];
        *rng.choose(&variants).unwrap()
    }
}

#[derive(Serialize, Debug)]
struct Person {
    sex: Sex,
    first_name: String,
    second_name: String,
    last_name: String
}

#[derive(Debug)]
struct NamePool {
    female: Vec<String>,
    male: Vec<String>,
    surname: Vec<String>,
}

impl NamePool {
    fn random_name(&self) -> Person {
        fn random_value(vec: &Vec<String>) -> &String {
            let between = Range::new(0, vec.len() - 1);
            let mut rng = rand::thread_rng();
            let index = between.ind_sample(&mut rng);
            &vec[index]
        }

        let sex: Sex = rand::random();
        let first_name = match sex {
            Sex::Male => random_value(&self.male),
            Sex::Female => random_value(&self.female)
        };

        let person = Person {
            sex: sex,
            first_name: first_name.to_string(),
            second_name: random_value(&self.male).to_string(),
            last_name: random_value(&self.surname).to_string()
        };

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

#[get("/")]
fn index(pool: rocket::State<NamePool>) -> JSON<Person> {
    let person = pool.random_name();
    JSON(person)
}

fn load_name_pool() -> NamePool {
    let female_names = merge_names(&[
        "src/names/female-names-v1-14376.txt",
        "src/names/female-names-v2-16673.txt",
    ]);

    let male_names = merge_names(&[
        "src/names/male-names-v1-14999.txt",
        "src/names/male-names-v2-21904.txt",
    ]);

    let surnames = merge_names(&[
        "src/names/surnames-184624.txt",
    ]);

    NamePool { female: female_names, male: male_names, surname: surnames }
}

fn merge_names(filenames: &[&str]) -> Vec<String> {
    let mut distinct = HashSet::new();
    for filename in filenames {
        let names = load_from_file(filename);
        for item in names {
            distinct.insert(item);
        }
    }

    let mut result = vec![];
    for item in distinct {
        result.push(item);
    }

    result
}

fn load_from_file(filename: &str) -> HashSet<String> {
    let content = Box::new(file::get_text(filename).unwrap_or("".to_string()));
    let mut names = HashSet::new();
    for line in content.as_ref().split_whitespace() {
        names.insert(line.to_string());
    }
    names
}

fn main() {
    let pool = load_name_pool();
    rocket::ignite()
        .manage(pool)
        .mount("/", routes![index]).launch();
}
<|endoftext|>


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

#[macro_use]
extern crate clap;
extern crate zmq;
#[macro_use]
extern crate mysql;

mod transaction;
mod mapper;
#[macro_use]
mod macros;
mod utils;

use clap::Arg;
use mapper::Mapper;
use std::sync::{Arc, Mutex, mpsc};
use std::thread;
use transaction::Transaction;

fn main() {
  let matches = app_from_crate!()
    .arg(
      Arg::with_name("ZMQ_URI")
        .short("z")
        .long("zmq")
        .takes_value(true)
        .value_name("URI")
        .required(true)
        .help("ZMQ source server URI"),
    )
    .arg(
      Arg::with_name("MYSQL_URI")
        .short("m")
        .long("mysql")
        .takes_value(true)
        .value_name("URI")
        .required(true)
        .help("MySQL destination server URI"),
    )
    .arg(
      Arg::with_name("WRITE_THREADS_COUNT")
        .short("w")
        .long("write-threads")
        .takes_value(true)
        .value_name("COUNT")
        .default_value("1")
        .help("Count of regular write worker threads"),
    )
    .arg(
      Arg::with_name("APPROVE_THREADS_COUNT")
        .short("a")
        .long("approve-threads")
        .takes_value(true)
        .value_name("COUNT")
        .default_value("1")
        .help("Count of milestone approval worker threads"),
    )
    .get_matches();
  let zmq_uri = matches.value_of("ZMQ_URI").expect(
    "ZMQ_URI were not provided",
  );
  let mysql_uri = matches.value_of("MYSQL_URI").expect(
    "MYSQL_URI were not provided",
  );
  let write_threads_count: usize =
    matches
      .value_of("WRITE_THREADS_COUNT")
      .expect("WRITE_THREADS_COUNT were not provided")
      .parse()
      .expect("WRITE_THREADS_COUNT not a number");
  let approve_threads_count: usize =
    matches
      .value_of("APPROVE_THREADS_COUNT")
      .expect("APPROVE_THREADS_COUNT were not provided")
      .parse()
      .expect("APPROVE_THREADS_COUNT not a number");

  let pool = mysql::Pool::new(mysql_uri).expect("MySQL connect failure");
  let ctx = zmq::Context::new();
  let socket = ctx.socket(zmq::SUB).expect("ZMQ socket create failure");
  let (write_tx, write_rx) = mpsc::channel::<String>();
  let (approve_tx, approve_rx) = mpsc::channel::<Vec<u64>>();
  socket.connect(zmq_uri).expect("ZMQ socket connect failure");
  socket.set_subscribe(b"tx ").expect("ZMQ subscribe failure");
  spawn_write_workers(&pool, write_rx, &approve_tx, write_threads_count);
  spawn_approve_workers(&pool, approve_rx, approve_threads_count);
  zmq_listen(&socket, &write_tx);
}

fn zmq_listen(socket: &zmq::Socket, tx: &mpsc::Sender<String>) {
  loop {
    match socket.recv_string(0) {
      Ok(Ok(string)) => tx.send(string).expect("Thread communication failure"),
      Ok(Err(err)) => eprintln!("Unexpected byte sequence: {:?}", err),
      Err(err) => eprintln!("{}", err),
    }
  }
}

fn spawn_write_workers(
  pool: &mysql::Pool,
  rx: mpsc::Receiver<String>,
  tx: &mpsc::Sender<Vec<u64>>,
  threads_count: usize,
) {
  let rx = Arc::new(Mutex::new(rx));
  for i in 0..threads_count {
    let (tx, rx) = (tx.clone(), rx.clone());
    let mut mapper = Mapper::new(pool).expect("MySQL mapper failure");
    thread::spawn(move || loop {
      let rx = rx.lock().expect("Mutex is poisoned");
      let string = rx.recv().expect("Thread communication failure");
      match Transaction::parse(&string) {
        Ok(transaction) => {
          match transaction.process(&mut mapper) {
            Ok(Some(vec)) => {
              tx.send(vec).expect("Thread communication failure")
            }
            Ok(None) => println!("write_thread#{} {:?}", i, transaction),
            Err(err) => eprintln!("Transaction processing error: {}", err),
          }
        }
        Err(err) => eprintln!("Transaction parsing error: {}", err),
      }
    });
  }
}

fn spawn_approve_workers(
  pool: &mysql::Pool,
  rx: mpsc::Receiver<Vec<u64>>,
  threads_count: usize,
) {
  let rx = Arc::new(Mutex::new(rx));
  for i in 0..threads_count {
    let rx = rx.clone();
    let mut mapper = Mapper::new(pool).expect("MySQL mapper failure");
    thread::spawn(move || loop {
      let rx = rx.lock().expect("Mutex is poisoned");
      let vec = rx.recv().expect("Thread communication failure");
      match Transaction::approve(&mut mapper, vec.clone()) {
        Ok(()) => println!("approve_thread#{} {:?}", i, vec),
        Err(err) => eprintln!("Transaction approve error: {}", err),
      }
    });
  }
}
<commit_msg>Added `--verbose` argument<commit_after>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate zmq;
#[macro_use]
extern crate mysql;

mod transaction;
mod mapper;
#[macro_use]
mod macros;
mod utils;

use clap::Arg;
use mapper::Mapper;
use std::sync::{Arc, Mutex, mpsc};
use std::thread;
use transaction::Transaction;

fn main() {
  let matches = app_from_crate!()
    .arg(
      Arg::with_name("ZMQ_URI")
        .short("z")
        .long("zmq")
        .takes_value(true)
        .value_name("URI")
        .required(true)
        .help("ZMQ source server URI"),
    )
    .arg(
      Arg::with_name("MYSQL_URI")
        .short("m")
        .long("mysql")
        .takes_value(true)
        .value_name("URI")
        .required(true)
        .help("MySQL destination server URI"),
    )
    .arg(
      Arg::with_name("WRITE_THREADS_COUNT")
        .short("w")
        .long("write-threads")
        .takes_value(true)
        .value_name("COUNT")
        .default_value("1")
        .help("Count of regular write worker threads"),
    )
    .arg(
      Arg::with_name("APPROVE_THREADS_COUNT")
        .short("a")
        .long("approve-threads")
        .takes_value(true)
        .value_name("COUNT")
        .default_value("1")
        .help("Count of milestone approval worker threads"),
    )
    .arg(Arg::with_name("VERBOSE").short("v").long("verbose").help(
      "Prints flowing messages",
    ))
    .get_matches();
  let zmq_uri = matches.value_of("ZMQ_URI").expect(
    "ZMQ_URI were not provided",
  );
  let mysql_uri = matches.value_of("MYSQL_URI").expect(
    "MYSQL_URI were not provided",
  );
  let write_threads_count: usize =
    matches
      .value_of("WRITE_THREADS_COUNT")
      .expect("WRITE_THREADS_COUNT were not provided")
      .parse()
      .expect("WRITE_THREADS_COUNT not a number");
  let approve_threads_count: usize =
    matches
      .value_of("APPROVE_THREADS_COUNT")
      .expect("APPROVE_THREADS_COUNT were not provided")
      .parse()
      .expect("APPROVE_THREADS_COUNT not a number");
  let verbose = matches.is_present("VERBOSE");

  let pool = mysql::Pool::new(mysql_uri).expect("MySQL connect failure");
  let ctx = zmq::Context::new();
  let socket = ctx.socket(zmq::SUB).expect("ZMQ socket create failure");
  let (write_tx, write_rx) = mpsc::channel::<String>();
  let (approve_tx, approve_rx) = mpsc::channel::<Vec<u64>>();
  socket.connect(zmq_uri).expect("ZMQ socket connect failure");
  socket.set_subscribe(b"tx ").expect("ZMQ subscribe failure");
  spawn_write_workers(
    &pool,
    write_rx,
    &approve_tx,
    write_threads_count,
    verbose,
  );
  spawn_approve_workers(&pool, approve_rx, approve_threads_count, verbose);
  zmq_listen(&socket, &write_tx);
}

fn zmq_listen(socket: &zmq::Socket, tx: &mpsc::Sender<String>) {
  loop {
    match socket.recv_string(0) {
      Ok(Ok(string)) => {
        tx.send(string).expect("Thread communication failure");
      }
      Ok(Err(err)) => {
        eprintln!("Unexpected byte sequence: {:?}", err);
      }
      Err(err) => {
        eprintln!("{}", err);
      }
    }
  }
}

fn spawn_write_workers(
  pool: &mysql::Pool,
  rx: mpsc::Receiver<String>,
  tx: &mpsc::Sender<Vec<u64>>,
  threads_count: usize,
  verbose: bool,
) {
  let rx = Arc::new(Mutex::new(rx));
  for i in 0..threads_count {
    let (tx, rx) = (tx.clone(), rx.clone());
    let mut mapper = Mapper::new(pool).expect("MySQL mapper failure");
    thread::spawn(move || loop {
      let rx = rx.lock().expect("Mutex is poisoned");
      let string = rx.recv().expect("Thread communication failure");
      match Transaction::parse(&string) {
        Ok(transaction) => {
          match transaction.process(&mut mapper) {
            Ok(Some(vec)) => {
              tx.send(vec).expect("Thread communication failure");
            }
            Ok(None) => {
              if verbose {
                println!("write_thread#{} {:?}", i, transaction);
              }
            }
            Err(err) => {
              eprintln!("Transaction processing error: {}", err);
            }
          }
        }
        Err(err) => {
          eprintln!("Transaction parsing error: {}", err);
        }
      }
    });
  }
}

fn spawn_approve_workers(
  pool: &mysql::Pool,
  rx: mpsc::Receiver<Vec<u64>>,
  threads_count: usize,
  verbose: bool,
) {
  let rx = Arc::new(Mutex::new(rx));
  for i in 0..threads_count {
    let rx = rx.clone();
    let mut mapper = Mapper::new(pool).expect("MySQL mapper failure");
    thread::spawn(move || loop {
      let rx = rx.lock().expect("Mutex is poisoned");
      let vec = rx.recv().expect("Thread communication failure");
      match Transaction::approve(&mut mapper, vec.clone()) {
        Ok(()) => {
          if verbose {
            println!("approve_thread#{} {:?}", i, vec);
          }
        }
        Err(err) => {
          eprintln!("Transaction approve error: {}", err);
        }
      }
    });
  }
}
<|endoftext|>