<commit_before>#[macro_use] extern crate log;
extern crate chan;
extern crate chan_signal;
extern crate crossbeam;
extern crate env_logger;
extern crate getopts;
extern crate hyper;
extern crate rustc_serialize;
extern crate ws;
#[macro_use] extern crate libotaplus;

use getopts::Options;
use std::env;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender, Receiver, channel};
use std::thread;
use std::time::Duration;
use chan_signal::Signal;
use chan::Receiver as ChanReceiver;

use libotaplus::datatype::{config, Config, Event, Command, Url};
use libotaplus::http_client::Hyper;
use libotaplus::interaction_library::Interpreter;
use libotaplus::interaction_library::broadcast::Broadcast;
use libotaplus::interaction_library::console::Console;
use libotaplus::interaction_library::gateway::Gateway;
use libotaplus::interaction_library::websocket::Websocket;
use libotaplus::interpreter::{OurInterpreter, Env};
use libotaplus::package_manager::PackageManager;

macro_rules! spawn_thread {
    ($name:expr, $body:block) => {
        {
            match thread::Builder::new().name($name.to_string()).spawn(move || {
                info!("Spawning {}", $name.to_string());
                $body
            }) {
                Err(e) => panic!("Couldn't spawn {}: {}", $name, e),
                Ok(handle) => handle
            }
        }
    }
}

fn spawn_interpreter(config: Config, crx: Receiver<Command>, etx: Sender<Event>) {

    let client = Arc::new(Mutex::new(Hyper::new()));

    let mut env = Env {
        config:       config.clone(),
        access_token: None,
        http_client:  client.clone(),
    };

    spawn_thread!("Interpreter", {
        OurInterpreter::run(&mut env, crx, etx);
    });
}

fn spawn_autoacceptor(erx: Receiver<Event>, ctx: Sender<Command>) {
    spawn_thread!("Autoacceptor of software updates", {
        AutoAcceptor::run(&mut (), erx, ctx);
    });
}

fn spawn_signal_handler(signals: ChanReceiver<Signal>, ctx: Sender<Command>) {
    spawn_thread!("TERM signal handler", {
        loop {
            match signals.recv() {
                Some(s) if s == Signal::TERM => {
                    let _ = ctx.send(Command::Shutdown);
                },
                _ => {}
            }
        }
    });
}

fn spawn_update_poller(ctx: Sender<Command>, config: Config) {
    spawn_thread!("Update poller", {
        loop {
            let _ = ctx.send(Command::GetPendingUpdates);
            thread::sleep(Duration::from_secs(config.ota.polling_interval));
        }
    });
}

fn perform_initial_sync(ctx: Sender<Command>) {
    let _ = ctx.clone().send(Command::Authenticate(None));
    let _ = ctx.clone().send(Command::PostInstalledPackages);
}

fn start_event_broadcasting(broadcast: Broadcast<Event>) {
    spawn_thread!("Event Broadcasting", {
        broadcast.start();
    });
}

struct AutoAcceptor;

impl Interpreter<(), Event, Command> for AutoAcceptor {
    fn interpret(_: &mut (), e: Event, ctx: Sender<Command>) {
        fn f(e: &Event, ctx: Sender<Command>) {
            match e {
                &Event::NewUpdateAvailable(ref id) => {
                    let _ = ctx.send(Command::AcceptUpdate(id.clone()));
                },
                _ => {}
            }
        }

        match e {
            Event::Batch(ref evs) => {
                for ev in evs {
                    f(&ev, ctx.clone())
                }
            }
            e => f(&e, ctx)
        }
    }
}

fn main() {

    env_logger::init().expect("Couldn't initialize logger");

    let config = build_config();

    let (etx, erx): (Sender<Event>, Receiver<Event>) = channel();
    let (ctx, crx): (Sender<Command>, Receiver<Command>) = channel();

    let mut broadcast: Broadcast<Event> = Broadcast::new(erx);

    // Must subscribe to the signal before spawning ANY other threads
    let signals = chan_signal::notify(&[Signal::TERM]);

    spawn_autoacceptor(broadcast.subscribe(), ctx.clone());

    spawn_interpreter(config.clone(), crx, etx.clone());

    Websocket::run(ctx.clone(), broadcast.subscribe());
    spawn_update_poller(ctx.clone(), config.clone());

    let events_for_repl = broadcast.subscribe();

    start_event_broadcasting(broadcast);

    perform_initial_sync(ctx.clone());

    spawn_signal_handler(signals, ctx.clone());

    if config.test.looping {
        println!("Ota Plus Client REPL started.");
        Console::run(ctx.clone(), events_for_repl);
    } else {
        thread::sleep(Duration::from_secs(60000000));
    }
}

fn build_config() -> Config {

    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("h", "help",
                 "print this help menu");
    opts.optopt("", "config",
                "change config path", "PATH");
    opts.optopt("", "auth-server",
                "change the auth server URL", "URL");
    opts.optopt("", "auth-client-id",
                "change auth client id", "ID");
    opts.optopt("", "auth-secret",
                "change auth secret", "SECRET");
    opts.optopt("", "auth-vin",
                "change auth vin", "VIN");
    opts.optopt("", "ota-server",
                "change ota server URL", "URL");
    opts.optopt("", "ota-packages-dir",
                "change downloaded directory for packages", "PATH");
    opts.optopt("", "ota-package-manager",
                "change package manager", "MANAGER");
    opts.optflag("", "repl",
                 "enable repl");

    let matches = opts.parse(&args[1..])
        .unwrap_or_else(|err| panic!(err.to_string()));

    if matches.opt_present("h") {
        let brief = format!("Usage: {} [options]", program);
        exit!("{}", opts.usage(&brief));
    }

    let mut config_file = env::var("OTA_PLUS_CLIENT_CFG")
        .unwrap_or("/opt/ats/ota/etc/ota.toml".to_string());

    if let Some(path) = matches.opt_str("config") {
        config_file = path;
    }

    let mut config = config::load_config(&config_file)
        .unwrap_or_else(|err| exit!("{}", err));

    if let Some(s) = matches.opt_str("auth-server") {
        match Url::parse(&s) {
            Ok(url)  => config.auth.server = url,
            Err(err) => exit!("Invalid auth-server URL: {}", err)
        }
    }

    if let Some(client_id) = matches.opt_str("auth-client-id") {
        config.auth.client_id = client_id;
    }

    if let Some(secret) = matches.opt_str("auth-secret") {
        config.auth.secret = secret;
    }

    if let Some(vin) = matches.opt_str("auth-vin") {
        config.auth.vin = vin;
    }

    if let Some(s) = matches.opt_str("ota-server") {
        match Url::parse(&s) {
            Ok(url)  => config.ota.server = url,
            Err(err) => exit!("Invalid ota-server URL: {}", err)
        }
    }

    if let Some(path) = matches.opt_str("ota-packages-dir") {
        config.ota.packages_dir = path;
    }

    if let Some(s) = matches.opt_str("ota-package-manager") {
        config.ota.package_manager = match s.to_lowercase().as_str() {
            "dpkg" => PackageManager::Dpkg,
            "rpm"  => PackageManager::Rpm,
            path   => PackageManager::File(path.to_string()),
        }
    }

    if matches.opt_present("repl") {
        config.test.looping = true;
    }

    return config
}

// Hack to build a binary with a predictable path for use in tests/. We
// can remove this when https://github.com/rust-lang/cargo/issues/1924
// is resolved.
#[test]
fn build_binary() {
    let output = std::process::Command::new("cargo")
        .arg("build")
        .output()
        .unwrap_or_else(|e| panic!("failed to execute child: {}", e));

    assert!(output.status.success())
}
<commit_msg>Also listen for INT signal (ctrl-c).<commit_after>#[macro_use] extern crate log;
extern crate chan;
extern crate chan_signal;
extern crate crossbeam;
extern crate env_logger;
extern crate getopts;
extern crate hyper;
extern crate rustc_serialize;
extern crate ws;
#[macro_use] extern crate libotaplus;

use getopts::Options;
use std::env;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Sender, Receiver, channel};
use std::thread;
use std::time::Duration;
use chan_signal::Signal;
use chan::Receiver as ChanReceiver;

use libotaplus::datatype::{config, Config, Event, Command, Url};
use libotaplus::http_client::Hyper;
use libotaplus::interaction_library::Interpreter;
use libotaplus::interaction_library::broadcast::Broadcast;
use libotaplus::interaction_library::console::Console;
use libotaplus::interaction_library::gateway::Gateway;
use libotaplus::interaction_library::websocket::Websocket;
use libotaplus::interpreter::{OurInterpreter, Env};
use libotaplus::package_manager::PackageManager;

macro_rules! spawn_thread {
    ($name:expr, $body:block) => {
        {
            match thread::Builder::new().name($name.to_string()).spawn(move || {
                info!("Spawning {}", $name.to_string());
                $body
            }) {
                Err(e) => panic!("Couldn't spawn {}: {}", $name, e),
                Ok(handle) => handle
            }
        }
    }
}

fn spawn_interpreter(config: Config, crx: Receiver<Command>, etx: Sender<Event>) {

    let client = Arc::new(Mutex::new(Hyper::new()));

    let mut env = Env {
        config:       config.clone(),
        access_token: None,
        http_client:  client.clone(),
    };

    spawn_thread!("Interpreter", {
        OurInterpreter::run(&mut env, crx, etx);
    });
}

fn spawn_autoacceptor(erx: Receiver<Event>, ctx: Sender<Command>) {
    spawn_thread!("Autoacceptor of software updates", {
        AutoAcceptor::run(&mut (), erx, ctx);
    });
}

fn spawn_signal_handler(signals: ChanReceiver<Signal>, ctx: Sender<Command>) {
    spawn_thread!("Signal handler", {
        loop {
            match signals.recv() {
                Some(Signal::TERM) | Some(Signal::INT) =>
                    ctx.send(Command::Shutdown).expect("send failed."),
                _                                      => {}
            }
        }
    });
}

fn spawn_update_poller(ctx: Sender<Command>, config: Config) {
    spawn_thread!("Update poller", {
        loop {
            let _ = ctx.send(Command::GetPendingUpdates);
            thread::sleep(Duration::from_secs(config.ota.polling_interval));
        }
    });
}

fn perform_initial_sync(ctx: Sender<Command>) {
    let _ = ctx.clone().send(Command::Authenticate(None));
    let _ = ctx.clone().send(Command::PostInstalledPackages);
}

fn start_event_broadcasting(broadcast: Broadcast<Event>) {
    spawn_thread!("Event Broadcasting", {
        broadcast.start();
    });
}

struct AutoAcceptor;

impl Interpreter<(), Event, Command> for AutoAcceptor {
    fn interpret(_: &mut (), e: Event, ctx: Sender<Command>) {
        fn f(e: &Event, ctx: Sender<Command>) {
            match e {
                &Event::NewUpdateAvailable(ref id) => {
                    let _ = ctx.send(Command::AcceptUpdate(id.clone()));
                },
                _ => {}
            }
        }

        match e {
            Event::Batch(ref evs) => {
                for ev in evs {
                    f(&ev, ctx.clone())
                }
            }
            e => f(&e, ctx)
        }
    }
}

fn main() {

    env_logger::init().expect("Couldn't initialize logger");

    let config = build_config();

    let (etx, erx): (Sender<Event>, Receiver<Event>) = channel();
    let (ctx, crx): (Sender<Command>, Receiver<Command>) = channel();

    let mut broadcast: Broadcast<Event> = Broadcast::new(erx);

    // Must subscribe to the signal before spawning ANY other threads
    let signals = chan_signal::notify(&[Signal::INT, Signal::TERM]);

    spawn_autoacceptor(broadcast.subscribe(), ctx.clone());

    spawn_interpreter(config.clone(), crx, etx.clone());

    Websocket::run(ctx.clone(), broadcast.subscribe());
    spawn_update_poller(ctx.clone(), config.clone());

    let events_for_repl = broadcast.subscribe();

    start_event_broadcasting(broadcast);

    perform_initial_sync(ctx.clone());

    spawn_signal_handler(signals, ctx.clone());

    if config.test.looping {
        println!("Ota Plus Client REPL started.");
        Console::run(ctx.clone(), events_for_repl);
    } else {
        thread::sleep(Duration::from_secs(60000000));
    }
}

fn build_config() -> Config {

    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("h", "help",
                 "print this help menu");
    opts.optopt("", "config",
                "change config path", "PATH");
    opts.optopt("", "auth-server",
                "change the auth server URL", "URL");
    opts.optopt("", "auth-client-id",
                "change auth client id", "ID");
    opts.optopt("", "auth-secret",
                "change auth secret", "SECRET");
    opts.optopt("", "auth-vin",
                "change auth vin", "VIN");
    opts.optopt("", "ota-server",
                "change ota server URL", "URL");
    opts.optopt("", "ota-packages-dir",
                "change downloaded directory for packages", "PATH");
    opts.optopt("", "ota-package-manager",
                "change package manager", "MANAGER");
    opts.optflag("", "repl",
                 "enable repl");

    let matches = opts.parse(&args[1..])
        .unwrap_or_else(|err| panic!(err.to_string()));

    if matches.opt_present("h") {
        let brief = format!("Usage: {} [options]", program);
        exit!("{}", opts.usage(&brief));
    }

    let mut config_file = env::var("OTA_PLUS_CLIENT_CFG")
        .unwrap_or("/opt/ats/ota/etc/ota.toml".to_string());

    if let Some(path) = matches.opt_str("config") {
        config_file = path;
    }

    let mut config = config::load_config(&config_file)
        .unwrap_or_else(|err| exit!("{}", err));

    if let Some(s) = matches.opt_str("auth-server") {
        match Url::parse(&s) {
            Ok(url)  => config.auth.server = url,
            Err(err) => exit!("Invalid auth-server URL: {}", err)
        }
    }

    if let Some(client_id) = matches.opt_str("auth-client-id") {
        config.auth.client_id = client_id;
    }

    if let Some(secret) = matches.opt_str("auth-secret") {
        config.auth.secret = secret;
    }

    if let Some(vin) = matches.opt_str("auth-vin") {
        config.auth.vin = vin;
    }

    if let Some(s) = matches.opt_str("ota-server") {
        match Url::parse(&s) {
            Ok(url)  => config.ota.server = url,
            Err(err) => exit!("Invalid ota-server URL: {}", err)
        }
    }

    if let Some(path) = matches.opt_str("ota-packages-dir") {
        config.ota.packages_dir = path;
    }

    if let Some(s) = matches.opt_str("ota-package-manager") {
        config.ota.package_manager = match s.to_lowercase().as_str() {
            "dpkg" => PackageManager::Dpkg,
            "rpm"  => PackageManager::Rpm,
            path   => PackageManager::File(path.to_string()),
        }
    }

    if matches.opt_present("repl") {
        config.test.looping = true;
    }

    return config
}

// Hack to build a binary with a predictable path for use in tests/. We
// can remove this when https://github.com/rust-lang/cargo/issues/1924
// is resolved.
#[test]
fn build_binary() {
    let output = std::process::Command::new("cargo")
        .arg("build")
        .output()
        .unwrap_or_else(|e| panic!("failed to execute child: {}", e));

    assert!(output.status.success())
}
<|endoftext|>


<commit_before>mod error;
mod parser;
mod commands;
mod interpreter;
mod value;

extern crate ansi_term;

/// Imprime mensagem de ajuda
fn print_help() {
    println!("Ta querendo ajuda, cumpade?");
    println!("O uso é o seguinte: birl [opções] [arquivo ou arquivos]");
    println!("Cê pode passar mais de um arquivo, só que apenas um pode ter a seção \"SHOW\", que \
              é");
    println!("o ponto de partida do teu programa.");
    println!("As opções são as seguintes:");
    println!("\t-a ou --ajuda-o-maluco-ta-doente       : Imprime essa mensagem de ajuda");
    println!("\t-v ou --vers[ã ou a]o-dessa-porra      : Imprime a versão do programa");
    println!("\t-e ou --ele-que-a-gente-quer [comando] : Imprime uma mensagem de ajuda para o \
              comando");
    println!("\t-t ou --tudo-cumpade                   : Imprime todos os comandos disponíveis");
    println!("\t-j ou --jaula [nome]                   : Diz ao interpretador pra usar outro \
              ponto de partida. Padrão: SHOW");
}

/// Versão numérica
pub static BIRLSCRIPT_VERSION: &'static str = "1.0.0";

/// Imprime a mensagem de versão
fn print_version() {
    println!("Versão dessa porra, cumpade:");
    println!("Interpretador BIRLSCRIPT v{}", BIRLSCRIPT_VERSION);
    println!("Copyleft(ɔ) 2016 Rafael R Nakano - Nenhum direito reservado");
}

/// Coleção de parametros passados ao interpretador
enum Param {
    /// Pedido para printar versão
    PrintVersion,
    /// Pedido para printar ajuda
    PrintHelp,
    /// Pedido para printar ajuda com um comando
    CommandHelp(String),
    /// Pedido para modificar o ponto de partida
    CustomInit(String),
    /// Arquivo passado para interpretação
    InputFile(String),
    /// Mostra todos os comandos disponiveis
    ShowCmds,
}

/// Faz parsing dos comandos passados e retorna uma lista deles
fn get_params() -> Vec<Param> {
    use std::env;
    let mut ret: Vec<Param> = vec![];
    let mut params = env::args();
    // Se o proximo argumento é um valor que deve ser ignorado
    let mut next_is_val = false;
    if params.len() >= 2 {
        params.next(); // Se livra do primeiro argumento
        loop {
            let p = match params.next() {
                Some(v) => v,
                None => break,
            };
            if next_is_val {
                next_is_val = false;
                continue;
            }
            match p.as_str() {
                "-" | "--" => warn!("Flag vazia passada."),
                "-a" |
                "--ajuda-o-maluco-ta-doente" => ret.push(Param::PrintHelp),
                "-v" |
                "--versão-dessa-porra" |
                "--versao-dessa-porra" => ret.push(Param::PrintVersion),
                "-e" |
                "--ele-que-a-gente-quer" => {
                    next_is_val = true;
                    let cmd = match params.next() {
                        Some(name) => name,
                        None => {
                            warn!("A flag \"-e ou --ele-que-a-gente-quer\" espera um \
                                      valor.");
                            break;
                        }
                    };
                    ret.push(Param::CommandHelp(cmd));
                }
                "-t" | "--tudo-cumpade" => ret.push(Param::ShowCmds),
                "-j" | "--jaula" => {
                    next_is_val = true;
                    let section = match params.next() {
                        Some(sect) => sect,
                        None => {
                            warn!("A flag \"-j ou --jaula\" espera um valor.");
                            break;
                        }
                    };
                    ret.push(Param::CustomInit(section));
                }
                _ => ret.push(Param::InputFile(p)),
            }
        }
    }
    ret
}

/// Printa ajuda para um comando
fn command_help(command: &str) {
    use parser::kw::*;
    use commands::*;
    let doc = match command {
        KW_MOVE => doc_move(),
        KW_CLEAR => doc_clear(),
        KW_DECL => doc_decl(),
        KW_DECLWV => doc_declwv(),
        KW_JUMP => doc_jump(),
        KW_CMP => doc_cmp(),
        KW_PRINTLN => doc_println(),
        KW_PRINT => doc_print(),
        KW_QUIT => doc_quit(),
        _ => String::from("Comando não encontrado"),
    };
    println!("{}", doc);
}

/// Imprime na tela todos os comandos disponíveis
fn show_cmds() {
    println!("Todos os comandos BIRL!");
    use parser::kw::*;
    let commands = vec![KW_MOVE,
                        KW_CLEAR,
                        KW_CMP,
                        KW_CMP_EQ,
                        KW_CMP_NEQ,
                        KW_CMP_LESS,
                        KW_CMP_LESSEQ,
                        KW_CMP_MORE,
                        KW_CMP_MOREEQ,
                        KW_DECL,
                        KW_DECLWV,
                        KW_JUMP,
                        KW_PRINT,
                        KW_PRINTLN,
                        KW_QUIT,
                        KW_INPUT,
                        KW_INPUT,
                        KW_INPUT_UP];
    for cmd in &commands {
        println!("{}", cmd);
    }
}

fn main() {
    let params = get_params();
    let mut files: Vec<String> = vec![];
    let mut env_default_sect = String::from("SHOW"); // JAULA padrão do ponto de entrada do programa
    for p in params {
        match p {
            Param::PrintVersion => print_version(),
            Param::PrintHelp => print_help(),
            Param::CommandHelp(cmd) => command_help(&cmd),
            Param::CustomInit(init) => env_default_sect = init,
            Param::InputFile(file) => files.push(file),
            Param::ShowCmds => show_cmds(),
        }
    }
    let mut environment = interpreter::Environment::new(env_default_sect);
    if files.len() > 0 {
        for file in files {
            environment.interpret(parser::parse(&file))
        }
        // Executa a jaula principal
        environment.start_program();
    } else {
        abort!("Nenhum arquivo passado para execução")
    }
}
<commit_msg>Alguns ajustes<commit_after>mod error;
mod parser;
mod commands;
mod interpreter;
mod value;

extern crate ansi_term;

/// Imprime mensagem de ajuda
fn print_help() {
    println!("Ta querendo ajuda, cumpade?");
    println!("O uso é o seguinte: birl [opções] [arquivo ou arquivos]");
    println!("Cê pode passar mais de um arquivo, só que apenas um pode ter a seção \"SHOW\", que \
              é");
    println!("o ponto de partida do teu programa.");
    println!("As opções são as seguintes:");
    println!("\t-a ou --ajuda-o-maluco-ta-doente       : Imprime essa mensagem de ajuda");
    println!("\t-v ou --vers[ã ou a]o-dessa-porra      : Imprime a versão do programa");
    println!("\t-e ou --ele-que-a-gente-quer [comando] : Imprime uma mensagem de ajuda para o \
              comando");
    println!("\t-t ou --tudo-cumpade                   : Imprime todos os comandos disponíveis");
    println!("\t-j ou --jaula [nome]                   : Diz ao interpretador pra usar outro \
              ponto de partida. Padrão: SHOW");
}

/// Versão numérica
pub static BIRLSCRIPT_VERSION: &'static str = "1.0.0";

/// Imprime a mensagem de versão
fn print_version() {
    println!("Versão dessa porra, cumpade:");
    println!("Interpretador BIRLSCRIPT v{}", BIRLSCRIPT_VERSION);
    println!("Copyleft(ɔ) 2016 Rafael R Nakano - Nenhum direito reservado");
}

/// Coleção de parametros passados ao interpretador
enum Param {
    /// Pedido para printar versão
    PrintVersion,
    /// Pedido para printar ajuda
    PrintHelp,
    /// Pedido para printar ajuda com um comando
    CommandHelp(String),
    /// Pedido para modificar o ponto de partida
    CustomInit(String),
    /// Arquivo passado para interpretação
    InputFile(String),
    /// Mostra todos os comandos disponiveis
    ShowCmds,
}

/// Faz parsing dos comandos passados e retorna uma lista deles
fn get_params() -> Vec<Param> {
    use std::env;
    let mut ret: Vec<Param> = vec![];
    let mut params = env::args();
    // Se o proximo argumento é um valor que deve ser ignorado
    let mut next_is_val = false;
    if params.len() >= 2 {
        params.next(); // Se livra do primeiro argumento
        loop {
            let p = match params.next() {
                Some(v) => v,
                None => break,
            };
            if next_is_val {
                next_is_val = false;
                continue;
            }
            match p.as_str() {
                "-" | "--" => warn!("Flag vazia passada."),
                "-a" |
                "--ajuda-o-maluco-ta-doente" => ret.push(Param::PrintHelp),
                "-v" |
                "--versão-dessa-porra" |
                "--versao-dessa-porra" => ret.push(Param::PrintVersion),
                "-e" |
                "--ele-que-a-gente-quer" => {
                    next_is_val = true;
                    let cmd = match params.next() {
                        Some(name) => name,
                        None => {
                            warn!("A flag \"-e ou --ele-que-a-gente-quer\" espera um \
                                      valor.");
                            break;
                        }
                    };
                    ret.push(Param::CommandHelp(cmd));
                }
                "-t" | "--tudo-cumpade" => ret.push(Param::ShowCmds),
                "-j" | "--jaula" => {
                    next_is_val = true;
                    let section = match params.next() {
                        Some(sect) => sect,
                        None => {
                            warn!("A flag \"-j ou --jaula\" espera um valor.");
                            break;
                        }
                    };
                    ret.push(Param::CustomInit(section));
                }
                _ => ret.push(Param::InputFile(p)),
            }
        }
    }
    ret
}

/// Printa ajuda para um comando
fn command_help(command: &str) {
    use parser::kw::*;
    use commands::*;
    let doc = match command {
        KW_MOVE => doc_move(),
        KW_CLEAR => doc_clear(),
        KW_DECL => doc_decl(),
        KW_DECLWV => doc_declwv(),
        KW_JUMP => doc_jump(),
        KW_CMP => doc_cmp(),
        KW_PRINTLN => doc_println(),
        KW_PRINT => doc_print(),
        KW_QUIT => doc_quit(),
        _ => String::from("Comando não encontrado"),
    };
    println!("{}", doc);
}

/// Imprime na tela todos os comandos disponíveis
fn show_cmds() {
    println!("Todos os comandos BIRL!");
    use parser::kw::*;
    let commands = vec![KW_MOVE,
                        KW_CLEAR,
                        KW_CMP,
                        KW_CMP_EQ,
                        KW_CMP_NEQ,
                        KW_CMP_LESS,
                        KW_CMP_LESSEQ,
                        KW_CMP_MORE,
                        KW_CMP_MOREEQ,
                        KW_DECL,
                        KW_DECLWV,
                        KW_JUMP,
                        KW_PRINT,
                        KW_PRINTLN,
                        KW_QUIT,
                        KW_INPUT,
                        KW_INPUT,
                        KW_INPUT_UP];
    for cmd in &commands {
        println!("{}", cmd);
    }
}

fn main() {
    let params = get_params();
    let mut files: Vec<String> = vec![];
    let mut env_default_sect = String::from("SHOW"); // JAULA padrão do ponto de entrada do programa
    let mut printed_something = false; // Se algo foi printado. Para que não jogue o erro quando pedir help, comandos ou version
    for p in params {
        match p {
            Param::PrintVersion => {
                printed_something = true;
                print_version()
            }
            Param::PrintHelp => {
                printed_something = true;
                print_help()
            }
            Param::CommandHelp(cmd) => {
                printed_something = true;
                command_help(&cmd)
            }
            Param::CustomInit(init) => env_default_sect = init,
            Param::InputFile(file) => files.push(file),
            Param::ShowCmds => {
                printed_something = true;
                show_cmds()
            }
        }
    }
    let mut environment = interpreter::Environment::new(env_default_sect);
    if files.len() > 0 {
        for file in files {
            environment.interpret(parser::parse(&file))
        }
        // Executa a jaula principal
        environment.start_program();
    } else {
        if !printed_something {
            abort!("Nenhum arquivo passado para a execução")
        }
    }
}
<|endoftext|>


<commit_before>#![deny(missing_debug_implementations, missing_copy_implementations,
    trivial_casts, trivial_numeric_casts,
    unsafe_code, unstable_features,
    unused_import_braces, unused_qualifications)]

#[macro_use]
extern crate slog;
extern crate slog_bunyan;
extern crate slog_stream;
extern crate slog_term;
#[macro_use]
extern crate clap;
extern crate regex;
extern crate notify;
extern crate bincode;
extern crate rustc_serialize;
extern crate byteorder;
extern crate filetime;
extern crate time;

use clap::{App, ArgMatches};
use shared::helpers;
use slog::{Level, LevelFilter, Logger, Duplicate, DrainExt};
use std::fs::{self, File};
use std::path::Path;

mod master;
mod slave;
mod shared;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

fn main() {
    let yaml = load_yaml!("cli.yml");
    let m = App::from_yaml(yaml)
        .version(VERSION)
        .get_matches();

    if let Some(ref m) = m.subcommand_matches("master") {
        run_master(m);
    } else if let Some(ref m) = m.subcommand_matches("slave") {
        run_slave(m);
    }
}

fn run_master(m: &ArgMatches) {
    let base_dir = get_base_dir(m.value_of("base_dir").unwrap());
    let remote_dir = m.value_of("remote_dir").unwrap();
    let remote_port = m.value_of("port");
    let verbose_mode = m.is_present("verbose");
    let mut ignore_strings = get_ignore_strings(m);

    let log = setup_log(&base_dir, verbose_mode, true);
    info!(log, "Starting BindRS");

    master::run(&log,
                &base_dir,
                remote_dir,
                remote_port,
                &mut ignore_strings)
}

fn run_slave(m: &ArgMatches) {
    let base_dir = get_base_dir(m.value_of("base_dir").unwrap());
    let mut ignore_strings = get_ignore_strings(m);
    let verbose_mode = m.is_present("verbose");

    let log = setup_log(&base_dir, verbose_mode, true);
    info!(log, "Starting BindRS");

    slave::run(&log, &base_dir, &mut ignore_strings)
}

fn get_ignore_strings(m: &ArgMatches) -> Vec<String> {
    match m.values_of("ignore") {
        Some(i) => i.into_iter().map(|str| str.to_owned()).collect(),
        None => vec![],
    }
}

fn get_base_dir(base_dir: &str) -> String {
    match helpers::resolve_path(base_dir) {
        Some(dir) => dir,
        None => {
            helpers::print_error_and_exit("failed to find base directory");
            "".to_owned()
        }
    }
}

fn setup_log(base_dir: &str, verbose_mode: bool, master_mode: bool) -> Logger {
    let mut path_buf = Path::new(base_dir).to_path_buf();
    path_buf.push(".bindrs");
    fs::create_dir_all(path_buf.as_path()).unwrap();

    path_buf.push("bindrs");
    path_buf.set_extension("log");

    let file = File::create(path_buf.as_path()).unwrap();
    let stream = slog_stream::stream(file, slog_bunyan::new().build());

    let level = match verbose_mode {
        true => Level::Debug,
        false => Level::Info,
    };

    if master_mode {
        let termlog = slog_term::streamer().async().full().build();
        Logger::root(Duplicate::new(LevelFilter::new(stream, level),
                                    LevelFilter::new(termlog, level))
                         .fuse(),
                     o!("version" => VERSION, "mode" => "master"))
    } else {
        Logger::root(LevelFilter::new(stream, level).fuse(),
                     o!("version" => VERSION, "mode" => "slave"))
    }
}
<commit_msg>Fix issue where slave was starting logger in master mode<commit_after>#![deny(missing_debug_implementations, missing_copy_implementations,
    trivial_casts, trivial_numeric_casts,
    unsafe_code, unstable_features,
    unused_import_braces, unused_qualifications)]

#[macro_use]
extern crate slog;
extern crate slog_bunyan;
extern crate slog_stream;
extern crate slog_term;
#[macro_use]
extern crate clap;
extern crate regex;
extern crate notify;
extern crate bincode;
extern crate rustc_serialize;
extern crate byteorder;
extern crate filetime;
extern crate time;

use clap::{App, ArgMatches};
use shared::helpers;
use slog::{Level, LevelFilter, Logger, Duplicate, DrainExt};
use std::fs::{self, File};
use std::path::Path;

mod master;
mod slave;
mod shared;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

fn main() {
    let yaml = load_yaml!("cli.yml");
    let m = App::from_yaml(yaml)
        .version(VERSION)
        .get_matches();

    if let Some(ref m) = m.subcommand_matches("master") {
        run_master(m);
    } else if let Some(ref m) = m.subcommand_matches("slave") {
        run_slave(m);
    }
}

fn run_master(m: &ArgMatches) {
    let base_dir = get_base_dir(m.value_of("base_dir").unwrap());
    let remote_dir = m.value_of("remote_dir").unwrap();
    let remote_port = m.value_of("port");
    let verbose_mode = m.is_present("verbose");
    let mut ignore_strings = get_ignore_strings(m);

    let log = setup_log(&base_dir, verbose_mode, true);
    info!(log, "Starting BindRS");

    master::run(&log,
                &base_dir,
                remote_dir,
                remote_port,
                &mut ignore_strings)
}

fn run_slave(m: &ArgMatches) {
    let base_dir = get_base_dir(m.value_of("base_dir").unwrap());
    let mut ignore_strings = get_ignore_strings(m);
    let verbose_mode = m.is_present("verbose");

    let log = setup_log(&base_dir, verbose_mode, false);
    info!(log, "Starting BindRS");

    slave::run(&log, &base_dir, &mut ignore_strings)
}

fn get_ignore_strings(m: &ArgMatches) -> Vec<String> {
    match m.values_of("ignore") {
        Some(i) => i.into_iter().map(|str| str.to_owned()).collect(),
        None => vec![],
    }
}

fn get_base_dir(base_dir: &str) -> String {
    match helpers::resolve_path(base_dir) {
        Some(dir) => dir,
        None => {
            helpers::print_error_and_exit("failed to find base directory");
            "".to_owned()
        }
    }
}

fn setup_log(base_dir: &str, verbose_mode: bool, master_mode: bool) -> Logger {
    let mut path_buf = Path::new(base_dir).to_path_buf();
    path_buf.push(".bindrs");
    fs::create_dir_all(path_buf.as_path()).unwrap();

    path_buf.push("bindrs");
    path_buf.set_extension("log");

    let file = File::create(path_buf.as_path()).unwrap();
    let stream = slog_stream::stream(file, slog_bunyan::new().build());

    let level = match verbose_mode {
        true => Level::Debug,
        false => Level::Info,
    };

    if master_mode {
        let termlog = slog_term::streamer().async().full().build();
        Logger::root(Duplicate::new(LevelFilter::new(stream, level),
                                    LevelFilter::new(termlog, level))
                         .fuse(),
                     o!("version" => VERSION, "mode" => "master"))
    } else {
        Logger::root(LevelFilter::new(stream, level).fuse(),
                     o!("version" => VERSION, "mode" => "slave"))
    }
}
<|endoftext|>


<commit_before>use csv;
use indicatif;
use lazy_static;
use prettytable;
use regex;
use rusqlite;
use rustyline;
use std::env;
use std::error::Error;
use std::fs::File;

fn _normalize_col(col: &str) -> String {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"\(.*?\)").unwrap();
    }
    RE.replace_all(col, "")
        .to_lowercase()
        .trim()
        .replace(" ", "_")
        .replace(".", "_")
        .replace("-", "_")
        .replace("/", "_")
        .replace("?", "")
}

fn _create_table(db: &mut rusqlite::Connection, table_name: &str, cols: &[String]) {
    let create_columns = cols
        .iter()
        .map(|c| format!("{} varchar", c))
        .collect::<Vec<String>>()
        .join(", ");
    db.execute(
        &format!("CREATE TABLE {} ({})", table_name, create_columns),
        &[] as &[&rusqlite::types::ToSql],
    )
    .unwrap();
}

fn _insert_row(db: &mut rusqlite::Connection, row: csv::StringRecord, insert_query: &str) {
    let mut stmt = db.prepare(insert_query).unwrap();

    let res = stmt.execute(&row);
    assert!(res.is_ok());
}

fn _load_table_from_path(
    db: &mut rusqlite::Connection,
    table_name: &str,
    path: String,
) -> Vec<String> {
    let mut num_rows = 0;
    let f = File::open(path).unwrap();
    let file_size = f.metadata().unwrap().len();
    let mut reader = csv::Reader::from_reader(f);

    let normalized_cols =
        reader
            .headers()
            .unwrap()
            .iter()
            .map(_normalize_col)
            .fold(vec![], |mut v, orig_col| {
                let mut col = orig_col.clone();
                let mut i = 1;
                while v.contains(&col) {
                    col = format!("{}_{}", orig_col, i);
                    i += 1
                }
                v.push(col);
                v
            });
    _create_table(db, table_name, &normalized_cols);

    let insert_query = format!(
        "INSERT INTO {} VALUES ({})",
        table_name,
        normalized_cols
            .iter()
            .map(|_| "?")
            .collect::<Vec<_>>()
            .join(", ")
    );
    let pb = indicatif::ProgressBar::new(file_size);
    pb.set_style(
        indicatif::ProgressStyle::default_bar()
            .template("[{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
            .progress_chars("#>-"),
    );
    let mut records = reader.records();
    while let Some(row) = records.next() {
        _insert_row(db, row.unwrap(), &insert_query);
        num_rows += 1;
        if num_rows % 10000 == 0 {
            pb.set_position(records.reader().position().byte())
        }
    }
    pb.finish();

    println!(
        "Loaded {} rows into {}({})",
        num_rows,
        table_name,
        normalized_cols.join(", "),
    );
    normalized_cols
}

struct FromAnySqlType {
    value: String,
}

impl rusqlite::types::FromSql for FromAnySqlType {
    fn column_result(
        value: rusqlite::types::ValueRef,
    ) -> Result<FromAnySqlType, rusqlite::types::FromSqlError> {
        let result = match value {
            rusqlite::types::ValueRef::Null => "null".to_string(),
            rusqlite::types::ValueRef::Integer(v) => v.to_string(),
            rusqlite::types::ValueRef::Real(v) => v.to_string(),
            rusqlite::types::ValueRef::Text(v) => v.to_string(),
            rusqlite::types::ValueRef::Blob(v) => String::from_utf8(v.to_vec()).unwrap(),
        };
        Ok(FromAnySqlType { value: result })
    }
}

fn _print_table(conn: &mut rusqlite::Connection, line: &str) {
    let mut table = prettytable::Table::new();
    table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
    let mut stmt = match conn.prepare(&line) {
        Ok(stmt) => stmt,
        Err(e) => {
            println!("{}", e.description());
            return;
        }
    };
    let mut results = stmt.query(&[] as &[&rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        let mut row = prettytable::Row::new(vec![]);
        for i in 0..r.column_count() {
            let cell: FromAnySqlType = r.get(i).unwrap();
            row.add_cell(prettytable::Cell::new(&cell.value));
        }
        table.add_row(row);
    }
    table.printstd();
}

struct SimpleWordCompleter {
    words: Vec<String>,
}

static BREAK_CHARS: [u8; 4] = [b' ', b'(', b')', b','];
impl SimpleWordCompleter {
    fn new(words: Vec<String>) -> SimpleWordCompleter {
        SimpleWordCompleter { words }
    }
}

impl rustyline::Helper for SimpleWordCompleter {}

impl rustyline::hint::Hinter for SimpleWordCompleter {
    fn hint(&self, _line: &str, _pos: usize, _ctx: &rustyline::Context) -> Option<String> {
        None
    }
}

impl rustyline::highlight::Highlighter for SimpleWordCompleter {}

impl rustyline::completion::Completer for SimpleWordCompleter {
    type Candidate = String;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        _ctx: &rustyline::Context,
    ) -> rustyline::Result<(usize, Vec<String>)> {
        let (start, word) = rustyline::completion::extract_word(line, pos, None, &BREAK_CHARS);

        let matches = self
            .words
            .iter()
            .filter(|w| w.starts_with(word))
            .cloned()
            .collect();
        Ok((start, matches))
    }
}

fn main() {
    let mut paths = env::args().skip(1);

    let mut conn = rusqlite::Connection::open_in_memory().unwrap();

    let mut base_words = vec![
        "distinct", "select", "from", "group", "by", "order", "where", "count", "limit", "offset",
    ]
    .iter()
    .map(|s| s.to_string())
    .collect::<Vec<String>>();

    if paths.len() == 1 {
        let mut col_names = _load_table_from_path(&mut conn, "t", paths.next().unwrap());
        base_words.append(&mut col_names);
    } else {
        for (idx, path) in paths.enumerate() {
            let mut col_names = _load_table_from_path(&mut conn, &format!("t{}", idx + 1), path);
            base_words.append(&mut col_names);
        }
    }

    let completer = SimpleWordCompleter::new(base_words);
    let mut rl = rustyline::Editor::new();
    rl.set_helper(Some(completer));
    loop {
        match rl.readline("> ") {
            Ok(line) => {
                if line.trim().is_empty() {
                    continue;
                }
                rl.add_history_entry(line.clone());
                _print_table(&mut conn, &line);
            }
            Err(rustyline::error::ReadlineError::Interrupted) => {
                println!("Interrupted");
                continue;
            }
            Err(rustyline::error::ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                println!("Error: {}", err);
                break;
            }
        }
    }
}
<commit_msg>Include a title in the table<commit_after>use csv;
use indicatif;
use lazy_static;
use prettytable;
use regex;
use rusqlite;
use rustyline;
use std::env;
use std::error::Error;
use std::fs::File;

fn _normalize_col(col: &str) -> String {
    lazy_static::lazy_static! {
        static ref RE: regex::Regex = regex::Regex::new(r"\(.*?\)").unwrap();
    }
    RE.replace_all(col, "")
        .to_lowercase()
        .trim()
        .replace(" ", "_")
        .replace(".", "_")
        .replace("-", "_")
        .replace("/", "_")
        .replace("?", "")
}

fn _create_table(db: &mut rusqlite::Connection, table_name: &str, cols: &[String]) {
    let create_columns = cols
        .iter()
        .map(|c| format!("{} varchar", c))
        .collect::<Vec<String>>()
        .join(", ");
    db.execute(
        &format!("CREATE TABLE {} ({})", table_name, create_columns),
        &[] as &[&rusqlite::types::ToSql],
    )
    .unwrap();
}

fn _insert_row(db: &mut rusqlite::Connection, row: csv::StringRecord, insert_query: &str) {
    let mut stmt = db.prepare(insert_query).unwrap();

    let res = stmt.execute(&row);
    assert!(res.is_ok());
}

fn _load_table_from_path(
    db: &mut rusqlite::Connection,
    table_name: &str,
    path: String,
) -> Vec<String> {
    let mut num_rows = 0;
    let f = File::open(path).unwrap();
    let file_size = f.metadata().unwrap().len();
    let mut reader = csv::Reader::from_reader(f);

    let normalized_cols =
        reader
            .headers()
            .unwrap()
            .iter()
            .map(_normalize_col)
            .fold(vec![], |mut v, orig_col| {
                let mut col = orig_col.clone();
                let mut i = 1;
                while v.contains(&col) {
                    col = format!("{}_{}", orig_col, i);
                    i += 1
                }
                v.push(col);
                v
            });
    _create_table(db, table_name, &normalized_cols);

    let insert_query = format!(
        "INSERT INTO {} VALUES ({})",
        table_name,
        normalized_cols
            .iter()
            .map(|_| "?")
            .collect::<Vec<_>>()
            .join(", ")
    );
    let pb = indicatif::ProgressBar::new(file_size);
    pb.set_style(
        indicatif::ProgressStyle::default_bar()
            .template("[{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({eta})")
            .progress_chars("#>-"),
    );
    let mut records = reader.records();
    while let Some(row) = records.next() {
        _insert_row(db, row.unwrap(), &insert_query);
        num_rows += 1;
        if num_rows % 10000 == 0 {
            pb.set_position(records.reader().position().byte())
        }
    }
    pb.finish();

    println!(
        "Loaded {} rows into {}({})",
        num_rows,
        table_name,
        normalized_cols.join(", "),
    );
    normalized_cols
}

struct FromAnySqlType {
    value: String,
}

impl rusqlite::types::FromSql for FromAnySqlType {
    fn column_result(
        value: rusqlite::types::ValueRef,
    ) -> Result<FromAnySqlType, rusqlite::types::FromSqlError> {
        let result = match value {
            rusqlite::types::ValueRef::Null => "null".to_string(),
            rusqlite::types::ValueRef::Integer(v) => v.to_string(),
            rusqlite::types::ValueRef::Real(v) => v.to_string(),
            rusqlite::types::ValueRef::Text(v) => v.to_string(),
            rusqlite::types::ValueRef::Blob(v) => String::from_utf8(v.to_vec()).unwrap(),
        };
        Ok(FromAnySqlType { value: result })
    }
}

fn _print_table(conn: &mut rusqlite::Connection, line: &str) {
    let mut table = prettytable::Table::new();
    table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
    let mut stmt = match conn.prepare(&line) {
        Ok(stmt) => stmt,
        Err(e) => {
            println!("{}", e.description());
            return;
        }
    };

    let mut title_row = prettytable::Row::new(vec![]);
    for col in stmt.column_names() {
        title_row.add_cell(prettytable::Cell::new(col));
    }
    table.set_titles(title_row);

    let mut results = stmt.query(&[] as &[&rusqlite::types::ToSql]).unwrap();
    while let Ok(Some(r)) = results.next() {
        let mut row = prettytable::Row::new(vec![]);
        for i in 0..r.column_count() {
            let cell: FromAnySqlType = r.get(i).unwrap();
            row.add_cell(prettytable::Cell::new(&cell.value));
        }
        table.add_row(row);
    }
    table.printstd();
}

struct SimpleWordCompleter {
    words: Vec<String>,
}

static BREAK_CHARS: [u8; 4] = [b' ', b'(', b')', b','];
impl SimpleWordCompleter {
    fn new(words: Vec<String>) -> SimpleWordCompleter {
        SimpleWordCompleter { words }
    }
}

impl rustyline::Helper for SimpleWordCompleter {}

impl rustyline::hint::Hinter for SimpleWordCompleter {
    fn hint(&self, _line: &str, _pos: usize, _ctx: &rustyline::Context) -> Option<String> {
        None
    }
}

impl rustyline::highlight::Highlighter for SimpleWordCompleter {}

impl rustyline::completion::Completer for SimpleWordCompleter {
    type Candidate = String;

    fn complete(
        &self,
        line: &str,
        pos: usize,
        _ctx: &rustyline::Context,
    ) -> rustyline::Result<(usize, Vec<String>)> {
        let (start, word) = rustyline::completion::extract_word(line, pos, None, &BREAK_CHARS);

        let matches = self
            .words
            .iter()
            .filter(|w| w.starts_with(word))
            .cloned()
            .collect();
        Ok((start, matches))
    }
}

fn main() {
    let mut paths = env::args().skip(1);

    let mut conn = rusqlite::Connection::open_in_memory().unwrap();

    let mut base_words = vec![
        "distinct", "select", "from", "group", "by", "order", "where", "count", "limit", "offset",
    ]
    .iter()
    .map(|s| s.to_string())
    .collect::<Vec<String>>();

    if paths.len() == 1 {
        let mut col_names = _load_table_from_path(&mut conn, "t", paths.next().unwrap());
        base_words.append(&mut col_names);
    } else {
        for (idx, path) in paths.enumerate() {
            let mut col_names = _load_table_from_path(&mut conn, &format!("t{}", idx + 1), path);
            base_words.append(&mut col_names);
        }
    }

    let completer = SimpleWordCompleter::new(base_words);
    let mut rl = rustyline::Editor::new();
    rl.set_helper(Some(completer));
    loop {
        match rl.readline("> ") {
            Ok(line) => {
                if line.trim().is_empty() {
                    continue;
                }
                rl.add_history_entry(line.clone());
                _print_table(&mut conn, &line);
            }
            Err(rustyline::error::ReadlineError::Interrupted) => {
                println!("Interrupted");
                continue;
            }
            Err(rustyline::error::ReadlineError::Eof) => {
                break;
            }
            Err(err) => {
                println!("Error: {}", err);
                break;
            }
        }
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(clippy)]
// #![deny(clippy_pedantic)]
extern crate termion;
extern crate serde_json;
#[macro_use]
extern crate log;
extern crate log4rs;

mod core;

use termion::{clear, cursor};
use termion::raw::IntoRawMode;
use termion::input::TermRead;
use std::io::{stdout, Write, stdin};
use std::sync::mpsc;
use std::{thread, time};
use core::Core;

struct Screen {
    stdout: termion::raw::RawTerminal<std::io::Stdout>,
    size: (u16, u16),
}

impl Screen {

    fn new() -> Screen {
        let mut stdout = stdout().into_raw_mode().unwrap();
        write!(stdout, "{}", clear::All).unwrap();
        stdout.flush().unwrap();
        Screen {
            size: termion::terminal_size().unwrap(),
            stdout: stdout,
        }
    }

    fn redraw(&mut self, update: &Update) {
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();
        for (_, line) in update.lines.iter().enumerate() {
            write!(self.stdout, "{}", cursor::Left(self.size.0)).unwrap();
            self.stdout.write_all(line.text.as_bytes()).unwrap();
        }
        // this is pretty weird
        write!(self.stdout, "{}", cursor::Goto(update.scroll_to.1 as u16 + 1, update.scroll_to.0 as u16 + 1)).unwrap();
        self.stdout.flush().unwrap();
    }

    fn init(&mut self) {
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();
        self.stdout.flush().unwrap();
    }

}

struct Update {
    lines: Vec<Line>,
    height: u64,
    scroll_to: (u64, u64),
    first_line: u64
}

impl Update {
    fn from_value(value: &serde_json::Value) -> Update {
        let object = value.as_object().unwrap();
        let scroll_to = object.get("scrollto").unwrap().as_array().unwrap();
        let mut lines: Vec<Line> = vec![];
        for line in object.get("lines").unwrap().as_array().unwrap().iter() {
            lines.push(Line::from_value(line));
        }
        info!("{:?}", object);
        Update {
            height: object.get("height").unwrap().as_u64().unwrap(),
            first_line: object.get("first_line").unwrap().as_u64().unwrap(),
            lines: lines,
            scroll_to: (scroll_to[0].as_u64().unwrap(), scroll_to[1].as_u64().unwrap()),
        }
    }
}

struct Line {
    text: String,
    selection: Option<(u64, u64)>,
    cursor: Option<u64>,
}

impl Line {
    fn from_value(value: &serde_json::Value) -> Line {
        let line_arr = value.as_array().unwrap();
        let mut line = Line {
            text: line_arr[0].as_str().unwrap().to_string(),
            cursor: None,
            selection: None,
        };
        for annotation in line_arr.iter().skip(1).map(|a| a.as_array().unwrap()) {
            match annotation[0].as_str().unwrap() {
                "cursor" => {
                    line.cursor = Some(annotation[1].as_u64().unwrap());
                },
                "sel" => {
                    line.selection = Some((annotation[1].as_u64().unwrap(), annotation[2].as_u64().unwrap()));
                },
                _ => {
                    error!("unknown annotation");
                }
            }
        }
        line
    }
}

fn update_screen(core: &mut Core, screen: &mut Screen) {
    if let Ok(msg) = core.update_rx.try_recv() {
        let update = Update::from_value(msg.as_object().unwrap().get("update").unwrap());
        screen.redraw(&update);
    } else {
        thread::sleep(time::Duration::from_millis(10));
    }
}

pub struct Input {
    tx: mpsc::Sender<termion::event::Event>,
    rx: mpsc::Receiver<termion::event::Event>,
}

impl Input {
    pub fn new() -> Input {
        let (tx, rx) = mpsc::channel();
        Input {
            tx: tx,
            rx: rx,
        }
    }

    pub fn run(&mut self) {
        let tx = self.tx.clone();
        thread::spawn(move || {
            for event_res in stdin().events() {
                match event_res {
                    Ok(event) => {
                        info!("event: {:?}", event);
                        tx.send(event).unwrap();
                    },
                    Err(err) => {
                        error!("{:?}", err);
                    }
                }
            }
        });
    }
}

impl Default for Input {
	fn default() -> Self {
		Self::new()
	}
}

fn main() {
    log4rs::init_file("log_config.yaml", Default::default()).unwrap();
    let mut core = Core::new("../xi-editor/rust/target/debug/xi-core");
    let mut screen = Screen::new();
    let mut input = Input::new();
    input.run();
    screen.init();
    loop {
        if let Ok(event) = input.rx.try_recv() {
            match event {
                termion::event::Event::Key(key) => {
                    match key {
                        termion::event::Key::Char(c) => {
                            core.char(c);
                        },
                        termion::event::Key::Ctrl(c) => {
                            if c == 'c' {
                                info!("received ^C: exiting");
                                return;
                            }
                        },
                        _ => {
                            error!("unsupported key event");
                        }
                    }
                },
                termion::event::Event::Mouse(_) => {
                    error!("mouse events are not supported yet");
                }
                _ => {
                    error!("unsupported event");
                }
            }
        } else {
            update_screen(&mut core, &mut screen);
        }
    }
}
<commit_msg>add backspace and left/right/up/down movements<commit_after>#![feature(plugin)]
#![plugin(clippy)]
// #![deny(clippy_pedantic)]
extern crate termion;
extern crate serde_json;
#[macro_use]
extern crate log;
extern crate log4rs;

mod core;

use termion::{clear, cursor};
use termion::raw::IntoRawMode;
use termion::input::TermRead;
use std::io::{stdout, Write, stdin};
use std::sync::mpsc;
use std::{thread, time};
use core::Core;

struct Screen {
    stdout: termion::raw::RawTerminal<std::io::Stdout>,
    size: (u16, u16),
}

impl Screen {

    fn new() -> Screen {
        let mut stdout = stdout().into_raw_mode().unwrap();
        write!(stdout, "{}", clear::All).unwrap();
        stdout.flush().unwrap();
        Screen {
            size: termion::terminal_size().unwrap(),
            stdout: stdout,
        }
    }

    fn redraw(&mut self, update: &Update) {
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();
        for (_, line) in update.lines.iter().enumerate() {
            write!(self.stdout, "{}", cursor::Left(self.size.0)).unwrap();
            self.stdout.write_all(line.text.as_bytes()).unwrap();
        }
        // this is pretty weird
        write!(self.stdout, "{}", cursor::Goto(update.scroll_to.1 as u16 + 1, update.scroll_to.0 as u16 + 1)).unwrap();
        self.stdout.flush().unwrap();
    }

    fn init(&mut self) {
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();
        self.stdout.flush().unwrap();
    }

}

struct Update {
    lines: Vec<Line>,
    height: u64,
    scroll_to: (u64, u64),
    first_line: u64
}

impl Update {
    fn from_value(value: &serde_json::Value) -> Update {
        let object = value.as_object().unwrap();
        let scroll_to = object.get("scrollto").unwrap().as_array().unwrap();
        let mut lines: Vec<Line> = vec![];
        for line in object.get("lines").unwrap().as_array().unwrap().iter() {
            lines.push(Line::from_value(line));
        }
        info!("{:?}", object);
        Update {
            height: object.get("height").unwrap().as_u64().unwrap(),
            first_line: object.get("first_line").unwrap().as_u64().unwrap(),
            lines: lines,
            scroll_to: (scroll_to[0].as_u64().unwrap(), scroll_to[1].as_u64().unwrap()),
        }
    }
}

struct Line {
    text: String,
    selection: Option<(u64, u64)>,
    cursor: Option<u64>,
}

impl Line {
    fn from_value(value: &serde_json::Value) -> Line {
        let line_arr = value.as_array().unwrap();
        let mut line = Line {
            text: line_arr[0].as_str().unwrap().to_string(),
            cursor: None,
            selection: None,
        };
        for annotation in line_arr.iter().skip(1).map(|a| a.as_array().unwrap()) {
            match annotation[0].as_str().unwrap() {
                "cursor" => {
                    line.cursor = Some(annotation[1].as_u64().unwrap());
                },
                "sel" => {
                    line.selection = Some((annotation[1].as_u64().unwrap(), annotation[2].as_u64().unwrap()));
                },
                _ => {
                    error!("unknown annotation");
                }
            }
        }
        line
    }
}

fn update_screen(core: &mut Core, screen: &mut Screen) {
    if let Ok(msg) = core.update_rx.try_recv() {
        let update = Update::from_value(msg.as_object().unwrap().get("update").unwrap());
        screen.redraw(&update);
    } else {
        thread::sleep(time::Duration::from_millis(10));
    }
}

pub struct Input {
    tx: mpsc::Sender<termion::event::Event>,
    rx: mpsc::Receiver<termion::event::Event>,
}

impl Input {
    pub fn new() -> Input {
        let (tx, rx) = mpsc::channel();
        Input {
            tx: tx,
            rx: rx,
        }
    }

    pub fn run(&mut self) {
        let tx = self.tx.clone();
        thread::spawn(move || {
            for event_res in stdin().events() {
                match event_res {
                    Ok(event) => {
                        info!("event: {:?}", event);
                        tx.send(event).unwrap();
                    },
                    Err(err) => {
                        error!("{:?}", err);
                    }
                }
            }
        });
    }
}

impl Default for Input {
	fn default() -> Self {
		Self::new()
	}
}

fn main() {
    log4rs::init_file("log_config.yaml", Default::default()).unwrap();
    let mut core = Core::new("../xi-editor/rust/target/debug/xi-core");
    let mut screen = Screen::new();
    let mut input = Input::new();
    input.run();
    screen.init();
    loop {
        if let Ok(event) = input.rx.try_recv() {
            match event {
                termion::event::Event::Key(key) => {
                    match key {
                        termion::event::Key::Char(c) => {
                            core.char(c);
                        },
                        termion::event::Key::Ctrl(c) => {
                            if c == 'c' {
                                info!("received ^C: exiting");
                                return;
                            }
                        },
                        termion::event::Key::Backspace => {
                            core.del();
                        },
                        termion::event::Key::Left => {
                            core.left();
                        },
                        termion::event::Key::Right => {
                            core.right();
                        },
                        termion::event::Key::Up => {
                            core.up();
                        },
                        termion::event::Key::Down => {
                            core.down();
                        },
                        _ => {
                            error!("unsupported key event");
                        }
                    }
                },
                termion::event::Event::Mouse(_) => {
                    error!("mouse events are not supported yet");
                }
                _ => {
                    error!("unsupported event");
                }
            }
        } else {
            update_screen(&mut core, &mut screen);
        }
    }
}
<|endoftext|>


<commit_before>mod spotify_api {
    pub const SCOPES: &'static [&'static str] = &["user-read-private", "streaming", "user-read-playback-state"];
    pub const AUTHORIZE: &'static str = "https://accounts.spotify.com/en/authorize";
    pub const TOKEN: &'static str = "https://accounts.spotify.com/api/token";
    pub const DEVICES: &'static str = "https://api.spotify.com/v1/me/player/devices";
}

#[derive(PartialEq)]
enum HttpMethod {
    GET,
    POST,
}

extern crate curl;
extern crate open;
extern crate regex;
extern crate rustc_serialize;
extern crate url;
extern crate ini;

use std::net::{TcpListener};
use std::io::{Read, Write, BufReader, BufRead};

use curl::easy::{Easy, List};
use regex::Regex;
use rustc_serialize::json::Json;
use rustc_serialize::json;
use rustc_serialize::Decodable;
use rustc_serialize::Decoder;
use url::percent_encoding;
use ini::Ini;

fn oauth_request_with_local_webserver(port: u32, url: &str, reply: &str) -> Vec<String> {
    if !open::that(url).is_ok() {
        return Vec::<String>::new()
    }
    let host = format!("127.0.0.1:{}", port);
    let listener = TcpListener::bind(host).unwrap();
    let stream = listener.accept().unwrap().0;
    let mut reader = BufReader::new(stream);
    let mut response = Vec::<String>::new();
    for line in reader.by_ref().lines() {
        let line_str = line.unwrap();
        response.push(line_str.clone());
        if line_str == "" {
            break;
        }
    }
    let _ = reader.into_inner().write(reply.as_bytes());
    response
}

fn spotify_auth_code(lines: Vec<String>) -> String {
    let mut auth_code = String::new();
    for line in lines {
        let line_str = line;
        let re = Regex::new(r"code=([^?& ]+)").unwrap();
        let ismatch = re.is_match(line_str.as_str());
        if ismatch {
            let cap = re.captures(line_str.as_str()).unwrap();
            auth_code = auth_code + &cap[1];
        }
    }
    auth_code
}

macro_rules! vec_of_strings {
    ($($x:expr),*) => (vec![$($x.to_string()),*]);
}

fn authenticate(settings: &Settings) -> String {
    let scopes = spotify_api::SCOPES.join(" ");
    let host = format!("http://127.0.0.1:{}", settings.port);
    let url = format!("{}?client_id={}&response_type=code&scope={}&redirect_uri={}",
                      spotify_api::AUTHORIZE,settings.client_id, scopes, host);
    let query = percent_encoding::utf8_percent_encode(&url, percent_encoding::QUERY_ENCODE_SET).collect::<String>();
    let response = "HTTP/1.1 200 OK\r\n\r\nAuthenticated with Spotify.\r\n\r\nYou can close this window.\r\n";
    let auth_lines = oauth_request_with_local_webserver(settings.port, &query, response);
    let auth_code = spotify_auth_code(auth_lines);
    auth_code
}

fn http(url: &str, query: &str, method: HttpMethod, access_token: Option<&str>) -> String {
    let mut data = query.as_bytes();
    let mut json_bytes = Vec::<u8>::new();
    {
        let mut easy = Easy::new();
        match method {
            HttpMethod::POST => {
                easy.url(url).unwrap();
                easy.post(true).unwrap();
                easy.post_field_size(data.len() as u64).unwrap();
            }
            _ => {
                let get_url = format!("{}?{}", url, query);
                easy.url(&get_url).unwrap();
            }
        }

        match access_token {
            Some(access_token) => {
                let mut list = List::new();
                let header = format!("Authorization: Bearer {}", access_token);
                list.append(&header).unwrap();
                easy.http_headers(list).unwrap();
            }
            None => {}
        }

        {
            let mut transfer = easy.transfer();
            if method == HttpMethod::POST {
                transfer.read_function(|buf| {
                    Ok(data.read(buf).unwrap_or(0))
                }).unwrap();
            }
            transfer.write_function(|x| {
                json_bytes.extend(x);
                Ok(x.len())
            }).unwrap();
            transfer.perform().unwrap();
        }
    }
    String::from_utf8(json_bytes).unwrap()
}

fn parse_spotify_token(json: &str) -> (String, String) {
    let json_data = Json::from_str(&json).unwrap();
    let obj = json_data.as_object().unwrap();
    let access_token = obj.get("access_token").unwrap().as_string().unwrap();
    let refresh_token = obj.get("refresh_token").unwrap().as_string().unwrap();
    (String::from(access_token),String::from(refresh_token))
}

//#[derive(RustcDecodable, RustcEncodable, Debug)]
#[derive(RustcEncodable, Debug)]
struct ConnectDevice {
    id: String,
    is_active: bool,
    is_restricted: bool,
    name: String,
    device_type: String,
    volume_percent: u32
}

impl Decodable for ConnectDevice {
    fn decode<D: Decoder>(d: &mut D) -> Result<ConnectDevice, D::Error> {
        d.read_struct("ConnectDevice", 6, |d| {
            let id = try!(d.read_struct_field("id", 0, |d| { d.read_str() }));
            let is_active = try!(d.read_struct_field("is_active", 1, |d| { d.read_bool() }));
            let is_restricted = try!(d.read_struct_field("is_restricted", 2, |d| { d.read_bool() }));
            let name = try!(d.read_struct_field("name", 3, |d| { d.read_str() }));
            let device_type = try!(d.read_struct_field("type", 4, |d| { d.read_str() }));
            let volume_percent = try!(d.read_struct_field("volume_percent", 5, |d| { d.read_u32() }));
            Ok(ConnectDevice{ id: id,
                              is_active: is_active,
                              is_restricted: is_restricted,
                              name: name,
                              device_type: device_type,
                              volume_percent: volume_percent})
        })
    }
}

#[derive(RustcDecodable, RustcEncodable)]
struct ConnectDeviceList {
    devices: Vec<ConnectDevice>
}

struct Settings {
    port: u32,
    secret: String,
    client_id: String,
}

fn read_settings() -> Settings {
    let conf = Ini::load_from_file("connectr.ini").unwrap();

    let section = conf.section(Some("connectr".to_owned())).unwrap();
    let port = section.get("port").unwrap().parse().unwrap();

    let section = conf.section(Some("application".to_owned())).unwrap();
    let secret = section.get("secret").unwrap();
    let client_id = section.get("client_id").unwrap();
    Settings { secret: secret.to_string(), client_id: client_id.to_string(), port: port }
}

fn main() {
    let settings = read_settings();
    let auth_code = authenticate(&settings);
    let query = format!("grant_type=authorization_code&code={}&redirect_uri=http://127.0.0.1:{}&client_id={}&client_secret={}",
                        auth_code, settings.port, settings.client_id, settings.secret);
    let query = percent_encoding::utf8_percent_encode(&query, percent_encoding::QUERY_ENCODE_SET).collect::<String>();
    let json_response = http(spotify_api::TOKEN, &query, HttpMethod::POST, None);
    let (access_token, refresh_token) = parse_spotify_token(&json_response);

    let json_response = http(spotify_api::DEVICES, "", HttpMethod::GET, Some(&access_token));

    let decoded: ConnectDeviceList = json::decode(&json_response).unwrap();

    println!("Auth Code: {}...", &auth_code[0..5]);
    println!("Access: {}... / Refresh: {}...", &access_token[0..5], &refresh_token[0..5]);
    for dev in decoded.devices {
        println!("{:?}", dev);
    }
}
<commit_msg>Validate client_id/secret.<commit_after>mod spotify_api {
    pub const SCOPES: &'static [&'static str] = &["user-read-private", "streaming", "user-read-playback-state"];
    pub const AUTHORIZE: &'static str = "https://accounts.spotify.com/en/authorize";
    pub const TOKEN: &'static str = "https://accounts.spotify.com/api/token";
    pub const DEVICES: &'static str = "https://api.spotify.com/v1/me/player/devices";
}

#[derive(PartialEq)]
enum HttpMethod {
    GET,
    POST,
}

extern crate curl;
extern crate open;
extern crate regex;
extern crate rustc_serialize;
extern crate url;
extern crate ini;

use std::net::{TcpListener};
use std::io::{Read, Write, BufReader, BufRead};

use std::process;
use curl::easy::{Easy, List};
use regex::Regex;
use rustc_serialize::json::Json;
use rustc_serialize::json;
use rustc_serialize::Decodable;
use rustc_serialize::Decoder;
use url::percent_encoding;
use ini::Ini;

fn oauth_request_with_local_webserver(port: u32, url: &str, reply: &str) -> Vec<String> {
    if !open::that(url).is_ok() {
        return Vec::<String>::new()
    }
    let host = format!("127.0.0.1:{}", port);
    let listener = TcpListener::bind(host).unwrap();
    let stream = listener.accept().unwrap().0;
    let mut reader = BufReader::new(stream);
    let mut response = Vec::<String>::new();
    for line in reader.by_ref().lines() {
        let line_str = line.unwrap();
        response.push(line_str.clone());
        if line_str == "" {
            break;
        }
    }
    let _ = reader.into_inner().write(reply.as_bytes());
    response
}

fn spotify_auth_code(lines: Vec<String>) -> String {
    let mut auth_code = String::new();
    for line in lines {
        let line_str = line;
        let re = Regex::new(r"code=([^?& ]+)").unwrap();
        let ismatch = re.is_match(line_str.as_str());
        if ismatch {
            let cap = re.captures(line_str.as_str()).unwrap();
            auth_code = auth_code + &cap[1];
        }
    }
    auth_code
}

macro_rules! vec_of_strings {
    ($($x:expr),*) => (vec![$($x.to_string()),*]);
}

fn authenticate(settings: &Settings) -> String {
    let scopes = spotify_api::SCOPES.join(" ");
    let host = format!("http://127.0.0.1:{}", settings.port);
    let url = format!("{}?client_id={}&response_type=code&scope={}&redirect_uri={}",
                      spotify_api::AUTHORIZE,settings.client_id, scopes, host);
    let query = percent_encoding::utf8_percent_encode(&url, percent_encoding::QUERY_ENCODE_SET).collect::<String>();
    let response = "HTTP/1.1 200 OK\r\n\r\n
Authenticated with Spotify.

You can close this window.

<button type=\"button\" onclick=\"window.open('', '_self', ''); window.close();\">Close</button>

";
    let auth_lines = oauth_request_with_local_webserver(settings.port, &query, response);
    let auth_code = spotify_auth_code(auth_lines);
    auth_code
}

fn http(url: &str, query: &str, method: HttpMethod, access_token: Option<&str>) -> String {
    let mut data = query.as_bytes();
    let mut json_bytes = Vec::<u8>::new();
    {
        let mut easy = Easy::new();
        match method {
            HttpMethod::POST => {
                easy.url(url).unwrap();
                easy.post(true).unwrap();
                easy.post_field_size(data.len() as u64).unwrap();
            }
            _ => {
                let get_url = format!("{}?{}", url, query);
                easy.url(&get_url).unwrap();
            }
        }

        match access_token {
            Some(access_token) => {
                let mut list = List::new();
                let header = format!("Authorization: Bearer {}", access_token);
                list.append(&header).unwrap();
                easy.http_headers(list).unwrap();
            }
            None => {}
        }

        {
            let mut transfer = easy.transfer();
            if method == HttpMethod::POST {
                transfer.read_function(|buf| {
                    Ok(data.read(buf).unwrap_or(0))
                }).unwrap();
            }
            transfer.write_function(|x| {
                json_bytes.extend(x);
                Ok(x.len())
            }).unwrap();
            transfer.perform().unwrap();
        }
    }
    String::from_utf8(json_bytes).unwrap()
}

fn parse_spotify_token(json: &str) -> (String, String) {
    let json_data = Json::from_str(&json).unwrap();
    let obj = json_data.as_object().unwrap();
    let access_token = obj.get("access_token").unwrap().as_string().unwrap();
    let refresh_token = obj.get("refresh_token").unwrap().as_string().unwrap();
    (String::from(access_token),String::from(refresh_token))
}

//#[derive(RustcDecodable, RustcEncodable, Debug)]
#[derive(RustcEncodable, Debug)]
struct ConnectDevice {
    id: String,
    is_active: bool,
    is_restricted: bool,
    name: String,
    device_type: String,
    volume_percent: u32
}

impl Decodable for ConnectDevice {
    fn decode<D: Decoder>(d: &mut D) -> Result<ConnectDevice, D::Error> {
        d.read_struct("ConnectDevice", 6, |d| {
            let id = try!(d.read_struct_field("id", 0, |d| { d.read_str() }));
            let is_active = try!(d.read_struct_field("is_active", 1, |d| { d.read_bool() }));
            let is_restricted = try!(d.read_struct_field("is_restricted", 2, |d| { d.read_bool() }));
            let name = try!(d.read_struct_field("name", 3, |d| { d.read_str() }));
            let device_type = try!(d.read_struct_field("type", 4, |d| { d.read_str() }));
            let volume_percent = try!(d.read_struct_field("volume_percent", 5, |d| { d.read_u32() }));
            Ok(ConnectDevice{ id: id,
                              is_active: is_active,
                              is_restricted: is_restricted,
                              name: name,
                              device_type: device_type,
                              volume_percent: volume_percent})
        })
    }
}

#[derive(RustcDecodable, RustcEncodable)]
struct ConnectDeviceList {
    devices: Vec<ConnectDevice>
}

struct Settings {
    port: u32,
    secret: String,
    client_id: String,
}

fn read_settings() -> Option<Settings> {
    let conf = Ini::load_from_file("connectr.ini").unwrap();

    let section = conf.section(Some("connectr".to_owned())).unwrap();
    let port = section.get("port").unwrap().parse().unwrap();

    let section = conf.section(Some("application".to_owned())).unwrap();
    let secret = section.get("secret").unwrap();
    let client_id = section.get("client_id").unwrap();
    if client_id.starts_with('<') || secret.starts_with('<') {
        println!("");
        println!("ERROR: Spotify Client ID or Secret not set in connectr.ini!");
        println!("");
        println!("Create a Spotify application at https://developer.spotify.com/my-applications/ and");
        println!("add the client ID and secret to connectr.ini.");
        println!("");
        println!("Be sure to add a redirect URI of http://127.0.0.1:<PORT> to your Spotify application,");
        println!("and make sure the port matches in connectr.ini.");
        println!("");
        return None;
    }
    Some(Settings { secret: secret.to_string(), client_id: client_id.to_string(), port: port })
}

fn main() {
    let settings = match read_settings() {
        Some(s) => s,
        None => process::exit(0),
    };
    let auth_code = authenticate(&settings);
    let query = format!("grant_type=authorization_code&code={}&redirect_uri=http://127.0.0.1:{}&client_id={}&client_secret={}",
                        auth_code, settings.port, settings.client_id, settings.secret);
    let query = percent_encoding::utf8_percent_encode(&query, percent_encoding::QUERY_ENCODE_SET).collect::<String>();
    let json_response = http(spotify_api::TOKEN, &query, HttpMethod::POST, None);
    let (access_token, refresh_token) = parse_spotify_token(&json_response);

    let json_response = http(spotify_api::DEVICES, "", HttpMethod::GET, Some(&access_token));

    let decoded: ConnectDeviceList = json::decode(&json_response).unwrap();

    println!("Auth Code: {}...", &auth_code[0..5]);
    println!("Access: {}... / Refresh: {}...", &access_token[0..5], &refresh_token[0..5]);
    for dev in decoded.devices {
        println!("{:?}", dev);
    }
}
<|endoftext|>


<commit_before>extern crate unicode_names;

use std::env;

fn main() {
    for argument in env::args().skip(1) {
        match unicode_names::character(argument.as_ref()) {
            Some(c) =>  println!("{} = {}", c, argument),
            None => println!("Not found"),
        }
    }
}
<commit_msg>Use descriptions or the chars themselves<commit_after>extern crate unicode_names;

use std::env;
use std::fmt;

struct Describable {
    c: char,
}

impl fmt::Display for Describable {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match unicode_names::name(self.c) {
            Some(n) =>  write!(f, "{} = {}", self.c, n),
            None => write!(f, "{} is unknown", self.c)
        }
    }
}

/// Takes a stringly description of a character (the character itself,
/// or a unicode code point name) and returns either None (if the
/// character description is not understood), or Some(Describable)
/// that holds the character.
fn from_arg(spec: &str) -> Option<Describable> {
    if spec.chars().count() == 1 {
        spec.chars().next().map(|c| Describable{c: c})
    } else {
        unicode_names::character(spec).map(|c| Describable{c: c})
    }
    // TODO: \u, 0x, 0-prefixed chars, decimals too
    // TODO: Return multiple chars if there's ambiguity.
}

fn main() {
    let args =
        env::args().skip(1)
        .flat_map(|argument| from_arg(argument.as_ref()));
    for c in args {
        println!("{}", c);
    }
}

#[test]
fn from_arg_translates_chars() {
    assert_eq!('c', from_arg("c").unwrap().c);
    assert_eq!(']', from_arg("]").unwrap().c);
}

#[test]
fn from_arg_translates_descriptions() {
    assert_eq!('c', from_arg("latin small letter c").unwrap().c);
    assert_eq!(']', from_arg("right square bracket").unwrap().c);
}
<|endoftext|>


<commit_before>extern crate alsa;
extern crate cairo_sys;
extern crate cairo;
extern crate chrono;
#[macro_use]
extern crate error_chain;
extern crate futures;
#[macro_use]
extern crate lazy_static;
extern crate mio;
extern crate pango;
extern crate pangocairo;
extern crate regex;
extern crate tokio_core;
extern crate tokio_timer;
extern crate xcb_util;
extern crate xcb;

use std::rc::Rc;

use tokio_core::reactor::Core;
use tokio_timer::Timer;

mod errors;
use errors::*;
mod text;
use text::*;
mod widgets;
use widgets::*;
mod bar;
use bar::*;

fn run() -> Result<()> {
    let mut core = Core::new().chain_err(|| "Could not create Tokio Core")?;
    let handle = core.handle();

    let inactive_attr = Attributes {
        font: pango::FontDescription::from_string("SourceCodePro 21"),
        fg_color: Color::white(),
        bg_color: None,
        padding: Padding::new(8.0, 8.0, 0.0, 0.0),
    };
    let active_attr = inactive_attr.with_bg_color(Some(Color::blue()));

    let sensors = vec!["Core 0", "Core 1"];
    let timer = Rc::new(Timer::default());
    let widgets: Vec<Box<Widget>> =
        vec![
            Box::new(Pager::new(
                handle.clone(),
                active_attr,
                inactive_attr.clone(),
            )) as Box<Widget>,
            Box::new(ActiveWindowTitle::new(
                handle.clone(),
                inactive_attr.clone(),
            )) as Box<Widget>,
            Box::new(Sensors::new(timer.clone(), inactive_attr.clone(), sensors)) as Box<Widget>,
            Box::new(Volume::new(handle.clone(), inactive_attr.clone())) as Box<Widget>,
            Box::new(Battery::new(timer.clone(), inactive_attr.clone())) as Box<Widget>,
            Box::new(Clock::new(timer.clone(), inactive_attr.clone())) as Box<Widget>,
        ];

    let bar = Bar::new(Position::Bottom)?;
    core.run(bar.run_event_loop(&handle, widgets)?)?;

    Ok(())
}

quick_main!(run);
<commit_msg>Refactor main(), stepping stone towards #22.<commit_after>extern crate alsa;
extern crate cairo_sys;
extern crate cairo;
extern crate chrono;
#[macro_use]
extern crate error_chain;
extern crate futures;
#[macro_use]
extern crate lazy_static;
extern crate mio;
extern crate pango;
extern crate pangocairo;
extern crate regex;
extern crate tokio_core;
extern crate tokio_timer;
extern crate xcb_util;
extern crate xcb;

use std::rc::Rc;

use tokio_core::reactor::{Core, Handle};
use tokio_timer::Timer;

mod errors;
use errors::*;
mod text;
use text::*;
mod widgets;
use widgets::*;
mod bar;
use bar::*;

struct Hue {
    core: Core,
    timer: Rc<Timer>,
    bar: Bar,
    widgets: Vec<Box<Widget>>,
}

impl Hue {
    pub fn new(position: Position) -> Result<Hue> {
        Ok(Hue {
            core: Core::new().chain_err(|| "Could not create Tokio Core")?,
            timer: Rc::default(),
            bar: Bar::new(position)?,
            widgets: Vec::new(),
        })
    }

    pub fn handle(&self) -> Handle {
        self.core.handle()
    }

    pub fn timer(&self) -> Rc<Timer> {
        self.timer.clone()
    }

    pub fn add_widget<W>(&mut self, widget: W) where W: Widget + 'static {
        self.widgets.push(Box::new(widget) as Box<Widget>);
    }

    pub fn run(mut self) -> Result<()> {
        let handle = self.handle();
        self.core.run(self.bar.run_event_loop(&handle, self.widgets)?)
    }
}

fn run() -> Result<()> {
    let attr = Attributes {
        font: pango::FontDescription::from_string("SourceCodePro 21"),
        fg_color: Color::white(),
        bg_color: None,
        padding: Padding::new(8.0, 8.0, 0.0, 0.0),
    };
    let active_attr = attr.with_bg_color(Some(Color::blue()));

    let mut hue = Hue::new(Position::Bottom)?;

    // Yucky macro to get around the fact that hue.add_widget(... hue.handle())
    // complains about the second immutable borrow. We should be able to get
    // rid of this if we implement a clever builder pattern for widgets which
    // Hue can use.
    macro_rules! add_widget {
        ($hue:ident, $widget:expr) => {
            let widget = $widget;
            $hue.add_widget(widget);
        }
    }
    add_widget!(hue, Pager::new(hue.handle(), active_attr, attr.clone()));
    add_widget!(hue, ActiveWindowTitle::new(hue.handle(), attr.clone()));
    add_widget!(hue, Sensors::new(hue.timer(), attr.clone(), vec!["Core 0", "Core 1"]));
    add_widget!(hue, Volume::new(hue.handle(), attr.clone()));
    add_widget!(hue, Battery::new(hue.timer(), attr.clone()));
    add_widget!(hue, Clock::new(hue.timer(), attr.clone()));

    hue.run()
}

quick_main!(run);
<|endoftext|>


<commit_before>use super::ast::{Span, SourceMap};

use std::io::prelude::*;
use term::{Terminal, color, Result as TResult};

pub trait ErrorContext<O: Write> {
    // I don't particularly like these names - JR
    fn get_source_map(&self) -> &SourceMap;
    fn get_terminal(&mut self) -> &mut Box<Terminal<Output=O> + Send>;

    /// Reports a message at a given location. Underlines the Span.
    fn span_error(&mut self,
                  span: Span,
                  message: String) -> TResult<()> {
        // TODO: We need to know if we wrap around to more then one line.
        let (line_no, col_no) = self.get_source_map().position(span).unwrap_or((0,0));
        let (line_with_padding, marker) = self.get_source_map().underline_span(span).unwrap_or((format!("??"),format!("??")));

        let filename_str = format!("{}:{}:{}: {}:{} ",
                               self.get_source_map().file_name,
                               line_no,
                               col_no,
                               line_no, // this should be the line where we end, not the same line
                               col_no + (span.hi - span.lo)); // this should be the column we end at

        try!(write!(self.get_terminal(), "{}", filename_str));

        try!(self.get_terminal().fg(color::RED));
        try!(write!(self.get_terminal(), "error: "));
        try!(self.get_terminal().reset());
        try!(writeln!(self.get_terminal(), "{}", message));

        let file_str_simple =
            format!("{}:{}: ",
            self.get_source_map().file_name,
            line_no);

        try!(write!(self.get_terminal(), "{} {}", file_str_simple, line_with_padding));

        let mut marker_padding = "".to_string();

        for _ in 0..file_str_simple.len() {
            marker_padding.push(' ');
        }

        try!(write!(self.get_terminal(), "{}", marker_padding));
        try!(self.get_terminal().fg(color::RED));
        try!(writeln!(self.get_terminal(), "{}", marker));
        try!(self.get_terminal().reset());
        try!(self.get_terminal().flush());

        Ok(())
    }
}

pub trait Report<O: Write, E: ErrorContext<O>> {
    fn report(self, cx: &mut E) -> TResult<()>;
}
<commit_msg>Clean up code formatting<commit_after>use super::ast::{Span, SourceMap};

use std::io::prelude::*;
use term::{Terminal, color, Result as TResult};

pub trait ErrorContext<O: Write> {
    // I don't particularly like these names - JR
    fn get_source_map(&self) -> &SourceMap;
    fn get_terminal(&mut self) -> &mut Box<Terminal<Output=O> + Send>;

    /// Reports a message at a given location. Underlines the Span.
    fn span_error(&mut self,
                  span: Span,
                  message: String) -> TResult<()> {
        // TODO: We need to know if we wrap around to more then one line.
        let (line_no, col_no) = self.get_source_map()
                                    .position(span)
                                    .unwrap_or((0,0));
        let (line_with_padding, marker) = self.get_source_map()
                                              .underline_span(span)
                                              .unwrap_or((format!("??"),format!("??")));

        let filename_str = format!("{}:{}:{}: {}:{} ",
                               self.get_source_map().file_name,
                               line_no,
                               col_no,
                               line_no, // this should be the line where we end, not the same line
                               col_no + (span.hi - span.lo)); // this should be the column we end at

        try!(write!(self.get_terminal(), "{}", filename_str));

        try!(self.get_terminal().fg(color::RED));
        try!(write!(self.get_terminal(), "error: "));
        try!(self.get_terminal().reset());
        try!(writeln!(self.get_terminal(), "{}", message));

        let file_str_simple =
            format!("{}:{}: ",
            self.get_source_map().file_name,
            line_no);

        try!(write!(self.get_terminal(), "{} {}", file_str_simple, line_with_padding));

        let mut marker_padding = "".to_string();

        for _ in 0..file_str_simple.len() {
            marker_padding.push(' ');
        }

        try!(write!(self.get_terminal(), "{}", marker_padding));
        try!(self.get_terminal().fg(color::RED));
        try!(writeln!(self.get_terminal(), "{}", marker));
        try!(self.get_terminal().reset());
        try!(self.get_terminal().flush());

        Ok(())
    }
}

pub trait Report<O: Write, E: ErrorContext<O>> {
    fn report(self, cx: &mut E) -> TResult<()>;
}
<|endoftext|>


<commit_before>
use codespan::Span;

use nom::{
    character::{

    },
    bytes::complete::{
        tag,
    },
    IResult,
};

/// Numerical literal in wright source code.
/// i.e. `10`, `0xCa1a0`, `0b0101_0101`, `100_000`
#[derive(Copy, Clone, Debug)]
pub struct NumLit {
    span: Span,
    inner: u128,
}

impl NumLit {
    fn new(sp: Span, num: u128) -> Self {
        Self {span: sp, inner: num}
    }

    /// Get the span associated with this numerical literal.
    pub fn span(&self) -> Span {self.span}

    /// Get the value of this numerical literal.
    pub fn get(&self) -> u128 {self.inner}

    fn from_hex(input: &str) -> Result<u128, std::num::ParseIntError> {
        u128::from_str_radix(input, 16)
    }

    fn from_dec(input: &str) -> Result<u128, std::num::ParseIntError> {
        u128::from_str_radix(input, 10)
    }

    fn from_bin(input: &str) -> Result<u128, std::num::ParseIntError> {
        u128::from_str_radix(input, 2)
    }

    fn clear_underscores(input: &str) -> String {
        input.replace("_", "")
    }

    fn hex_primary(input: &str) -> IResult<&str, u128> {
        tag("0x")
    }

    /// Parse a numerical literal to a value.
    pub fn parse(input: &str) -> IResult<&str, u128> {
        unimplemented!()
    }
}<commit_msg>todo<commit_after>
use codespan::Span;

use nom::{
    character::{

    },
    bytes::complete::{
        tag,
    },
    IResult,
};

/// Numerical literal in wright source code.
/// i.e. `10`, `0xCa1a0`, `0b0101_0101`, `100_000`
#[derive(Copy, Clone, Debug)]
pub struct NumLit {
    span: Span,
    inner: u128,
}

impl NumLit {
    fn new(sp: Span, num: u128) -> Self {
        Self {span: sp, inner: num}
    }

    /// Get the span associated with this numerical literal.
    pub fn span(&self) -> Span {self.span}

    /// Get the value of this numerical literal.
    pub fn get(&self) -> u128 {self.inner}

    fn from_hex(input: &str) -> Result<u128, std::num::ParseIntError> {
        u128::from_str_radix(input, 16)
    }

    fn from_dec(input: &str) -> Result<u128, std::num::ParseIntError> {
        u128::from_str_radix(input, 10)
    }

    fn from_bin(input: &str) -> Result<u128, std::num::ParseIntError> {
        u128::from_str_radix(input, 2)
    }

    fn clear_underscores(input: &str) -> String {
        input.replace("_", "")
    }

    fn hex_primary(input: &str) -> IResult<&str, u128> {
        unimplemented!()
    }

    /// Parse a numerical literal to a value.
    pub fn parse(input: &str) -> IResult<&str, u128> {
        unimplemented!()
    }
}<|endoftext|>