<commit_before>#![feature(io)]

extern crate argparse;
extern crate bit_set;
extern crate compress;
extern crate libc;

use std::fs;
use std::io;
use std::mem;
use std::path;
use std::slice;

use argparse::Store;

use bit_set::BitSet;

use libc::c_void;

//magic:
use std::io::Read;
use std::os::unix::io::AsRawFd;

type CharResult = Result<char, io::CharsError>;

static TRI_MAX: usize = 64 * 64 * 64;

fn simplify(wut: char) -> u8 {
    let c = match wut {
        'a' ... 'z' => (wut as u8 - 'a' as u8 + 'A' as u8) as char,
        _ => wut,
    };

    if c > 128 as char {
        if c.is_whitespace() {
            return 2;
        }
        if c.is_control() {
            return 0;
        }
        return 63;
    }

    let sym_end = 33u8;
    let letters = 21u8;

    match c {
        '\r' | '\n' => 1,
        '\t' | '\x0c' | '\x0b' | ' ' => 2,
        '!' => 3,
        '"' | '\'' | '`' => 4,
        '$' => 5,
        '%' => 6,
        '&' => 7,
        '(' ... '@' => 8 + (c as u8 - '(' as u8),
        'A' ... 'I' => sym_end + (c as u8 - 'A' as u8),
        'J' | 'K' => sym_end + letters,
        'L' ... 'P' => sym_end + (c as u8 - 'A' as u8) - 2,
        'Q' => sym_end + letters,
        'R' ... 'W' => sym_end + (c as u8 - 'A' as u8) - 3,
        'X' | 'Z' => sym_end + letters,
        'Y' => sym_end + letters - 1,
        '[' => 55,
        '\\' => 56,
        ']'=> 57,
        '^' | '~' | '#' => 58,
        '_' => 59,
        '{' ... '}' => 60 + (c as u8 - '{' as u8),
        _ => 0,
    }
}

fn explain(wut: u8) -> char {
    match wut {
        0 => 'X',
        1 => 'N',
        2 => ' ',
        3 => '!',
        4 => '"',
        5 => '$',
        6 => '%',
        7 => '&',
        8 ... 32 => (wut - 8 + '(' as u8) as char,
        33 ... 41 => (wut - 33 + 'a' as u8) as char,
        42 ... 46 => (wut - 42 + 'l' as u8) as char,
        47 ... 52 => (wut - 47 + 'r' as u8) as char,
        53 => 'y',
        54 => 'X',
        55 => '[',
        56 => '\\',
        57 => ']',
        58 => '#',
        59 => '_',
        60 ... 62 => (wut - 60 + '{' as u8) as char,
        63 => 'U',
        _ => 'D',
    }
}

fn unpack(wut: usize) -> String {
    let mut ret = String::with_capacity(3);
    ret.push(explain((wut / 64 / 64 % 64) as u8));
    ret.push(explain((wut / 64 % 64) as u8));
    ret.push(explain((wut % 64) as u8));
    return ret;
}

fn trigrams_for<T: Iterator<Item=CharResult>>(input: T) -> Result<BitSet, String> {
    let mut line: u64 = 1;
    let mut prev: [u8; 3] = [0; 3];
    let mut ret: BitSet = BitSet::with_capacity(64 * 64 * 64);

    for (off, maybe_char) in input.enumerate() {
        let c = try!(maybe_char.map_err(|e| {
            format!("line {}: file char {}: failed: {}", line, off, e)
        }));
        if '\n' == c {
            line += 1;
        }
        if '\0' == c {
            return Err(format!("line {}: null found: not a text file", line));
        }
        prev[0] = prev[1];
        prev[1] = prev[2];
        prev[2] = simplify(c);
        let tri: usize = 64 * 64 * prev[0] as usize + 64 * prev[1] as usize + prev[2] as usize;
        ret.insert(tri);
    }
    return Ok(ret);
}

struct Mapped<'a, T: 'a> {
    file: fs::File,
    map: *mut c_void,
    data: &'a mut [T],
}

impl <'a, T: 'a> Mapped<'a, T> {
    fn fixed_len<P>(path: P, len: usize) -> io::Result<Mapped<'a, T>>
        where P: AsRef<path::Path> {
        let file = fs::OpenOptions::new().read(true).write(true).create(true).open(path)?;
        file.set_len(mem::size_of::<T>() as u64 * len as u64)?;
        let map: *mut c_void = unsafe {
            libc::mmap(0 as *mut c_void,
                       len * mem::size_of::<T>(),
                       libc::PROT_READ | libc::PROT_WRITE,
                       libc::MAP_SHARED,
                       file.as_raw_fd(),
                       0)
        };

        if libc::MAP_FAILED == map {
            return Err(io::Error::last_os_error());
        }

        let data = unsafe { slice::from_raw_parts_mut(map as *mut T, len) };
        Ok(Mapped { file, map, data })
    }

    fn remap(&mut self, len: usize) -> io::Result<()> {
        self.file.set_len(mem::size_of::<T>() as u64 * len as u64)?;
        let new_map = unsafe {
            libc::mremap(self.map,
                         self.data.len() * mem::size_of::<T>(),
                         len * mem::size_of::<T>(),
                         libc::MREMAP_MAYMOVE)
        };

        if libc::MAP_FAILED == new_map {
            return Err(io::Error::last_os_error());
        }

        self.data = unsafe { slice::from_raw_parts_mut(new_map as *mut T, len) };
        self.map = new_map;
        Ok(())
    }
}

impl <'a, T: 'a> Drop for Mapped<'a, T> {
    fn drop(&mut self) {
        unsafe {
            assert_eq!(0, libc::munmap(self.map, self.data.len() * mem::size_of::<T>()));
        }
    }
}

fn main() {
    let mut from: String = "".to_string();
    let mut simple: u64 = 0;
    {
        let mut ap = argparse::ArgumentParser::new();
        ap.set_description("totally not a load of tools glued together");
        ap.refer(&mut from)
                .required()
                .add_option(&["-f", "--input-file"], Store,
                            "pack file to read");
        ap.refer(&mut simple)
                .add_option(&["--simple"], Store,
                            "not a pack, just a normal decompressed file");
        ap.parse_args_or_exit();
    }

    let mut idx: Mapped<u32> = Mapped::fixed_len("idx", TRI_MAX).unwrap();

    let page_size: usize = 1024;

    let pages_len: usize = (match fs::metadata("pages") {
        Ok(m) => m.len(),
        Err(e) => if e.kind() == io::ErrorKind::NotFound {
            0
        } else {
            panic!("couldn't get info on pages file: {}", e)
        }
    } / (mem::size_of::<u64>() as u64)) as usize;

    let mut pages: Mapped<u64> = Mapped::fixed_len("pages", pages_len + (page_size * 100)).unwrap();
    let mut avail_pages: usize = pages.data.len() / page_size;
    let mut free_page: usize = avail_pages - 98;

    loop {
        if 0 != pages.data[(free_page - 1) * page_size] {
            break;
        }
        free_page -= 1;
        if 1 == free_page {
            break;
        }
    }

    if 0 != simple {
        let fh = fs::File::open(from).expect("input file must exist and be readable");
        let trigrams = trigrams_for(fh.chars()).expect("trigramming must work");
        for found in trigrams.iter() {
            let mut page = idx.data[found] as usize;
            if 0 == page {
                page = free_page;
                idx.data[found] = page as u32;
                free_page += 1;
                if free_page > avail_pages {
                    avail_pages += 100;
                    pages.remap(avail_pages * page_size).unwrap();
                }
            }

            let header_loc = page * page_size;
            let header = pages.data[header_loc];
            assert!(header < page_size as u64);
            pages.data[header_loc] += 1;
            pages.data[page * page_size + 1 + header as usize] = simple;
        }
        return;
    }

    unimplemented!();
}
<commit_msg>rely on resizing to grow pack<commit_after>#![feature(io)]

extern crate argparse;
extern crate bit_set;
extern crate compress;
extern crate libc;

use std::fs;
use std::io;
use std::mem;
use std::path;
use std::slice;

use argparse::Store;

use bit_set::BitSet;

use libc::c_void;

//magic:
use std::io::Read;
use std::os::unix::io::AsRawFd;

type CharResult = Result<char, io::CharsError>;

static TRI_MAX: usize = 64 * 64 * 64;

fn simplify(wut: char) -> u8 {
    let c = match wut {
        'a' ... 'z' => (wut as u8 - 'a' as u8 + 'A' as u8) as char,
        _ => wut,
    };

    if c > 128 as char {
        if c.is_whitespace() {
            return 2;
        }
        if c.is_control() {
            return 0;
        }
        return 63;
    }

    let sym_end = 33u8;
    let letters = 21u8;

    match c {
        '\r' | '\n' => 1,
        '\t' | '\x0c' | '\x0b' | ' ' => 2,
        '!' => 3,
        '"' | '\'' | '`' => 4,
        '$' => 5,
        '%' => 6,
        '&' => 7,
        '(' ... '@' => 8 + (c as u8 - '(' as u8),
        'A' ... 'I' => sym_end + (c as u8 - 'A' as u8),
        'J' | 'K' => sym_end + letters,
        'L' ... 'P' => sym_end + (c as u8 - 'A' as u8) - 2,
        'Q' => sym_end + letters,
        'R' ... 'W' => sym_end + (c as u8 - 'A' as u8) - 3,
        'X' | 'Z' => sym_end + letters,
        'Y' => sym_end + letters - 1,
        '[' => 55,
        '\\' => 56,
        ']'=> 57,
        '^' | '~' | '#' => 58,
        '_' => 59,
        '{' ... '}' => 60 + (c as u8 - '{' as u8),
        _ => 0,
    }
}

fn explain(wut: u8) -> char {
    match wut {
        0 => 'X',
        1 => 'N',
        2 => ' ',
        3 => '!',
        4 => '"',
        5 => '$',
        6 => '%',
        7 => '&',
        8 ... 32 => (wut - 8 + '(' as u8) as char,
        33 ... 41 => (wut - 33 + 'a' as u8) as char,
        42 ... 46 => (wut - 42 + 'l' as u8) as char,
        47 ... 52 => (wut - 47 + 'r' as u8) as char,
        53 => 'y',
        54 => 'X',
        55 => '[',
        56 => '\\',
        57 => ']',
        58 => '#',
        59 => '_',
        60 ... 62 => (wut - 60 + '{' as u8) as char,
        63 => 'U',
        _ => 'D',
    }
}

fn unpack(wut: usize) -> String {
    let mut ret = String::with_capacity(3);
    ret.push(explain((wut / 64 / 64 % 64) as u8));
    ret.push(explain((wut / 64 % 64) as u8));
    ret.push(explain((wut % 64) as u8));
    return ret;
}

fn trigrams_for<T: Iterator<Item=CharResult>>(input: T) -> Result<BitSet, String> {
    let mut line: u64 = 1;
    let mut prev: [u8; 3] = [0; 3];
    let mut ret: BitSet = BitSet::with_capacity(64 * 64 * 64);

    for (off, maybe_char) in input.enumerate() {
        let c = try!(maybe_char.map_err(|e| {
            format!("line {}: file char {}: failed: {}", line, off, e)
        }));
        if '\n' == c {
            line += 1;
        }
        if '\0' == c {
            return Err(format!("line {}: null found: not a text file", line));
        }
        prev[0] = prev[1];
        prev[1] = prev[2];
        prev[2] = simplify(c);
        let tri: usize = 64 * 64 * prev[0] as usize + 64 * prev[1] as usize + prev[2] as usize;
        ret.insert(tri);
    }
    return Ok(ret);
}

struct Mapped<'a, T: 'a> {
    file: fs::File,
    map: *mut c_void,
    data: &'a mut [T],
}

impl <'a, T: 'a> Mapped<'a, T> {
    fn fixed_len<P>(path: P, len: usize) -> io::Result<Mapped<'a, T>>
        where P: AsRef<path::Path> {
        let file = fs::OpenOptions::new().read(true).write(true).create(true).open(path)?;
        file.set_len(mem::size_of::<T>() as u64 * len as u64)?;
        let map: *mut c_void = unsafe {
            libc::mmap(0 as *mut c_void,
                       len * mem::size_of::<T>(),
                       libc::PROT_READ | libc::PROT_WRITE,
                       libc::MAP_SHARED,
                       file.as_raw_fd(),
                       0)
        };

        if libc::MAP_FAILED == map {
            return Err(io::Error::last_os_error());
        }

        let data = unsafe { slice::from_raw_parts_mut(map as *mut T, len) };
        Ok(Mapped { file, map, data })
    }

    fn remap(&mut self, len: usize) -> io::Result<()> {
        self.file.set_len(mem::size_of::<T>() as u64 * len as u64)?;
        let new_map = unsafe {
            libc::mremap(self.map,
                         self.data.len() * mem::size_of::<T>(),
                         len * mem::size_of::<T>(),
                         libc::MREMAP_MAYMOVE)
        };

        if libc::MAP_FAILED == new_map {
            return Err(io::Error::last_os_error());
        }

        self.data = unsafe { slice::from_raw_parts_mut(new_map as *mut T, len) };
        self.map = new_map;
        Ok(())
    }
}

impl <'a, T: 'a> Drop for Mapped<'a, T> {
    fn drop(&mut self) {
        unsafe {
            assert_eq!(0, libc::munmap(self.map, self.data.len() * mem::size_of::<T>()));
        }
    }
}

fn main() {
    let mut from: String = "".to_string();
    let mut simple: u64 = 0;
    {
        let mut ap = argparse::ArgumentParser::new();
        ap.set_description("totally not a load of tools glued together");
        ap.refer(&mut from)
                .required()
                .add_option(&["-f", "--input-file"], Store,
                            "pack file to read");
        ap.refer(&mut simple)
                .add_option(&["--simple"], Store,
                            "not a pack, just a normal decompressed file");
        ap.parse_args_or_exit();
    }

    let mut idx: Mapped<u32> = Mapped::fixed_len("idx", TRI_MAX).unwrap();

    let page_size: usize = 1024;

    let pages_len: usize = match fs::metadata("pages") {
        Ok(m) => {
            let proposed: u64 = m.len() / mem::size_of::<u64>() as u64;
            assert!(proposed < usize::max_value() as u64);
            proposed as usize
        },
        Err(e) => if e.kind() == io::ErrorKind::NotFound {
            2 * page_size
        } else {
            panic!("couldn't get info on pages file: {}", e)
        }
    };

    let mut pages: Mapped<u64> = Mapped::fixed_len("pages", pages_len).unwrap();
    let mut avail_pages: usize = pages.data.len() / page_size;
    let mut free_page: usize = avail_pages;

    loop {
        if 0 != pages.data[(free_page - 1) * page_size] {
            break;
        }
        if 1 == free_page {
            break;
        }
        free_page -= 1;
    }

    if 0 != simple {
        let fh = fs::File::open(from).expect("input file must exist and be readable");
        let trigrams = trigrams_for(fh.chars()).expect("trigramming must work");
        for found in trigrams.iter() {
            let mut page = idx.data[found] as usize;
            if 0 == page {
                page = free_page;
                idx.data[found] = page as u32;
                free_page += 1;
                if free_page > avail_pages {
                    avail_pages += 100;
                    pages.remap(avail_pages * page_size).unwrap();
                }
            }

            let header_loc = page * page_size;
            let header = pages.data[header_loc];
            assert!(header < page_size as u64);
            pages.data[header_loc] += 1;
            pages.data[page * page_size + 1 + header as usize] = simple;
        }
        return;
    }

    unimplemented!();
}
<|endoftext|>


<commit_before>//! Parser main file

use std::env;
use std::process;
use std::io::stderr;
use std::io::Write;

/// Check if the given character is a digit (between 0 and 9)
///
/// # Arguments
///
/// * `character` - character to check
fn is_digit(character: char) -> bool {
    let digits: [char; 10] = ['0','1','2','3','4','5','6','7','8','9'];
    return digits.contains(&character);
}

/// Check if the given character is a plus (+)
///
/// # Arguments
///
/// * `character` - character to check
fn is_plus(character: char) -> bool {
    return character == '+';
}

/// Recursive method that reads the given instructions from 'iterator'
/// and fills the 'output' string object with assembly code
///
/// # Arguments
///
/// * `iterator` - iterator over the input characters
/// (mutable reference in order to iterate)
/// * `ouput` - String object to generate the ouput assembly
/// (mutable reference in order to add code)
fn iterate(
    iterator: &mut std::str::Chars,
    output: &mut std::string::String
) {
    let character = iterator.next();
    match character {
        Some(character) => {

            if is_digit(character) {

                output.push_str("mov eax, ");
                output.push(character);
                output.push('\n');

                iterate(iterator, output);
            }
            else if is_plus(character) {

                output.push_str("mov ebx, eax\n");

                let next_character = iterator.next();
                match next_character {
                    Some(next_character) => {
                        output.push_str("mov eax, ");
                        output.push(next_character);
                        output.push('\n');
                    }
                    None => {}
                }

                output.push_str("add eax, ebx\n");

                iterate(iterator, output);
            }
        }
        None => {}
    };
}

fn main() {

    let args: Vec<_> = env::args().collect();

    if args.len() != 2 {
        writeln!(&mut stderr(), "No input.").unwrap();
        process::exit(1);
    }

    let mut output = String::from(
r#"
bits 64

segment .text
    global _start

_start:
"#
    );

    let mut iterator = args[1].chars();
    iterate(&mut iterator, &mut output);

    output +=
r#"
mov eax, 1
int 0x80
"#;

    print!("{}", output);
}
<commit_msg>use more functions and add doc<commit_after>//! Parser main file

use std::env;
use std::process;
use std::io::stderr;
use std::io::Write;

/// Check if the given character is a digit (between 0 and 9)
///
/// # Arguments
///
/// * `character` - character to check
fn is_digit(character: char) -> bool {
    let digits: [char; 10] = ['0','1','2','3','4','5','6','7','8','9'];
    return digits.contains(&character);
}

/// Check if the given character is a plus (+)
///
/// # Arguments
///
/// * `character` - character to check
fn is_plus(character: char) -> bool {
    return character == '+';
}

/// Generates assembly instructions when a digit is met
///
/// # Arguments:
///
/// * `ouput` - String object to generate the ouput assembly
/// (mutable reference in order to add code)
/// * `character` - character to check
fn read_digit(
    output: &mut std::string::String,
    character: char
) {
    output.push_str("mov eax, ");
    output.push(character);
    output.push('\n');
}

/// Generates assembly instructions when a '+' is met
///
/// # Arguments:
///
/// * `iterator` - iterator over the input characters
/// (mutable reference in order to iterate)
/// * `ouput` - String object to generate the ouput assembly
/// (mutable reference in order to add code)
fn read_plus(
    iterator: &mut std::str::Chars,
    output: &mut std::string::String
) {
    output.push_str("mov ebx, eax\n");

    let character = iterator.next();
    match character {
        Some(character) => {
            read_digit(output, character);
        }
        None => {}
    }

    output.push_str("add eax, ebx\n");
}

/// Check if the given character matches with a symbol type
///
/// # Arguments:
///
/// * `iterator` - iterator over the input characters
/// (mutable reference in order to iterate)
/// * `ouput` - String object to generate the ouput assembly
/// (mutable reference in order to add code)
/// * `character` - character to check
fn handle_character(
    iterator: &mut std::str::Chars,
    output: &mut std::string::String,
    character: char
)
{
    let character_is_digit: bool = is_digit(character);
    let character_is_plus: bool = is_plus(character);

    if !character_is_digit && !character_is_plus {
        return;
    }

    if character_is_digit {
        read_digit(output, character);
    }

    if character_is_plus {
        read_plus(iterator, output);
    }

    iterate(iterator, output);
}

/// Recursive method that reads the given instructions from 'iterator'
/// and fills the 'output' string object with assembly code
///
/// # Arguments
///
/// * `iterator` - iterator over the input characters
/// (mutable reference in order to iterate)
/// * `ouput` - String object to generate the ouput assembly
/// (mutable reference in order to add code)
fn iterate(
    iterator: &mut std::str::Chars,
    output: &mut std::string::String
) {
    let character = iterator.next();
    match character {
        Some(character) => {
            handle_character(iterator, output, character);
        }
        None => {}
    };
}

fn main() {

    let args: Vec<_> = env::args().collect();

    if args.len() != 2 {
        writeln!(&mut stderr(), "No input.").unwrap();
        process::exit(1);
    }

    let mut output = String::from(
r#"
bits 64

segment .text
    global _start

_start:
"#
    );

    let mut iterator = args[1].chars();
    iterate(&mut iterator, &mut output);

    output +=
r#"
mov eax, 1
int 0x80
"#;

    print!("{}", output);
}
<|endoftext|>


<commit_before>#![feature(custom_derive)]
#![feature(fmt_flags)]

extern crate hyper;
extern crate rustc_serialize;

use std::fmt;
use std::io::Read;

use hyper::client::{Client, Response, RequestBuilder};
use hyper::header::{Accept, Connection, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel};

use rustc_serialize::json;

struct MyClient {
    client : Client,
    app_id : String,
    app_key : String,
}

#[derive(Hash, Debug, RustcEncodeable, RustcDecodable)]
struct Line {
    id : String,
    name : String,
    modeName : String,
    routeSections : Vec<RouteSection>
}

#[derive(Hash, Debug, RustcEncodeable, RustcDecodable)]
struct RouteSection {
    name : String,
    direction : String,
    originator : String,
    destination : String,
}

impl fmt::Display for Line {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({})", self.id)
    }
}

impl MyClient {
    fn new() -> MyClient {
        return MyClient{
            client : Client::new(),
            app_id : String::new(),
            app_key : String::new(),
        }
    }

    fn get(&mut self, endpoint : &str) -> Response {
        self.client.get("https://api.tfl.gov.uk/line/route?app_id=&app_key=")
            .header(Connection::close())
            .header(Accept(vec![
                qitem(Mime(TopLevel::Application,
                    SubLevel::Ext("json".to_owned()), vec![])),
            ]))
            .send().unwrap()
    }
}

fn main() {
    let mut client = MyClient::new();
    let mut resp = client.get("/line/route");
    let mut body = String::new();
    resp.read_to_string(&mut body).unwrap();
    let lines : Vec<Line> = json::decode(&body).unwrap();
    for line in lines {
        println!("{}:", line.name);
        for routeSection in line.routeSections {
            println!("\t{}", routeSection.name);
        }
    }
}
<commit_msg>Print out a line/route report with duplicate state<commit_after>#![feature(custom_derive)]
#![feature(fmt_flags)]

extern crate hyper;
extern crate rustc_serialize;

use std::fmt;
use std::io::Read;
use std::collections::HashSet;

use hyper::client::{Client, Response, RequestBuilder};
use hyper::header::{Accept, Connection, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel};

use rustc_serialize::json;

struct MyClient {
    client : Client,
    app_id : String,
    app_key : String,
}

#[derive(Hash, Debug, RustcEncodeable, RustcDecodable)]
struct Line {
    id : String,
    name : String,
    modeName : String,
    routeSections : Vec<RouteSection>
}

#[derive(Hash, Debug, RustcEncodeable, RustcDecodable)]
struct RouteSection {
    name : String,
    direction : String,
    originator : String,
    destination : String,
}

impl fmt::Display for Line {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({})", self.id)
    }
}

impl MyClient {
    fn new() -> MyClient {
        return MyClient{
            client : Client::new(),
            app_id : String::new(),
            app_key : String::new(),
        }
    }

    fn get(&mut self, endpoint : &str) -> Response {
        self.client.get("https://api.tfl.gov.uk/line/route?app_id=&app_key=")
            .header(Connection::close())
            .header(Accept(vec![
                qitem(Mime(TopLevel::Application,
                    SubLevel::Ext("json".to_owned()), vec![])),
            ]))
            .send().unwrap()
    }
}

fn main() {
    let mut line_names : HashSet<String> = HashSet::new();
    let mut route_section_names : HashSet<String> = HashSet::new();
    let mut client = MyClient::new();
    let mut resp = client.get("/line/route");
    let mut body = String::new();
    resp.read_to_string(&mut body).unwrap();
    let lines : Vec<Line> = json::decode(&body).unwrap();
    for line in lines {
        let line_known = line_names.contains(&line.name);
        println!("{}, Duplicate: {}:", line.name, line_known);
        line_names.insert(line.name.clone());
        for routeSection in line.routeSections {
            let route_section_known = route_section_names.contains(&routeSection.name);
            println!("\t{}, Duplicate: {}", routeSection.name, route_section_known);
            route_section_names.insert(routeSection.name.clone());
        }
    }
}
<|endoftext|>


<commit_before>#![allow(unused_features)] // so we can still feature(os) when testing
#![feature(collections, path, io, core, os)]
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use std::os;
use docopt::Docopt;

mod elem;
mod parser;
mod error;
mod database;
mod mass;

const USAGE: &'static str = "
usage: chemtool <formula>
       chemtool [-h | --help]

options:
    -h --help    Display this message and then exit.
";

#[derive(RustcDecodable)]
struct Args {
    arg_formula: String,
}

#[cfg(not(test))]
fn main() {
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());
    let mut path = Path::new(&os::args()[0]);
    path.set_filename("elemdb.csv");
    let input = args.arg_formula.as_slice();
    if let Err(e) = mass::pretty_print_mass(input, &path) {
        e.print(input);
    }
}<commit_msg>Minor layout change of the help message<commit_after>#![allow(unused_features)] // so we can still feature(os) when testing
#![feature(collections, path, io, core, os)]
extern crate "rustc-serialize" as rustc_serialize;
extern crate docopt;

use std::os;
use docopt::Docopt;

mod elem;
mod parser;
mod error;
mod database;
mod mass;

const USAGE: &'static str = "
usage:
    chemtool <formula>
    chemtool [-h | --help]

options:
    -h --help    Display this message and then exit.
";

#[derive(RustcDecodable)]
struct Args {
    arg_formula: String,
}

#[cfg(not(test))]
fn main() {
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());
    let mut path = Path::new(&os::args()[0]);
    path.set_filename("elemdb.csv");
    let input = args.arg_formula.as_slice();
    if let Err(e) = mass::pretty_print_mass(input, &path) {
        e.print(input);
    }
}<|endoftext|>


<commit_before>extern crate cobalt;
extern crate getopts;

use getopts::{optopt, optflag, getopts, usage};
use std::os;

fn print_version() {
    // parse this from Cargo.toml
    let version = vec!["0", "0", "1"];

    println!("{}", version.connect("."));
}

fn main() {
    let args: Vec<String> = os::args();

    let opts = [
        optopt("s", "source", "Build from example/folder", "[example/folder]"),
        optopt("d", "destination", "Build into example/folder/build", "[example/folder]"),
        optflag("h", "help", "Print this help menu"),
        optflag("v", "version", "Display version")
    ];

    // TODO: panic!()
    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    let source = if matches.opt_present("s") {
        Path::new(matches.opt_str("s").unwrap())
    } else {
        Path::new(".".to_string())
    };

    let dest   = if matches.opt_present("d") {
        Path::new(matches.opt_str("d").unwrap())
    } else {
        Path::new(".".to_string())
    };

    if matches.opt_present("h") {
        println!("{}", usage("cobalt", opts));
        return;
    }

    if matches.opt_present("v") {
        print_version();
        return;
    }

    let command = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        println!("{}", usage("cobalt", opts));
        return;
    };

    match command.as_slice() {
        "build" => {
            println!("building from {} into {}", source.display(), dest.display());
            match cobalt::build(&source, &dest){
                Ok(_) => {},
                // TODO panic!
                Err(e) => panic!("{}", e)
            };
        },

        _ => {
            println!("{}", usage("cobalt", opts));
            return;
        }
    }
}
<commit_msg>remove obsolete comments, better usage instruction formatting<commit_after>extern crate cobalt;
extern crate getopts;

use getopts::{optopt, optflag, getopts, usage};
use std::os;

fn print_version() {
    // parse this from Cargo.toml
    let version = vec!["0", "0", "1"];

    println!("{}", version.connect("."));
}

fn main() {
    let args: Vec<String> = os::args();

    let opts = [
        optopt("s", "source", "Build from example/folder", "[example/folder]"),
        optopt("d", "destination", "Build into example/folder/build", "[example/folder]"),
        optflag("h", "help", "Print this help menu"),
        optflag("v", "version", "Display version")
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };

    let source = if matches.opt_present("s") {
        Path::new(matches.opt_str("s").unwrap())
    } else {
        Path::new(".".to_string())
    };

    let dest   = if matches.opt_present("d") {
        Path::new(matches.opt_str("d").unwrap())
    } else {
        Path::new(".".to_string())
    };

    if matches.opt_present("h") {
        println!("{}", usage("\n\tcobalt build", opts));
        return;
    }

    if matches.opt_present("v") {
        print_version();
        return;
    }

    let command = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        println!("{}", usage("\n\tcobalt build", opts));
        return;
    };

    match command.as_slice() {
        "build" => {
            println!("building from {} into {}", source.display(), dest.display());
            match cobalt::build(&source, &dest){
                Ok(_) => {},
                Err(e) => panic!("{}", e)
            };
        },

        _ => {
            println!("{}", usage("\n\tcobalt build", opts));
            return;
        }
    }
}
<|endoftext|>


<commit_before>extern crate time;

fn main() {
    let dow = ["Duinday", "Sitaday", "Wikiday", "Tuxday", "Gnuday", "Commonday"];
    let moy = ["Jabim", "Zodrak", "Trogool", "Yanar", "Shkumbe", "Habniah", "Skarl", "Mikon", "Pertunda", "Kib", "Broket"];
    let dpm = 33;
    let dpw = dow.len();
    let mpy = moy.len();
    let (year, month, day, yoff) = current_day(time::now());
    let wd = (yoff + month * 3 + day) % dpw;
    let mut moff = yoff;

    for mm in 0..mpy {
        let spaces = (17 - moy[mm].len()) / 2;
        for _ in 0..spaces {
            print!(" ");
        }
        println!("{}", moy[mm]);

        for dd in 0..dpw {
            let mut abbrd = String::from(dow[dd]);
            abbrd.truncate(2);
            print!("{} ", abbrd);
        }

        let dd = if mm == month { day } else { 1000 };
        moff = print_days(moff, dpm, dpw, dd);
    }

    println!("Today is {}, {:02} of {} {}.  [{}.{:02}.{:02}]", dow[wd], day, moy[month], year, year, month + 1, day);
}

fn print_days(moff: usize, dpm: usize, dpw: usize, highlight: usize) -> usize {
    if moff != 0 {
        println!("");
    }

    for _ in 0 .. moff {
        print!("   ");
    }

    for day in 1 .. dpm + 1 {
        if (day + moff) % dpw == 1 {
            println!("");
        }

        let ch = if day == highlight { ">" } else if day + 1 == highlight { "<" } else { " " };
        print!("{:2}{}", day, ch);
    }

    println!("");
    return if moff == 0 { 3 } else { 0 };
}

fn current_day(current_time: time::Tm) -> (usize, usize, usize, usize) {
    let epoch_days_offset = 1483889;
    let seconds_per_day = 86400;
    let now = current_time.to_timespec();
    let days_since_epoch = (epoch_days_offset + now.sec / seconds_per_day) as i32;
    let days_per_year = 365;
    let leap_year = 4;
    let not_leap_year = 33;
    let days_per_month = 33;

    let mut count = 1;
    let mut days_remaining = days_since_epoch;
    let mut year = 0;
    while days_remaining > days_per_year + 1 {
        days_remaining -= days_per_year;
        year += 1;
        if count > not_leap_year {
            count = 1;
        }
        if count % leap_year == 0 {
            days_remaining -= 1;
        }
        count += 1;
    }
    if days_remaining == days_per_year + 1 && count % leap_year == 0 {
        days_remaining = 0;
        year += 1;
    }

    let month = days_remaining / days_per_month;
    let day = days_remaining - month * days_per_month;
    return (year, month as usize, day as usize + 1, (year % 2 * 3) as usize);
}

<commit_msg>Track leap years<commit_after>extern crate time;

fn main() {
    let dow = ["Duinday", "Sitaday", "Wikiday", "Tuxday", "Gnuday", "Commonday"];
    let moy = ["Jabim", "Zodrak", "Trogool", "Yanar", "Shkumbe", "Habniah", "Skarl", "Mikon", "Pertunda", "Kib", "Broket"];
    let dpm = 33;
    let dpw = dow.len();
    let mpy = moy.len();
    let (year, month, day, yoff) = current_day(time::now());
    let wd = (yoff + month * 3 + day) % dpw;
    let mut moff = yoff;

    for mm in 0..mpy {
        let spaces = (17 - moy[mm].len()) / 2;
        for _ in 0..spaces {
            print!(" ");
        }
        println!("{}", moy[mm]);

        for dd in 0..dpw {
            let mut abbrd = String::from(dow[dd]);
            abbrd.truncate(2);
            print!("{} ", abbrd);
        }

        let dd = if mm == month { day } else { 1000 };
        moff = print_days(moff, dpm, dpw, dd);
    }

    println!("Today is {}, {:02} of {} {}.  [{}.{:02}.{:02}]", dow[wd], day, moy[month], year, year, month + 1, day);
}

fn print_days(moff: usize, dpm: usize, dpw: usize, highlight: usize) -> usize {
    if moff != 0 {
        println!("");
    }

    for _ in 0 .. moff {
        print!("   ");
    }

    for day in 1 .. dpm + 1 {
        if (day + moff) % dpw == 1 {
            println!("");
        }

        let ch = if day == highlight { ">" } else if day + 1 == highlight { "<" } else { " " };
        print!("{:2}{}", day, ch);
    }

    println!("");
    return if moff == 0 { 3 } else { 0 };
}

fn current_day(current_time: time::Tm) -> (usize, usize, usize, usize) {
    let epoch_days_offset = 1483889;
    let seconds_per_day = 86400;
    let now = current_time.to_timespec();
    let days_since_epoch = (epoch_days_offset + now.sec / seconds_per_day) as i32;
    let days_per_year = 365;
    let leap_year = 4;
    let not_leap_year = 33;
    let days_per_month = 33;

    let mut count = 1;
    let mut days_remaining = days_since_epoch;
    let mut year = 0;
    while days_remaining > days_per_year + 1 {
        days_remaining -= days_per_year;
        year += 1;
        if count > not_leap_year {
            count = 1;
        }
        if count % leap_year == 0 {
            days_remaining -= 1;
        }
        count += 1;
    }
    if days_remaining == days_per_year + 1 && count % leap_year == 0 {
        days_remaining = 0;
        year += 1;
    }

    let month = days_remaining / days_per_month;
    let day = days_remaining - month * days_per_month;
    return (year, month as usize, day as usize + 1, (year % 2 * 3) as usize);
}

fn is_leap_year(year: usize) -> bool {
    let rem = year % 33;
    return rem != 0 && rem % 4 == 0;
}

<|endoftext|>


<commit_before>extern crate regex;
extern crate getopts;
#[macro_use]
extern crate lazy_static;
use std::collections::HashMap;
use std::{env, fs, io};
use std::io::prelude::*;
mod options;
use options::*;
use std::process;
use std::collections::hash_map::Entry::{Occupied, Vacant};

macro_rules! regex { ($re:expr) => { ::regex::Regex::new($re).unwrap() } }
macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
        )
    );

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const KEY_FILE: &'static str = "KEY_FILE";
const ENV: &'static str = "ENV";
const MISSING: &'static str = "Missing properties";

fn replace_var(line: String,
               keys: &Option<KeysAndSources>,
               props_first: bool,
               used_keys: &mut HashMap<String, Vec<(String, String)>>,
               result: &mut Vec<String>) {
    let re = regex!(r"(?P<full>\$\{\s*(?P<var>[^\}]*)\s*\})");
    let mut str_line: String = line.clone();
    let mut splits: Vec<&str> = line.split("#").collect();
    let mut split_line: String = splits[0].to_string();

    for cap in re.captures_iter(&*split_line) {
        cap.name("var")
           .map(|v| {
               let env_prop = env_or_prop(v.trim(), props_first, keys)
                                  .map(|v2| {
                                      str_line = re.replace(&*split_line,
                                                            &*(v2.1.value));
                                      (v2.0, v2.1)
                                  });

               env_prop.and_then(|v2| {
                   used_keys.get_mut(&v2.1.source)
                            .and_then(|r| {
                                Some(r.push((v2.0.clone(), v2.1.value.clone())))
                            })
                            .or_else(|| {
                                used_keys.insert(v2.1.source.clone(),
                                                 vec![(v2.0.clone(),
                                                       v2.1.value.clone())]);
                                Some(())
                            })
               })
           });
    }
    result.push(str_line.to_string());
}

fn no_property(key: &str,
               env: Option<String>)
               -> Option<(String, ValueAndSource)> {
    match env {
        Some(e) => {
            Some((key.to_string(),
                  ValueAndSource {
                value: e.to_string(),
                source: ENV.to_string(),
            }))
        }
        None => {
            Some((key.to_string(),
                  ValueAndSource {
                value: key.to_string(),
                source: MISSING.to_string(),
            }))
        }
    }
}

fn env_or_prop(key: &str,
               props_first: bool,
               keys: &Option<KeysAndSources>)
               -> Option<(String, ValueAndSource)> {
    match keys {
        &Some(ref k) => {
            let dict = &(k.dictionary);
            let prop = dict.get(key).and_then(|v| Some(v.clone()));
            let env = env::var_os(key)
                          .map(|v2| v2.clone().into_string().ok().unwrap());
            match (prop, props_first) {
                (Some(prop), true) => {
                    Some((key.to_string(),
                          ValueAndSource {
                        value: prop.value.clone(),
                        source: prop.source.clone(),
                    }))
                }
                (Some(prop), false) => {
                    match env {
                        Some(e) => {
                            println!("[WARNING]: key \"${{{}}}\" from \"{}\" \
                                      was overridden by an environment \
                                      variable.",
                                     key.to_string(),
                                     prop.source);
                            Some((key.to_string(),
                                  ValueAndSource {
                                value: e.to_string(),
                                source: ENV.to_string(),
                            }))
                        }
                        None => {
                            Some((key.to_string(),
                                  ValueAndSource {
                                value: prop.value.clone(),
                                source: prop.source.clone(),
                            }))
                        }
                    }
                }
                (None, true) => {
                    no_property(key, env)
                }
                (None, false) => {
                    no_property(key, env)
                }
            }
        }
        &None => {
            match env::var_os(key)
                      .map(|v| v.into_string().ok())
                      .unwrap_or(None) {
                Some(e) => {
                    Some((key.to_string(),
                          ValueAndSource {
                        value: e.to_string(),
                        source: ENV.to_string(),
                    }))
                }
                None => {
                    Some((key.to_string(),
                          ValueAndSource {
                        value: key.to_string(),
                        source: MISSING.to_string(),
                    }))
                }
            }
        }
    }
}

fn process(file: fs::File,
           used_keys: &mut HashMap<String, Vec<(String, String)>>,
           dict: &Option<KeysAndSources>,
           props_first: bool)
           -> Vec<String> {
    let buf_reader = io::BufReader::new(&file);
    let mut result: Vec<String> = vec![];

    for line in buf_reader.lines() {
        replace_var(line.unwrap(), &dict, props_first, used_keys, &mut result);
    }

    result
}

fn extract_keys(file: fs::File) -> HashMap<String, String> {
    let key_regex = regex!(r"^\s*(?P<key>\S*)\s*=\s*(?P<val>.*)");
    let buf_reader = io::BufReader::new(&file);
    let mut keys = HashMap::new();

    for line in buf_reader.lines().enumerate() {
        let curr_line: String = line.1.unwrap();
        let splits: Vec<&str> = curr_line.split("#").collect();
        //&*line.1
        //      .ok()
        //      .expect("Could not read line");
        for cap in key_regex.captures_iter(splits[0]) {
            let key = cap.name("key")
                         .unwrap_or("???");
            let val = cap.name("val")
                         .unwrap_or("???");
            if !keys.contains_key(key) {
                keys.insert(key.to_string(), val.to_string());
            }
        }
    }

    keys
}

#[derive(Debug)]
#[derive(Clone)]
struct ValueAndSource {
    value: String,
    source: String,
}

fn insert_if_not_exist(dict: &mut HashMap<String, ValueAndSource>,
                       keys_from_files: &mut HashMap<String, Vec<String>>,
                       matches: HashMap<String, String>,
                       filename: String) {
    for key_val in matches.iter() {
        let key: String = (*key_val.0).clone();
        let val: String = (*key_val.1).clone();
        match dict.entry(key.clone()) {
            Vacant(e) => {
                e.insert(ValueAndSource {
                    value: val,
                    source: filename.clone(),
                });
                match keys_from_files.entry(filename.clone()) {
                    Vacant(k) => {
                        k.insert(vec![key.clone()]);
                    }
                    Occupied(mut k) => {
                        k.get_mut().push(key.clone());
                    }
                }

                // keys_from_files.insert(filename.clone(), key.clone());
            }
            Occupied(e) => {
                println!("[ERROR]: key ({}) in file ({}) tried to override \
                          the same key from file ({})",
                         key,
                         filename,
                         e.get().source);
                process::exit(1);
            }
        }
    }
}

fn read_keyfiles_to_dict(key_filenames: Vec<String>) -> Option<KeysAndSources> {
    let mut dict: HashMap<String, ValueAndSource> = HashMap::new();
    let mut keys_from_files = HashMap::new();
    for filename in key_filenames {
        fs::File::open(&filename)
            .and_then(|f| Ok(extract_keys(f)))
            .map(|v| {
                insert_if_not_exist(&mut dict,
                                    &mut keys_from_files,
                                    v,
                                    filename);
            });
    }

    match dict.len() {
        0 => None,
        _ => {
            Some(KeysAndSources {
                dictionary: Box::new(dict),
                sources: Box::new(keys_from_files),
            })
        }
    }
}

fn create_file(filename: &String) -> fs::File {
    match fs::File::create(filename) {
        Ok(f) => Some(f),
        Err(e) => {
            println_stderr!("[ERROR]: Could not create file: {}\n{}\n",
                            filename,
                            e);
            APP.print_usage_and_panic();
            None
        }
    }
    .unwrap()
}

fn open_file(filename: &String) -> fs::File {
    match fs::File::open(filename) {
        Ok(f) => Some(f),
        Err(e) => {
            println_stderr!("[ERROR]: Could not open file: {}\n{}\n",
                            filename,
                            e);
            APP.print_usage_and_panic();
            None
        }
    }
    .unwrap()
}

struct Settings<'a> {
    key_filenames: Vec<String>,
    prop_filename: &'a String,
    result_filename: &'a String,
    props_first: bool,
}

struct KeysAndSources {
    dictionary: Box<HashMap<String, ValueAndSource>>,
    sources: Box<HashMap<String, Vec<String>>>,
}

fn print_result(used_keys: &mut HashMap<String, Vec<(String, String)>>,
                prop_filename: &String) {

    println!("======== RESULTS ========");

    let env_vars = &used_keys.remove(ENV);
    if env_vars.is_some() {
        let vars = env_vars.clone().unwrap();
        println!("# Environment variables:");
        for v in vars {
            println!("${{{}}}", v.0);
        }
        println!("");
    }

    let missing_vars = used_keys.remove(MISSING);

    for src in used_keys.iter() {
        println!("# {}:", src.0);
        let mut keys_src = src.1.clone();
        keys_src.sort();
        for s in keys_src {
            println!("${{{}}}", s.0);
        }
        println!("");
    }

    if missing_vars.is_some() {
        let vars = missing_vars.unwrap();
        println!("Missing variables found in {}:", prop_filename);
        for v in vars {
            println!("${{{}}}", v.0);
        }
        println!("");
        println_stderr!("ERROR: Must replace all found variables\n\n");
        process::exit(1);
    }

    if env_vars.is_none() && used_keys.len() == 0 && missing_vars.is_none() {
        println!("[No variables to substitute!]\n");
    }

    println!("...DONE!");
}

fn calc_result(settings: Settings) {
    let prop_file = open_file(&settings.prop_filename);
    let mut result_buff = Vec::new();
    let mut used_keys = HashMap::new();
    let keys_and_sources = read_keyfiles_to_dict(settings.key_filenames);

    let processed_buffer = process(prop_file,
                                   &mut used_keys,
                                   &keys_and_sources,
                                   settings.props_first);

    for line in processed_buffer.iter() {
        result_buff.push(line.clone());
    }

    let mut result_file = create_file(settings.result_filename);

    for line in result_buff {
        result_file.write_all(&format!("{}\n", line).as_bytes()).unwrap();
    }
    result_file.flush().unwrap();

    print_result(&mut used_keys, settings.prop_filename);
}

fn get_arg_parser() -> ApplicationOptions {
    ApplicationOptions::new(|| {
        let mut o = OptionsAndFlags::new();
        o.parsing_style(getopts::ParsingStyle::StopAtFirstFree);
        o.opt("k",
              "keys",
              "keyfile(s) with variable substitutions, can occur multiple \
               times",
              "FILE(s)",
              getopts::HasArg::Yes,
              getopts::Occur::Multi)
         .unwrap();
        o.opt("p",
              "props-first",
              "properties takes precedence over environment variables \
               (default: off)",
              "",
              getopts::HasArg::No,
              getopts::Occur::Optional)
         .unwrap();
        o.opt("?",
              "help",
              "print this help menu",
              "",
              getopts::HasArg::No,
              getopts::Occur::Optional)
         .unwrap();
        o.opt("V",
              "version",
              "prints current version number",
              "",
              getopts::HasArg::No,
              getopts::Occur::Optional)
         .unwrap();
        Ok(o)
    })
}

struct PropAndResultFilenames<'a> {
    prop_filename: &'a String,
    result_filename: &'a String,
}

fn get_prop_and_result_filename<'a>(free_args: &'a Vec<String>)
                                    -> PropAndResultFilenames<'a> {
    match free_args.len() {
        1 => {
            // Replace vars in the provided file
            PropAndResultFilenames {
                prop_filename: &free_args[0],
                result_filename: &free_args[0],
            }
        }
        2 => {
            // Replace vars into a separate output file
            PropAndResultFilenames {
                prop_filename: &free_args[0],
                result_filename: &free_args[1],
            }
        }
        _ => {
            println_stderr!("Wrong number of file parameters: {}",
                            free_args.len());
            APP.print_usage();
            process::exit(1);
        }
    }
}

fn get_props_first(matches: &Box<getopts::Matches>) -> bool {
    if matches.opt_present("p") {
        true
    } else {
        false
    }

}

fn get_parsed_args() -> Box<getopts::Matches> {
    Box::new(APP.parse().unwrap_or_else(|e| {
        println_stderr!("{}", e);
        process::exit(1);
    }))
}

// Singletons
lazy_static! {
    static ref APP: ApplicationOptions = get_arg_parser();
}

#[allow(dead_code)]
fn main() {
    let matches = get_parsed_args();

    if matches.opt_present("V") {
        println!("proper: v{}", VERSION);
        process::exit(0);
    }

    let prop_and_result_filenames = get_prop_and_result_filename(&matches.free);
    calc_result(Settings {
        key_filenames: matches.opt_strs("k"),
        prop_filename: prop_and_result_filenames.prop_filename,
        result_filename: prop_and_result_filenames.result_filename,
        props_first: get_props_first(&matches),
    });
}

#[cfg(test)]
mod tests {
    use super::replace_var;
    use std::collections::HashMap;
    use super::ValueAndSource;
    use super::KeysAndSources;

    struct TestData {
        key_map: KeysAndSources,
        used_keys: HashMap<String, Vec<(String, String)>>,
        result: Vec<String>,
    }

    fn create_test_data(keys: Vec<(&str, &str)>) -> TestData {
        let mut test_data = TestData {
            key_map: KeysAndSources {
                dictionary: Box::new(HashMap::new()),
                sources: Box::new(HashMap::new()),
            },
            used_keys: HashMap::new(),
            result: Vec::new(),
        };

        for k in keys {
            test_data.key_map.dictionary.insert(k.0.to_string(),
                                                ValueAndSource {
                                                    value: k.1.to_string(),
                                                    source: "source_file"
                                                                .to_string(),
                                                });
        }

        test_data
    }

    #[test]
    fn can_sub_single_var() {
        let mut test_data = create_test_data(vec![("val", "world!")]);
        replace_var("Hello ${val}".to_string(),
                    &Some(test_data.key_map),
                    true,
                    &mut test_data.used_keys,
                    &mut test_data.result);
        assert!(test_data.result.len() == 1);
        assert!(test_data.used_keys["source_file"][0].0 == "val".to_string());
        assert!(test_data.result[0] == "Hello world!".to_string());
    }
}
<commit_msg>Fixed all compile-warnings. Needs further cleanup.<commit_after>extern crate regex;
extern crate getopts;
#[macro_use]
extern crate lazy_static;
use std::collections::HashMap;
use std::{env, fs, io};
use std::io::prelude::*;
mod options;
use options::*;
use std::process;
use std::collections::hash_map::Entry::{Occupied, Vacant};

macro_rules! regex { ($re:expr) => { ::regex::Regex::new($re).unwrap() } }
macro_rules! println_stderr(
    ($($arg:tt)*) => (
        match writeln!(&mut ::std::io::stderr(), $($arg)* ) {
            Ok(_) => {},
            Err(x) => panic!("Unable to write to stderr: {}", x),
        }
        )
    );

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const ENV: &'static str = "ENV";
const MISSING: &'static str = "Missing properties";

fn replace_var(line: String,
               keys: &Option<KeysAndSources>,
               props_first: bool,
               used_keys: &mut HashMap<String, Vec<(String, String)>>,
               result: &mut Vec<String>) {
    let re = regex!(r"(?P<full>\$\{\s*(?P<var>[^\}]*)\s*\})");
    let mut str_line: String = line.clone();
    let splits: Vec<&str> = line.split("#").collect();
    let split_line: String = splits[0].to_string();

    for cap in re.captures_iter(&*split_line) {
        cap.name("var")
           .map(|v| {
               let env_prop = env_or_prop(v.trim(), props_first, keys)
                                  .map(|v2| {
                                      str_line = re.replace(&*split_line,
                                                            &*(v2.1.value));
                                      (v2.0, v2.1)
                                  });

               env_prop.and_then(|v2| {
                   used_keys.get_mut(&v2.1.source)
                            .and_then(|r| {
                                Some(r.push((v2.0.clone(), v2.1.value.clone())))
                            })
                            .or_else(|| {
                                used_keys.insert(v2.1.source.clone(),
                                                 vec![(v2.0.clone(),
                                                       v2.1.value.clone())]);
                                Some(())
                            })
               })
           });
    }
    result.push(str_line.to_string());
}

fn no_property(key: &str,
               env: Option<String>)
               -> Option<(String, ValueAndSource)> {
    match env {
        Some(e) => {
            Some((key.to_string(),
                  ValueAndSource {
                value: e.to_string(),
                source: ENV.to_string(),
            }))
        }
        None => {
            Some((key.to_string(),
                  ValueAndSource {
                value: key.to_string(),
                source: MISSING.to_string(),
            }))
        }
    }
}

fn env_or_prop(key: &str,
               props_first: bool,
               keys: &Option<KeysAndSources>)
               -> Option<(String, ValueAndSource)> {
    match keys {
        &Some(ref k) => {
            let dict = &(k.dictionary);
            let prop = dict.get(key).and_then(|v| Some(v.clone()));
            let env = env::var_os(key)
                          .map(|v2| v2.clone().into_string().ok().unwrap());
            match (prop, props_first) {
                (Some(prop), true) => {
                    Some((key.to_string(),
                          ValueAndSource {
                        value: prop.value.clone(),
                        source: prop.source.clone(),
                    }))
                }
                (Some(prop), false) => {
                    match env {
                        Some(e) => {
                            println!("[WARNING]: key \"${{{}}}\" from \"{}\" \
                                      was overridden by an environment \
                                      variable.",
                                     key.to_string(),
                                     prop.source);
                            Some((key.to_string(),
                                  ValueAndSource {
                                value: e.to_string(),
                                source: ENV.to_string(),
                            }))
                        }
                        None => {
                            Some((key.to_string(),
                                  ValueAndSource {
                                value: prop.value.clone(),
                                source: prop.source.clone(),
                            }))
                        }
                    }
                }
                (None, true) => {
                    no_property(key, env)
                }
                (None, false) => {
                    no_property(key, env)
                }
            }
        }
        &None => {
            match env::var_os(key)
                      .map(|v| v.into_string().ok())
                      .unwrap_or(None) {
                Some(e) => {
                    Some((key.to_string(),
                          ValueAndSource {
                        value: e.to_string(),
                        source: ENV.to_string(),
                    }))
                }
                None => {
                    Some((key.to_string(),
                          ValueAndSource {
                        value: key.to_string(),
                        source: MISSING.to_string(),
                    }))
                }
            }
        }
    }
}

fn process(file: fs::File,
           used_keys: &mut HashMap<String, Vec<(String, String)>>,
           dict: &Option<KeysAndSources>,
           props_first: bool)
           -> Vec<String> {
    let buf_reader = io::BufReader::new(&file);
    let mut result: Vec<String> = vec![];

    for line in buf_reader.lines() {
        replace_var(line.unwrap(), &dict, props_first, used_keys, &mut result);
    }

    result
}

fn extract_keys(file: fs::File) -> HashMap<String, String> {
    let key_regex = regex!(r"^\s*(?P<key>\S*)\s*=\s*(?P<val>.*)");
    let buf_reader = io::BufReader::new(&file);
    let mut keys = HashMap::new();

    for line in buf_reader.lines().enumerate() {
        let curr_line: String = line.1.unwrap();
        let splits: Vec<&str> = curr_line.split("#").collect();
        for cap in key_regex.captures_iter(splits[0]) {
            let key = cap.name("key")
                         .unwrap_or("???");
            let val = cap.name("val")
                         .unwrap_or("???");
            if !keys.contains_key(key) {
                keys.insert(key.to_string(), val.to_string());
            }
        }
    }

    keys
}

#[derive(Debug)]
#[derive(Clone)]
struct ValueAndSource {
    value: String,
    source: String,
}

fn insert_if_not_exist(dict: &mut HashMap<String, ValueAndSource>,
                       keys_from_files: &mut HashMap<String, Vec<String>>,
                       matches: HashMap<String, String>,
                       filename: String) {
    for key_val in matches.iter() {
        let key: String = (*key_val.0).clone();
        let val: String = (*key_val.1).clone();
        match dict.entry(key.clone()) {
            Vacant(e) => {
                e.insert(ValueAndSource {
                    value: val,
                    source: filename.clone(),
                });
                match keys_from_files.entry(filename.clone()) {
                    Vacant(k) => {
                        k.insert(vec![key.clone()]);
                    }
                    Occupied(mut k) => {
                        k.get_mut().push(key.clone());
                    }
                }

                // keys_from_files.insert(filename.clone(), key.clone());
            }
            Occupied(e) => {
                println!("[ERROR]: key ({}) in file ({}) tried to override \
                          the same key from file ({})",
                         key,
                         filename,
                         e.get().source);
                process::exit(1);
            }
        }
    }
}

fn read_keyfiles_to_dict(key_filenames: Vec<String>) -> Option<KeysAndSources> {
    let mut dict: HashMap<String, ValueAndSource> = HashMap::new();
    let mut keys_from_files = HashMap::new();
    for filename in key_filenames {
        fs::File::open(&filename)
            .and_then(|f| Ok(extract_keys(f)))
            .map(|v| {
                insert_if_not_exist(&mut dict,
                                    &mut keys_from_files,
                                    v,
                                    filename);
            }).unwrap();
    }

    match dict.len() {
        0 => None,
        _ => {
            Some(KeysAndSources {
                dictionary: Box::new(dict),
                //sources: Box::new(keys_from_files),
            })
        }
    }
}

fn create_file(filename: &String) -> fs::File {
    match fs::File::create(filename) {
        Ok(f) => Some(f),
        Err(e) => {
            println_stderr!("[ERROR]: Could not create file: {}\n{}\n",
                            filename,
                            e);
            APP.print_usage_and_panic();
            None
        }
    }
    .unwrap()
}

fn open_file(filename: &String) -> fs::File {
    match fs::File::open(filename) {
        Ok(f) => Some(f),
        Err(e) => {
            println_stderr!("[ERROR]: Could not open file: {}\n{}\n",
                            filename,
                            e);
            APP.print_usage_and_panic();
            None
        }
    }
    .unwrap()
}

struct Settings<'a> {
    key_filenames: Vec<String>,
    prop_filename: &'a String,
    result_filename: &'a String,
    props_first: bool,
}

struct KeysAndSources {
    dictionary: Box<HashMap<String, ValueAndSource>>,
    //sources: Box<HashMap<String, Vec<String>>>,
}

fn print_result(used_keys: &mut HashMap<String, Vec<(String, String)>>,
                prop_filename: &String) {

    println!("======== RESULTS ========");

    let env_vars = &used_keys.remove(ENV);
    if env_vars.is_some() {
        let vars = env_vars.clone().unwrap();
        println!("# Environment variables:");
        for v in vars {
            println!("${{{}}}", v.0);
        }
        println!("");
    }

    let missing_vars = used_keys.remove(MISSING);

    for src in used_keys.iter() {
        println!("# {}:", src.0);
        let mut keys_src = src.1.clone();
        keys_src.sort();
        for s in keys_src {
            println!("${{{}}}", s.0);
        }
        println!("");
    }

    if missing_vars.is_some() {
        let vars = missing_vars.unwrap();
        println!("Missing variables found in {}:", prop_filename);
        for v in vars {
            println!("${{{}}}", v.0);
        }
        println!("");
        println_stderr!("ERROR: Must replace all found variables\n\n");
        process::exit(1);
    }

    if env_vars.is_none() && used_keys.len() == 0 && missing_vars.is_none() {
        println!("[No variables to substitute!]\n");
    }

    println!("...DONE!");
}

fn calc_result(settings: Settings) {
    let prop_file = open_file(&settings.prop_filename);
    let mut result_buff = Vec::new();
    let mut used_keys = HashMap::new();
    let keys_and_sources = read_keyfiles_to_dict(settings.key_filenames);

    let processed_buffer = process(prop_file,
                                   &mut used_keys,
                                   &keys_and_sources,
                                   settings.props_first);

    for line in processed_buffer.iter() {
        result_buff.push(line.clone());
    }

    let mut result_file = create_file(settings.result_filename);

    for line in result_buff {
        result_file.write_all(&format!("{}\n", line).as_bytes()).unwrap();
    }
    result_file.flush().unwrap();

    print_result(&mut used_keys, settings.prop_filename);
}

fn get_arg_parser() -> ApplicationOptions {
    ApplicationOptions::new(|| {
        let mut o = OptionsAndFlags::new();
        o.parsing_style(getopts::ParsingStyle::StopAtFirstFree);
        o.opt("k",
              "keys",
              "keyfile(s) with variable substitutions, can occur multiple \
               times",
              "FILE(s)",
              getopts::HasArg::Yes,
              getopts::Occur::Multi)
         .unwrap();
        o.opt("p",
              "props-first",
              "properties takes precedence over environment variables \
               (default: off)",
              "",
              getopts::HasArg::No,
              getopts::Occur::Optional)
         .unwrap();
        o.opt("?",
              "help",
              "print this help menu",
              "",
              getopts::HasArg::No,
              getopts::Occur::Optional)
         .unwrap();
        o.opt("V",
              "version",
              "prints current version number",
              "",
              getopts::HasArg::No,
              getopts::Occur::Optional)
         .unwrap();
        Ok(o)
    })
}

struct PropAndResultFilenames<'a> {
    prop_filename: &'a String,
    result_filename: &'a String,
}

fn get_prop_and_result_filename<'a>(free_args: &'a Vec<String>)
                                    -> PropAndResultFilenames<'a> {
    match free_args.len() {
        1 => {
            // Replace vars in the provided file
            PropAndResultFilenames {
                prop_filename: &free_args[0],
                result_filename: &free_args[0],
            }
        }
        2 => {
            // Replace vars into a separate output file
            PropAndResultFilenames {
                prop_filename: &free_args[0],
                result_filename: &free_args[1],
            }
        }
        _ => {
            println_stderr!("Wrong number of file parameters: {}",
                            free_args.len());
            APP.print_usage();
            process::exit(1);
        }
    }
}

fn get_props_first(matches: &Box<getopts::Matches>) -> bool {
    if matches.opt_present("p") {
        true
    } else {
        false
    }

}

fn get_parsed_args() -> Box<getopts::Matches> {
    Box::new(APP.parse().unwrap_or_else(|e| {
        println_stderr!("{}", e);
        process::exit(1);
    }))
}

// Singletons
lazy_static! {
    static ref APP: ApplicationOptions = get_arg_parser();
}

#[allow(dead_code)]
fn main() {
    let matches = get_parsed_args();

    if matches.opt_present("V") {
        println!("proper: v{}", VERSION);
        process::exit(0);
    }

    let prop_and_result_filenames = get_prop_and_result_filename(&matches.free);
    calc_result(Settings {
        key_filenames: matches.opt_strs("k"),
        prop_filename: prop_and_result_filenames.prop_filename,
        result_filename: prop_and_result_filenames.result_filename,
        props_first: get_props_first(&matches),
    });
}

#[cfg(test)]
mod tests {
    use super::replace_var;
    use std::collections::HashMap;
    use super::ValueAndSource;
    use super::KeysAndSources;

    struct TestData {
        key_map: KeysAndSources,
        used_keys: HashMap<String, Vec<(String, String)>>,
        result: Vec<String>,
    }

    fn create_test_data(keys: Vec<(&str, &str)>) -> TestData {
        let mut test_data = TestData {
            key_map: KeysAndSources {
                dictionary: Box::new(HashMap::new()),
                //sources: Box::new(HashMap::new()),
            },
            used_keys: HashMap::new(),
            result: Vec::new(),
        };

        for k in keys {
            test_data.key_map.dictionary.insert(k.0.to_string(),
                                                ValueAndSource {
                                                    value: k.1.to_string(),
                                                    source: "source_file"
                                                                .to_string(),
                                                });
        }

        test_data
    }

    #[test]
    fn can_sub_single_var() {
        let mut test_data = create_test_data(vec![("val", "world!")]);
        replace_var("Hello ${val}".to_string(),
                    &Some(test_data.key_map),
                    true,
                    &mut test_data.used_keys,
                    &mut test_data.result);
        assert!(test_data.result.len() == 1);
        assert!(test_data.used_keys["source_file"][0].0 == "val".to_string());
        assert!(test_data.result[0] == "Hello world!".to_string());
    }
}
<|endoftext|>


<commit_before>extern crate clap;
use clap::{Arg, App, AppSettings};
use slog::{Logger, LevelFilter, Level, DrainExt};
use yaml_rust::{YamlLoader};
use std::io::prelude::*;
use std::fs::File;
use yaml_rust::Yaml;
use std::collections::BTreeMap;

#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate yaml_rust;

fn main() {
  let matches: clap::ArgMatches = create_app().get_matches();
  let stream = slog_term::streamer().full().build();
  let verbose: bool = matches.is_present("verbose");
  let log = if verbose {
    slog::Logger::root(stream.fuse(), o!())
  } else {
    slog::Logger::root(LevelFilter::new(stream, Level::Info).fuse(), o!())
  };

  let config_file = matches.value_of("config_file").unwrap();
  info!(log, "Starting engine"; "config_file" => config_file);

  let config = match load_config_file(config_file) {
    Ok(content) => content,
    Err(e) => {
      error!(log, "Failed to load config file."; "error" => format!("{:?}", e));
      panic!(1)
    }
  };

  let yaml_documents = match YamlLoader::load_from_str(&config) {
    Ok(yaml) => yaml,
    Err(e) => {
      error!(log, "Failed to parse config file."; "error" => format!("{:?}", e));
      panic!(2)
    }
  };
  let yaml_config = &yaml_documents[0];
  let dot_files: &Yaml = &yaml_config["files"];
  if dot_files.is_badvalue() {
    warn!(log, "Empty files list");
  }

  info!(log, "Parsed config file. Liftoff!");
  process_dot_files(&log, dot_files);
}

fn process_dot_files(log: &Logger, dot_files: &Yaml) {
  info!(log, "Processing dotfiles");
  let maybe_entries: Option<BTreeMap<Yaml, Yaml>> = dot_files.clone().into_hash();
  if let Some(entries) = maybe_entries {
    for (key, value) in entries {
      match (key.as_str(), value.as_str()) {
        (Some(target), Some(source)) =>
          info!(log, "Process entry"; "target" => target, "source" => source),
        _ => {}
      }
    }
  } else {
    warn!(log, "Found no entries to process");
  }
}

fn load_config_file(file: &str) -> Result<String, std::io::Error> {
  let mut file = try!(File::open(file));
  let mut content = String::new();
  try!(file.read_to_string(&mut content));
  return Ok(content)
}

fn create_app<'a>() -> App<'a, 'a> {
  App::new("dotcopter")
    .version("0.1")
    .setting(AppSettings::ColoredHelp)
    .author("Patrick Haun <bomgar85@googlemail.com>")
    .about("manages dotfiles installation")
    .arg(Arg::with_name("force").short("f").long("force").takes_value(false))
    .arg(Arg::with_name("verbose")
           .long("verbose")
           .short("v")
           .help("debug output")
           .required(false)
           .takes_value(false))
    .arg(Arg::with_name("config_file").required(true))
}
<commit_msg>add dot_file struct<commit_after>extern crate clap;
use clap::{Arg, App, AppSettings};
use slog::{Logger, LevelFilter, Level, DrainExt};
use yaml_rust::{YamlLoader};
use std::io::prelude::*;
use std::fs::File;
use yaml_rust::Yaml;
use std::collections::BTreeMap;

#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate yaml_rust;

#[derive(Debug)]
struct DotFile {
  source: String,
  target: String
}

fn main() {
  let matches: clap::ArgMatches = create_app().get_matches();
  let stream = slog_term::streamer().full().build();
  let verbose: bool = matches.is_present("verbose");
  let log = if verbose {
    slog::Logger::root(stream.fuse(), o!())
  } else {
    slog::Logger::root(LevelFilter::new(stream, Level::Info).fuse(), o!())
  };

  let config_file = matches.value_of("config_file").unwrap();
  info!(log, "Starting engine"; "config_file" => config_file);

  let config = match load_config_file(config_file) {
    Ok(content) => content,
    Err(e) => {
      error!(log, "Failed to load config file."; "error" => format!("{:?}", e));
      panic!(1)
    }
  };

  let yaml_documents = match YamlLoader::load_from_str(&config) {
    Ok(yaml) => yaml,
    Err(e) => {
      error!(log, "Failed to parse config file."; "error" => format!("{:?}", e));
      panic!(2)
    }
  };
  let yaml_config = &yaml_documents[0];
  let dot_files: &Yaml = &yaml_config["files"];
  if dot_files.is_badvalue() {
    warn!(log, "Empty files list");
  }

  info!(log, "Parsed config file. Liftoff!");
  process_dot_files(&log, dot_files);
}

fn process_dot_files(log: &Logger, dot_files: &Yaml) {
  info!(log, "Processing dotfiles");
  let maybe_entries: Option<BTreeMap<Yaml, Yaml>> = dot_files.clone().into_hash();
  if let Some(entries) = maybe_entries {
    for (key, value) in entries {
      match (key.as_str(), value.as_str()) {
        (Some(target), Some(source)) =>
          process_dot_file(log, DotFile{ source: source.to_string(), target: target.to_string() }),
        _ => {}
      }
    }
  } else {
    warn!(log, "Found no entries to process");
  }
}

fn process_dot_file(log: &Logger, dot_file: DotFile) {
  info!(log, "Process entry"; "target" => dot_file.target, "source" => dot_file.source);
}

fn load_config_file(file: &str) -> Result<String, std::io::Error> {
  let mut file = try!(File::open(file));
  let mut content = String::new();
  try!(file.read_to_string(&mut content));
  return Ok(content)
}

fn create_app<'a>() -> App<'a, 'a> {
  App::new("dotcopter")
    .version("0.1")
    .setting(AppSettings::ColoredHelp)
    .author("Patrick Haun <bomgar85@googlemail.com>")
    .about("manages dotfiles installation")
    .arg(Arg::with_name("force").short("f").long("force").takes_value(false))
    .arg(Arg::with_name("verbose")
           .long("verbose")
           .short("v")
           .help("debug output")
           .required(false)
           .takes_value(false))
    .arg(Arg::with_name("config_file").required(true))
}
<|endoftext|>


<commit_before>extern crate discord;
extern crate websocket;

use discord::{Discord, ChannelRef, State};
use discord::model::{Event, User, ReactionEmoji};
use websocket::result::WebSocketError;

const REACTION_EMOJI: &'static str = "rot13";

fn rot13(c: char) -> char {
    let base: u8 = match c {
        'a'...'z' => 'a' as u8,
        'A'...'Z' => 'A' as u8,
        _ => return c
    };

    let ordinal = c as u8 - base;
    let rot = (ordinal + 13) % 26;
    (rot + base) as char
}

fn main() {
    use std::env;
    println!("The Rotting 13!");

    let discord = Discord::from_bot_token(
        &env::var("DISCORD_TOKEN").expect("Expected token")
    ).expect("login failed!");

    let (mut connection, ready) = discord.connect().expect("Connection failed");
    let mut state = State::new(ready);

    println!("Connected!");

    loop {
        let event = match connection.recv_event() {
            Ok(event) => event,
            Err(err) => {
                if let discord::Error::WebSocket(ws_err) = err {
                    match ws_err {
                        WebSocketError::IoError(_) => {},
                        _ => {
                            // We were disconnected, try to reconnect
                            println!("Reconnecting...");
                            let (new_connection, ready) = discord.connect().expect("Reconnect failed");
                            connection = new_connection;
                            state = State::new(ready);
                            println!("Reconnected!");
                        },
                    }
                    continue
                }

                if let discord::Error::Closed(code, body) = err {
                    println!("[Error] Connection closed with status {:?}: {}", code, body);
                    break
                }

                println!("[Warning] Receive error: {:?}", err);
                continue
            }
        };
        state.update(&event);

        match event {
            Event::MessageCreate(message) => {
                if message.author.id == state.user().id {
                    continue
                }

                match state.find_channel(message.channel_id) {
                    Some(ChannelRef::Private(channel)) => {
                        let original_message = message.content;
                        let mut new_message = String::new();
                        for chr in original_message.chars() {
                            new_message.push(rot13(chr));
                        }

                        let _ = discord.send_message(channel.id, &new_message, "", false);
                    },
                    Some(ChannelRef::Public(_, channel)) => {
                        // state.user() is not of the same type as message.mentions
                        // Is there a better way to do this?
                        let res: Vec<&User> = message.mentions.iter()
                            .filter(|&u| u.id == state.user().id)
                            .collect();

                        if res.len() > 0 {
                            let _ = discord.send_message(channel.id, "I encode and decode ROT13 messages, just send me a DM!", "", false);
                        }
                    },
                    None => println!("Got a message from an unknown channel??? From {} saying {}", message.author.name, message.content),
                    _ => {},
                }
            },
            Event::ReactionAdd(reaction) => {
                let received_emoji = match reaction.emoji {
                    ReactionEmoji::Custom { name, .. } => name,
                    ReactionEmoji::Unicode(emoji) => emoji
                };

                if received_emoji != REACTION_EMOJI {
                    continue
                }

                let message = match discord.get_message(reaction.channel_id, reaction.message_id) {
                    Ok(msg) => msg,
                    Err(_) => {
                        println!("Could not find the message that was reacted to. Message ID {}", reaction.message_id);
                        continue
                    }
                };

                match discord.create_private_channel(reaction.user_id) {
                    Ok(channel) => {
                        let original_message = message.content;
                        let mut new_message = String::new();
                        for chr in original_message.chars() {
                            new_message.push(rot13(chr));
                        }

                        let _ = discord.send_message(channel.id, &new_message, "", false);
                    },
                    Err(_) => println!("Got an invalid reaction??? From user ID {} on message ID {}", reaction.user_id, reaction.message_id),
                }
            },
            _ => {}
        }
    }
}<commit_msg>Added clever Playing presence message.<commit_after>extern crate discord;
extern crate websocket;

use discord::{Discord, ChannelRef, State};
use discord::model::{Event, User, ReactionEmoji};
use websocket::result::WebSocketError;

const REACTION_EMOJI: &'static str = "rot13";

fn rot13(c: char) -> char {
    let base: u8 = match c {
        'a'...'z' => 'a' as u8,
        'A'...'Z' => 'A' as u8,
        _ => return c
    };

    let ordinal = c as u8 - base;
    let rot = (ordinal + 13) % 26;
    (rot + base) as char
}

fn main() {
    use std::env;
    println!("The Rotting 13!");

    let discord = Discord::from_bot_token(
        &env::var("DISCORD_TOKEN").expect("Expected token")
    ).expect("login failed!");

    let (mut connection, ready) = discord.connect().expect("Connection failed");
    let mut state = State::new(ready);

    println!("Connected!");

    let game_name = String::from("with words");
    connection.set_game_name(game_name);

    loop {
        let event = match connection.recv_event() {
            Ok(event) => event,
            Err(err) => {
                if let discord::Error::WebSocket(ws_err) = err {
                    match ws_err {
                        WebSocketError::IoError(_) => {},
                        _ => {
                            // We were disconnected, try to reconnect
                            println!("Reconnecting...");
                            let (new_connection, ready) = discord.connect().expect("Reconnect failed");
                            connection = new_connection;
                            state = State::new(ready);
                            println!("Reconnected!");
                        },
                    }
                    continue
                }

                if let discord::Error::Closed(code, body) = err {
                    println!("[Error] Connection closed with status {:?}: {}", code, body);
                    break
                }

                println!("[Warning] Receive error: {:?}", err);
                continue
            }
        };
        state.update(&event);

        match event {
            Event::MessageCreate(message) => {
                if message.author.id == state.user().id {
                    continue
                }

                match state.find_channel(message.channel_id) {
                    Some(ChannelRef::Private(channel)) => {
                        let original_message = message.content;
                        let mut new_message = String::new();
                        for chr in original_message.chars() {
                            new_message.push(rot13(chr));
                        }

                        let _ = discord.send_message(channel.id, &new_message, "", false);
                    },
                    Some(ChannelRef::Public(_, channel)) => {
                        // state.user() is not of the same type as message.mentions
                        // Is there a better way to do this?
                        let res: Vec<&User> = message.mentions.iter()
                            .filter(|&u| u.id == state.user().id)
                            .collect();

                        if res.len() > 0 {
                            let _ = discord.send_message(channel.id, "I encode and decode ROT13 messages, just send me a DM!", "", false);
                        }
                    },
                    None => println!("Got a message from an unknown channel??? From {} saying {}", message.author.name, message.content),
                    _ => {},
                }
            },
            Event::ReactionAdd(reaction) => {
                let received_emoji = match reaction.emoji {
                    ReactionEmoji::Custom { name, .. } => name,
                    ReactionEmoji::Unicode(emoji) => emoji
                };

                if received_emoji != REACTION_EMOJI {
                    continue
                }

                let message = match discord.get_message(reaction.channel_id, reaction.message_id) {
                    Ok(msg) => msg,
                    Err(_) => {
                        println!("Could not find the message that was reacted to. Message ID {}", reaction.message_id);
                        continue
                    }
                };

                match discord.create_private_channel(reaction.user_id) {
                    Ok(channel) => {
                        let original_message = message.content;
                        let mut new_message = String::new();
                        for chr in original_message.chars() {
                            new_message.push(rot13(chr));
                        }

                        let _ = discord.send_message(channel.id, &new_message, "", false);
                    },
                    Err(_) => println!("Got an invalid reaction??? From user ID {} on message ID {}", reaction.user_id, reaction.message_id),
                }
            },
            _ => {}
        }
    }
}<|endoftext|>