<commit_before>#[macro_use]
extern crate clap;
extern crate rand;
extern crate ultra;

use std::ascii::AsciiExt;

use rand::Rng;
use ultra::{Enigma, decrypt};

trait CasedString {
    fn with_case_of(&self, target: &str) -> String;
}

impl CasedString for String {
    fn with_case_of(&self, target: &str) -> String {
        self.chars().zip(target.chars()).map(|(s, t)| {
            match t.is_lowercase() {
                true => s.to_ascii_lowercase(),
                false => s
            }
        }).collect()
    }
}

fn main() {
    let app = clap_app!(ultra =>
        (version: crate_version!())
        (@setting ArgRequiredElseHelp)
        (@setting ColoredHelp)
        (@arg decrypt: --decrypt -d "Decrypt a given piece of ciphertext")
        (@arg randomize: --randomize -R "Encrypt a message with random Enigma settings")
        (@arg ROTORS: --rotor -w +takes_value "Rotor order (default: \"123\")")
        (@arg KEY: --key -k +takes_value "Key settings (default: \"AAA\")")
        (@arg RING: --ring -r +takes_value "Ring settings (default: \"AAA\")")
        (@arg PLUGBOARD: --plugboard -p +takes_value "Plugboard settings (default: \"\")")
        (@arg MESSAGE: +required "The message to encrypt/decrypt")
    );

    let matches = app.get_matches();
    let msg = matches.value_of("MESSAGE").unwrap();

    if matches.is_present("decrypt") {
        let (plaintext, rotors, key, ring) = decrypt(msg);
        println!("{}", plaintext.with_case_of(msg));
        println!("(Rotors: {}, Key Setting: {}, Ring Setting: {})", rotors, key, ring);
    }

    else if matches.is_present("randomize") {
        let mut rng = rand::thread_rng();

        let rotors: String = {
            let mut rotor_pool: Vec<char> = "12345".chars().collect();
            let mut rotors = Vec::with_capacity(3);

            for _ in 0..3 {
                let len = rotor_pool.len();
                rotors.push(rotor_pool.remove(rng.gen_range(0, len)));
            }

            rotors.into_iter().collect()
        };

        let mut key = String::with_capacity(3);
        let mut ring = String::with_capacity(3);
        let alpha: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars().collect();

        for _ in 0..3 {
            key.push(*rng.choose(&alpha).unwrap());
            ring.push(*rng.choose(&alpha).unwrap());
        }

        let mut enigma = Enigma::new(&rotors, &key, &ring, 'B', "");
        println!("{}", enigma.encrypt(msg).with_case_of(msg));
        println!("(Rotors: {}, Key Setting: {}, Ring Setting: {})", &rotors, &key, &ring);
    }

    else {
        let rotors = matches.value_of("ROTORS").unwrap_or("123");
        let key = matches.value_of("KEY").unwrap_or("AAA");
        let ring = matches.value_of("RING").unwrap_or("AAA");
        let plugboard = matches.value_of("PLUGBOARD").unwrap_or("");

        let mut enigma = Enigma::new(rotors, key, ring, 'B', plugboard);
        println!("{}", enigma.encrypt(msg).with_case_of(msg));
    }
}
<commit_msg>Print Enigma configuration to stderr<commit_after>#[macro_use]
extern crate clap;
extern crate rand;
extern crate ultra;

use std::ascii::AsciiExt;
use std::io::{Write, stderr};

use rand::Rng;
use ultra::{Enigma, decrypt};

macro_rules! eprintln {
    ($($tt:tt)*) => {
        let _ = writeln!(&mut stderr(), $($tt)*);
    }
}

trait CasedString {
    fn with_case_of(&self, target: &str) -> String;
}

impl CasedString for String {
    fn with_case_of(&self, target: &str) -> String {
        self.chars().zip(target.chars()).map(|(s, t)| {
            match t.is_lowercase() {
                true => s.to_ascii_lowercase(),
                false => s
            }
        }).collect()
    }
}

fn main() {
    let app = clap_app!(ultra =>
        (version: crate_version!())
        (@setting ArgRequiredElseHelp)
        (@setting ColoredHelp)
        (@arg decrypt: --decrypt -d "Decrypt a given piece of ciphertext")
        (@arg randomize: --randomize -R "Encrypt a message with random Enigma settings")
        (@arg ROTORS: --rotor -w +takes_value "Rotor order (default: \"123\")")
        (@arg KEY: --key -k +takes_value "Key settings (default: \"AAA\")")
        (@arg RING: --ring -r +takes_value "Ring settings (default: \"AAA\")")
        (@arg PLUGBOARD: --plugboard -p +takes_value "Plugboard settings (default: \"\")")
        (@arg MESSAGE: +required "The message to encrypt/decrypt")
    );

    let matches = app.get_matches();
    let msg = matches.value_of("MESSAGE").unwrap();

    if matches.is_present("decrypt") {
        let (plaintext, rotors, key, ring) = decrypt(msg);
        println!("{}", plaintext.with_case_of(msg));
        eprintln!("(Rotors: {}, Key Setting: {}, Ring Setting: {})", rotors, key, ring);
    }

    else if matches.is_present("randomize") {
        let mut rng = rand::thread_rng();

        let rotors: String = {
            let mut rotor_pool: Vec<char> = "12345".chars().collect();
            let mut rotors = Vec::with_capacity(3);

            for _ in 0..3 {
                let len = rotor_pool.len();
                rotors.push(rotor_pool.remove(rng.gen_range(0, len)));
            }

            rotors.into_iter().collect()
        };

        let mut key = String::with_capacity(3);
        let mut ring = String::with_capacity(3);
        let alpha: Vec<char> = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars().collect();

        for _ in 0..3 {
            key.push(*rng.choose(&alpha).unwrap());
            ring.push(*rng.choose(&alpha).unwrap());
        }

        let mut enigma = Enigma::new(&rotors, &key, &ring, 'B', "");
        println!("{}", enigma.encrypt(msg).with_case_of(msg));
        eprintln!("(Rotors: {}, Key Setting: {}, Ring Setting: {})", rotors, key, ring);
    }

    else {
        let rotors = matches.value_of("ROTORS").unwrap_or("123");
        let key = matches.value_of("KEY").unwrap_or("AAA");
        let ring = matches.value_of("RING").unwrap_or("AAA");
        let plugboard = matches.value_of("PLUGBOARD").unwrap_or("");

        let mut enigma = Enigma::new(rotors, key, ring, 'B', plugboard);
        println!("{}", enigma.encrypt(msg).with_case_of(msg));
    }
}
<|endoftext|>


<commit_before>extern crate docopt;
extern crate serde_json;
extern crate vegas_lattice;

use std::error::Error;
use std::fs::File;
use std::io::{stdin, Read};
use std::path::Path;

use docopt::{ArgvMap, Docopt};
use vegas_lattice::{io, Alloy, Axis, Lattice, Mask};

const USAGE: &str = "
Vegas lattice.

Usage:
    vegas-lattice check [<input>]
    vegas-lattice pretty [<input>]
    vegas-lattice drop [-x -y -z] [<input>]
    vegas-lattice expand [--x=<x> --y=<y> --z=<z>] [<input>]
    vegas-lattice alloy <source> (<target> <ratio>)... [<input>]
    vegas-lattice mask [--ppu=<ppu>] <mask> [<input>]
    vegas-lattice into (xyz|tsv) [<input>]
    vegas-lattice (-h | --help)
    vegas-lattice --version

Options:
    -p --ppu=<ppu>  Pixels per unit [default: 10].
    -h --help       Show this message.
    --version       Show version and exit.
";

fn read(input: &str) -> Result<Lattice, Box<Error>> {
    let mut data = String::new();
    if !input.is_empty() {
        let mut file = File::open(input)?;
        file.read_to_string(&mut data)?;
    } else {
        stdin().read_to_string(&mut data)?;
    };
    let lattice: Lattice = data.parse()?;
    Ok(lattice)
}

fn write(lattice: Lattice) {
    println!("{}", serde_json::to_string(&lattice).unwrap());
}

fn write_pretty(lattice: Lattice) {
    println!("{}", io::to_string_lattice(&lattice).unwrap());
}

fn check_error(res: Result<(), Box<Error>>) {
    if let Err(e) = res {
        eprintln!("Error: {}", e.description());
        if let Some(cause) = e.cause() {
            eprintln!("Cause: {}", cause);
        }
    }
}

// Commands over here

fn check(args: ArgvMap) -> Result<(), Box<Error>> {
    let lattice = read(args.get_str("<input>"))?;
    write(lattice);
    Ok(())
}

fn pretty(args: ArgvMap) -> Result<(), Box<Error>> {
    let lattice = read(args.get_str("<input>"))?;
    write_pretty(lattice);
    Ok(())
}

fn drop(args: ArgvMap) -> Result<(), Box<Error>> {
    let mut lattice = read(args.get_str("<input>"))?;
    for (key, axis) in Axis::map(Some("-".to_string())) {
        if args.get_bool(&key) {
            lattice = lattice.drop(axis);
        }
    }
    write(lattice);
    Ok(())
}

fn expand(args: ArgvMap) -> Result<(), Box<Error>> {
    let map = Axis::map(Some("--".to_string()));
    let mut lattice = read(args.get_str("<input>"))?;
    for (flag, axis) in map.into_iter() {
        let string_value = args.get_str(&flag);
        if !string_value.is_empty() {
            let size: usize = string_value.parse()?;
            lattice = lattice.expand_along(axis, size);
        }
    }
    write(lattice);
    Ok(())
}

fn alloy(args: ArgvMap) -> Result<(), Box<Error>> {
    let source = args.get_str("<source>");
    let kinds = args.get_vec("<target>");
    let ratios = args
        .get_vec("<ratio>")
        .into_iter()
        .map(|r| r.parse())
        .collect::<Result<Vec<u32>, _>>()?;
    let alloy = Alloy::new(kinds, ratios);
    let mut lattice = read(args.get_str("<input>"))?;
    lattice = lattice.alloy_sites(source, alloy);
    write(lattice);
    Ok(())
}

fn mask(args: ArgvMap) -> Result<(), Box<Error>> {
    let path = args.get_str("<mask>");
    let ppu: f64 = args.get_str("--ppu").parse()?;
    let mask = Mask::new(&Path::new(path), ppu)?;
    let mut lattice = read(args.get_str("<input>"))?;
    lattice = lattice.apply_mask(mask);
    write(lattice);
    Ok(())
}

fn into(args: ArgvMap) -> Result<(), Box<Error>> {
    let lattice = read(args.get_str("<input>"))?;
    if args.get_bool("tsv") {
        for site in lattice.sites().iter() {
            let (x, y, z) = site.position();
            println!("{}\t{}\t{}\t{}", x, y, z, site.kind())
        }
    } else if args.get_bool("xyz") {
        println!("{}\n", lattice.sites().len());
        for site in lattice.sites().iter() {
            let (x, y, z) = site.position();
            println!("{} {} {} {}", site.kind(), x, y, z)
        }
    }
    Ok(())
}

fn main() {
    let args = Docopt::new(USAGE)
        .and_then(|doc| doc.version(Some("0.0.1".to_string())).parse())
        .unwrap_or_else(|e| e.exit());

    if args.get_bool("check") {
        check_error(check(args));
    } else if args.get_bool("pretty") {
        check_error(pretty(args));
    } else if args.get_bool("drop") {
        check_error(drop(args));
    } else if args.get_bool("alloy") {
        check_error(alloy(args));
    } else if args.get_bool("expand") {
        check_error(expand(args));
    } else if args.get_bool("mask") {
        check_error(mask(args));
    } else if args.get_bool("into") {
        check_error(into(args));
    } else {
        println!("{:?}", args);
    }
}
<commit_msg>Move that to source api<commit_after>extern crate docopt;
extern crate serde_json;
extern crate vegas_lattice;

use std::error::Error;
use std::fs::File;
use std::io::{stdin, Read};
use std::path::Path;

use docopt::{ArgvMap, Docopt};
use vegas_lattice::{io, Alloy, Axis, Lattice, Mask};

const USAGE: &str = "
Vegas lattice.

Usage:
    vegas-lattice check [<input>]
    vegas-lattice pretty [<input>]
    vegas-lattice drop [-x -y -z] [<input>]
    vegas-lattice expand [--x=<x> --y=<y> --z=<z>] [<input>]
    vegas-lattice alloy <source> (<target> <ratio>)... [<input>]
    vegas-lattice mask [--ppu=<ppu>] <mask> [<input>]
    vegas-lattice into (xyz|tsv) [<input>]
    vegas-lattice (-h | --help)
    vegas-lattice --version

Options:
    -p --ppu=<ppu>  Pixels per unit [default: 10].
    -h --help       Show this message.
    --version       Show version and exit.
";

fn read(input: &str) -> Result<Lattice, Box<Error>> {
    let mut data = String::new();
    if !input.is_empty() {
        let mut file = File::open(input)?;
        file.read_to_string(&mut data)?;
    } else {
        stdin().read_to_string(&mut data)?;
    };
    let lattice: Lattice = data.parse()?;
    Ok(lattice)
}

fn write(lattice: Lattice) {
    println!("{}", serde_json::to_string(&lattice).unwrap());
}

fn write_pretty(lattice: Lattice) {
    println!("{}", io::to_string_lattice(&lattice).unwrap());
}

fn check_error(res: Result<(), Box<Error>>) {
    if let Err(e) = res {
        eprintln!("Error: {}", e.description());
        if let Some(source) = e.source() {
            eprintln!("Cause: {}", source);
        }
    }
}

// Commands over here

fn check(args: ArgvMap) -> Result<(), Box<Error>> {
    let lattice = read(args.get_str("<input>"))?;
    write(lattice);
    Ok(())
}

fn pretty(args: ArgvMap) -> Result<(), Box<Error>> {
    let lattice = read(args.get_str("<input>"))?;
    write_pretty(lattice);
    Ok(())
}

fn drop(args: ArgvMap) -> Result<(), Box<Error>> {
    let mut lattice = read(args.get_str("<input>"))?;
    for (key, axis) in Axis::map(Some("-".to_string())) {
        if args.get_bool(&key) {
            lattice = lattice.drop(axis);
        }
    }
    write(lattice);
    Ok(())
}

fn expand(args: ArgvMap) -> Result<(), Box<Error>> {
    let map = Axis::map(Some("--".to_string()));
    let mut lattice = read(args.get_str("<input>"))?;
    for (flag, axis) in map.into_iter() {
        let string_value = args.get_str(&flag);
        if !string_value.is_empty() {
            let size: usize = string_value.parse()?;
            lattice = lattice.expand_along(axis, size);
        }
    }
    write(lattice);
    Ok(())
}

fn alloy(args: ArgvMap) -> Result<(), Box<Error>> {
    let source = args.get_str("<source>");
    let kinds = args.get_vec("<target>");
    let ratios = args
        .get_vec("<ratio>")
        .into_iter()
        .map(|r| r.parse())
        .collect::<Result<Vec<u32>, _>>()?;
    let alloy = Alloy::new(kinds, ratios);
    let mut lattice = read(args.get_str("<input>"))?;
    lattice = lattice.alloy_sites(source, alloy);
    write(lattice);
    Ok(())
}

fn mask(args: ArgvMap) -> Result<(), Box<Error>> {
    let path = args.get_str("<mask>");
    let ppu: f64 = args.get_str("--ppu").parse()?;
    let mask = Mask::new(&Path::new(path), ppu)?;
    let mut lattice = read(args.get_str("<input>"))?;
    lattice = lattice.apply_mask(mask);
    write(lattice);
    Ok(())
}

fn into(args: ArgvMap) -> Result<(), Box<Error>> {
    let lattice = read(args.get_str("<input>"))?;
    if args.get_bool("tsv") {
        for site in lattice.sites().iter() {
            let (x, y, z) = site.position();
            println!("{}\t{}\t{}\t{}", x, y, z, site.kind())
        }
    } else if args.get_bool("xyz") {
        println!("{}\n", lattice.sites().len());
        for site in lattice.sites().iter() {
            let (x, y, z) = site.position();
            println!("{} {} {} {}", site.kind(), x, y, z)
        }
    }
    Ok(())
}

fn main() {
    let args = Docopt::new(USAGE)
        .and_then(|doc| doc.version(Some("0.0.1".to_string())).parse())
        .unwrap_or_else(|e| e.exit());

    if args.get_bool("check") {
        check_error(check(args));
    } else if args.get_bool("pretty") {
        check_error(pretty(args));
    } else if args.get_bool("drop") {
        check_error(drop(args));
    } else if args.get_bool("alloy") {
        check_error(alloy(args));
    } else if args.get_bool("expand") {
        check_error(expand(args));
    } else if args.get_bool("mask") {
        check_error(mask(args));
    } else if args.get_bool("into") {
        check_error(into(args));
    } else {
        println!("{:?}", args);
    }
}
<|endoftext|>


<commit_before>#![feature(collections)]
#![feature(convert)]
#![feature(core)]

extern crate toml;
extern crate iron;
extern crate router;
extern crate core;

use std::fs::File;
use std::io::Read;
use core::convert::From as ConvertFrom;

//use iron::Iron;
use iron::prelude::*;
use iron::headers::*;
use iron::{status, headers};

//use core::str::Str;

use router::{Router};

use liquidwrapper::LiquidTemplate;
use templatewrapper::TemplateWrapper;

mod templatewrapper;
mod liquidwrapper;

#[derive(Clone)]
struct SbWikiServer {
    listenaddr: String,
    wikipath: String,
    wikifrontpage: String,
}

impl SbWikiServer {
    //TODO: pass config object instead of bunch of argument
    pub fn new(cfgfile: &'static str) -> SbWikiServer {
        let listenaddr    = String::from("localhost:31337");
        let wikipath      = String::from("/wiki");
        let wikifrontpage = String::from("FrontPage");

        //loading toml configure file
        let mut fd = File::open(cfgfile).unwrap();
        let mut buf: Vec<u8> = Vec::new();
        fd.read_to_end(&mut buf);
        let data = String::from_utf8(buf).unwrap();
        let mut parser = toml::Parser::new(data.as_str());

        match parser.parse() {
            Some(value) => println!("Value from config file! : {:?}", value),
            None => println!("No data!"),
        } // temporal routine of parsing config file

        SbWikiServer {
            listenaddr: listenaddr,
            wikipath: wikipath,
            wikifrontpage: wikifrontpage,
        }
    }

    pub fn open(self, isTLS: bool) {
        let mut wikidocument = self.wikipath.clone();
        wikidocument.push_str("/*");

        let mut router = Router::new();
        //TODO: fetch it from toml config.
        let wpath = self.wikipath.clone();
        let listenaddr = self.listenaddr.clone();

        let cloned = self.clone();
        router.get(wpath.as_str(),
                   move |req: &mut Request| -> IronResult<Response>
                   {cloned.wikiredirect(req)});

        let cloned = self.clone();
        router.get(wikidocument.as_str(),
                   move |req: &mut Request| -> IronResult<Response>
                   {cloned.wikihandler(req)});

        let cloned = self.clone();
        router.get("/",
                   move |req: &mut Request| -> IronResult<Response>
                   {cloned.roothandler(req)});

        let cloned = self.clone();
        router.get("/:query",
                   move |req: &mut Request| -> IronResult<Response>
                   {cloned.roothandler(req)});

        Iron::new(router).http(
            listenaddr.as_str()).unwrap();
    }

    fn roothandler(&self, req: &mut Request)
                   -> IronResult<Response> {
        let ref query = req.extensions.get::<Router>()
            .unwrap().find("query").unwrap_or("/");

        Ok(Response::with((status::Ok, *query)))
    }

    fn wikiredirect(&self, req: &mut Request)
                    -> IronResult<Response> {
        let mut url = req.url.path.clone();
        let mut header = Headers::new();
        let mut newpath = String::new();

        //TODO: customizable root page.
        url.push(self.wikifrontpage.clone());

        for i in url.iter() {
            newpath.push_str("/");
            newpath.push_str(i);
        }

        println!("{}", newpath);
        header.set(Location(newpath));

        let mut resp: Response = Response::with((status::MovedPermanently));
        resp.headers = header;

        Ok(resp)
    }

    fn wikihandler(&self, req: &mut Request)
                   -> IronResult<Response> {
        let ref requrl = req.url.clone();
        let mut urlpath = requrl.path.clone();

        for i in urlpath.iter() {
            println!("{}", i);
        }

        Ok(Response::with((status::Ok, "Watch console.")))
    }
}

fn main() {
    //TODO: make it can fetch config file name from command line argument.
    let sbwiki: SbWikiServer = SbWikiServer::new("config.toml");

    sbwiki.open(false);

    let template: LiquidTemplate = LiquidTemplate::new(String::from("hello.liquid"));
}
<commit_msg>use read_to_string method<commit_after>#![feature(collections)]
#![feature(convert)]
#![feature(core)]

extern crate toml;
extern crate iron;
extern crate router;
extern crate core;

use std::fs::File;
use std::io::Read;
use core::convert::From as ConvertFrom;

//use iron::Iron;
use iron::prelude::*;
use iron::headers::*;
use iron::{status, headers};

//use core::str::Str;

use router::{Router};

use liquidwrapper::LiquidTemplate;
use templatewrapper::TemplateWrapper;

mod templatewrapper;
mod liquidwrapper;

#[derive(Clone)]
struct SbWikiServer {
    listenaddr: String,
    wikipath: String,
    wikifrontpage: String,
}

impl SbWikiServer {
    //TODO: pass config object instead of bunch of argument
    pub fn new(cfgfile: &'static str) -> SbWikiServer {
        let listenaddr    = String::from("localhost:31337");
        let wikipath      = String::from("/wiki");
        let wikifrontpage = String::from("FrontPage");

        //loading toml configure file
        let mut confs = String::new();
        File::open(cfgfile).unwrap().read_to_string(&mut confs);
        let mut parser = toml::Parser::new(confs.as_str());

        match parser.parse() {
            Some(value) => println!("Value from config file! : {:?}", value),
            None => println!("No data!"),
        } // temporal routine of parsing config file

        SbWikiServer {
            listenaddr: listenaddr,
            wikipath: wikipath,
            wikifrontpage: wikifrontpage,
        }
    }

    pub fn open(self, isTLS: bool) {
        let mut wikidocument = self.wikipath.clone();
        wikidocument.push_str("/*");

        let mut router = Router::new();
        //TODO: fetch it from toml config.
        let wpath = self.wikipath.clone();
        let listenaddr = self.listenaddr.clone();

        let cloned = self.clone();
        router.get(wpath.as_str(),
                   move |req: &mut Request| -> IronResult<Response>
                   {cloned.wikiredirect(req)});

        let cloned = self.clone();
        router.get(wikidocument.as_str(),
                   move |req: &mut Request| -> IronResult<Response>
                   {cloned.wikihandler(req)});

        let cloned = self.clone();
        router.get("/",
                   move |req: &mut Request| -> IronResult<Response>
                   {cloned.roothandler(req)});

        let cloned = self.clone();
        router.get("/:query",
                   move |req: &mut Request| -> IronResult<Response>
                   {cloned.roothandler(req)});

        Iron::new(router).http(
            listenaddr.as_str()).unwrap();
    }

    fn roothandler(&self, req: &mut Request)
                   -> IronResult<Response> {
        let ref query = req.extensions.get::<Router>()
            .unwrap().find("query").unwrap_or("/");

        Ok(Response::with((status::Ok, *query)))
    }

    fn wikiredirect(&self, req: &mut Request)
                    -> IronResult<Response> {
        let mut url = req.url.path.clone();
        let mut header = Headers::new();
        let mut newpath = String::new();

        //TODO: customizable root page.
        url.push(self.wikifrontpage.clone());

        for i in url.iter() {
            newpath.push_str("/");
            newpath.push_str(i);
        }

        println!("{}", newpath);
        header.set(Location(newpath));

        let mut resp: Response = Response::with((status::MovedPermanently));
        resp.headers = header;

        Ok(resp)
    }

    fn wikihandler(&self, req: &mut Request)
                   -> IronResult<Response> {
        let ref requrl = req.url.clone();
        let mut urlpath = requrl.path.clone();

        for i in urlpath.iter() {
            println!("{}", i);
        }

        Ok(Response::with((status::Ok, "Watch console.")))
    }
}

fn main() {
    //TODO: make it can fetch config file name from command line argument.
    let sbwiki: SbWikiServer = SbWikiServer::new("config.toml");

    sbwiki.open(false);

    let template: LiquidTemplate = LiquidTemplate::new(String::from("hello.liquid"));
}
<|endoftext|>


<commit_before>//! This module contains Pair enum.

/// Pair lists all pairs that can be traded on supported exchanges.
/// Update date : 11/04/2017.
/// Note : Kraken uses XBT instead of BTC (so the XBT/EUR pair becomes BTC/EUR).
/// Order of quote currency <-> base currency is important. For example, Kraken supports ZEC_BTC
/// but Poloniex is doing the opposite :  BTC_ZEC, which equal to 1/ZEC_BTC. So: ZEC_BTC != BTC_ZEC
#[derive(Debug, Copy, Clone, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Pair {
    BTC_AMP,
    BTC_ARDR,
    BTC_BBR,
    BTC_BCN,
    BTC_BCY,
    BTC_BELA,
    BTC_BITS,
    BTC_BLK,
    BTC_BTCD,
    BTC_BTM,
    BTC_BTS,
    BTC_BURST,
    BTC_C2,
    BTC_CAD_d,
    BTC_CAD,
    BTC_CLAM,
    BTC_CURE,
    BTC_DASH,
    BTC_DCR,
    BTC_DGB,
    BTC_DOGE,
    BTC_EMC2,
    BTC_ETC,
    BTC_ETH,
    BTC_EUR_d,
    BTC_EUR,
    BTC_EXP,
    BTC_FCT,
    BTC_FLDC,
    BTC_FLO,
    BTC_GAME,
    BTC_GBP_d,
    BTC_GBP,
    BTC_GNT,
    BTC_GRC,
    BTC_HUC,
    BTC_HZ,
    BTC_IOC,
    BTC_JPY_d,
    BTC_JPY,
    BTC_LBC,
    BTC_LSK,
    BTC_LTC,
    BTC_MAID,
    BTC_MYR,
    BTC_NAUT,
    BTC_NAV,
    BTC_NEOS,
    BTC_NMC,
    BTC_NOBL,
    BTC_NOTE,
    BTC_NSR,
    BTC_NXC,
    BTC_NXT,
    BTC_OMNI,
    BTC_PASC,
    BTC_PINK,
    BTC_POT,
    BTC_PPC,
    BTC_QBK,
    BTC_QORA,
    BTC_QTL,
    BTC_RADS,
    BTC_RBY,
    BTC_REP,
    BTC_RIC,
    BTC_SBD,
    BTC_SC,
    BTC_SDC,
    BTC_SJCX,
    BTC_STEEM,
    BTC_STR,
    BTC_STRAT,
    BTC_SYS,
    BTC_UNITY,
    BTC_USD_d,
    BTC_USD,
    BTC_VIA,
    BTC_VOX,
    BTC_VRC,
    BTC_VTC,
    BTC_XBC,
    BTC_XCP,
    BTC_XEM,
    BTC_XMG,
    BTC_XMR,
    BTC_XPM,
    BTC_XRP,
    BTC_XVC,
    BTC_ZEC,
    DASH_BTC,
    ETC_BTC,
    ETC_ETH,
    ETC_EUR,
    ETC_USD,
    ETH_BTC,
    ETH_CAD_d,
    ETH_CAD,
    ETH_ETC,
    ETH_EUR_d,
    ETH_EUR,
    ETH_GBP_d,
    ETH_GBP,
    ETH_GNT,
    ETH_JPY_d,
    ETH_JPY,
    ETH_LSK,
    ETH_REP,
    ETH_STEEM,
    ETH_USD_d,
    ETH_USD,
    ETH_XBT_d,
    ETH_ZEC,
    EUR_USD,
    ICN_BTC,
    ICN_ETH,
    LTC_BTC,
    LTC_EUR,
    LTC_USD,
    MLN_BTC,
    MLN_ETH,
    REP_BTC,
    REP_ETH,
    REP_EUR,
    REP_USD,
    USDT_BTC,
    USDT_DASH,
    USDT_ETC,
    USDT_ETH,
    USDT_LTC,
    USDT_NXT,
    USDT_REP,
    USDT_STR,
    USDT_USD,
    USDT_XMR,
    USDT_XRP,
    USDT_ZEC,
    XDG_BTC,
    XLM_BTC,
    XLM_EUR,
    XLM_USD,
    XMR_BBR,
    XMR_BCN,
    XMR_BLK,
    XMR_BTC,
    XMR_BTCD,
    XMR_DASH,
    XMR_EUR,
    XMR_LTC,
    XMR_MAID,
    XMR_NXT,
    XMR_QORA,
    XMR_USD,
    XMR_ZEC,
    XRP_BTC,
    XRP_EUR,
    XRP_USD,
    ZEC_BTC,
    ZEC_EUR,
    ZEC_USD,
}
<commit_msg>Update pair list (Poloniex added GNO currency and delisted some others).<commit_after>//! This module contains Pair enum.

/// Pair lists all pairs that can be traded on supported exchanges.
/// Update date : 11/04/2017.
/// Note : Kraken uses XBT instead of BTC (so the XBT/EUR pair becomes BTC/EUR).
/// Order of quote currency <-> base currency is important. For example, Kraken supports ZEC_BTC
/// but Poloniex is doing the opposite :  BTC_ZEC, which equal to 1/ZEC_BTC. So: ZEC_BTC != BTC_ZEC
#[derive(Debug, Copy, Clone, PartialEq)]
#[allow(non_camel_case_types)]
pub enum Pair {
    BTC_AMP,
    BTC_ARDR,
    BTC_BCN,
    BTC_BCY,
    BTC_BELA,
    BTC_BLK,
    BTC_BTCD,
    BTC_BTM,
    BTC_BTS,
    BTC_BURST,
    BTC_CAD_d,
    BTC_CAD,
    BTC_CLAM,
    BTC_DASH,
    BTC_DCR,
    BTC_DGB,
    BTC_DOGE,
    BTC_EMC2,
    BTC_ETC,
    BTC_ETH,
    BTC_EUR_d,
    BTC_EUR,
    BTC_EXP,
    BTC_FCT,
    BTC_FLDC,
    BTC_FLO,
    BTC_GAME,
    BTC_GBP_d,
    BTC_GBP,
    BTC_GNO,
    BTC_GNT,
    BTC_GRC,
    BTC_HUC,
    BTC_JPY_d,
    BTC_JPY,
    BTC_LBC,
    BTC_LSK,
    BTC_LTC,
    BTC_MAID,
    BTC_NAUT,
    BTC_NAV,
    BTC_NEOS,
    BTC_NMC,
    BTC_NOTE,
    BTC_NXC,
    BTC_NXT,
    BTC_OMNI,
    BTC_PASC,
    BTC_PINK,
    BTC_POT,
    BTC_PPC,
    BTC_RADS,
    BTC_REP,
    BTC_RIC,
    BTC_SBD,
    BTC_SC,
    BTC_SJCX,
    BTC_STEEM,
    BTC_STR,
    BTC_STRAT,
    BTC_SYS,
    BTC_USD_d,
    BTC_USD,
    BTC_VIA,
    BTC_VRC,
    BTC_VTC,
    BTC_XBC,
    BTC_XCP,
    BTC_XEM,
    BTC_XMR,
    BTC_XPM,
    BTC_XRP,
    BTC_XVC,
    BTC_ZEC,
    DASH_BTC,
    ETC_BTC,
    ETC_ETH,
    ETC_EUR,
    ETC_USD,
    ETH_BTC,
    ETH_CAD_d,
    ETH_CAD,
    ETH_ETC,
    ETH_EUR_d,
    ETH_EUR,
    ETH_GBP_d,
    ETH_GBP,
    ETH_GNT,
    ETH_GNO,
    ETH_JPY_d,
    ETH_JPY,
    ETH_LSK,
    ETH_REP,
    ETH_STEEM,
    ETH_USD_d,
    ETH_USD,
    ETH_XBT_d,
    ETH_ZEC,
    EUR_USD,
    ICN_BTC,
    ICN_ETH,
    LTC_BTC,
    LTC_EUR,
    LTC_USD,
    MLN_BTC,
    MLN_ETH,
    REP_BTC,
    REP_ETH,
    REP_EUR,
    REP_USD,
    USDT_BTC,
    USDT_DASH,
    USDT_ETC,
    USDT_ETH,
    USDT_LTC,
    USDT_NXT,
    USDT_REP,
    USDT_STR,
    USDT_USD,
    USDT_XMR,
    USDT_XRP,
    USDT_ZEC,
    XDG_BTC,
    XLM_BTC,
    XLM_EUR,
    XLM_USD,
    XMR_BCN,
    XMR_BLK,
    XMR_BTC,
    XMR_BTCD,
    XMR_DASH,
    XMR_EUR,
    XMR_LTC,
    XMR_MAID,
    XMR_NXT,
    XMR_USD,
    XMR_ZEC,
    XRP_BTC,
    XRP_EUR,
    XRP_USD,
    ZEC_BTC,
    ZEC_EUR,
    ZEC_USD,
}
<|endoftext|>


<commit_before>extern crate nix;

use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use std::collections::HashSet;
use nix::sys::statvfs::vfs::Statvfs;

fn main() {
    let file = File::open("/proc/mounts").unwrap();
    let reader = BufReader::new(&file);

    let mut excludes = HashSet::new();
    let exclude_types = "cgroup autofs securityfs configfs pstore binfmt_misc debugfs \
                         hugetlbfs devpts mqueue proc sysfs";
    for t in exclude_types.split_whitespace() {
        excludes.insert(t);
    }

    for line in reader.lines() {
        match line {
            Ok(line) => {
                let fields: Vec<&str> = line.split_whitespace().collect();
                if excludes.contains(fields[2]) {
                    continue;
                }
                let statvfs = Statvfs::for_path(fields[1]).unwrap();
                println!("{} {} {} {}", fields[0], fields[1], fields[2], statvfs.f_bsize);
            },
            Err(_) => panic!(),
        }
    }
}
<commit_msg>Display stats.<commit_after>extern crate nix;

use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use std::collections::HashSet;
use nix::sys::statvfs::vfs::Statvfs;

// http://stackoverflow.com/questions/5194057/better-way-to-convert-file-sizes-in-python
fn iec(n: u64) -> String {
    let units = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];

    let i = (n as f64).log(1024_f64).floor() as u32;
    let p = 1024_u64.pow(i);
    let s = (n as f64)/(p as f64);
    format!("{:.0}{}", s, units[i as usize])
}

fn main() {
    let file = File::open("/proc/mounts").unwrap();
    let reader = BufReader::new(&file);

    let mut excludes = HashSet::new();
    let exclude_types = "cgroup autofs securityfs configfs pstore binfmt_misc debugfs \
                         hugetlbfs devpts mqueue proc sysfs";
    for t in exclude_types.split_whitespace() {
        excludes.insert(t);
    }

    let headers = ["Filesystem", "Size", "Used", "Avail", "Mounted on"];
    println!("{:30} {:>9} {:>9} {:>9} {:16}",
             headers[0], headers[1], headers[2], headers[3], headers[4]);

    for line in reader.lines() {
        match line {
            Ok(line) => {
                let fields: Vec<&str> = line.split_whitespace().collect();
                if excludes.contains(fields[2]) {
                    continue;
                }
                let statvfs = Statvfs::for_path(fields[1]).unwrap();
                let size = statvfs.f_blocks * statvfs.f_bsize;
                let avail = statvfs.f_bavail * statvfs.f_bsize;
                let used = size - avail;
                //let percent =
                println!("{:30} {:>9} {:>9} {:>9} {:16}",
                         fields[0], iec(size), iec(avail), iec(used), fields[1]);
            },
            Err(_) => panic!(),
        }
    }
}
<|endoftext|>


<commit_before>#![feature(unboxed_closures)]
#![feature(collections)]
#![feature(convert)]
extern crate regex;
extern crate peruse;
//extern crate test;

//#[test]
//use test::Bencher;

use std::collections::HashMap;
use grammar::*;
use parser::program;
use lexer::token;
use std::os;
use std::fs::File;
use std::env::args;
use std::path::Path;
use std::io::Read;

pub mod lexer;
pub mod grammar;
pub mod parser;

fn main() {

  let mut args = args();

  let file = args.nth(1).unwrap();

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

  interp(contents.as_str());

}

type Environment = HashMap<String,i32>;

fn interp<'a>(raw: &'a str) {
  let lexer = token();
  match lexer.parse(raw) {
    Ok((tokens, rest)) => {
      println!("{:?}", tokens);
      if rest != "" {
        println!("Parser error at: {:?}", rest)
      } else {
        let parser = program();
        match parser.parse(tokens.as_slice()) {
          Ok((Block(stmts), rest)) => {
            if rest.len() > 0 {
              println!("Error: unexpected token {:?}", rest[0]);
            } else {
              run(&stmts);
            }
          }
          Err(err) => {println!("Parse Error: {:?}", err);}
        };
      }
    },
    Err(err) => {
      println!("Lexer error: {:?}", err);
    }
  }

}

fn run(prog: &Vec<Statement>) {
  fn runi32ernal(prog: &Vec<Statement>, env: &mut Environment) {
    for s in prog.iter() {
      match *s {
        Statement::Assign(ref var, ref expr) => {
          match eval(expr, env) {
            Ok(res)   => {env.insert(var.clone(), res);}
            Err(err)  => {
              println!("ERROR: {}", err);
              return;
            }
          }
        },
        Statement::Output(ref expr) => match eval(expr, env) {
          Ok(val) => {println!("{}", val)}
          Err(err) => {
            println!("ERROR: {}", err);
            return;
          }
        },
        Statement::If(ref lhs, ref cmp, ref rhs, ref then_block, ref else_block) => {
          let is_true = compare(lhs, cmp, rhs, env);
          if is_true {
            let &Block(ref p) = then_block;
            runi32ernal(p, env);
          } else {
            match else_block {
              &Some(Block(ref p)) => {runi32ernal(p, env);}
              &None => {}
            }
          }
        }
        Statement::While(ref lhs, ref cmp, ref rhs, ref block) => {
          let &Block(ref stmts) = block;
          while compare(lhs, cmp, rhs, env) {
            runi32ernal(stmts, env);
          }
        }
      }
    }
  }
  let mut env: HashMap<String,i32> = HashMap::new();
  runi32ernal(prog, &mut env);
}

fn compare(lhs: &Expr, cmp: &Comparator, rhs: &Expr, env: &Environment) -> bool {
  let l = eval(lhs, env);
  let r = eval(rhs, env);
  match *cmp {
    Comparator::CEq   => l == r,
    Comparator::CNeq  => l != r,
    Comparator::CLt   => l < r,
    Comparator::CGt   => l > r,
    Comparator::CLeq  => l <= r,
    Comparator::CGeq  => l >= r,
  }
}

fn eval(expr: &Expr, env: &HashMap<String,i32>) -> Result<i32, String> {
  match *expr {
    Expr::Variable(ref var) => match env.get(var) {
      Some(val) => Ok(*val),
      None => Err(format!("Undefined var {}", var)),
    },
    Expr::Num(val) => Ok(val),
    Expr::AddSub(ref ops) => {
      let mut sum = 0;
      for &AddTerm(ref sign, ref op) in ops.iter() {
        match eval(op, env) {
          Ok(value) => match *sign {
            AddOp::Add => {sum += value;}
            AddOp::Subtract => {sum -= value;}
          },
          Err(err) => {
            return Err(err);
          }
        }
      }
      Ok(sum)
    },
    Expr::MultDiv(ref ops) => {
      let mut total = 1;
      for &MultTerm(ref sign,ref op) in ops.iter() {
        match eval(op, env) {
          Ok(value) => match *sign{
            MultOp::Multiply  => {total *= value;}
            MultOp::Divide    => {total /= value;}
            MultOp::Modulo    => {total %= value;}
          },
          Err(err) => {
            return Err(err);
          }
        }
      }
      Ok(total)
    }
  }
}

/*
#[bench]
fn bench_run(b: &mut Bencher) {
    let prog = "n1 = 1
n2 = 1
n = n1 + n2
i = 0

while i < 30 {
  n2 = n1
  n1 = n
  n = n1 + n2
  i = i + 1
}
";
  let lexer = token();
  let (tokens, rest) = lexer.parse(prog).unwrap();
  let parser = program();
  b.iter(|| {
    parser.parse(tokens.as_slice());
  })
}

#[bench]
fn bench_fizzbuzz(b: &mut Bencher) {
    let prog = "n = 1

while n <= 100 {
  if n % 3 == 0 {
    if n % 5 == 0 {
      out 10
    } else {
      out 1
    }
  } else if n % 5 == 0 {
    out 0
  }
  n = n + 1
}
";
  let lexer = token();
  let (tokens, rest) = lexer.parse(prog).unwrap();
  let parser = program();
  b.iter(|| {
    parser.parse(tokens.as_slice());
  })
}

*/

<commit_msg>Added #![feature(custom_derive)] to the crate to add derives to custom traits<commit_after>#![feature(custom_derive)]
#![feature(unboxed_closures)]
#![feature(collections)]
#![feature(convert)]
extern crate regex;
extern crate peruse;
//extern crate test;

//#[test]
//use test::Bencher;

use std::collections::HashMap;
use grammar::*;
use parser::program;
use lexer::token;
use std::os;
use std::fs::File;
use std::env::args;
use std::path::Path;
use std::io::Read;

pub mod lexer;
pub mod grammar;
pub mod parser;

fn main() {

  let mut args = args();

  let file = args.nth(1).unwrap();

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

  interp(contents.as_str());

}

type Environment = HashMap<String,i32>;

fn interp<'a>(raw: &'a str) {
  let lexer = token();
  match lexer.parse(raw) {
    Ok((tokens, rest)) => {
      println!("{:?}", tokens);
      if rest != "" {
        println!("Parser error at: {:?}", rest)
      } else {
        let parser = program();
        match parser.parse(tokens.as_slice()) {
          Ok((Block(stmts), rest)) => {
            if rest.len() > 0 {
              println!("Error: unexpected token {:?}", rest[0]);
            } else {
              run(&stmts);
            }
          }
          Err(err) => {println!("Parse Error: {:?}", err);}
        };
      }
    },
    Err(err) => {
      println!("Lexer error: {:?}", err);
    }
  }

}

fn run(prog: &Vec<Statement>) {
  fn runi32ernal(prog: &Vec<Statement>, env: &mut Environment) {
    for s in prog.iter() {
      match *s {
        Statement::Assign(ref var, ref expr) => {
          match eval(expr, env) {
            Ok(res)   => {env.insert(var.clone(), res);}
            Err(err)  => {
              println!("ERROR: {}", err);
              return;
            }
          }
        },
        Statement::Output(ref expr) => match eval(expr, env) {
          Ok(val) => {println!("{}", val)}
          Err(err) => {
            println!("ERROR: {}", err);
            return;
          }
        },
        Statement::If(ref lhs, ref cmp, ref rhs, ref then_block, ref else_block) => {
          let is_true = compare(lhs, cmp, rhs, env);
          if is_true {
            let &Block(ref p) = then_block;
            runi32ernal(p, env);
          } else {
            match else_block {
              &Some(Block(ref p)) => {runi32ernal(p, env);}
              &None => {}
            }
          }
        }
        Statement::While(ref lhs, ref cmp, ref rhs, ref block) => {
          let &Block(ref stmts) = block;
          while compare(lhs, cmp, rhs, env) {
            runi32ernal(stmts, env);
          }
        }
      }
    }
  }
  let mut env: HashMap<String,i32> = HashMap::new();
  runi32ernal(prog, &mut env);
}

fn compare(lhs: &Expr, cmp: &Comparator, rhs: &Expr, env: &Environment) -> bool {
  let l = eval(lhs, env);
  let r = eval(rhs, env);
  match *cmp {
    Comparator::CEq   => l == r,
    Comparator::CNeq  => l != r,
    Comparator::CLt   => l < r,
    Comparator::CGt   => l > r,
    Comparator::CLeq  => l <= r,
    Comparator::CGeq  => l >= r,
  }
}

fn eval(expr: &Expr, env: &HashMap<String,i32>) -> Result<i32, String> {
  match *expr {
    Expr::Variable(ref var) => match env.get(var) {
      Some(val) => Ok(*val),
      None => Err(format!("Undefined var {}", var)),
    },
    Expr::Num(val) => Ok(val),
    Expr::AddSub(ref ops) => {
      let mut sum = 0;
      for &AddTerm(ref sign, ref op) in ops.iter() {
        match eval(op, env) {
          Ok(value) => match *sign {
            AddOp::Add => {sum += value;}
            AddOp::Subtract => {sum -= value;}
          },
          Err(err) => {
            return Err(err);
          }
        }
      }
      Ok(sum)
    },
    Expr::MultDiv(ref ops) => {
      let mut total = 1;
      for &MultTerm(ref sign,ref op) in ops.iter() {
        match eval(op, env) {
          Ok(value) => match *sign{
            MultOp::Multiply  => {total *= value;}
            MultOp::Divide    => {total /= value;}
            MultOp::Modulo    => {total %= value;}
          },
          Err(err) => {
            return Err(err);
          }
        }
      }
      Ok(total)
    }
  }
}

/*
#[bench]
fn bench_run(b: &mut Bencher) {
    let prog = "n1 = 1
n2 = 1
n = n1 + n2
i = 0

while i < 30 {
  n2 = n1
  n1 = n
  n = n1 + n2
  i = i + 1
}
";
  let lexer = token();
  let (tokens, rest) = lexer.parse(prog).unwrap();
  let parser = program();
  b.iter(|| {
    parser.parse(tokens.as_slice());
  })
}

#[bench]
fn bench_fizzbuzz(b: &mut Bencher) {
    let prog = "n = 1

while n <= 100 {
  if n % 3 == 0 {
    if n % 5 == 0 {
      out 10
    } else {
      out 1
    }
  } else if n % 5 == 0 {
    out 0
  }
  n = n + 1
}
";
  let lexer = token();
  let (tokens, rest) = lexer.parse(prog).unwrap();
  let parser = program();
  b.iter(|| {
    parser.parse(tokens.as_slice());
  })
}

*/

<|endoftext|>


<commit_before>extern crate rustc_serialize;
use rustc_serialize::json::Json;
use std::collections::HashMap;

fn eval(input: &[Json]) -> Json {
  let mut env = std::default::Default::default();
  do_run(input, &mut env)
}

fn substitute(expr: &Json, env: &mut HashMap<String, i64>) -> i64 {
  match *expr {
    Json::Array(ref arr) => do_run(arr.as_slice(), env).as_i64().unwrap(),
    Json::I64(ref i) => *i,
    Json::U64(ref i) => *i as i64,
    _ => panic!("cannot substitute: {:?} (env: {:?})", expr, env)
  }
}

#[allow(unreachable_code)]
fn do_run(input: &[Json], env: &mut HashMap<String, i64>) -> Json {
  match input[0] {
    Json::String(ref s) => {
      match s.as_str() {
        "step" => {
          let ret: Vec<_> = (&input[1..]).iter().map(|line| do_run(line.as_array().unwrap().as_slice(), env)).collect();
          ret.into_iter().last().unwrap()
        }
        "until" => {
          loop {
            let c = do_run(input[1].as_array().unwrap().as_slice(), env);
            if c.is_boolean() && c.as_boolean().unwrap() {
              return Json::Null;
            }
            do_run(input[2].as_array().unwrap().as_slice(), env);
          }
          unreachable!()
        }
        "get" => {
          let key = input[1].as_string().unwrap();
          env.get(key).cloned().map(Json::I64).unwrap()
        }
        "set" => {
          let key = input[1].as_string().unwrap();
          let val = substitute(&input[2], env);
          *env.entry(key.to_owned()).or_insert(0) = val;
          Json::Null
        }
        "=" => {
          let lhs = substitute(&input[1], env);
          let rhs = substitute(&input[2], env);
          Json::Boolean(lhs == rhs)
        }
        "+" => {
          let lhs = substitute(&input[1], env);
          let rhs = substitute(&input[2], env);
          Json::I64(lhs + rhs)
        }
        _ => panic!("invalid operator: {:?}", s)
      }
    }
    _ => panic!("invalid token: {:?}", input[0])
  }
}

fn main() {
  let source = r#"
["step",
  ["set", "i", 10],
  ["set", "sum", 0],
  ["until", ["=", ["get", "i"], 0], [
    "step",
    ["set", "sum", ["+", ["get", "sum"], ["get", "i"]]],
    ["set", "i", ["+", ["get", "i"], -1]]
  ]],
  ["get", "sum"]
]
"#;
  let input: Vec<Json> = Json::from_str(source).unwrap().as_array().unwrap().to_owned();
  println!("{:?}", eval(input.as_slice()));
}
<commit_msg>define Evaluator<commit_after>extern crate rustc_serialize;
use rustc_serialize::json::Json;
use std::collections::HashMap;

struct Evaluator(HashMap<String, i64>);

impl Evaluator {
  fn new() -> Evaluator { Evaluator(HashMap::new()) }

  fn eval(&mut self, input: &[Json]) -> Json {
    match input[0] {
      Json::String(ref s) => {
        match s.as_str() {
          "step" => self.eval_step(&input[1..]),
          "until" => self.eval_until(&input[1..]),
          "get" => self.eval_get(&input[1..]),
          "set" => self.eval_set(&input[1..]),
          "=" => self.eval_equals(&input[1..]),
          "+" => self.eval_add(&input[1..]),
          _ => panic!("invalid operator: {:?}", s)
        }
      }
      _ => panic!("invalid token: {:?}", input[0])
    }
  }

  fn eval_step(&mut self, lines: &[Json]) -> Json {
    let ret = self.eval(lines[0].as_array().unwrap().as_slice());
    if lines.len() == 1 {
      ret
    } else {
      self.eval_step(&lines[1..])
    }
  }

  #[allow(unreachable_code)]
  fn eval_until(&mut self, args: &[Json]) -> Json {
    loop {
      let c = self.eval(args[0].as_array().unwrap().as_slice());
      if c.is_boolean() && c.as_boolean().unwrap() {
        return Json::Null;
      }
      self.eval(args[1].as_array().unwrap().as_slice());
    }
  }

  fn eval_get(&mut self, args: &[Json]) -> Json {
    let key = args[0].as_string().unwrap();
    self.0.get(key).cloned().map(Json::I64).unwrap()
  }

  fn eval_set(&mut self, args: &[Json]) -> Json {
    let key = args[0].as_string().unwrap();
    let val = self.substitute(&args[1]);
    *self.0.entry(key.to_owned()).or_insert(0) = val;
    Json::Null
  }

  fn eval_equals(&mut self, args: &[Json]) -> Json {
    let lhs = self.substitute(&args[0]);
    let rhs = self.substitute(&args[1]);
    Json::Boolean(lhs == rhs)
  }

  fn eval_add(&mut self, args: &[Json]) -> Json {
    let lhs = self.substitute(&args[0]);
    let rhs = self.substitute(&args[1]);
    Json::I64(lhs + rhs)
  }

  fn substitute(&mut self, expr: &Json) -> i64 {
    match *expr {
      Json::Array(ref arr) => self.eval(arr.as_slice()).as_i64().unwrap(),
      Json::I64(ref i) => *i,
      Json::U64(ref i) => *i as i64,
      _ => panic!("cannot substitute: {:?} (env: {:?})", expr, self.0)
    }
  }
}

fn main() {
  let source = r#"
["step",
  ["set", "i", 10],
  ["set", "sum", 0],
  ["until", ["=", ["get", "i"], 0], [
    "step",
    ["set", "sum", ["+", ["get", "sum"], ["get", "i"]]],
    ["set", "i", ["+", ["get", "i"], -1]]
  ]],
  ["get", "sum"]
]
"#;

  let input: Vec<Json> = Json::from_str(source).unwrap().as_array().unwrap().to_owned();
  println!("{:?}", Evaluator::new().eval(input.as_slice()));
}
<|endoftext|>


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

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

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

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

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

use hyper::{Get, StatusCode};
use hyper::header::{Accept, ContentType};
use hyper::server::{Http, Request, Response, Service};
use redis::{Commands, RedisError, RedisResult};
use regex::Regex;
use rquery::Document;
use semver::Version;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;

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

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

fn download_latest_buildpack_release() -> String {
    let xml = download_url(RUBY_LANGPACK_RELEASES_URL).expect("Could not download Atom releases!");
    let atom_doc = Document::new_from_xml_string(&xml[..]).expect("Could not parse Atom releases!");
    atom_doc
        .select("entry id")
        .expect("Could not find latest buildpack tag!")
        .text()
        .rsplitn(2, '/')
        .next()
        .expect("No slashes in the GitHub release ID!")
        .to_string()
}

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

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

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

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

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

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

fn determine_content_type(headers: &hyper::Headers) -> ContentType {
    use std::ops::Deref;
    if headers.has::<Accept>() {
        let accept = headers
            .get::<Accept>()
            .expect("Accept header not found?!")
            .deref();
        let use_json = accept.into_iter().any(
            |qitem| qitem.item == "application/json",
        );

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

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

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

fn min_bundler_version() -> String {
    match env::var("MIN_BUNDLER_VERSION") {
        Ok(version) => version,
        _ => String::from(DEFAULT_MIN_BUNDLER_VERSION),
    }
}

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

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

struct HBBUYServer;

impl Service for HBBUYServer {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = futures::future::FutureResult<Self::Response, Self::Error>;

    fn call(&self, req: Request) -> Self::Future {
        let response = match (req.uri().path(), req.method()) {
            ("/", &Get) => {
                let redis = connect_to_redis();
                let result = is_bundler_upgraded(&redis);
                let content_type = determine_content_type(req.headers());
                let data = match &format!("{}", content_type)[..] {
                    "application/json; charset=utf-8" => result_to_json(result),
                    _ => result_to_html(result),
                };

                Response::new().with_header(content_type).with_body(data)
            }
            (_, &Get) => Response::new().with_status(StatusCode::NotFound),
            (_, _) => Response::new().with_status(StatusCode::MethodNotAllowed),
        };

        futures::future::ok(response)
    }
}

fn main() {
    env_logger::init().expect("Could not initialize env_logger!");
    let addr = format!("0.0.0.0:{}", server_port()).parse().expect(
        "Could not parse address/port",
    );
    let server = Http::new().bind(&addr, || Ok(HBBUYServer)).expect(
        "Could not create server!",
    );
    server.run().expect(
        "Could not set up HTTP request handler!",
    );
}
<commit_msg>JSON content type changed when Hyper upgraded<commit_after>// Copyright (C) 2016, 2017 Mark Lee

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

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

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

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

use hyper::{Get, StatusCode};
use hyper::header::{Accept, ContentType};
use hyper::server::{Http, Request, Response, Service};
use redis::{Commands, RedisError, RedisResult};
use regex::Regex;
use rquery::Document;
use semver::Version;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;

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

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

fn download_latest_buildpack_release() -> String {
    let xml = download_url(RUBY_LANGPACK_RELEASES_URL).expect("Could not download Atom releases!");
    let atom_doc = Document::new_from_xml_string(&xml[..]).expect("Could not parse Atom releases!");
    atom_doc
        .select("entry id")
        .expect("Could not find latest buildpack tag!")
        .text()
        .rsplitn(2, '/')
        .next()
        .expect("No slashes in the GitHub release ID!")
        .to_string()
}

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

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

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

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

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

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

fn determine_content_type(headers: &hyper::Headers) -> ContentType {
    use std::ops::Deref;
    if headers.has::<Accept>() {
        let accept = headers
            .get::<Accept>()
            .expect("Accept header not found?!")
            .deref();
        let use_json = accept.into_iter().any(
            |qitem| qitem.item == "application/json",
        );

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

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

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

fn min_bundler_version() -> String {
    match env::var("MIN_BUNDLER_VERSION") {
        Ok(version) => version,
        _ => String::from(DEFAULT_MIN_BUNDLER_VERSION),
    }
}

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

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

struct HBBUYServer;

impl Service for HBBUYServer {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = futures::future::FutureResult<Self::Response, Self::Error>;

    fn call(&self, req: Request) -> Self::Future {
        let response = match (req.uri().path(), req.method()) {
            ("/", &Get) => {
                let redis = connect_to_redis();
                let result = is_bundler_upgraded(&redis);
                let content_type = determine_content_type(req.headers());
                let data = match &format!("{}", content_type)[..] {
                    "application/json" => result_to_json(result),
                    _ => result_to_html(result),
                };

                Response::new().with_header(content_type).with_body(data)
            }
            (_, &Get) => Response::new().with_status(StatusCode::NotFound),
            (_, _) => Response::new().with_status(StatusCode::MethodNotAllowed),
        };

        futures::future::ok(response)
    }
}

fn main() {
    env_logger::init().expect("Could not initialize env_logger!");
    let addr = format!("0.0.0.0:{}", server_port()).parse().expect(
        "Could not parse address/port",
    );
    let server = Http::new().bind(&addr, || Ok(HBBUYServer)).expect(
        "Could not create server!",
    );
    server.run().expect(
        "Could not set up HTTP request handler!",
    );
}
<|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;
                            }
                        },
                        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);
        }
    }
}
<commit_msg>handle scrolling<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,
        }
    }

    // TODO: handle lines that are longer than terminal width.
    // Should we wrap them or truncate them?
    fn redraw(&mut self, update: &Update) {
        write!(self.stdout, "{}", termion::clear::All).unwrap();
        write!(self.stdout, "{}", cursor::Up(self.size.1)).unwrap();

        let nb_lines = update.lines.len();
        if nb_lines > 0 {
            for line in update.lines.iter().take(nb_lines - 1) {
                write!(self.stdout, "{}", cursor::Left(self.size.0)).unwrap();
                self.stdout.write_all(line.text.as_bytes()).unwrap();
            }

            // If the last line has a trailing \n, we need to remove it
            let mut last_line = update.lines[nb_lines - 1].text.clone();
            match last_line.pop() {
                Some('\n') | None => {
                },
                Some(c) => {
                    last_line.push(c);
                }
            }
            write!(self.stdout, "{}", cursor::Left(self.size.0)).unwrap();
            self.stdout.write_all(last_line.as_bytes()).unwrap();
        }

        write!(self.stdout, "{}", cursor::Goto(
                // columns
                update.scroll_to.1 as u16 + 1,
                // lines
                (update.scroll_to.0 - update.first_line + 1) as u16)).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));
        }
        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) {
    // TODO: check if terminal size changed. If so, send a `render_line` command to the backend,
    // and a `scroll` command for future updates.
    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) => {
                        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();
    core.scroll(0, screen.size.1 as u64 - 2);
    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|>