<commit_before>#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use]
extern crate clap;
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate serde_derive;

mod auth_utils;
mod file_utils;

use rocket::response::NamedFile;
use rocket::State;
use rocket_contrib::templates::Template;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::atomic::AtomicUsize;

struct Timings {
    launch_time: chrono::DateTime<chrono::Local>,
    last_ms_from_launch: AtomicUsize, // milliseconds from the launch time
}

#[derive(Deserialize)]
struct Config {
    log_file: PathBuf,
    backup_file: PathBuf,
    cooldown_ms: usize, // cooldown in milliseconds
    template_context: TemplateContext,
    auth: AuthData,
}

#[derive(Deserialize)]
struct AuthData {
    salt: String,
    hash: String,
}

#[derive(Serialize, Deserialize)]
struct TemplateContext {
    title: String,
    question: String,
    thanks: String,
    acks: String,
}

fn load_config_file<T>(path: T) -> Result<Config, io::Error>
where
    T: AsRef<Path>,
{
    use file_utils::read_file;
    use std::io::ErrorKind;

    let toml_str = read_file(path.as_ref())?;
    toml::from_str(&toml_str)
        .map_err(|_| io::Error::new(ErrorKind::InvalidInput, "Could not parse TOML file"))
}

#[inline]
fn count_ms_from_datetime(dt0: chrono::DateTime<chrono::Local>) -> usize {
    chrono::Local::now()
        .signed_duration_since(dt0)
        .num_milliseconds() as usize
}

#[get("/")]
fn index(conf: State<Config>) -> Template {
    Template::render("index", &conf.template_context)
}

#[get("/log_file/<token>")]
fn serve_log_file(token: String, conf: State<Config>) -> Result<NamedFile, io::Error> {
    use auth_utils::check_access_token;
    use std::io::ErrorKind;

    // What can happen here:
    // * salt is invalid base64
    // * access token is invalid base64
    // * hash value in the config file is invalid base64
    // * there is no log file (yet)
    // * access token is unacceptable
    // All these cases result in the same 404 response.
    //
    // I warned you that this program is quick and dirty!
    if check_access_token(&token, &conf.auth.salt, &conf.auth.hash) {
        NamedFile::open(&conf.log_file)
    } else {
        Err(io::Error::new(
            ErrorKind::PermissionDenied,
            "access token invalid",
        ))
    }
}

#[delete("/log_file/<token>")]
fn reset_log_file(token: String, conf: State<Config>) -> Result<(), io::Error> {
    use auth_utils::check_access_token;
    use std::fs;
    use std::io::ErrorKind;

    if check_access_token(&token, &conf.auth.salt, &conf.auth.hash) {
        fs::rename(&conf.log_file, &conf.backup_file)
    } else {
        Err(io::Error::new(
            ErrorKind::PermissionDenied,
            "access token invalid",
        ))
    }
}

#[get("/public/<file..>")]
fn files(file: PathBuf) -> Option<NamedFile> {
    NamedFile::open(Path::new("static/").join(file)).ok()
}

#[post("/feedback/<fb>")]
fn feedback(fb: String, tmgs: State<Timings>, conf: State<Config>) -> Result<(), io::Error> {
    use file_utils::append_line_to_file;
    use std::io::ErrorKind;
    use std::sync::atomic::Ordering;

    let curr_timestamp = count_ms_from_datetime(tmgs.launch_time);
    let ms_since_last = curr_timestamp - tmgs.last_ms_from_launch.load(Ordering::Relaxed);
    tmgs.last_ms_from_launch
        .store(curr_timestamp, Ordering::Relaxed);

    if ms_since_last < conf.cooldown_ms {
        return Err(io::Error::new(ErrorKind::Other, "button mashing detected"));
    }

    let fb_int = match fb.as_ref() {
        "so_very_negative" => Ok(-3),
        "very_negative" => Ok(-2),
        "negative" => Ok(-1),
        "neutral" => Ok(0),
        "positive" => Ok(1),
        "very_positive" => Ok(2),
        "so_very_positive" => Ok(3), // Yes! So. Very. Positive.
        _ => Err(io::Error::new(
            ErrorKind::InvalidInput,
            "invalid feedback value",
        )),
    }?;

    let line = format!("{},{:>2}", chrono::Local::now().to_rfc3339(), fb_int);
    append_line_to_file(&conf.log_file, line)?;

    Ok(())
}

fn main() {
    use clap::{App, Arg};
    use std::process;

    let m = App::new("emoji-feedback")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Emoji feedback server")
        .arg(
            Arg::with_name("config_file_path")
                .help("Path to the config file")
                .index(1)
                .required(true),
        )
        .get_matches();

    let config_file_path = Path::new(m.value_of("config_file_path").unwrap());

    match load_config_file(&config_file_path) {
        Ok(conf) => {
            let tmgs = Timings {
                launch_time: chrono::Local::now(),
                last_ms_from_launch: AtomicUsize::new(0),
            };

            rocket::ignite()
                .mount(
                    "/",
                    routes![index, files, serve_log_file, reset_log_file, feedback],
                )
                .manage(tmgs)
                .manage(conf)
                .attach(Template::fairing())
                .launch();
        }
        Err(e) => {
            println!("{}", e);
            process::exit(1);
        }
    }
}
<commit_msg>Reduce usage of use aliases<commit_after>#![feature(proc_macro_hygiene, decl_macro)]

#[macro_use]
extern crate clap;
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate serde_derive;

mod auth_utils;
mod file_utils;

use rocket::response::NamedFile;
use rocket::State;
use std::path::{Path, PathBuf};

struct Timings {
    launch_time: chrono::DateTime<chrono::Local>,
    last_ms_from_launch: std::sync::atomic::AtomicUsize, // milliseconds from the launch time
}

#[derive(Deserialize)]
struct Config {
    log_file: PathBuf,
    backup_file: PathBuf,
    cooldown_ms: usize, // cooldown in milliseconds
    template_context: TemplateContext,
    auth: AuthData,
}

#[derive(Deserialize)]
struct AuthData {
    salt: String,
    hash: String,
}

#[derive(Serialize, Deserialize)]
struct TemplateContext {
    title: String,
    question: String,
    thanks: String,
    acks: String,
}

fn load_config_file<T>(path: T) -> Result<Config, std::io::Error>
where
    T: AsRef<Path>,
{
    use file_utils::read_file;
    use std::io::ErrorKind;

    let toml_str = read_file(path.as_ref())?;
    toml::from_str(&toml_str)
        .map_err(|_| std::io::Error::new(ErrorKind::InvalidInput, "Could not parse TOML file"))
}

#[inline]
fn count_ms_from_datetime(dt0: chrono::DateTime<chrono::Local>) -> usize {
    chrono::Local::now()
        .signed_duration_since(dt0)
        .num_milliseconds() as usize
}

#[get("/")]
fn index(conf: State<Config>) -> rocket_contrib::templates::Template {
    rocket_contrib::templates::Template::render("index", &conf.template_context)
}

#[get("/log_file/<token>")]
fn serve_log_file(token: String, conf: State<Config>) -> Result<NamedFile, std::io::Error> {
    use auth_utils::check_access_token;
    use std::io::ErrorKind;

    // What can happen here:
    // * salt is invalid base64
    // * access token is invalid base64
    // * hash value in the config file is invalid base64
    // * there is no log file (yet)
    // * access token is unacceptable
    // All these cases result in the same 404 response.
    //
    // I warned you that this program is quick and dirty!
    if check_access_token(&token, &conf.auth.salt, &conf.auth.hash) {
        NamedFile::open(&conf.log_file)
    } else {
        Err(std::io::Error::new(
            ErrorKind::PermissionDenied,
            "access token invalid",
        ))
    }
}

#[delete("/log_file/<token>")]
fn reset_log_file(token: String, conf: State<Config>) -> Result<(), std::io::Error> {
    use auth_utils::check_access_token;
    use std::fs;
    use std::io::ErrorKind;

    if check_access_token(&token, &conf.auth.salt, &conf.auth.hash) {
        fs::rename(&conf.log_file, &conf.backup_file)
    } else {
        Err(std::io::Error::new(
            ErrorKind::PermissionDenied,
            "access token invalid",
        ))
    }
}

#[get("/public/<file..>")]
fn files(file: PathBuf) -> Option<NamedFile> {
    NamedFile::open(Path::new("static/").join(file)).ok()
}

#[post("/feedback/<fb>")]
fn feedback(fb: String, tmgs: State<Timings>, conf: State<Config>) -> Result<(), std::io::Error> {
    use file_utils::append_line_to_file;
    use std::io::ErrorKind;
    use std::sync::atomic::Ordering;

    let curr_timestamp = count_ms_from_datetime(tmgs.launch_time);
    let ms_since_last = curr_timestamp - tmgs.last_ms_from_launch.load(Ordering::Relaxed);
    tmgs.last_ms_from_launch
        .store(curr_timestamp, Ordering::Relaxed);

    if ms_since_last < conf.cooldown_ms {
        return Err(std::io::Error::new(
            ErrorKind::Other,
            "button mashing detected",
        ));
    }

    let fb_int = match fb.as_ref() {
        "so_very_negative" => Ok(-3),
        "very_negative" => Ok(-2),
        "negative" => Ok(-1),
        "neutral" => Ok(0),
        "positive" => Ok(1),
        "very_positive" => Ok(2),
        "so_very_positive" => Ok(3), // Yes! So. Very. Positive.
        _ => Err(std::io::Error::new(
            ErrorKind::InvalidInput,
            "invalid feedback value",
        )),
    }?;

    let line = format!("{},{:>2}", chrono::Local::now().to_rfc3339(), fb_int);
    append_line_to_file(&conf.log_file, line)?;

    Ok(())
}

fn main() {
    use clap::{App, Arg};
    use std::process;

    let m = App::new("emoji-feedback")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Emoji feedback server")
        .arg(
            Arg::with_name("config_file_path")
                .help("Path to the config file")
                .index(1)
                .required(true),
        )
        .get_matches();

    let config_file_path = Path::new(m.value_of("config_file_path").unwrap());

    match load_config_file(&config_file_path) {
        Ok(conf) => {
            let tmgs = Timings {
                launch_time: chrono::Local::now(),
                last_ms_from_launch: std::sync::atomic::AtomicUsize::new(0),
            };

            rocket::ignite()
                .mount(
                    "/",
                    routes![index, files, serve_log_file, reset_log_file, feedback],
                )
                .manage(tmgs)
                .manage(conf)
                .attach(rocket_contrib::templates::Template::fairing())
                .launch();
        }
        Err(e) => {
            println!("{}", e);
            process::exit(1);
        }
    }
}
<|endoftext|>


<commit_before>use structopt::StructOpt;
use structopt::clap::AppSettings;
use std::path;
use std::vec;
use std::fs;
use std::io;

/// The command line interface to the CaSILE toolkit, a book publishing
/// workflow employing SILE and other wizardry
#[derive(StructOpt)]
#[structopt(version = env!("VERGEN_SEMVER"))]
#[structopt(setting = AppSettings::InferSubcommands)]
struct Cli {

    /// Activate debug mode
    #[structopt(short, long, env = "DEBUG")]
    debug: bool,

    /// Outputs verbose feedback where possible
    #[structopt(short, long)]
    verbose: bool,

    #[structopt(subcommand)]
    subcommand: Subcommand,

}

#[derive(StructOpt)]
enum Subcommand {

    /// Executes a make target
    Make {

        /// Target as defined in CaSILE makefile
        target: Vec<String>,

    },

    /// Configure a book repository
    Setup {

        /// Path to project repository
        #[structopt(parse(from_os_str), default_value = "./")]
        path: path::PathBuf,

    },

    /// Pass through other commands to shell
    #[structopt(external_subcommand)]
    Other(Vec<String>),

}

fn main () -> io::Result<()> {

    let args = Cli::from_args();

    if args.debug {
        println!("User requested debug mode")
    }

    if args.verbose {
        println!("User requested verbose output")
    }

    return match args.subcommand {
        Subcommand::Make{ target } => make(target),
        Subcommand::Setup{ path } => setup(path),
        _a => shell(),
    }

}

fn make (_target: vec::Vec<String>) -> io::Result<()> {

    println!("Make make make sense or I’ll make you make makefiles.");

    Ok(())
}

fn setup (path: path::PathBuf) -> io::Result<()> {

    let pathmeta = fs::metadata(path);

    match pathmeta {
        Ok(file) => {
            if file.is_dir() {
                println!("Run setup, “They said you were this great colossus!”")
            } else {
                println!("Not a dir, Frank!")
            }
        }
        Err(error) => { return Err(error.into()) },
    };

    Ok(())

}

fn shell () -> io::Result<()> {

    println!("Ship all this off to the shell, maybe they can handle it.");

    Ok(())
}
<commit_msg>chore(cli): Return an error if asked to setup on a non-directory<commit_after>use structopt::StructOpt;
use structopt::clap::AppSettings;
use std::path;
use std::vec;
use std::fs;
use std::io;
use std::io::{Error, ErrorKind};

/// The command line interface to the CaSILE toolkit, a book publishing
/// workflow employing SILE and other wizardry
#[derive(StructOpt)]
#[structopt(version = env!("VERGEN_SEMVER"))]
#[structopt(setting = AppSettings::InferSubcommands)]
struct Cli {

    /// Activate debug mode
    #[structopt(short, long, env = "DEBUG")]
    debug: bool,

    /// Outputs verbose feedback where possible
    #[structopt(short, long)]
    verbose: bool,

    #[structopt(subcommand)]
    subcommand: Subcommand,

}

#[derive(StructOpt)]
enum Subcommand {

    /// Executes a make target
    Make {

        /// Target as defined in CaSILE makefile
        target: Vec<String>,

    },

    /// Configure a book repository
    Setup {

        /// Path to project repository
        #[structopt(parse(from_os_str), default_value = "./")]
        path: path::PathBuf,

    },

    /// Pass through other commands to shell
    #[structopt(external_subcommand)]
    Other(Vec<String>),

}

fn main () -> io::Result<()> {

    let args = Cli::from_args();

    if args.debug {
        println!("User requested debug mode")
    }

    if args.verbose {
        println!("User requested verbose output")
    }

    return match args.subcommand {
        Subcommand::Make{ target } => make(target),
        Subcommand::Setup{ path } => setup(path),
        _a => shell(),
    }

}

fn make (_target: vec::Vec<String>) -> io::Result<()> {

    println!("Make make make sense or I’ll make you make makefiles.");

    Ok(())
}

fn setup (path: path::PathBuf) -> io::Result<()> {

    let metadata = fs::metadata(path)?;

    return match metadata.is_dir() {
        true => Ok(
            println!("Run setup, “They said you were this great colossus!”")
            ),
        false => Err(Error::new(ErrorKind::InvalidInput, "Not a dir, Frank!")),
    }

}

fn shell () -> io::Result<()> {

    println!("Ship all this off to the shell, maybe they can handle it.");

    Ok(())
}
<|endoftext|>


<commit_before>extern crate rand;

use std::io;
use std::io::Write;
use std::process::Command;

enum Error {
    Usage(&'static str),
    NonZero(String, Option<i32>),
    IoError(io::Error)
}

use Error::*;

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self { IoError(e) }
}

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

trait CommandOut : Sized {
    fn run(&mut Command) -> Result<Self>;
}

impl CommandOut for () {
    fn run(cmd: &mut Command) -> Result<Self> {
        let exit = try!(cmd.spawn().and_then(|mut c| c.wait()));
        if exit.success() {
            Ok(())
        } else {
            Err(NonZero(format!("{:?}", cmd), exit.code()))
        }
    }
}

impl CommandOut for String {
    fn run(cmd: &mut Command) -> Result<Self> {
        use std::process::Stdio;
        use std::io::Read;
        let mut buffer = String::new();
        let mut child = try!(cmd.stderr(Stdio::null()).stdout(Stdio::piped()).spawn());
        let rdres = child.stdout.as_mut().unwrap().read_to_string(&mut buffer);
        let wtres = child.wait();
        let exit = try!(rdres.and(wtres).map_err(IoError));
        if exit.success() {
            Ok(buffer)
        } else {
            Err(NonZero(format!("{:?}", cmd), exit.code()))
        }
    }
}

fn run<T>(cmd: &mut Command) -> Result<T> where T : CommandOut {
    T::run(cmd)
}

fn probably_unique_name(length: u64) -> String {
    use rand::distributions::{IndependentSample, Range};
    let between = Range::new(0, 36);
    let mut rng = rand::thread_rng();
    unsafe {
        std::str::from_utf8_unchecked(&(0..length).map(|_| between.ind_sample(&mut rng)).map(|v| if v < 10 { v + 48 } else { v + 87 }).collect::<Vec<u8>>()).to_string()
    }
}

fn create(nixpkgs : String, config_file : String) -> Result<String> {
    let name = format!("fr{}", probably_unique_name(9));
    match run(Command::new("nixos-container").arg("create").arg(&name).arg("--config").arg(format!("imports = [ {} ];", config_file)).env("NIX_PATH", format!("nixpkgs={}", nixpkgs))) {
        Ok(()) => Ok(name),
        Err(e) => { let _ = destroy(&name); Err(e) }
    }
}

fn destroy(container_name : &str) -> Result<()> {
    run(Command::new("nixos-container").arg("destroy").arg(container_name))
}

fn go() -> Result<()> {
    let mut args = std::env::args();
    args.next(); // Discard program name
    let nixpkgs = try!(args.next().ok_or(Error::Usage("Missing nixpkgs argument")));
    let config_file = try!(args.next().ok_or(Error::Usage("Missing config file argument")));

    let container_name = try!(create(nixpkgs, config_file));
    println!("{}", container_name);
    try!(destroy(&container_name));
    Ok(())
}

fn main() {
    go().unwrap_or_else(|e| std::process::exit(match e {
        NonZero(cmd, code) => { writeln!(io::stderr(), "Command {:?} failed with code {:?}", cmd, code).unwrap(); 2 },
        Usage(msg) => { writeln!(io::stderr(), "Usage error: {}", msg).unwrap(); 1 },
        IoError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 }
    }));
}
<commit_msg>NEW: Run test and return exit code<commit_after>extern crate rand;

use std::io;
use std::io::Write;
use std::process::Command;

enum Error {
    Usage(&'static str),
    NonZero(String, Option<i32>),
    IoError(io::Error)
}

use Error::*;

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self { IoError(e) }
}

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

trait CommandOut : Sized {
    fn run(&mut Command) -> Result<Self>;
}

impl CommandOut for () {
    fn run(cmd: &mut Command) -> Result<Self> {
        let exit = try!(cmd.spawn().and_then(|mut c| c.wait()));
        if exit.success() {
            Ok(())
        } else {
            Err(NonZero(format!("{:?}", cmd), exit.code()))
        }
    }
}

impl CommandOut for String {
    fn run(cmd: &mut Command) -> Result<Self> {
        use std::process::Stdio;
        use std::io::Read;
        let mut buffer = String::new();
        let mut child = try!(cmd.stderr(Stdio::null()).stdout(Stdio::piped()).spawn());
        let rdres = child.stdout.as_mut().unwrap().read_to_string(&mut buffer);
        let wtres = child.wait();
        let exit = try!(rdres.and(wtres).map_err(IoError));
        if exit.success() {
            Ok(buffer)
        } else {
            Err(NonZero(format!("{:?}", cmd), exit.code()))
        }
    }
}

fn run<T>(cmd: &mut Command) -> Result<T> where T : CommandOut {
    T::run(cmd)
}

fn probably_unique_name(length: usize) -> String {
    use rand::distributions::{IndependentSample, Range};
    let between = Range::new(0, 36);
    let mut rng = rand::thread_rng();
    unsafe {
        std::str::from_utf8_unchecked(&(0..length).map(|_| between.ind_sample(&mut rng)).map(|v| if v < 10 { v + 48 } else { v + 87 }).collect::<Vec<u8>>()).to_string()
    }
}

fn create(nixpkgs : &str, config_file : &str) -> Result<String> {
    let name = format!("fr{}", probably_unique_name(9));
    match run(Command::new("nixos-container").arg("create").arg(&name).arg("--config").arg(format!("imports = [ {} ];", config_file)).env("NIX_PATH", format!("nixpkgs={}", nixpkgs))) {
        Ok(()) => Ok(name),
        Err(e) => { let _ = destroy(&name); Err(e) }
    }
}

fn run_test(container_name : &str, args : &[String]) -> Result<i32> {
    try!(run(Command::new("nixos-container").arg("start").arg(container_name)));
    match run(Command::new("nixos-container").arg("run").arg(container_name).arg("--").args(args)) {
        Ok(()) => Ok(0),
        Err(NonZero(_, Some(code))) => Ok(code),
        Err(e) => Err(e)
    }
}

fn destroy(container_name : &str) -> Result<()> {
    run(Command::new("nixos-container").arg("destroy").arg(container_name))
}

fn go() -> Result<i32> {
    let mut args = std::env::args();
    args.next(); // Discard program name
    let nixpkgs = try!(args.next().ok_or(Error::Usage("Missing nixpkgs argument")));
    let config_file = try!(args.next().ok_or(Error::Usage("Missing config file argument")));
    let test_args : Vec<String> = args.collect();
    if test_args.len() == 0 {
        return Err(Usage("Missing test args"));
    }

    let container_name = try!(create(&nixpkgs, &config_file));
    let res = run_test(&container_name, &test_args);
    try!(destroy(&container_name));
    res
}

fn main() {
    std::process::exit(go().unwrap_or_else(|e| match e {
        NonZero(cmd, code) => { writeln!(io::stderr(), "Command {:?} failed with code {:?}", cmd, code).unwrap(); 2 },
        Usage(msg) => { writeln!(io::stderr(), "Usage error: {}", msg).unwrap(); 1 },
        IoError(err) => { writeln!(io::stderr(), "{}", err).unwrap(); 2 }
    }));
}
<|endoftext|>


<commit_before>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, UdpSocket};
use std::thread;
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: SocketAddr) -> UdpSocket {
    let attempt = UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

fn send_message(send_addr: SocketAddr, target: SocketAddr, data: &[u8]) {
    let _ = socket(send_addr).send_to(data, target);
}

fn main() {
    let command_vals = ["white",
                        "red",
                        "orange",
                        "yellow",
                        "green",
                        "cyan",
                        "blue",
                        "purple",
                        "black",
                        "question",
                        "exclamation",
                        "quit"];
    let command_params = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    let handle = thread::spawn(move || {
        // bind to the correct UDP port
        let numeric_port = value_t!(command_params.value_of("PORT"), u16).unwrap_or(1738);
        // it's safe to unwrap here, cos we already checked success
        let to_send = command_params.value_of("COMMAND").unwrap();
        let ip = Ipv4Addr::new(127, 0, 0, 1);
        let listen_addr = SocketAddrV4::new(ip, numeric_port);
        let send_addr = SocketAddrV4::new(ip, 0);
        // and send our message
        send_message(SocketAddr::V4(send_addr),
                     SocketAddr::V4(listen_addr),
                     to_send.as_bytes());
    });
    let _ = handle.join();
}
<commit_msg>No need to spin up a thread<commit_after>// UDP stuff adapted from http://illegalargumentexception.blogspot.co.uk/2015/05/rust-send-and-receive-on-localhost-with.html
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr, UdpSocket};
#[macro_use]
extern crate clap;
use clap::{Arg, App};

fn socket(listen_on: SocketAddr) -> UdpSocket {
    let attempt = UdpSocket::bind(listen_on);
    let socket;
    match attempt {
        Ok(sock) => {
            socket = sock;
        }
        Err(err) => panic!("Could not bind: {}", err),
    }
    socket
}

fn send_message(send_addr: SocketAddr, target: SocketAddr, data: &[u8]) {
    let _ = socket(send_addr).send_to(data, target);
}

fn main() {
    let command_vals = ["white",
                        "red",
                        "orange",
                        "yellow",
                        "green",
                        "cyan",
                        "blue",
                        "purple",
                        "black",
                        "question",
                        "exclamation",
                        "quit"];
    let command_params = App::new("rust_anybar")
        .version(&crate_version!()[..])
        .author("Stephan Hügel <urschrei@gmail.com>")
        .about("A Rust command-line client for Anybar")
        .args_from_usage("-p --port=[PORT] 'Set destination UDP port. Input must be 0 – 65535, \
                          and defaults to 17388")
        .arg(Arg::with_name("COMMAND")
            .help("The command you wish to send to Anybar")
            .index(1)
            .possible_values(&command_vals)
            .required(true))
        .get_matches();
    // bind to the correct UDP port
    let numeric_port = value_t!(command_params.value_of("PORT"), u16).unwrap_or(1738);
    // it's safe to unwrap here, cos we already checked success
    let to_send = command_params.value_of("COMMAND").unwrap();
    let ip = Ipv4Addr::new(127, 0, 0, 1);
    let listen_addr = SocketAddrV4::new(ip, numeric_port);
    let send_addr = SocketAddrV4::new(ip, 0);
    // and send our message
    send_message(SocketAddr::V4(send_addr),
                 SocketAddr::V4(listen_addr),
                 to_send.as_bytes());
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::time::Duration;
use std::path::PathBuf;

use structopt::StructOpt;

#[derive(StructOpt)]
#[structopt(name = "RRG", about = "A GRR agent rewritten in Rust.")]
pub struct Opts {
    #[structopt(long="log-verbosity", name="LEVEL", default_value="info",
                help="A log verbosity level")]
    pub log_verbosity: Verbosity,

    #[structopt(long="log-std", name="STD",
                help="A standard stream to log to")]
    pub log_std: Option<Std>,

    #[structopt(long="log-file", name="FILE",
                help="A file to log to")]
    pub log_file: Option<PathBuf>,

    #[structopt(long="heartbeat-rate", name="DURATION", default_value="5s",
                parse(try_from_str = humantime::parse_duration),
                help="A frequency of Fleetspeak heartbeat messages.")]
    pub heartbeat_rate: Duration,
}

pub fn from_args() -> Opts {
    Opts::from_args()
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Verbosity {
    level: log::LevelFilter,
}

impl Verbosity {

    pub fn level(&self) -> log::LevelFilter {
        self.level
    }
}

impl std::str::FromStr for Verbosity {

    type Err = String; // TODO.

    fn from_str(string: &str) -> std::result::Result<Verbosity, String> {
        use log::LevelFilter::*;

        let level = match string {
            "quiet" => Off,
            "error" => Error,
            "warn" => Warn,
            "info" => Info,
            "debug" => Debug,
            "trace" => Trace,
            _ => return Err(format!("invalid verbosity choice '{}'", string)),
        };

        Ok(Verbosity {
            level: level,
        })
    }
}

// TODO: This should just be a wrapper around `simplelog::TerminalMode`, but
// it does not implement standard traits. So, for now, we just re-implement it
// like that.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Std {
    Out,
    Err,
    Mix,
}

impl Std {

    pub fn mode(&self) -> simplelog::TerminalMode {
        use simplelog::TerminalMode::*;

        match self {
            Std::Out => Stdout,
            Std::Err => Stderr,
            Std::Mix => Mixed,
        }
    }
}

impl std::str::FromStr for Std {

    type Err = String; // TODO.

    fn from_str(string: &str) -> std::result::Result<Std, String> {
        match string {
            "out" => Ok(Std::Out),
            "err" => Ok(Std::Err),
            "mix" => Ok(Std::Mix),
            _ => Err(format!("invalid std choice '{}'", string)),
        }
    }
}
<commit_msg>Improve help messages for command-line options.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::time::Duration;
use std::path::PathBuf;

use structopt::StructOpt;

#[derive(StructOpt)]
#[structopt(name = "RRG", about = "A GRR agent rewritten in Rust.")]
pub struct Opts {
    #[structopt(long="log-verbosity", name="LEVEL", default_value="info",
                help="Specifies the level of log verbosity")]
    pub log_verbosity: Verbosity,

    #[structopt(long="log-std", name="STD",
                help="Enables logging to the specified standard stream")]
    pub log_std: Option<Std>,

    #[structopt(long="log-file", name="FILE",
                help="Enables logging to the specified file")]
    pub log_file: Option<PathBuf>,

    #[structopt(long="heartbeat-rate", name="DURATION", default_value="5s",
                parse(try_from_str = humantime::parse_duration),
                help="Specifies the frequency of heartbeat messages")]
    pub heartbeat_rate: Duration,
}

pub fn from_args() -> Opts {
    Opts::from_args()
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Verbosity {
    level: log::LevelFilter,
}

impl Verbosity {

    pub fn level(&self) -> log::LevelFilter {
        self.level
    }
}

impl std::str::FromStr for Verbosity {

    type Err = String; // TODO.

    fn from_str(string: &str) -> std::result::Result<Verbosity, String> {
        use log::LevelFilter::*;

        let level = match string {
            "quiet" => Off,
            "error" => Error,
            "warn" => Warn,
            "info" => Info,
            "debug" => Debug,
            "trace" => Trace,
            _ => return Err(format!("invalid verbosity choice '{}'", string)),
        };

        Ok(Verbosity {
            level: level,
        })
    }
}

// TODO: This should just be a wrapper around `simplelog::TerminalMode`, but
// it does not implement standard traits. So, for now, we just re-implement it
// like that.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Std {
    Out,
    Err,
    Mix,
}

impl Std {

    pub fn mode(&self) -> simplelog::TerminalMode {
        use simplelog::TerminalMode::*;

        match self {
            Std::Out => Stdout,
            Std::Err => Stderr,
            Std::Mix => Mixed,
        }
    }
}

impl std::str::FromStr for Std {

    type Err = String; // TODO.

    fn from_str(string: &str) -> std::result::Result<Std, String> {
        match string {
            "out" => Ok(Std::Out),
            "err" => Ok(Std::Err),
            "mix" => Ok(Std::Mix),
            _ => Err(format!("invalid std choice '{}'", string)),
        }
    }
}
<|endoftext|>


<commit_before>use macroquad::prelude::*;

use colour::Colour;
use graph::{Graph, Position};
use game::{Game, GameState};

mod colour;
mod graph;
mod grid;
mod game;

fn macroquad_colour(colour: Colour) -> Color {
    match colour {
        Colour::Red => color_u8!(255, 0, 0, 255),
        Colour::Yellow => color_u8!(255, 255, 0, 255),
        Colour::Green => color_u8!(0, 176, 0, 255),
        Colour::LightBrown => color_u8!(255, 204, 102, 255),
        Colour::Purple => color_u8!(128, 0, 128, 255),
        Colour::Cyan => color_u8!(0, 255, 255, 255),
        Colour::Blue => color_u8!(0, 0, 255, 255),
        Colour::Fuchsia => color_u8!(255, 0, 255, 255),
    }
}

fn draw_cell(grid_x: f32, grid_y: f32, cell_size: f32, position: &Position, colour: Colour) {
    let color = macroquad_colour(colour);
    draw_rectangle(
        grid_x + cell_size * position.column as f32,
        grid_y + cell_size * position.row as f32,
        cell_size,
        cell_size,
        color,
    );
}

struct Ui {
    /// Size of the grid in cells.
    size: usize,

    /// Size of the grid in pixels.  Assumes the grid is always square.
    grid_size: f32,

    /// Offset of the grid relative to the whole window along the horizontal axis.
    grid_x: f32,

    /// Offset of the grid relative to the whole window along the vertical axis.
    grid_y: f32,
}

impl Ui {
    fn create(size: usize) -> Ui {
        let mut ui = Ui {
            size,
            grid_size: 0.0,
            grid_x: 0.0,
            grid_y: 0.0,
        };

        ui.resize();

        ui
    }

    fn resize(&mut self) {
        let screen_height = screen_height();
        let screen_width = screen_width();

        let grid_size = screen_height.min(screen_width);
        self.grid_size = grid_size;
        self.grid_x = (screen_width - grid_size) / 2.0;
        self.grid_y = (screen_height - grid_size) / 2.0;
    }

    fn render(&self, game: &Game) {
        clear_background(BLACK);

        let grid_x = self.grid_x;
        let grid_y = self.grid_y;
        let cell_size = self.cell_size();

        let graph = &game.graph;
        for component in graph.neighbours.keys() {
            for position in &graph.components[component].cells {
                draw_cell(grid_x, grid_y, cell_size, position, graph.components[component].colour);
            }
        }
    }

    fn cell_size(&self) -> f32 {
        self.grid_size / self.size as f32
    }

    fn cell_position(&self, raw_position: (f32, f32)) -> Option<Position> {
        let (raw_x, raw_y) = raw_position;

        let cell_size = self.cell_size();
        let x = (raw_x - self.grid_x) / cell_size;
        let y = (raw_y - self.grid_y) / cell_size;

        let size = self.size as f32;
        if x < 0.0 || y < 0.0 || x > size || y > size {
            // Out of bounds, nothing to do
            None
        } else {
            Some(Position{column: x.floor() as usize, row: y.floor() as usize})
        }
    }

    fn handle_click(&self, game: &mut Game) {
        if let Some(position) = self.cell_position(mouse_position()) {
            let clicked_component = game.graph.find_component(&position);
            let colour = clicked_component.colour;

            println!("Detected click at cell ({}, {}) with colour {:#?}", position.column, position.row, colour);

            game.fill_component_of_top_left_cell_with(colour);

            match game.state {
                GameState::Solving =>
                if game.graph.components.len() == 1 {
                    game.state = GameState::Solved;
                    println!("Done");
                }
                GameState::Solved => (),
            }
        }
    }
}

#[macroquad::main("Flood-It")]
async fn main() {
    let size = 8;

    let mut game = Game::create(size);
    let mut ui = Ui::create(size);

    loop {
        if let Some(KeyCode::Q) = get_last_key_pressed() {
            break;
        }

        if is_mouse_button_pressed(MouseButton::Left) {
            ui.handle_click(&mut game);
        }

        ui.resize();
        ui.render(&game);

        next_frame().await
    }
}
<commit_msg>Pass mouse position to method<commit_after>use macroquad::prelude::*;

use colour::Colour;
use graph::{Graph, Position};
use game::{Game, GameState};

mod colour;
mod graph;
mod grid;
mod game;

fn macroquad_colour(colour: Colour) -> Color {
    match colour {
        Colour::Red => color_u8!(255, 0, 0, 255),
        Colour::Yellow => color_u8!(255, 255, 0, 255),
        Colour::Green => color_u8!(0, 176, 0, 255),
        Colour::LightBrown => color_u8!(255, 204, 102, 255),
        Colour::Purple => color_u8!(128, 0, 128, 255),
        Colour::Cyan => color_u8!(0, 255, 255, 255),
        Colour::Blue => color_u8!(0, 0, 255, 255),
        Colour::Fuchsia => color_u8!(255, 0, 255, 255),
    }
}

fn draw_cell(grid_x: f32, grid_y: f32, cell_size: f32, position: &Position, colour: Colour) {
    let color = macroquad_colour(colour);
    draw_rectangle(
        grid_x + cell_size * position.column as f32,
        grid_y + cell_size * position.row as f32,
        cell_size,
        cell_size,
        color,
    );
}

struct Ui {
    /// Size of the grid in cells.
    size: usize,

    /// Size of the grid in pixels.  Assumes the grid is always square.
    grid_size: f32,

    /// Offset of the grid relative to the whole window along the horizontal axis.
    grid_x: f32,

    /// Offset of the grid relative to the whole window along the vertical axis.
    grid_y: f32,
}

impl Ui {
    fn create(size: usize) -> Ui {
        let mut ui = Ui {
            size,
            grid_size: 0.0,
            grid_x: 0.0,
            grid_y: 0.0,
        };

        ui.resize();

        ui
    }

    fn resize(&mut self) {
        let screen_height = screen_height();
        let screen_width = screen_width();

        let grid_size = screen_height.min(screen_width);
        self.grid_size = grid_size;
        self.grid_x = (screen_width - grid_size) / 2.0;
        self.grid_y = (screen_height - grid_size) / 2.0;
    }

    fn render(&self, game: &Game) {
        clear_background(BLACK);

        let grid_x = self.grid_x;
        let grid_y = self.grid_y;
        let cell_size = self.cell_size();

        let graph = &game.graph;
        for component in graph.neighbours.keys() {
            for position in &graph.components[component].cells {
                draw_cell(grid_x, grid_y, cell_size, position, graph.components[component].colour);
            }
        }
    }

    fn cell_size(&self) -> f32 {
        self.grid_size / self.size as f32
    }

    fn cell_position(&self, raw_position: (f32, f32)) -> Option<Position> {
        let (raw_x, raw_y) = raw_position;

        let cell_size = self.cell_size();
        let x = (raw_x - self.grid_x) / cell_size;
        let y = (raw_y - self.grid_y) / cell_size;

        let size = self.size as f32;
        if x < 0.0 || y < 0.0 || x > size || y > size {
            // Out of bounds, nothing to do
            None
        } else {
            Some(Position{column: x.floor() as usize, row: y.floor() as usize})
        }
    }

    fn handle_click(&self, game: &mut Game, mouse_position: (f32, f32)) {
        if let Some(position) = self.cell_position(mouse_position) {
            let clicked_component = game.graph.find_component(&position);
            let colour = clicked_component.colour;

            println!("Detected click at cell ({}, {}) with colour {:#?}", position.column, position.row, colour);

            game.fill_component_of_top_left_cell_with(colour);

            match game.state {
                GameState::Solving =>
                if game.graph.components.len() == 1 {
                    game.state = GameState::Solved;
                    println!("Done");
                }
                GameState::Solved => (),
            }
        }
    }
}

#[macroquad::main("Flood-It")]
async fn main() {
    let size = 8;

    let mut game = Game::create(size);
    let mut ui = Ui::create(size);

    loop {
        if let Some(KeyCode::Q) = get_last_key_pressed() {
            break;
        }

        if is_mouse_button_pressed(MouseButton::Left) {
            ui.handle_click(&mut game, mouse_position());
        }

        ui.resize();
        ui.render(&game);

        next_frame().await
    }
}
<|endoftext|>


<commit_before>// TODO(tailhook) until we fill all stabs
#![allow(dead_code)]

#[macro_use] extern crate log;
#[macro_use] extern crate quick_error;
#[macro_use] extern crate lazy_static;
extern crate env_logger;
extern crate futures;
extern crate futures_cpupool;
extern crate quire;
extern crate time;
extern crate argparse;
extern crate tokio_core;
extern crate tokio_service;
extern crate minihttp;
extern crate netbuf;
extern crate mime;
extern crate sha1;
extern crate mime_guess;
extern crate tk_sendfile;
extern crate tk_bufstream;
extern crate rustc_serialize;
extern crate tokio_curl;
extern crate curl;
extern crate byteorder;
extern crate httparse;
extern crate httpbin;
extern crate slab;

mod intern;
mod config;
mod handler;
mod handlers;
mod routing;
mod serializer;
mod default_error_page;
mod response;
mod websocket;
mod chat;

// Utils
mod short_circuit;
mod either;

use std::io::{self, Write};
use std::time::Duration;
use std::process::exit;

use futures::stream::Stream;
use argparse::{ArgumentParser, Parse, StoreTrue, Print};
use tokio_core::reactor::Core;
use tokio_core::reactor::Interval;
use minihttp::client::HttpClient;

use config::{ListenSocket, Handler};
use handler::Main;
use chat::handler::ChatAPI;
pub use response::Pickler;

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

    let mut config = String::from("/etc/swindon/main.yaml");
    let mut check = false;
    let mut verbose = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Runs tree of processes");
        ap.refer(&mut config)
          .add_option(&["-c", "--config"], Parse,
            "Configuration file name")
          .metavar("FILE");
        ap.refer(&mut check)
          .add_option(&["-C", "--check-config"], StoreTrue,
            "Check configuration file and exit");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version");
        ap.refer(&mut verbose)
            .add_option(&["--verbose"], StoreTrue,
            "Print some user-friendly startup messages");
        ap.parse_args_or_exit();
    }

    let mut configurator = match config::Configurator::new(&config) {
        Ok(cfg) => cfg,
        Err(e) => {
            writeln!(&mut io::stderr(), "{}", e).ok();
            exit(1);
        }
    };
    let cfg = configurator.config();

    if check {
        exit(0);
    }

    let chat_pro = chat::Processor::new();
    let mut lp = Core::new().unwrap();
    let main_handler = Main {
        config: cfg.clone(),
        handle: lp.handle(),
        http_client: HttpClient::new(lp.handle()),
        chat_processor: chat_pro.clone(),
    };
    // TODO(tailhook) do something when config updates
    for sock in &cfg.get().listen {
        match sock {
            &ListenSocket::Tcp(addr) => {
                if verbose {
                    println!("Listening at {}", addr);
                }
                minihttp::serve(&lp.handle(), addr, main_handler.clone());
            }
        }
    }
    let root = cfg.get();
    for (name, h) in root.handlers.iter() {
        if let &Handler::SwindonChat(ref chat) = h {
            let sess = root.session_pools
                .get(&chat.session_pool).unwrap();
            match sess.listen {
                ListenSocket::Tcp(addr) => {
                    if verbose {
                        println!("Listening {} at {}", name, addr);
                    }
                    let chat_handler = ChatAPI {
                        config: cfg.clone(),
                        chat_pool: chat_pro.pool(&chat.session_pool),
                    };
                    minihttp::serve(&lp.handle(), addr,
                        chat_handler.clone());
                }
            }
        }
    }
    handlers::files::update_pools(&cfg.get().disk_pools);
    chat_pro.update_pools(&cfg.get().session_pools);

    let config_updater = Interval::new(Duration::new(10, 0), &lp.handle())
        .expect("interval created")
        .for_each(move |_| {
            match configurator.try_update() {
                Ok(false) => {}
                Ok(true) => {
                    // TODO(tailhook) update listening sockets
                    info!("Updated config");
                    handlers::files::update_pools(&cfg.get().disk_pools);
                    chat_pro.update_pools(&cfg.get().session_pools);
                }
                Err(e) => {
                    error!("{}", e);
                }
            }
            Ok(())
        });

    lp.run(config_updater).unwrap();
}
<commit_msg>Fix description in in command-line<commit_after>// TODO(tailhook) until we fill all stabs
#![allow(dead_code)]

#[macro_use] extern crate log;
#[macro_use] extern crate quick_error;
#[macro_use] extern crate lazy_static;
extern crate env_logger;
extern crate futures;
extern crate futures_cpupool;
extern crate quire;
extern crate time;
extern crate argparse;
extern crate tokio_core;
extern crate tokio_service;
extern crate minihttp;
extern crate netbuf;
extern crate mime;
extern crate sha1;
extern crate mime_guess;
extern crate tk_sendfile;
extern crate tk_bufstream;
extern crate rustc_serialize;
extern crate tokio_curl;
extern crate curl;
extern crate byteorder;
extern crate httparse;
extern crate httpbin;
extern crate slab;

mod intern;
mod config;
mod handler;
mod handlers;
mod routing;
mod serializer;
mod default_error_page;
mod response;
mod websocket;
mod chat;

// Utils
mod short_circuit;
mod either;

use std::io::{self, Write};
use std::time::Duration;
use std::process::exit;

use futures::stream::Stream;
use argparse::{ArgumentParser, Parse, StoreTrue, Print};
use tokio_core::reactor::Core;
use tokio_core::reactor::Interval;
use minihttp::client::HttpClient;

use config::{ListenSocket, Handler};
use handler::Main;
use chat::handler::ChatAPI;
pub use response::Pickler;

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

    let mut config = String::from("/etc/swindon/main.yaml");
    let mut check = false;
    let mut verbose = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Runs a web server");
        ap.refer(&mut config)
          .add_option(&["-c", "--config"], Parse,
            "Configuration file name")
          .metavar("FILE");
        ap.refer(&mut check)
          .add_option(&["-C", "--check-config"], StoreTrue,
            "Check configuration file and exit");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version");
        ap.refer(&mut verbose)
            .add_option(&["--verbose"], StoreTrue,
            "Print some user-friendly startup messages");
        ap.parse_args_or_exit();
    }

    let mut configurator = match config::Configurator::new(&config) {
        Ok(cfg) => cfg,
        Err(e) => {
            writeln!(&mut io::stderr(), "{}", e).ok();
            exit(1);
        }
    };
    let cfg = configurator.config();

    if check {
        exit(0);
    }

    let chat_pro = chat::Processor::new();
    let mut lp = Core::new().unwrap();
    let main_handler = Main {
        config: cfg.clone(),
        handle: lp.handle(),
        http_client: HttpClient::new(lp.handle()),
        chat_processor: chat_pro.clone(),
    };
    // TODO(tailhook) do something when config updates
    for sock in &cfg.get().listen {
        match sock {
            &ListenSocket::Tcp(addr) => {
                if verbose {
                    println!("Listening at {}", addr);
                }
                minihttp::serve(&lp.handle(), addr, main_handler.clone());
            }
        }
    }
    let root = cfg.get();
    for (name, h) in root.handlers.iter() {
        if let &Handler::SwindonChat(ref chat) = h {
            let sess = root.session_pools
                .get(&chat.session_pool).unwrap();
            match sess.listen {
                ListenSocket::Tcp(addr) => {
                    if verbose {
                        println!("Listening {} at {}", name, addr);
                    }
                    let chat_handler = ChatAPI {
                        config: cfg.clone(),
                        chat_pool: chat_pro.pool(&chat.session_pool),
                    };
                    minihttp::serve(&lp.handle(), addr,
                        chat_handler.clone());
                }
            }
        }
    }
    handlers::files::update_pools(&cfg.get().disk_pools);
    chat_pro.update_pools(&cfg.get().session_pools);

    let config_updater = Interval::new(Duration::new(10, 0), &lp.handle())
        .expect("interval created")
        .for_each(move |_| {
            match configurator.try_update() {
                Ok(false) => {}
                Ok(true) => {
                    // TODO(tailhook) update listening sockets
                    info!("Updated config");
                    handlers::files::update_pools(&cfg.get().disk_pools);
                    chat_pro.update_pools(&cfg.get().session_pools);
                }
                Err(e) => {
                    error!("{}", e);
                }
            }
            Ok(())
        });

    lp.run(config_updater).unwrap();
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_imports, unused_variables)]

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

extern crate docopt;
extern crate rustc_serialize;
extern crate hyper;
extern crate tabwriter;

mod config;
mod client;
mod inquirer;

use client::{Account, Transaction, get_accounts, get_account_balance, get_transactions};
use config::{Config, get_config_path, get_config_file, read_config, get_config_file_to_write, write_config};
use inquirer::{Question, Answer, ask_question};

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

use std::io::ErrorKind;

use std::io::Write;
use tabwriter::TabWriter;

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>] [--only-numbers]
    teller [list] transactions [<account>] [--only-numbers] [--timeframe=<tf>]
    teller [--help | --version]

Commands:
    list accounts           List accounts.
    list transactions       List transactions (default: current).
    show balance            Show the balance of an account (default: current).

Options:
    -h --help               Show this screen.
    -V --version            Show version.
    -n --only-numbers       Show numbers without currency codes.
    -p --interval=<itv>     Group by an interval of time (default: monthly).
    -tf --timeframe=<tf>    Operate upon a named period of time (default: year).
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_list: bool,
    cmd_show: bool,
    cmd_accounts: bool,
    cmd_balance: bool,
    cmd_transactions: bool,
    arg_account: AccountType,
    flag_only_numbers: bool,
    flag_interval: Interval,
    flag_timeframe: Timeframe,
    flag_help: bool,
    flag_version: bool,
}

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

#[derive(Debug)]
enum Interval {
    Monthly,
    None
}

#[derive(Debug)]
enum Timeframe {
    Year,
    None
}

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

impl Decodable for Interval {
    fn decode<D: Decoder>(d: &mut D) -> Result<Interval, D::Error> {
        let s = try!(d.read_str());
        let default_interval = Interval::Monthly;
        Ok(match &*s {
            "monthly" => Interval::Monthly,
            _ => default_interval,
        })
    }
}

impl Decodable for Timeframe {
    fn decode<D: Decoder>(d: &mut D) -> Result<Timeframe, D::Error> {
        let s = try!(d.read_str());
        let default_timeframe = Timeframe::Year;
        Ok(match &*s {
            "year" => Timeframe::Year,
            _ => default_timeframe,
        })
    }
}

fn ready_config() -> Option<Config> {
    let config_file_path = get_config_path();
    match get_config_file(&config_file_path) {
        None => {
            println!("A config file could not be found at: {}", config_file_path.display());
            match init_config() {
                None => None,
                Some(config) => {
                    match get_config_file_to_write(&config_file_path) {
                        Ok(mut config_file) => {
                            let _ = write_config(&mut config_file, &config);
                            Some(config)
                        },
                        Err(e) => panic!("ERROR: opening file to write: {}", e),
                    }
                },
            }
        },
        Some(mut config_file) => {
            match read_config(&mut config_file) {
                Ok(config) => Some(config),
                Err(e) => panic!("ERROR: attempting to read file {}: {}", config_file_path.display(), e),
            }
        },
    }
}

fn init_config() -> Option<Config> {
    println!("You will need to set the `auth_token` and give aliases to your bank accounts");
    print!("\n");

    let get_auth_token_question = Question::new(
        "auth_token".to_string(),
        "What is your `auth_token` on teller.io?".to_string()
    );

    let auth_token_answer = ask_question(&get_auth_token_question);

    let mut config = Config::new_with_auth_token_only(auth_token_answer.value);

    print!("\n");
    let accounts = match get_accounts(&config) {
        Ok(accounts) => accounts,
        Err(e) => panic!("Unable to list accounts: {}", e),
    };
    represent_list_accounts(&accounts, &config);

    println!("Please type the row (e.g. 3) of the account you wish to place against an alias and
              press <enter> to set this in the config. Leave empty if irrelevant.");
    print!("\n");

    let questions = vec![
        Question::new(
            "current".to_string(),
            "Which is your current account?".to_string()
        ),
        Question::new(
            "savings".to_string(),
            "Which is your savings account?".to_string()
        ),
        Question::new(
            "business".to_string(),
            "Which is your business account?".to_string()
        ),
    ];

    let answers: Vec<Answer> = questions.iter().map(ask_question).collect();
    let non_empty_answers: Vec<&Answer> = answers.iter().filter(|&answer| !answer.value.is_empty()).collect();
    let mut fa_iter = non_empty_answers.iter();

    match fa_iter.find(|&answer| answer.name == "current") {
        None => (),
        Some(answer) => {
            let row_number: u32 = answer.value.parse().expect(&format!("ERROR: {:?} did not contain a number", answer));
            config.current = accounts[(row_number - 1) as usize].id.to_owned()
        },
    };
    match fa_iter.find(|&answer| answer.name == "savings") {
        None => (),
        Some(answer) => {
            let row_number: u32 = answer.value.parse().expect(&format!("ERROR: {:?} did not contain a number", answer));
            config.savings = accounts[(row_number - 1) as usize].id.to_owned()
        }
    };
    match fa_iter.find(|&answer| answer.name == "business") {
        None => (),
        Some(answer) => {
            let row_number: u32 = answer.value.parse().expect(&format!("ERROR: {:?} did not contain a number", answer));
            config.business = accounts[(row_number - 1) as usize].id.to_owned()
        }
    };

    if config.auth_token.is_empty() {
        error!("`auth_token` was invalid so a config could not be created");
        None
    } else {
        Some(config)
    }
}

fn pick_command(arguments: Args) {
    match arguments {
        Args { cmd_accounts, .. } if cmd_accounts == true => {
            match ready_config() {
                None => println!("Configuration could not be found or created so command not executed"),
                Some(config) => list_accounts(&config),
            }
        },
        Args { cmd_balance, ref arg_account, flag_only_numbers, .. } if cmd_balance == true => {
            match ready_config() {
                None => println!("Configuration could not be found or created so command not executed"),
                Some(config) => show_balance(&config, &arg_account, &flag_only_numbers),
            }
        },
        Args { cmd_transactions, ref arg_account, flag_only_numbers, .. } if cmd_transactions == true => {
            match ready_config() {
                None => println!("Configuration could not be found or created so command not executed"),
                Some(config) => list_transactions(&config, &arg_account, &flag_only_numbers),
            }
        },
        Args { flag_help, flag_version, .. } if flag_help == true || flag_version == true => (),
        _ => println!("{}", USAGE),
    }
}

fn get_account_alias_for_id(account_id: &String, config: &Config) -> String {
    if *account_id == config.current {
        "(current)".to_string()
    } else if *account_id == config.savings {
        "(savings)".to_string()
    } else if *account_id == config.business {
        "(business)".to_string()
    } else {
        "".to_string()
    }
}

fn represent_list_accounts(accounts: &Vec<Account>, config: &Config) {
    let mut accounts_table = String::new();
    accounts_table.push_str("row\taccount no.\tbalance\n");
    for (idx, account) in accounts.iter().enumerate() {
        let row_number = (idx + 1) as u32;
        let account_alias = get_account_alias_for_id(&account.id, &config);
        let new_account_row = format!("{} {}\t****{}\t{}\t{}\n", row_number, account_alias, account.account_number_last_4, account.balance, account.currency);
        accounts_table = accounts_table + &new_account_row;
    }

    let mut tw = TabWriter::new(Vec::new());
    write!(&mut tw, "{}", accounts_table).unwrap();
    tw.flush().unwrap();

    let accounts_str = String::from_utf8(tw.unwrap()).unwrap();

    println!("{}", accounts_str)
}

fn list_accounts(config: &Config) {
    match get_accounts(&config) {
        Ok(accounts) => represent_list_accounts(&accounts, &config),
        Err(e) => panic!("Unable to list accounts: {}", e),
    }
}

fn get_balance_for_display(balance_with_currency: (String, String), only_numbers: &bool) -> String {
    if *only_numbers {
        balance_with_currency.0
    } else {
        balance_with_currency.0 + " " + &balance_with_currency.1
    }
}

fn represent_show_balance(balance_with_currency: (String, String), only_numbers: &bool) {
    println!("{}", get_balance_for_display(balance_with_currency, &only_numbers))
}

fn get_account_id(config: &Config, account: &AccountType) -> String {
    let default_account_id = config.current.to_owned();
    match *account {
        AccountType::Current => config.current.to_owned(),
        AccountType::Savings => config.savings.to_owned(),
        AccountType::Business => config.business.to_owned(),
        _ => default_account_id,
    }
}

fn show_balance(config: &Config, account: &AccountType, only_numbers: &bool) {
    let account_id = get_account_id(&config, &account);
    match get_account_balance(&config, account_id.to_string()) {
        Ok(balance) => represent_show_balance(balance, &only_numbers),
        Err(e) => panic!("Unable to get account balance: {}", e),
    }
}

fn represent_list_transactions(transactions: &Vec<Transaction>, only_numbers: &bool) {
    let currency = "GBP".to_string(); // TODO: This shouldn't be hardcoded. Comes from account

    let mut transactions_table = String::new();
    transactions_table.push_str("row\tdate\tcounterparty\tamount\tdescription\n");
    for (idx, transaction) in transactions.iter().enumerate() {
        let row_number = (idx + 1) as u32;
        let balance = get_balance_for_display((transaction.amount.to_owned(), currency.to_owned()), &only_numbers);
        let new_transaction_row = format!("{}\t{}\t{}\t{}\t{}\n", row_number, transaction.date, transaction.counterparty, balance, transaction.description);
        transactions_table = transactions_table + &new_transaction_row;
    }

    let mut tw = TabWriter::new(Vec::new());
    write!(&mut tw, "{}", transactions_table).unwrap();
    tw.flush().unwrap();

    let transactions_str = String::from_utf8(tw.unwrap()).unwrap();

    println!("{}", transactions_str)
}

fn list_transactions(config: &Config, account: &AccountType, only_numbers: &bool) {
    let account_id = get_account_id(&config, &account);
    match get_transactions(&config, account_id.to_string()) {
        Ok(transactions) => represent_list_transactions(&transactions, &only_numbers),
        Err(e) => panic!("Unable to list transactions: {}", e),
    }
}

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

    let arguments: Args = Docopt::new(USAGE)
        .and_then(|d| {
            d.version(VERSION.map(|v| v.to_string()))
             .decode()
        })
        .unwrap_or_else(|e| e.exit());

    pick_command(arguments)
}
<commit_msg>feat(init): added an init command to help fixing bad config<commit_after>#![allow(dead_code, unused_imports, unused_variables)]

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

extern crate docopt;
extern crate rustc_serialize;
extern crate hyper;
extern crate tabwriter;

mod config;
mod client;
mod inquirer;

use client::{Account, Transaction, get_accounts, get_account_balance, get_transactions};
use std::path::PathBuf;
use config::{Config, get_config_path, get_config_file, read_config, get_config_file_to_write, write_config};
use inquirer::{Question, Answer, ask_question};

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

use std::io::ErrorKind;

use std::io::Write;
use tabwriter::TabWriter;

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

Usage:
    teller init
    teller [list] accounts
    teller [show] balance [<account>] [--only-numbers]
    teller [list] transactions [<account>] [--only-numbers] [--timeframe=<tf>]
    teller [--help | --version]

Commands:
    init                    Configure.
    list accounts           List accounts.
    list transactions       List transactions (default: current).
    show balance            Show the balance of an account (default: current).

Options:
    -h --help               Show this screen.
    -V --version            Show version.
    -n --only-numbers       Show numbers without currency codes.
    -p --interval=<itv>     Group by an interval of time (default: monthly).
    -tf --timeframe=<tf>    Operate upon a named period of time (default: year).
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_init: bool,
    cmd_list: bool,
    cmd_show: bool,
    cmd_accounts: bool,
    cmd_balance: bool,
    cmd_transactions: bool,
    arg_account: AccountType,
    flag_only_numbers: bool,
    flag_interval: Interval,
    flag_timeframe: Timeframe,
    flag_help: bool,
    flag_version: bool,
}

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

#[derive(Debug)]
enum Interval {
    Monthly,
    None
}

#[derive(Debug)]
enum Timeframe {
    Year,
    None
}

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

impl Decodable for Interval {
    fn decode<D: Decoder>(d: &mut D) -> Result<Interval, D::Error> {
        let s = try!(d.read_str());
        let default_interval = Interval::Monthly;
        Ok(match &*s {
            "monthly" => Interval::Monthly,
            _ => default_interval,
        })
    }
}

impl Decodable for Timeframe {
    fn decode<D: Decoder>(d: &mut D) -> Result<Timeframe, D::Error> {
        let s = try!(d.read_str());
        let default_timeframe = Timeframe::Year;
        Ok(match &*s {
            "year" => Timeframe::Year,
            _ => default_timeframe,
        })
    }
}

fn get_config() -> Option<Config> {
    let config_file_path = get_config_path();
    match get_config_file(&config_file_path) {
        None => {
            println!("A config file could not be found at: {}", config_file_path.display());
            println!("You will need to set the `auth_token` and give aliases to your bank accounts");
            print!("\n");
            configure_cli(&config_file_path)
        },
        Some(mut config_file) => {
            match read_config(&mut config_file) {
                Ok(config) => Some(config),
                Err(e) => panic!("ERROR: attempting to read file {}: {}", config_file_path.display(), e),
            }
        },
    }
}

fn configure_cli(config_file_path: &PathBuf) -> Option<Config> {
    match init_config() {
        None => None,
        Some(config) => {
            match get_config_file_to_write(&config_file_path) {
                Ok(mut config_file) => {
                    let _ = write_config(&mut config_file, &config);
                    Some(config)
                },
                Err(e) => panic!("ERROR: opening file to write: {}", e),
            }
        },
    }
}

fn init_config() -> Option<Config> {
    let get_auth_token_question = Question::new(
        "auth_token".to_string(),
        "What is your `auth_token` on teller.io?".to_string()
    );

    let auth_token_answer = ask_question(&get_auth_token_question);

    let mut config = Config::new_with_auth_token_only(auth_token_answer.value);

    print!("\n");
    let accounts = match get_accounts(&config) {
        Ok(accounts) => accounts,
        Err(e) => panic!("Unable to list accounts: {}", e),
    };
    represent_list_accounts(&accounts, &config);

    println!("Please type the row (e.g. 3) of the account you wish to place against an alias and press <enter> to set this in the config. Leave empty if irrelevant.");
    print!("\n");

    let questions = vec![
        Question::new(
            "current".to_string(),
            "Which is your current account?".to_string()
        ),
        Question::new(
            "savings".to_string(),
            "Which is your savings account?".to_string()
        ),
        Question::new(
            "business".to_string(),
            "Which is your business account?".to_string()
        ),
    ];

    let answers: Vec<Answer> = questions.iter().map(ask_question).collect();
    let non_empty_answers: Vec<&Answer> = answers.iter().filter(|&answer| !answer.value.is_empty()).collect();
    let mut fa_iter = non_empty_answers.iter();

    match fa_iter.find(|&answer| answer.name == "current") {
        None => (),
        Some(answer) => {
            let row_number: u32 = answer.value.parse().expect(&format!("ERROR: {:?} did not contain a number", answer));
            config.current = accounts[(row_number - 1) as usize].id.to_owned()
        },
    };
    match fa_iter.find(|&answer| answer.name == "savings") {
        None => (),
        Some(answer) => {
            let row_number: u32 = answer.value.parse().expect(&format!("ERROR: {:?} did not contain a number", answer));
            config.savings = accounts[(row_number - 1) as usize].id.to_owned()
        }
    };
    match fa_iter.find(|&answer| answer.name == "business") {
        None => (),
        Some(answer) => {
            let row_number: u32 = answer.value.parse().expect(&format!("ERROR: {:?} did not contain a number", answer));
            config.business = accounts[(row_number - 1) as usize].id.to_owned()
        }
    };

    if config.auth_token.is_empty() {
        error!("`auth_token` was invalid so a config could not be created");
        None
    } else {
        Some(config)
    }
}

fn pick_command(arguments: Args) {
    match arguments {
        Args { cmd_init, .. } if cmd_init == true => {
            let config_file_path = get_config_path();
            println!("To create the config ({}) we need to find out your `auth_token` and assign aliases to some common bank accounts.", config_file_path.display());
            print!("\n");
            configure_cli(&config_file_path);
            ()
        },
        Args { cmd_accounts, .. } if cmd_accounts == true => {
            match get_config() {
                None => println!("Configuration could not be found or created so command not executed"),
                Some(config) => list_accounts(&config),
            }
        },
        Args { cmd_balance, ref arg_account, flag_only_numbers, .. } if cmd_balance == true => {
            match get_config() {
                None => println!("Configuration could not be found or created so command not executed"),
                Some(config) => show_balance(&config, &arg_account, &flag_only_numbers),
            }
        },
        Args { cmd_transactions, ref arg_account, flag_only_numbers, .. } if cmd_transactions == true => {
            match get_config() {
                None => println!("Configuration could not be found or created so command not executed"),
                Some(config) => list_transactions(&config, &arg_account, &flag_only_numbers),
            }
        },
        Args { flag_help, flag_version, .. } if flag_help == true || flag_version == true => (),
        _ => println!("{}", USAGE),
    }
}

fn get_account_alias_for_id(account_id: &String, config: &Config) -> String {
    if *account_id == config.current {
        "(current)".to_string()
    } else if *account_id == config.savings {
        "(savings)".to_string()
    } else if *account_id == config.business {
        "(business)".to_string()
    } else {
        "".to_string()
    }
}

fn represent_list_accounts(accounts: &Vec<Account>, config: &Config) {
    let mut accounts_table = String::new();
    accounts_table.push_str("row\taccount no.\tbalance\n");
    for (idx, account) in accounts.iter().enumerate() {
        let row_number = (idx + 1) as u32;
        let account_alias = get_account_alias_for_id(&account.id, &config);
        let new_account_row = format!("{} {}\t****{}\t{}\t{}\n", row_number, account_alias, account.account_number_last_4, account.balance, account.currency);
        accounts_table = accounts_table + &new_account_row;
    }

    let mut tw = TabWriter::new(Vec::new());
    write!(&mut tw, "{}", accounts_table).unwrap();
    tw.flush().unwrap();

    let accounts_str = String::from_utf8(tw.unwrap()).unwrap();

    println!("{}", accounts_str)
}

fn list_accounts(config: &Config) {
    match get_accounts(&config) {
        Ok(accounts) => represent_list_accounts(&accounts, &config),
        Err(e) => panic!("Unable to list accounts: {}", e),
    }
}

fn get_balance_for_display(balance_with_currency: (String, String), only_numbers: &bool) -> String {
    if *only_numbers {
        balance_with_currency.0
    } else {
        balance_with_currency.0 + " " + &balance_with_currency.1
    }
}

fn represent_show_balance(balance_with_currency: (String, String), only_numbers: &bool) {
    println!("{}", get_balance_for_display(balance_with_currency, &only_numbers))
}

fn get_account_id(config: &Config, account: &AccountType) -> String {
    let default_account_id = config.current.to_owned();
    match *account {
        AccountType::Current => config.current.to_owned(),
        AccountType::Savings => config.savings.to_owned(),
        AccountType::Business => config.business.to_owned(),
        _ => default_account_id,
    }
}

fn show_balance(config: &Config, account: &AccountType, only_numbers: &bool) {
    let account_id = get_account_id(&config, &account);
    match get_account_balance(&config, account_id.to_string()) {
        Ok(balance) => represent_show_balance(balance, &only_numbers),
        Err(e) => panic!("Unable to get account balance: {}", e),
    }
}

fn represent_list_transactions(transactions: &Vec<Transaction>, only_numbers: &bool) {
    let currency = "GBP".to_string(); // TODO: This shouldn't be hardcoded. Comes from account

    let mut transactions_table = String::new();
    transactions_table.push_str("row\tdate\tcounterparty\tamount\tdescription\n");
    for (idx, transaction) in transactions.iter().enumerate() {
        let row_number = (idx + 1) as u32;
        let balance = get_balance_for_display((transaction.amount.to_owned(), currency.to_owned()), &only_numbers);
        let new_transaction_row = format!("{}\t{}\t{}\t{}\t{}\n", row_number, transaction.date, transaction.counterparty, balance, transaction.description);
        transactions_table = transactions_table + &new_transaction_row;
    }

    let mut tw = TabWriter::new(Vec::new());
    write!(&mut tw, "{}", transactions_table).unwrap();
    tw.flush().unwrap();

    let transactions_str = String::from_utf8(tw.unwrap()).unwrap();

    println!("{}", transactions_str)
}

fn list_transactions(config: &Config, account: &AccountType, only_numbers: &bool) {
    let account_id = get_account_id(&config, &account);
    match get_transactions(&config, account_id.to_string()) {
        Ok(transactions) => represent_list_transactions(&transactions, &only_numbers),
        Err(e) => panic!("Unable to list transactions: {}", e),
    }
}

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

    let arguments: Args = Docopt::new(USAGE)
        .and_then(|d| {
            d.version(VERSION.map(|v| v.to_string()))
             .decode()
        })
        .unwrap_or_else(|e| e.exit());

    pick_command(arguments)
}
<|endoftext|>


<commit_before>mod lib;

extern crate pokemon;
extern crate rustc_serialize;
extern crate csv;
extern crate rand;

// use std::fs::File;
// use rustc_serialize::json::decode;
use lib::pokemon::{Pokemon, get_pokemon, get_random, get_all};

fn main() {

    println!("{:?}", get_all());
    println!("{:?}", get_pokemon(1, 9));
    println!("{:?}", get_random());

}
<commit_msg>remove main since this is purely a library<commit_after><|endoftext|>