<commit_before>#![feature(os)]
#![feature(core)]
#![feature(collections)]
#![feature(unicode)]
#![feature(str_char)]

extern crate git2;
extern crate chrono;
extern crate getopts;
extern crate unicode;
extern crate docopt;
extern crate rustc_serialize;
extern crate collections;
extern crate core;

use std::path::Path;
use docopt::Docopt;

#[derive(RustcDecodable)]
pub struct Args {
    arg_path: String
}

#[cfg(not(test))]
fn main() {
    const USAGE: &'static str = "
usage: gitstat [options] <path>
Options:
-h, --help show this message
";
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    // println!("Total count of commits in '{}' is: {}", path.display(), stat.total);
    match gitstat::run(&args) {
        Ok(()) => {}
        Err(e) => println!("error: {}", e),
    }
}

mod gitstat {
    use git2;
    use git2::{Repository,Commit,Oid,Signature,Time,Tree,Object,ObjectType};
    use std::collections::HashMap;
    use std::collections::hash_map::Entry;
    use std::sync::{Mutex, Arc};
    use std::path::Path;
    use collections::vec::IntoIter;
    use core::iter::IntoIterator;
    use std::ops::Add;
    use Args;
    use chrono::datetime::DateTime;
    use chrono::naive::datetime::NaiveDateTime;
    use chrono::{Timelike, Datelike};
    use std::cmp;

    struct Files {
        files: Vec<String>
    }

    impl Files {

        pub fn new(repo: &Repository, head: &Tree) -> Result<Files, git2::Error> {
            let mut vec = Vec::new();

            for entry in head.iter() {
                let name: String = format!("{}", entry.name().unwrap());

                match entry.kind() {
                    Some(ObjectType::Tree) => {
                        let object: Object = try!(entry.to_object(repo));
                        let subfolder = try!(Files::new(repo, &object.as_tree().unwrap()));
                        vec.push_all(&subfolder.files);
                    },
                    Some(ObjectType::Blob) => vec.push(name),
                    _ => println!("{}", entry.kind().unwrap())
                }
            }

            Ok(Files {
                files: vec
            })
        }

        pub fn len(&self) -> usize {
            self.files.len()
        }
    }

    impl IntoIterator for Files {
        type Item = String;
        type IntoIter = IntoIter<String>;

        fn into_iter(self) -> IntoIter<String> {
            self.files.into_iter()
        }
    }

    pub fn run(args: &Args) -> Result<(), git2::Error> {
        let path = Path::new(&args.arg_path);
        let repo = try!(Repository::open(path));

        let authors: HashMap<String, usize> = try!(self::get_authors(&repo));

        // iterate over everything.
        for (name, count) in authors.iter() {
            println!("{}: {}", *name, *count);
        }

        Ok(())
    }

    /// Helper method for gets HEAD commit of given git repository
    fn get_head_commit(repo: &Box<Repository>) -> Option<Commit> {
        repo.head().ok()
            .and_then(|h| h.target())
            .and_then(|oid| repo.find_commit(oid).ok())
    }

    fn get_authors(repo: &Repository) -> Result<HashMap<String, usize>, git2::Error> {
        let mut heatmap = [[0u32; 24]; 7];
        let mut authors: HashMap<String, usize> = HashMap::new();
        let mut revwalk = try!(repo.revwalk());

        revwalk.push_head();
        revwalk.set_sorting(git2::SORT_TOPOLOGICAL);
        // let mutex = Mutex::new(repo);

        // mailmap::read_mailmap_file(path);
        for oid in revwalk {
            let commit = try!(repo.find_commit(oid));

            let uniq_name: String = get_uniq_name(&commit.author());
            let (weekday, hour) = get_heatmat_coords(&commit.time());
            let tree = try!(commit.tree());
            let files = try!(Files::new(&repo, &tree));

            // for item in files {
            //     println!("{}", item);
            // }
            println!("{} {}", oid, files.len());

            heatmap[weekday as usize][hour as usize] += 1;
            // println!("{} {}", time.seconds(), time.offset_minutes());

            match authors.entry(uniq_name) {
                Entry::Vacant(entry) => entry.insert(1),
                Entry::Occupied(mut entry) => {
                    *entry.get_mut() += 1;
                    entry.into_mut()
                }
            };
        }

        // find max
        let mut max: u32 = 0;
        for hours in heatmap.iter() {
            for count in hours.iter() {
                max = cmp::max(*count, max);
            }
        }

        let arts = ['.', '▪', '◾', '◼', '⬛'];

        for hours in heatmap.iter() {
            for count in hours.iter() {
                print!("{:4}", arts[(count % arts.len() as u32) as usize]);
                // print!("{:4}", count);
            }
            println!("");
        }

        Ok(authors)
    }

    fn get_uniq_name(author: &Signature) -> String {
        format!("{} <{}>", author.name().unwrap(), author.email().unwrap())
    }

    fn get_heatmat_coords(time: &Time) -> (u32, u32) {
        let timestamp: NaiveDateTime = NaiveDateTime::from_timestamp(time.seconds(), 0);

        (timestamp.weekday().num_days_from_monday(), timestamp.hour())
    }

    /// Module `mailmap` which implement logic for parse .mailmap files
    mod mailmap {

        use std::env;
        use std::fs::File;
        use std::io::{BufReader, BufRead};
        use std::string::String;
        use std::path::Path;
        use unicode::str::UnicodeStr;

        pub fn read_mailmap_file(basedir: &Path) {
            // get full path to .mailmap
            let path = match env::current_dir() {
                Ok(path) => path.join(".mailmap"),
                Err(error) => panic!("{}", error),
            };

            // create BufReader instance for .mailmap file
            let mut reader = match File::open(&path) {
                Ok(file) => BufReader::new(file),
                Err(error) => panic!("{}", error),
            };

            for line in reader.lines() {
                match line {
                    Ok(line) => self::read_mailmap_line(line.as_slice()),
                    Err(x) => break
                };
            }
        }

        struct Author {
            name: String,
            email: String
        }

        struct MailmapLine {
            new_author: Author,
            old_author: Author
        }

        fn read_mailmap_line(line: &str) -> Option<MailmapLine> {
            if line.len() > 0 && line.char_at(0) != '#' {
                parse_mailmap_name_email(line);
            }

            None
        }

        fn parse_mailmap_name_email(line: &str) -> Option<Author> {
            let left = match line.find('<') {
                Some(i) => i,
                None => -1
            };

            let right = match line.find('>') {
                Some(i) => i,
                None => -1
            };

            // if left == -1 || right == -1 {
            //     return None;
            // }

            if left > right {
                return None;
            }

            if left+1 == right {
                return None;
            }

            Some(Author {
                name: String::from_str(UnicodeStr::trim(&line[..left])),
                email: String::from_str(UnicodeStr::trim(&line[left..right]))
            })

        }

        #[test]
        fn test_parse_name_email() {
            assert!(self::parse_mailmap_name_email("name <email>").is_some());
            assert!(self::parse_mailmap_name_email("name <>").is_none());
            assert!(self::parse_mailmap_name_email(">").is_none());
            assert!(self::parse_mailmap_name_email("<").is_none());
            assert!(self::parse_mailmap_name_email("><").is_none());
            assert!(self::parse_mailmap_name_email("").is_none());
            assert!(self::parse_mailmap_name_email("<email>").is_some());
        }

    }
}

#[cfg(test)]
mod tests {
    use std::old_io::TempDir;
    use git2::Repository;
    use gitstat::run;

    // fn repo_init() -> (TempDir, Repository) {
    //     let td = TempDir::new("test").unwrap();
    //     let repo = Repository::init(td.path()).unwrap();
    //     {
    //         let mut config = repo.config().unwrap();
    //         config.set_str("user.name", "name").unwrap();
    //         config.set_str("user.email", "email").unwrap();
    //         let mut index = repo.index().unwrap();
    //         let id = index.write_tree().unwrap();
    //
    //         let tree = repo.find_tree(id).unwrap();
    //         let sig = repo.signature().unwrap();
    //         repo.commit(Some("HEAD"), &sig, &sig, "initial", &tree, []).unwrap();
    //     }
    //     (td, repo)
    // }
    //
    // #[test]
    // fn smoke_run() {
    //     let (td, _repo) = self::repo_init();
    //     let path = td.unwrap();
    //     let result = run(&path);
    //     assert!(result.is_ok());
    //     assert_eq!(result.unwrap().total, 1);
    // }
}
<commit_msg>fix heatmap counting<commit_after>#![feature(os)]
#![feature(core)]
#![feature(collections)]
#![feature(unicode)]
#![feature(str_char)]

extern crate git2;
extern crate chrono;
extern crate getopts;
extern crate unicode;
extern crate docopt;
extern crate rustc_serialize;
extern crate collections;
extern crate core;

use std::path::Path;
use docopt::Docopt;

#[derive(RustcDecodable)]
pub struct Args {
    arg_path: String
}

#[cfg(not(test))]
fn main() {
    const USAGE: &'static str = "
usage: gitstat [options] <path>
Options:
-h, --help show this message
";
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    // println!("Total count of commits in '{}' is: {}", path.display(), stat.total);
    match gitstat::run(&args) {
        Ok(()) => {}
        Err(e) => println!("error: {}", e),
    }
}

mod gitstat {
    use git2;
    use git2::{Repository,Commit,Oid,Signature,Time,Tree,Object,ObjectType};
    use std::collections::HashMap;
    use std::collections::hash_map::Entry;
    use std::sync::{Mutex, Arc};
    use std::path::Path;
    use collections::vec::IntoIter;
    use core::iter::IntoIterator;
    use std::ops::Add;
    use Args;
    use chrono::datetime::DateTime;
    use chrono::naive::datetime::NaiveDateTime;
    use chrono::{Timelike, Datelike};
    use std::cmp;

    struct Files {
        files: Vec<String>
    }

    impl Files {

        pub fn new(repo: &Repository, head: &Tree) -> Result<Files, git2::Error> {
            let mut vec = Vec::new();

            for entry in head.iter() {
                let name: String = format!("{}", entry.name().unwrap());

                match entry.kind() {
                    Some(ObjectType::Tree) => {
                        let object: Object = try!(entry.to_object(repo));
                        let subfolder = try!(Files::new(repo, &object.as_tree().unwrap()));
                        vec.push_all(&subfolder.files);
                    },
                    Some(ObjectType::Blob) => vec.push(name),
                    _ => {}
                }
            }

            Ok(Files {
                files: vec
            })
        }

        pub fn len(&self) -> usize {
            self.files.len()
        }
    }

    impl IntoIterator for Files {
        type Item = String;
        type IntoIter = IntoIter<String>;

        fn into_iter(self) -> IntoIter<String> {
            self.files.into_iter()
        }
    }

    pub fn run(args: &Args) -> Result<(), git2::Error> {
        let path = Path::new(&args.arg_path);
        let repo = try!(Repository::open(path));

        let authors: HashMap<String, usize> = try!(self::get_authors(&repo));

        // iterate over everything.
        for (name, count) in authors.iter() {
            println!("{}: {}", *name, *count);
        }

        Ok(())
    }

    /// Helper method for gets HEAD commit of given git repository
    fn get_head_commit(repo: &Box<Repository>) -> Option<Commit> {
        repo.head().ok()
            .and_then(|h| h.target())
            .and_then(|oid| repo.find_commit(oid).ok())
    }

    fn get_authors(repo: &Repository) -> Result<HashMap<String, usize>, git2::Error> {
        let mut heatmap = [0; 24*7];
        let mut authors: HashMap<String, usize> = HashMap::new();
        let mut revwalk = try!(repo.revwalk());
        // let mut files_number = Vec::new();

        revwalk.push_head();
        revwalk.set_sorting(git2::SORT_TOPOLOGICAL);
        // let mutex = Mutex::new(repo);

        for oid in revwalk {
            let commit = try!(repo.find_commit(oid));

            let uniq_name: String = get_uniq_name(&commit.author());
            let (weekday, hour) = get_heatmat_coords(&commit.time());
            let tree = try!(commit.tree());
            // let files = try!(Files::new(&repo, &tree));

            // files_number.push(files.len());

            heatmap[(weekday * 24 + hour) as usize] += 1;
            // println!("{} {}", time.seconds(), time.offset_minutes());

            match authors.entry(uniq_name) {
                Entry::Vacant(entry) => entry.insert(1),
                Entry::Occupied(mut entry) => {
                    *entry.get_mut() += 1;
                    entry.into_mut()
                }
            };
        }

        // find max
        let mut max: u32 = 0;
        for count in heatmap.iter() {
            max = cmp::max(*count, max);
        }

        let arts = ['.', '▪', '◾', '◼', '⬛'];

        print!(" ");
        for i in 0..24 {
            print!("{:3}", i);
        }
        println!("");
        for i in 0..24*7 {
            if i % 24 == 0 {
                print!("{}: ", i / 24);
            }
            print!("{:3}", arts[(heatmap[i] as f32 / max as f32 * (arts.len() - 1) as f32) as usize]);
            // print!("{:3}", heatmap[i]);
            if (i + 1) % 24 == 0 {
                println!("");
            }
        }

        Ok(authors)
    }

    fn get_uniq_name(author: &Signature) -> String {
        format!("{} <{}>", author.name().unwrap(), author.email().unwrap())
    }

    fn get_heatmat_coords(time: &Time) -> (u32, u32) {
        let timestamp: NaiveDateTime = NaiveDateTime::from_timestamp(time.seconds(), 0);

        (timestamp.weekday().num_days_from_monday(), timestamp.hour())
    }
}

#[cfg(test)]
mod tests {
    use std::old_io::TempDir;
    use git2::Repository;
    use gitstat::run;

    // fn repo_init() -> (TempDir, Repository) {
    //     let td = TempDir::new("test").unwrap();
    //     let repo = Repository::init(td.path()).unwrap();
    //     {
    //         let mut config = repo.config().unwrap();
    //         config.set_str("user.name", "name").unwrap();
    //         config.set_str("user.email", "email").unwrap();
    //         let mut index = repo.index().unwrap();
    //         let id = index.write_tree().unwrap();
    //
    //         let tree = repo.find_tree(id).unwrap();
    //         let sig = repo.signature().unwrap();
    //         repo.commit(Some("HEAD"), &sig, &sig, "initial", &tree, []).unwrap();
    //     }
    //     (td, repo)
    // }
    //
    // #[test]
    // fn smoke_run() {
    //     let (td, _repo) = self::repo_init();
    //     let path = td.unwrap();
    //     let result = run(&path);
    //     assert!(result.is_ok());
    //     assert_eq!(result.unwrap().total, 1);
    // }
}
<|endoftext|>


<commit_before>extern crate rustylifx;
extern crate clap;
extern crate termcolor;

use rustylifx::{colour, messages, network, response};
use rustylifx::colour::{HSB, HSBK};
use rustylifx::network::Device;

use std::io;
use std::io::Write;
use std::process;
use std::thread;
use std::time::Duration;

use clap::{Arg, App};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

fn exit_usage(s: String) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);
    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow))) {
        Ok(v) => {},
        Err(e) => {},
    };

    match writeln!(&mut stdout, "\n\n{}", s) {
        Ok(v) => {},
        Err(e) => {},
    };

    println!("Usage: `lifxcmd --help`");
    process::exit(1);
}

fn exit_error(s: String) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);
    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red))) {
        Ok(v) => {},
        Err(e) => {},
    };

    match writeln!(&mut stdout, "\n\n{}", s) {
        Ok(v) => {},
        Err(e) => {},
    };

    process::exit(1);
}

fn main() {
    // Configure flags.
    let matches = App::new("Lifx Command")
        .version("0.1.1")
        .author("Russell Mackenzie")
        .about("Control Lifx devices from the command line.")
        .arg(Arg::with_name("address")
            .short("a")
            .long("address")
            .value_name("HOST ADDRESS")
            .help("Specifies the address of the target device")
            .takes_value(true))
        .arg(Arg::with_name("power")
            .short("p")
            .long("power")
            .value_name("POWER LEVEL")
            .help("Changes the power level on/off")
            .takes_value(true))
        .arg(Arg::with_name("colour")
            .short("c")
            .long("colour")
            .value_name("COLOUR NAME")
            .help("Changes the colour")
            .takes_value(true))
        .arg(Arg::with_name("flash")
            .short("f")
            .long("flash")
            .value_name("FLASH COLOUR NAME")
            .help("Specifies the name of the colour to flash")
            .takes_value(true))
        .arg(Arg::with_name("interval")
            .short("i")
            .long("interval")
            .value_name("FLASH INTERVAL")
            .help("The length of the flash")
            .takes_value(true))
        .arg(Arg::with_name("duration")
            .short("d")
            .long("duration")
            .value_name("TRANSITION DURATION")
            .help("The duration of the colour transition")
            .takes_value(true))
        .arg(Arg::with_name("echo")
            .short("e")
            .long("echo")
            .value_name("DISPLAY CURRENT STATE")
            .help("Display the current state of the device")
            .takes_value(false))
        .arg(Arg::with_name("hue")
            .short("h")
            .long("hue")
            .value_name("HUE")
            .help("Set the hue of the device")
            .takes_value(true))
        .arg(Arg::with_name("saturation")
            .short("s")
            .long("saturation")
            .value_name("SATURATION")
            .help("Set the saturation of the device")
            .takes_value(true))
        .arg(Arg::with_name("brightness")
            .short("b")
            .long("brightness")
            .value_name("BRIGHTNESS")
            .help("Set the brightness of the device")
            .takes_value(true))
        .get_matches();

    // Find the device, by flag, else broadcast.
    let device = match matches.value_of("address").unwrap_or("") {
        "" => {
            // Locate device.
            match messages::get_service() {
                Ok(v)   => v,
                Err(e)  => {
                    exit_error(format!("Failed finding device: {}",e));
                    return
                },
            }
        },
        ip => {
            // Set device.
            const PORT: u16 = 56700;
            network::Device {
                socket_addr: format!("{}:{}", ip, PORT).parse().expect("invalid socket address"),
                response: None,
            }
        },
    };

    // Check if state display was specified.
    if matches.is_present("state") {
        let device = get_device_state(device);
        display(&device);
        return
    }

    // Set the power level on/off.
    if let Some(v) = matches.value_of("power") {
        let res = match v {
            "on"  => messages::set_device_on(&device),
            "off" => messages::set_device_off(&device),
            _ => {
                exit_usage("Power state is invalid, should be on or off.".to_string());
                return
            },
        };

        if res.is_err() {
            exit_error(format!("Failed setting device power state: {:?}", res.err()));
            return
        }
    };

    // Check if transition duration was specified.
    let duration = match matches.value_of("duration") {
        Some(v) => {
            match v.parse::<u32>() {
                Ok(n) => n,
                Err(e) => {
                    exit_usage(format!("Duration is not a valid number: {}", e));
                    return
                },
            }
        }
        None => 0,
    };

    // Set the colour by name if flag exists.
    if let Some(v) = matches.value_of("colour") {
        let _ = messages::set_device_state(&device, &colour::get_colour(v), 1000, duration);
        return
    }

    // Set the colour by HSB if specified.
    // HSB: 360º, 100%, 100%
    let mut hue = match matches.value_of("hue") {
        Some(v) => {
            match v.parse::<i16>() {
                Ok(n) => {
                    if n >= 0 && n <= 360 {
                        n
                    } else {
                        exit_usage(format!("Hue is outside the valid range, should be 0 - 360 (degrees)"));
                        return
                    }
                }
                Err(e) => {
                    exit_usage(format!("Hue is not a valid number: {}", e));
                    return
                },
            }
        }
        None => -1,
    };

    let mut saturation = match matches.value_of("saturation") {
        Some(v) => {
            match v.parse::<i16>() {
                Ok(n) => {
                    if n >= 0 && n <= 100 {
                        n
                    } else {
                        exit_usage("Saturation is outside the valid range, should be 0 - 100 (percent)".to_string());
                        return
                    }
                }
                Err(e) => {
                    exit_usage(format!("Saturation is not a valid number: {}", e));
                    return
                },
            }
        }
        None => -1,
    };

    let mut brightness = match matches.value_of("brightness") {
        Some(v) => {
            match v.parse::<i16>() {
                Ok(n) => {
                    if n >= 0 && n <= 100 {
                        n
                    } else {
                        exit_usage("Brightness is outside the valid range, should be 0 - 100 (percent)".to_string());
                        return
                    }
                }
                Err(e) => {
                    exit_usage(format!("Brightness is not a valid number: {}", e));
                    return
                },
            }
        }
        None => -1,
    };

    if hue >= 0 || saturation >= 0 || brightness >= 0 {
        if hue < 0 {
            hue = 360;
        }

        if saturation < 0 {
            saturation = 100;
        }

        if brightness < 0 {
            brightness = 100;
        }

        let _ = messages::set_device_state(&device,
                                           &colour::HSB {
                                               hue: hue as u16,
                                               saturation: saturation as u8,
                                               brightness: brightness as u8,
                                           },
                                           1000,
                                           duration);
        return
    }

    // Check if the flash interval was specified.
    let interval = match matches.value_of("interval") {
        Some(v) => {
            match v.parse::<u64>() {
                Ok(n) => n,
                Err(e) => {
                    exit_usage(format!("Interval is not a valid number: {}", e));
                    return
                },
            }
        }
        None => 1000,
    };

    // Flash if flag exists.
    if let Some(v) = matches.value_of("flash") {
        flash(device, colour::get_colour(v), interval);
        return
    };

    // TODO: ponder fade
    // let fade_len: u32 = 3000;
    // fade(&device, colour::get_colour("crimson"), fade_len);
    // thread::sleep(Duration::from_millis((fade_len + 1) as u64));
    // Fade device back to initial state.
    // fade(&device, initial_state.unwrap(), 3000);
}

fn get_device_state(device: Device) -> Device {
    // TODO: sort out this hacky sleep.
    thread::sleep(Duration::from_millis(1000));
    messages::get_device_state(device).unwrap()
}

fn flash(device: Device, flash_colour: HSB, duration_ms: u64) {
    let device = get_device_state(device);

    // Extract current HSVK from device state data.
    let resp = match device.response {
        Some(ref v) => v,
        None => panic!("no response"),
    };

    let payload = match resp.payload {
        response::Payload::State(ref v) => Some(v),
        _ => None,
    };

    let initial_state = match payload {
        Some(v) => {
            let h = colour::hue_word_to_degrees(v.hsbk.hue);
            let s = colour::saturation_word_to_percent(v.hsbk.saturation as u16);
            let b = colour::brightness_word_to_percent(v.hsbk.brightness as u16);
            Some(colour::HSB::new(h, s, b))
        }
        None => None,
    };

    if let Some(v) = initial_state {
        // Change device state temporarily.
        let _ = messages::set_device_state(&device, &flash_colour, 2500, 0);
        thread::sleep(Duration::from_millis(duration_ms));

        // Return device to initial state.
        let _ = messages::set_device_state(&device, &v, 2500, 0);
    }
}

fn display(device: &Device) {
    let resp = match device.response {
        Some(ref v) => v,
        None => {
            println!("No response from device.");
            return;
        }
    };

    println!("\nDevice State:");
    println!("Source: {:?}", resp.source);
    println!("Mac addr: {:?}", resp.mac_address);
    println!("Firmware: {:?}", resp.firmware);

    println!("\nResponse:");
    println!("Size: {}", resp.size);

    // packed byte
    println!("Sequence num: {:?}", resp.sequence_number);
    println!("Reserved_1 (timestamp?): {:?}", resp.reserved_1);
    println!("Message type: {:?}", resp.message_type);
    println!("Reserved_2: {:?}", resp.reserved_2);

    match resp.payload {
        response::Payload::StateService(ref v) => {
            println!("Service: {:?}", v.service);
            println!("Port: {:?}", v.port);
            println!("Unknown: {:?}", v.unknown);
        }
        response::Payload::State(ref v) => {
            println!("Body: {:?}", v.body);
            println!("HSBK: {:?}", v.hsbk);
            println!("current hue: {:?}", v.hsbk.hue);
            println!("current hue degrees: {:?}º",
                     colour::hue_word_to_degrees(v.hsbk.hue));
            println!("current sat: {:?}", v.hsbk.saturation);
            println!("current sat percent: {:?}%",
                     colour::saturation_word_to_percent(v.hsbk.saturation as u16));
            println!("current bri: {:?}", v.hsbk.brightness);
            println!("current bri percent: {:?}%",
                     colour::brightness_word_to_percent(v.hsbk.brightness as u16));
            println!("current kel: {:?}", v.hsbk.kelvin);
        }
        _ => (),
    };

    println!("==========");
}
<commit_msg>Minor refator, result checks, writes.<commit_after>extern crate rustylifx;
extern crate clap;
extern crate termcolor;

use rustylifx::{colour, messages, network, response};
use rustylifx::colour::HSB;
use rustylifx::network::Device;

use std::io::Write;
use std::process;
use std::thread;
use std::time::Duration;

use clap::{Arg, App};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

fn exit_usage(s: String) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);
    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Yellow))) {
        Ok(_) => (),
        Err(e) => println!("Failed setting terminal output colour: {}", e),
    };

    let s_out = format!("\n\n{}\nUsage: `lifxcmd --help`", s);
    if writeln!(&mut stdout,"{}", s_out).is_err() {
        println!("{}", s_out);
    };

    process::exit(1);
}

fn exit_error(s: String) {
    let mut stdout = StandardStream::stdout(ColorChoice::Always);
    match stdout.set_color(ColorSpec::new().set_fg(Some(Color::Red))) {
        Ok(_) => (),
        Err(e) => println!("Failed setting terminal output colour: {}", e),
    };

    let s_out = format!("\n\n{}", s);
    if writeln!(&mut stdout,"{}", s_out).is_err() {
        println!("{}", s_out);
    };

    process::exit(1);
}

fn main() {
    // Configure flags.
    let matches = App::new("Lifx Command")
        .version("0.1.1")
        .author("Russell Mackenzie")
        .about("Control Lifx devices from the command line.")
        .arg(Arg::with_name("address")
            .short("a")
            .long("address")
            .value_name("HOST ADDRESS")
            .help("Specifies the address of the target device")
            .takes_value(true))
        .arg(Arg::with_name("power")
            .short("p")
            .long("power")
            .value_name("POWER LEVEL")
            .help("Changes the power level on/off")
            .takes_value(true))
        .arg(Arg::with_name("colour")
            .short("c")
            .long("colour")
            .value_name("COLOUR NAME")
            .help("Changes the colour")
            .takes_value(true))
        .arg(Arg::with_name("flash")
            .short("f")
            .long("flash")
            .value_name("FLASH COLOUR NAME")
            .help("Specifies the name of the colour to flash")
            .takes_value(true))
        .arg(Arg::with_name("interval")
            .short("i")
            .long("interval")
            .value_name("FLASH INTERVAL")
            .help("The length of the flash")
            .takes_value(true))
        .arg(Arg::with_name("duration")
            .short("d")
            .long("duration")
            .value_name("TRANSITION DURATION")
            .help("The duration of the colour transition")
            .takes_value(true))
        .arg(Arg::with_name("echo")
            .short("e")
            .long("echo")
            .value_name("DISPLAY CURRENT STATE")
            .help("Display the current state of the device")
            .takes_value(false))
        .arg(Arg::with_name("hue")
            .short("h")
            .long("hue")
            .value_name("HUE")
            .help("Set the hue of the device")
            .takes_value(true))
        .arg(Arg::with_name("saturation")
            .short("s")
            .long("saturation")
            .value_name("SATURATION")
            .help("Set the saturation of the device")
            .takes_value(true))
        .arg(Arg::with_name("brightness")
            .short("b")
            .long("brightness")
            .value_name("BRIGHTNESS")
            .help("Set the brightness of the device")
            .takes_value(true))
        .get_matches();

    // Find the device, by flag, else broadcast.
    let device = match matches.value_of("address").unwrap_or("") {
        "" => {
            // Locate device.
            match messages::get_service() {
                Ok(v)   => v,
                Err(e)  => {
                    exit_error(format!("Failed finding device: {}",e));
                    return
                },
            }
        },
        ip => {
            // Set device.
            const PORT: u16 = 56700;
            network::Device {
                socket_addr: format!("{}:{}", ip, PORT).parse().expect("invalid socket address"),
                response: None,
            }
        },
    };

    // Check if state display was specified.
    if matches.is_present("state") {
        let device = get_device_state(device);
        display(&device);
        return
    }

    // Set the power level on/off.
    if let Some(v) = matches.value_of("power") {
        let res = match v {
            "on"  => messages::set_device_on(&device),
            "off" => messages::set_device_off(&device),
            _ => {
                exit_usage("Power state is invalid, should be on or off.".to_string());
                return
            },
        };

        if res.is_err() {
            exit_error(format!("Failed setting device power state: {:?}", res.err()));
            return
        }
    };

    // Check if transition duration was specified.
    let duration = match matches.value_of("duration") {
        Some(v) => {
            match v.parse::<u32>() {
                Ok(n) => n,
                Err(e) => {
                    exit_usage(format!("Duration is not a valid number: {}", e));
                    return
                },
            }
        }
        None => 0,
    };

    // Set the colour by name if flag exists.
    if let Some(v) = matches.value_of("colour") {
        let _ = messages::set_device_state(&device, &colour::get_colour(v), 1000, duration);
        return
    }

    // Set the colour by HSB if specified.
    // HSB: 360º, 100%, 100%
    let mut hue = match matches.value_of("hue") {
        Some(v) => {
            match v.parse::<i16>() {
                Ok(n) => {
                    if n >= 0 && n <= 360 {
                        n
                    } else {
                        exit_usage("Hue is outside the valid range, should be 0 - 360 (degrees)".to_string());
                        return
                    }
                }
                Err(e) => {
                    exit_usage(format!("Hue is not a valid number: {}", e));
                    return
                },
            }
        }
        None => -1,
    };

    let mut saturation = match matches.value_of("saturation") {
        Some(v) => {
            match v.parse::<i16>() {
                Ok(n) => {
                    if n >= 0 && n <= 100 {
                        n
                    } else {
                        exit_usage("Saturation is outside the valid range, should be 0 - 100 (percent)".to_string());
                        return
                    }
                }
                Err(e) => {
                    exit_usage(format!("Saturation is not a valid number: {}", e));
                    return
                },
            }
        }
        None => -1,
    };

    let mut brightness = match matches.value_of("brightness") {
        Some(v) => {
            match v.parse::<i16>() {
                Ok(n) => {
                    if n >= 0 && n <= 100 {
                        n
                    } else {
                        exit_usage("Brightness is outside the valid range, should be 0 - 100 (percent)".to_string());
                        return
                    }
                }
                Err(e) => {
                    exit_usage(format!("Brightness is not a valid number: {}", e));
                    return
                },
            }
        }
        None => -1,
    };

    if hue >= 0 || saturation >= 0 || brightness >= 0 {
        if hue < 0 {
            hue = 360;
        }

        if saturation < 0 {
            saturation = 100;
        }

        if brightness < 0 {
            brightness = 100;
        }

        let _ = messages::set_device_state(&device,
                                           &colour::HSB {
                                               hue: hue as u16,
                                               saturation: saturation as u8,
                                               brightness: brightness as u8,
                                           },
                                           1000,
                                           duration);
        return
    }

    // Check if the flash interval was specified.
    let interval = match matches.value_of("interval") {
        Some(v) => {
            match v.parse::<u64>() {
                Ok(n) => n,
                Err(e) => {
                    exit_usage(format!("Interval is not a valid number: {}", e));
                    return
                },
            }
        }
        None => 1000,
    };

    // Flash if flag exists.
    if let Some(v) = matches.value_of("flash") {
        flash(device, colour::get_colour(v), interval);
        return
    };

    // TODO: ponder fade
    // let fade_len: u32 = 3000;
    // fade(&device, colour::get_colour("crimson"), fade_len);
    // thread::sleep(Duration::from_millis((fade_len + 1) as u64));
    // Fade device back to initial state.
    // fade(&device, initial_state.unwrap(), 3000);
}

fn get_device_state(device: Device) -> Device {
    // TODO: sort out this hacky sleep.
    thread::sleep(Duration::from_millis(1000));
    messages::get_device_state(device).unwrap()
}

fn flash(device: Device, flash_colour: HSB, duration_ms: u64) {
    let device = get_device_state(device);

    // Extract current HSVK from device state data.
    let resp = match device.response {
        Some(ref v) => v,
        None => panic!("no response"),
    };

    let payload = match resp.payload {
        response::Payload::State(ref v) => Some(v),
        _ => None,
    };

    let initial_state = match payload {
        Some(v) => {
            let h = colour::hue_word_to_degrees(v.hsbk.hue);
            let s = colour::saturation_word_to_percent(v.hsbk.saturation as u16);
            let b = colour::brightness_word_to_percent(v.hsbk.brightness as u16);
            Some(colour::HSB::new(h, s, b))
        }
        None => None,
    };

    if let Some(v) = initial_state {
        // Change device state temporarily.
        let _ = messages::set_device_state(&device, &flash_colour, 2500, 0);
        thread::sleep(Duration::from_millis(duration_ms));

        // Return device to initial state.
        let _ = messages::set_device_state(&device, &v, 2500, 0);
    }
}

fn display(device: &Device) {
    let resp = match device.response {
        Some(ref v) => v,
        None => {
            println!("No response from device.");
            return;
        }
    };

    println!("\nDevice State:");
    println!("Source: {:?}", resp.source);
    println!("Mac addr: {:?}", resp.mac_address);
    println!("Firmware: {:?}", resp.firmware);

    println!("\nResponse:");
    println!("Size: {}", resp.size);

    // packed byte
    println!("Sequence num: {:?}", resp.sequence_number);
    println!("Reserved_1 (timestamp?): {:?}", resp.reserved_1);
    println!("Message type: {:?}", resp.message_type);
    println!("Reserved_2: {:?}", resp.reserved_2);

    match resp.payload {
        response::Payload::StateService(ref v) => {
            println!("Service: {:?}", v.service);
            println!("Port: {:?}", v.port);
            println!("Unknown: {:?}", v.unknown);
        }
        response::Payload::State(ref v) => {
            println!("Body: {:?}", v.body);
            println!("HSBK: {:?}", v.hsbk);
            println!("current hue: {:?}", v.hsbk.hue);
            println!("current hue degrees: {:?}º",
                     colour::hue_word_to_degrees(v.hsbk.hue));
            println!("current sat: {:?}", v.hsbk.saturation);
            println!("current sat percent: {:?}%",
                     colour::saturation_word_to_percent(v.hsbk.saturation as u16));
            println!("current bri: {:?}", v.hsbk.brightness);
            println!("current bri percent: {:?}%",
                     colour::brightness_word_to_percent(v.hsbk.brightness as u16));
            println!("current kel: {:?}", v.hsbk.kelvin);
        }
        _ => (),
    };

    println!("==========");
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::io::{self, ErrorKind, Read, BufWriter};
use std::path::Path;
use std::process::exit;
use std::time::{Duration, Instant};

use getopts::Options;

use ppbert::prelude::*;
use ppbert::parsers::*;
use ppbert::pp::*;

const PROG_NAME: &str = "ppbert";

#[derive(Clone, Copy)]
enum ParserChoice {
    ByExtension,
    ForceBert1,
    ForceBert2,
    ForceDiskLog,
}

fn opt_usize(m: &getopts::Matches, opt: &str, default: usize) -> usize {
    match m.opt_get_default(opt, default) {
        Ok(n) => n,
        Err(_) => {
            eprintln!("'{}' must be a number", opt);
            exit(1);
        }
    }
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("V", "version", "display version");
    opts.optflag("h", "help", "display this help");
    opts.optopt("i", "indent", "indent with NUM spaces", "NUM");
    opts.optopt("m", "per-line", "print at most NUM basic terms per line", "NUM");
    opts.optflag("p", "parse", "parse only, not pretty print");
    opts.optflag("1", "bert1", "force ppbert to use regular BERT parser");
    opts.optflag("2", "bert2", "force ppbert to use BERT2 parser");
    opts.optflag("d", "disk-log", "force ppbert to use DiskLog parser");
    opts.optflag("v", "verbose", "show diagnostics on stderr");
    opts.optflag("j", "json", "print as JSON");
    opts.optflag("t", "transform-proplists", "convert proplists to JSON objects");

    let mut matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}: {}", PROG_NAME, e);
            eprintln!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
            exit(1);
        }
    };

    if matches.opt_present("help") {
        println!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, VERSION);
        exit(0);
    }

    // If no files to process, use stdin.
    if matches.free.is_empty() {
        matches.free.push("-".to_owned());
    }

    let indent_width = opt_usize(&matches, "indent", 2);
    let max_per_line = opt_usize(&matches, "per-line", 6);
    let parse_only = matches.opt_present("parse");
    let json = matches.opt_present("json");
    let transform_proplists = matches.opt_present("transform-proplists");
    let verbose = matches.opt_present("verbose");

    let parser_choice =
        if matches.opt_present("bert1") {
            ParserChoice::ForceBert1
        } else if matches.opt_present("bert2") {
            ParserChoice::ForceBert2
        } else if matches.opt_present("disk-log") {
            ParserChoice::ForceDiskLog
        } else {
            ParserChoice::ByExtension
        };

    let pp: Box<dyn PrettyPrinter> = match (json, transform_proplists) {
        (true, false)  => Box::new(JsonPrettyPrinter::new(false)),
        (true, true)   => Box::new(JsonPrettyPrinter::new(true)),
        (false, false) => Box::new(ErlangPrettyPrinter::new(indent_width, max_per_line)),
        (false, true)  => {
            eprintln!("{}: --transform-proplists is only valid with the --json flag", PROG_NAME);
            Box::new(ErlangPrettyPrinter::new(indent_width, max_per_line))
        }
    };

    let mut return_code = 0;
    for file in &matches.free {
        let res = handle_file(file, parse_only, verbose, parser_choice, &pp);
        match res {
            Ok(()) => (),
            Err(ref e) => {
                if broken_pipe(e) {
                    break;
                }
                return_code = 1;
                eprintln!("{}: {}: {}", PROG_NAME, file, e);
            }
        }
    }
    exit(return_code);
}

fn broken_pipe(err: &BertError) -> bool {
    match *err {
        BertError::IoError(ref ioerr) =>
            ioerr.kind() == ErrorKind::BrokenPipe,
        _ => false
    }
}

fn read_bytes(filename: &str) -> Result<Vec<u8>> {
    if filename == "-" {
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let mut buf: Vec<u8> = Vec::with_capacity(4096);
        stdin.read_to_end(&mut buf)?;
        return Ok(buf);
    } else {
        let buf = fs::read(filename)?;
        return Ok(buf);
    }
}

fn parser_from_ext(filename: &str, bytes: Vec<u8>) -> Box<Parser> {
    let ext: Option<&str> =
        Path::new(filename)
        .extension()
        .and_then(|x| x.to_str());
    match ext {
        Some("bert") | Some("bert1") => Box::new(Bert1Parser::new(bytes)),
        Some("bert2") => Box::new(Bert2Parser::new(bytes)),
        Some("log") => Box::new(DiskLogParser::new(bytes)),
        _ => {
            eprintln!("{}: cannot find an appropriate parser for {}; using BERT",
                      PROG_NAME, filename);
            Box::new(Bert1Parser::new(bytes))
        },
    }
}

fn handle_file(
    filename: &str,
    parse_only: bool,
    verbose: bool,
    parser_choice: ParserChoice,
    pp: &Box<dyn PrettyPrinter>,
) -> Result<()> {
    // Read file or stdin into buffer
    let now = Instant::now();
    let bytes = read_bytes(filename)?;
    let read_dur = now.elapsed();

    let mut parser: Box<Parser> = match parser_choice {
        ParserChoice::ForceBert1 => Box::new(Bert1Parser::new(bytes)),
        ParserChoice::ForceBert2 => Box::new(Bert2Parser::new(bytes)),
        ParserChoice::ForceDiskLog => Box::new(DiskLogParser::new(bytes)),
        ParserChoice::ByExtension => parser_from_ext(filename, bytes),
    };

    let mut parse_dur = Duration::new(0, 0);
    let mut pp_dur = Duration::new(0, 0);

    loop {
        let now = Instant::now();
        let term = match parser.next() {
            Some(term) => term?,
            None => break,
        };
        parse_dur += now.elapsed();
        if !parse_only {
            let now = Instant::now();
            let stdout = BufWriter::new(io::stdout());
            pp.write(&term, Box::new(stdout))?;
            pp_dur += now.elapsed();
        }
    }

    if verbose {
        eprintln!("{}: {} read time: {:?}", PROG_NAME, filename, read_dur);
        eprintln!("{}: {} parse time: {:?}", PROG_NAME, filename, parse_dur);
        if !parse_only {
            eprintln!("{}: {} print time: {:?}", PROG_NAME, filename, pp_dur);
        }
    }

    return Ok(());
}
<commit_msg>Add option -b/--as-bert to print back as BERT<commit_after>use std::env;
use std::fs;
use std::io::{self, ErrorKind, Read, BufWriter};
use std::path::Path;
use std::process::exit;
use std::time::{Duration, Instant};

use getopts::Options;

use ppbert::prelude::*;
use ppbert::parsers::*;
use ppbert::pp::*;

const PROG_NAME: &str = "ppbert";

#[derive(Clone, Copy)]
enum ParserChoice {
    ByExtension,
    ForceBert1,
    ForceBert2,
    ForceDiskLog,
}

fn opt_usize(m: &getopts::Matches, opt: &str, default: usize) -> usize {
    match m.opt_get_default(opt, default) {
        Ok(n) => n,
        Err(_) => {
            eprintln!("'{}' must be a number", opt);
            exit(1);
        }
    }
}

fn main() {
    let mut opts = Options::new();
    opts.optflag("V", "version", "display version");
    opts.optflag("h", "help", "display this help");
    opts.optopt("i", "indent", "indent with NUM spaces", "NUM");
    opts.optopt("m", "per-line", "print at most NUM basic terms per line", "NUM");
    opts.optflag("p", "parse", "parse only, not pretty print");
    opts.optflag("1", "bert1", "force ppbert to use regular BERT parser");
    opts.optflag("2", "bert2", "force ppbert to use BERT2 parser");
    opts.optflag("d", "disk-log", "force ppbert to use DiskLog parser");
    opts.optflag("v", "verbose", "show diagnostics on stderr");
    opts.optflag("j", "json", "print as JSON");
    opts.optflag("t", "transform-proplists", "convert proplists to JSON objects");
    opts.optflag("b", "as-bert", "print as BERT");

    let mut matches = match opts.parse(env::args().skip(1)) {
        Ok(m) => m,
        Err(e) => {
            eprintln!("{}: {}", PROG_NAME, e);
            eprintln!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
            exit(1);
        }
    };

    if matches.opt_present("help") {
        println!("{}", opts.usage(&format!("{} {}", PROG_NAME, VERSION)));
        exit(0);
    }

    if matches.opt_present("version") {
        println!("{} {}", PROG_NAME, VERSION);
        exit(0);
    }

    // If no files to process, use stdin.
    if matches.free.is_empty() {
        matches.free.push("-".to_owned());
    }

    let indent_width = opt_usize(&matches, "indent", 2);
    let max_per_line = opt_usize(&matches, "per-line", 6);
    let parse_only = matches.opt_present("parse");
    let json = matches.opt_present("json");
    let as_bert = matches.opt_present("as-bert");
    let transform_proplists = matches.opt_present("transform-proplists");
    let verbose = matches.opt_present("verbose");

    let parser_choice =
        if matches.opt_present("bert1") {
            ParserChoice::ForceBert1
        } else if matches.opt_present("bert2") {
            ParserChoice::ForceBert2
        } else if matches.opt_present("disk-log") {
            ParserChoice::ForceDiskLog
        } else {
            ParserChoice::ByExtension
        };

    let pp: Box<dyn PrettyPrinter> =
        if json {
            Box::new(JsonPrettyPrinter::new(transform_proplists))
        } else if as_bert {
            Box::new(BertWriter::new())
        } else {
            Box::new(ErlangPrettyPrinter::new(indent_width, max_per_line))
        };

    let mut return_code = 0;
    for file in &matches.free {
        let res = handle_file(file, parse_only, verbose, parser_choice, &pp);
        match res {
            Ok(()) => (),
            Err(ref e) => {
                if broken_pipe(e) {
                    break;
                }
                return_code = 1;
                eprintln!("{}: {}: {}", PROG_NAME, file, e);
            }
        }
    }
    exit(return_code);
}

fn broken_pipe(err: &BertError) -> bool {
    match *err {
        BertError::IoError(ref ioerr) =>
            ioerr.kind() == ErrorKind::BrokenPipe,
        _ => false
    }
}

fn read_bytes(filename: &str) -> Result<Vec<u8>> {
    if filename == "-" {
        let stdin = io::stdin();
        let mut stdin = stdin.lock();
        let mut buf: Vec<u8> = Vec::with_capacity(4096);
        stdin.read_to_end(&mut buf)?;
        return Ok(buf);
    } else {
        let buf = fs::read(filename)?;
        return Ok(buf);
    }
}

fn parser_from_ext(filename: &str, bytes: Vec<u8>) -> Box<Parser> {
    let ext: Option<&str> =
        Path::new(filename)
        .extension()
        .and_then(|x| x.to_str());
    match ext {
        Some("bert") | Some("bert1") => Box::new(Bert1Parser::new(bytes)),
        Some("bert2") => Box::new(Bert2Parser::new(bytes)),
        Some("log") => Box::new(DiskLogParser::new(bytes)),
        _ => {
            eprintln!("{}: cannot find an appropriate parser for {}; using BERT",
                      PROG_NAME, filename);
            Box::new(Bert1Parser::new(bytes))
        },
    }
}

fn handle_file(
    filename: &str,
    parse_only: bool,
    verbose: bool,
    parser_choice: ParserChoice,
    pp: &Box<dyn PrettyPrinter>,
) -> Result<()> {
    // Read file or stdin into buffer
    let now = Instant::now();
    let bytes = read_bytes(filename)?;
    let read_dur = now.elapsed();

    let mut parser: Box<Parser> = match parser_choice {
        ParserChoice::ForceBert1 => Box::new(Bert1Parser::new(bytes)),
        ParserChoice::ForceBert2 => Box::new(Bert2Parser::new(bytes)),
        ParserChoice::ForceDiskLog => Box::new(DiskLogParser::new(bytes)),
        ParserChoice::ByExtension => parser_from_ext(filename, bytes),
    };

    let mut parse_dur = Duration::new(0, 0);
    let mut pp_dur = Duration::new(0, 0);

    loop {
        let now = Instant::now();
        let term = match parser.next() {
            Some(term) => term?,
            None => break,
        };
        parse_dur += now.elapsed();
        if !parse_only {
            let now = Instant::now();
            let stdout = BufWriter::new(io::stdout());
            pp.write(&term, Box::new(stdout))?;
            pp_dur += now.elapsed();
        }
    }

    if verbose {
        eprintln!("{}: {} read time: {:?}", PROG_NAME, filename, read_dur);
        eprintln!("{}: {} parse time: {:?}", PROG_NAME, filename, parse_dur);
        if !parse_only {
            eprintln!("{}: {} print time: {:?}", PROG_NAME, filename, pp_dur);
        }
    }

    return Ok(());
}
<|endoftext|>


<commit_before>mod regression {
    automod::dir!("tests/regression");
}
<commit_msg>Ignore let_underscore_drop clippy pedantic lint in test suite<commit_after>#![allow(clippy::let_underscore_drop)]

mod regression {
    automod::dir!("tests/regression");
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate rustc_serialize;

mod state;

use std::io::Read;
use rustc_serialize::json;
use hyper::{Get, Post};
use hyper::header::ContentLength;
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri::AbsolutePath;

macro_rules! try_return(
    ($e:expr) => {{
        match $e {
            Ok(v) => v,
            Err(e) => { println!("Error: {}", e); return; }
        }
    }}
    );

fn handle_request(mut req: Request, mut res: Response) {
    let mut body: String  = "".to_string();
    req.read_to_string(&mut body);

    let path = match req.uri {
        AbsolutePath(ref path) => {
            path
        },
        _ => panic!("Unsupported URI format."),
    };

    match path.as_ref() {
        "/" => {
            match req.method {
                Get => {
                    try_return!(res.send(b"Hey, what up."));
                },
                _ => {
                    *res.status_mut() = hyper::BadRequest;
                    (*res.headers_mut()).set(ContentLength(0));
                }
            }
        },
        "/state" => {
            match req.method {
                Get => {
                    let game = state::initial_state();
                    let encoded = json::encode(&game).unwrap();
                    try_return!(res.send(encoded.as_bytes()));
                },
                _ => {
                    *res.status_mut() = hyper::BadRequest;
                    (*res.headers_mut()).set(ContentLength(0));
                }
            }
        },
        "/turn" => {
            match req.method {
                Post => {
                    try_return!(res.send(body.as_bytes()));
                },
                _ => {
                    *res.status_mut() = hyper::BadRequest;
                    (*res.headers_mut()).set(ContentLength(0));
                }
            }
        },
        _ => {
            *res.status_mut() = hyper::NotFound;
            (*res.headers_mut()).set(ContentLength(0));
        }
    }
}

fn main() {
    let server = Server::http("localhost:3001").unwrap();
    let _guard = server.handle(handle_request);
    println!("Starting server on localhost:3001");
}
<commit_msg>Simplify pattern matching in server<commit_after>extern crate hyper;
extern crate rustc_serialize;

mod state;

use std::io::Read;
use rustc_serialize::json;
use hyper::{Get, Post};
use hyper::header::ContentLength;
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri::AbsolutePath;

macro_rules! try_return(
    ($e:expr) => {{
        match $e {
            Ok(v) => v,
            Err(e) => { println!("Error: {}", e); return; }
        }
    }}
    );

fn handle_request(mut req: Request, mut res: Response) {
    let mut body: String  = "".to_string();
    req.read_to_string(&mut body);

    let path = match req.uri {
        AbsolutePath(ref path) => {
            path
        },
        _ => panic!("Unsupported URI format."),
    };

    match (req.method, path.as_ref()) {
        (Get, "/") => {
            try_return!(res.send(b"Hey, what up."));
        },
        (Get, "/state") => {
            let game = state::initial_state();
            let encoded = json::encode(&game).unwrap();
            try_return!(res.send(encoded.as_bytes()));
        },
        (Post, "/turn") => {
            try_return!(res.send(body.as_bytes()));
        },
        _ => {
            *res.status_mut() = hyper::BadRequest;
            (*res.headers_mut()).set(ContentLength(0));
        }
    }
}

fn main() {
    let server = Server::http("localhost:3001").unwrap();
    let _guard = server.handle(handle_request);
    println!("Starting server on localhost:3001");
}
<|endoftext|>


<commit_before>#![feature(globs)]
#![allow(non_snake_case)]

extern crate getopts;
extern crate jni;
extern crate libc;
extern crate serialize;

use getopts::{optflag, getopts, OptGroup};
use jni::*;
use jni::classpath::load_static_method;
use libc::*;
use serialize::json;
use std::io::File;
use std::os;

#[link(name = "packrnative", kind = "static")]
#[link(name = "CoreFoundation", kind = "framework")]
#[link(name = "CoreServices", kind = "framework")]
extern {
	fn cfRunLoopRun(callback: extern fn(&Receiver<c_int>), signal:&Receiver<c_int>);
	fn cfRunLoopStop();
}

#[deriving(Decodable)]
struct Config {
	jar: String,
	mainClass: String,
	vmArgs: Vec<String>
}

fn print_usage(program: &str, _opts: &[OptGroup]) {
    println!("Usage: {} [options]", program);
    println!("-h --help\tUsage");
    println!("-- {{args}}\tUnparsed arguments passed through to Java main() method.");
}

fn read_config(path: &Path) -> Config {
	let content = File::open(path).read_to_string().unwrap();
	let config:Config = json::decode(content.as_slice()).unwrap();

	println!("jar: {:s}", config.jar);
	println!("main class: {:s}", config.mainClass);

	for arg in config.vmArgs.iter() {
		println!("VM argument: {}", arg);
	}

	config
}

fn init_jvm_arguments(jni:&mut JNI, config: &Config) {
	let num_args = config.vmArgs.len();

	let cp_path = os::make_absolute(&Path::new(config.jar.as_slice()));
	let class_path = format!("-Djava.class.path={}", cp_path.display());

	println!("class path: {}", class_path);

	jni.init_vm_args(num_args + 1u);
	jni.push_vm_arg(0u, class_path.as_slice());

	for i in range(0u, num_args) {
		let ref vm_arg = config.vmArgs[i];
		jni.push_vm_arg(i + 1, vm_arg.as_slice());
	}
}

#[cfg(target_os = "macos")]
fn get_libjvm_path(jre: Path) -> Path {
	let mut path = jre.clone();
	path.push("lib/server/libjvm.dylib");
	path
}

#[cfg(target_os = "linux")]
fn get_libjvm_path(jre: Path) -> Path {
	let mut path = jre.clone();
	path.push("lib/amd64/server/libjvm.so");
	path
}

fn check_for_exceptions(jni:&JNI) {
	let throwable = jni.exception_occured();
	if !JNI::is_null(throwable) {
		jni.exception_describe();
		jni.exception_clear();
		fail!("Exception caught!");
	}
}

fn load_jvm(jni:&mut JNI, config:&Config) {
	// create & fill arguments
	init_jvm_arguments(jni, config);

	// load lib, create VM instance
	match jni.load_jvm() {
		Err(err) => fail!(err),
		Ok(_) => {}
	};

	// attach to current thread
    /*if jni.attach_current_thread() != JNI_OK {
    	fail!("Could not attach JVM to thread");
    }
	println!("JVM attached to thread ...");*/
}

fn call_main(jni:&JNI, path_to_jar:&str, main_class_name:&str, args:&Vec<String>) {

	// do class-loader voodoo

	let (main_class, main_method) = load_static_method(jni, path_to_jar, main_class_name);

	// pass program arguments

	let java_lang_String:Jclass = jni.find_class("java/lang/String");
	check_for_exceptions(jni);
	assert!(!JNI::is_null(java_lang_String));

	let argc = args.len();

	let argv = jni.new_object_array(argc as Jint, java_lang_String, 0u64);
	check_for_exceptions(jni);
	assert!(!JNI::is_null(argv));

	for i in range(0u, argc) {
		let arg = jni.new_string_utf(args[i].as_slice());
		jni.set_object_array_element(argv, i as Jint, arg);
	}

   	// call main()

   	match (main_class, main_method) {
   		(0u64, 0u64) => println!("Could not find {} in {}", main_method, main_class),
   		(_, _) => jni.call_static_void_method_a(main_class, main_method, [argv])
   	};

   	println!("Quit from JVM ...");
}

fn destroy_vm(jni:&JNI) {
    println!("Unloading JVM ...");
    jni.destroy_jvm();
}

fn spawn_vm() {

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

    let opts = [
		optflag("h", "help", "print this help menu")
    ];

	let matches = match getopts(args.tail(), opts) {
        Err(f) => {
        	println!("{} Run {} --help to show options.", f.to_string(), program);
        	return;
        }
        Ok(m) => { m }
    };

	if matches.opt_present("h") {
        print_usage(program.as_slice(), opts);
        return;
    }

    let config_path = os::make_absolute(&Path::new("config.json"));
    println!("config path: {}", config_path.display());

    let root_path = config_path.dir_path();
    println!("pwd: {}", root_path.display());

	// change working dir (MacOS: starts at parent folder of .app)
	if !os::change_dir(&root_path) {
		fail!("Could not change working directory");
	}

    let libjvmpath = get_libjvm_path(os::make_absolute(&Path::new("jre")));
    println!("JRE path: {}", libjvmpath.display());

    // read config JSON
    let config = read_config(&config_path);

	let cp_path = os::make_absolute(&Path::new(config.jar.as_slice()));
	let class_path = format!("file://{}", cp_path.display());

	println!("Loading JVM library ...");
    let mut jni:JNI = match JNI::new(&libjvmpath) {
    	Err(error) => fail!(error),
    	Ok(jni) => jni
    };

	println!("Creating JVM instance ...");
    load_jvm(&mut jni, &config);

    println!("Invoking {:s}.main()", config.mainClass);
    call_main(&jni, class_path.as_slice(), config.mainClass.as_slice(), &matches.free);

    destroy_vm(&jni);
}

#[cfg(target_os = "macos")]
extern fn run_loop_callback(signal:&Receiver<c_int>) {
	match signal.try_recv() {
		Err(_) => {},
		Ok(_) => unsafe {
			cfRunLoopStop();
		}
	}
}

#[cfg(target_os = "macos")]
fn main() {

	let (tx, rx): (Sender<c_int>, Receiver<c_int>) = std::comm::channel();
	let proc_tx = tx.clone();

	spawn(proc() {
		spawn_vm();
		proc_tx.send(0);
	});

	unsafe {
		cfRunLoopRun(run_loop_callback, &rx);
	}
}
<commit_msg>Renamed fail!() -> panic!().<commit_after>#![feature(globs)]
#![allow(non_snake_case)]

extern crate getopts;
extern crate jni;
extern crate libc;
extern crate serialize;

use getopts::{optflag, getopts, OptGroup};
use jni::*;
use jni::classpath::load_static_method;
use libc::*;
use serialize::json;
use std::io::File;
use std::os;

#[link(name = "packrnative", kind = "static")]
#[link(name = "CoreFoundation", kind = "framework")]
#[link(name = "CoreServices", kind = "framework")]
extern {
	fn cfRunLoopRun(callback: extern fn(&Receiver<c_int>), signal:&Receiver<c_int>);
	fn cfRunLoopStop();
}

#[deriving(Decodable)]
struct Config {
	jar: String,
	mainClass: String,
	vmArgs: Vec<String>
}

fn print_usage(program: &str, _opts: &[OptGroup]) {
    println!("Usage: {} [options]", program);
    println!("-h --help\tUsage");
    println!("-- {{args}}\tUnparsed arguments passed through to Java main() method.");
}

fn read_config(path: &Path) -> Config {
	let content = File::open(path).read_to_string().unwrap();
	let config:Config = json::decode(content.as_slice()).unwrap();

	println!("jar: {:s}", config.jar);
	println!("main class: {:s}", config.mainClass);

	for arg in config.vmArgs.iter() {
		println!("VM argument: {}", arg);
	}

	config
}

fn init_jvm_arguments(jni:&mut JNI, config: &Config) {
	let num_args = config.vmArgs.len();

	let cp_path = os::make_absolute(&Path::new(config.jar.as_slice()));
	let class_path = format!("-Djava.class.path={}", cp_path.display());

	println!("class path: {}", class_path);

	jni.init_vm_args(num_args + 1u);
	jni.push_vm_arg(0u, class_path.as_slice());

	for i in range(0u, num_args) {
		let ref vm_arg = config.vmArgs[i];
		jni.push_vm_arg(i + 1, vm_arg.as_slice());
	}
}

#[cfg(target_os = "macos")]
fn get_libjvm_path(jre: Path) -> Path {
	let mut path = jre.clone();
	path.push("lib/server/libjvm.dylib");
	path
}

#[cfg(target_os = "linux")]
fn get_libjvm_path(jre: Path) -> Path {
	let mut path = jre.clone();
	path.push("lib/amd64/server/libjvm.so");
	path
}

fn check_for_exceptions(jni:&JNI) {
	let throwable = jni.exception_occured();
	if !JNI::is_null(throwable) {
		jni.exception_describe();
		jni.exception_clear();
		panic!("Exception caught!");
	}
}

fn load_jvm(jni:&mut JNI, config:&Config) {
	// create & fill arguments
	init_jvm_arguments(jni, config);

	// load lib, create VM instance
	match jni.load_jvm() {
		Err(err) => panic!(err),
		Ok(_) => {}
	};

	// attach to current thread
    /*if jni.attach_current_thread() != JNI_OK {
    	panic!("Could not attach JVM to thread");
    }
	println!("JVM attached to thread ...");*/
}

fn call_main(jni:&JNI, path_to_jar:&str, main_class_name:&str, args:&Vec<String>) {

	// do class-loader voodoo

	let (main_class, main_method) = load_static_method(jni, path_to_jar, main_class_name);

	// pass program arguments

	let java_lang_String:Jclass = jni.find_class("java/lang/String");
	check_for_exceptions(jni);
	assert!(!JNI::is_null(java_lang_String));

	let argc = args.len();

	let argv = jni.new_object_array(argc as Jint, java_lang_String, 0u64);
	check_for_exceptions(jni);
	assert!(!JNI::is_null(argv));

	for i in range(0u, argc) {
		let arg = jni.new_string_utf(args[i].as_slice());
		jni.set_object_array_element(argv, i as Jint, arg);
	}

   	// call main()

   	match (main_class, main_method) {
   		(0u64, 0u64) => println!("Could not find {} in {}", main_method, main_class),
   		(_, _) => jni.call_static_void_method_a(main_class, main_method, [argv])
   	};

   	println!("Quit from JVM ...");
}

fn destroy_vm(jni:&JNI) {
    println!("Unloading JVM ...");
    jni.destroy_jvm();
}

fn spawn_vm() {

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

    let opts = [
		optflag("h", "help", "print this help menu")
    ];

	let matches = match getopts(args.tail(), opts) {
        Err(f) => {
        	println!("{} Run {} --help to show options.", f.to_string(), program);
        	return;
        }
        Ok(m) => { m }
    };

	if matches.opt_present("h") {
        print_usage(program.as_slice(), opts);
        return;
    }

    let config_path = os::make_absolute(&Path::new("config.json"));
    println!("config path: {}", config_path.display());

    let root_path = config_path.dir_path();
    println!("pwd: {}", root_path.display());

	// change working dir (MacOS: starts at parent folder of .app)
	if !os::change_dir(&root_path) {
		panic!("Could not change working directory");
	}

    let libjvmpath = get_libjvm_path(os::make_absolute(&Path::new("jre")));
    println!("JRE path: {}", libjvmpath.display());

    // read config JSON
    let config = read_config(&config_path);

	let cp_path = os::make_absolute(&Path::new(config.jar.as_slice()));
	let class_path = format!("file://{}", cp_path.display());

	println!("Loading JVM library ...");
    let mut jni:JNI = match JNI::new(&libjvmpath) {
    	Err(error) => panic!(error),
    	Ok(jni) => jni
    };

	println!("Creating JVM instance ...");
    load_jvm(&mut jni, &config);

    println!("Invoking {:s}.main()", config.mainClass);
    call_main(&jni, class_path.as_slice(), config.mainClass.as_slice(), &matches.free);

    destroy_vm(&jni);
}

#[cfg(target_os = "macos")]
extern fn run_loop_callback(signal:&Receiver<c_int>) {
	match signal.try_recv() {
		Err(_) => {},
		Ok(_) => unsafe {
			cfRunLoopStop();
		}
	}
}

#[cfg(target_os = "macos")]
fn main() {

	let (tx, rx): (Sender<c_int>, Receiver<c_int>) = std::comm::channel();
	let proc_tx = tx.clone();

	spawn(proc() {
		spawn_vm();
		proc_tx.send(0);
	});

	unsafe {
		cfRunLoopRun(run_loop_callback, &rx);
	}
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate term;

extern crate otp_cop;

use std::{env, io, process, thread};
use std::io::Write;
use std::sync::{mpsc, Arc};

use otp_cop::service::{CreateServiceResult, ServiceFactory};

struct ParallelIter<T> {
    count: usize,
    pos: usize,
    rx: mpsc::Receiver<T>
}

impl<T> Iterator for ParallelIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if self.pos < self.count {
            self.count += 1;
            return self.rx.recv().ok();
        } else {
            return None;
        }
    }
}

fn parallel<T, U, F1>(objs: Vec<T>, f1: F1) -> ParallelIter<U>
        where F1: 'static + Fn(T) -> U + Send + Sync, T: 'static + Send, U: 'static + Send {
    let (tx, rx) = mpsc::channel();
    let count = objs.len();
    let shared_f1 = Arc::new(f1);
    for o in objs {
        let f1 = shared_f1.clone();
        let tx = tx.clone();
        thread::spawn(move || {
            tx.send(f1(o)).unwrap();
        });
    }

    return ParallelIter{count: count, pos: 0, rx: rx};
}

fn main() {
    let service_factories = vec![
        Box::new(otp_cop::SlackServiceFactory) as Box<ServiceFactory>,
        Box::new(otp_cop::GithubServiceFactory) as Box<ServiceFactory>,
    ];

    let mut opts = getopts::Options::new();

    for factory in service_factories.iter() {
        factory.add_options(&mut opts);
    }

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(matches) => matches,
        Err(e) => {
            writeln!(io::stderr(), "{}", e.to_string()).unwrap();
            process::exit(1);
        }
    };

    let mut services = vec![];

    for factory in service_factories.iter() {
        match factory.create_service(&matches) {
            CreateServiceResult::Service(s) => services.push(s),
            CreateServiceResult::MissingArguments(args) => {
                writeln!(io::stderr(), "Missing arguments: {:?}", args).unwrap();
                process::exit(1);
            }
            CreateServiceResult::None => continue,
        }
    }

    if services.is_empty() {
        writeln!(io::stderr(), "{}", opts.usage("otp-cop: <args>")).unwrap();
        process::exit(1);
    }

    let count = services.len();
    for (i, result) in parallel(services, |service| service.get_users()).enumerate() {
        match result {
            Ok(result) => {
                let header = format!("{} ({})", result.service_name, result.users.len());
                println!("{}", header);
                println!("{}", repeat_char("=", header.len()));
                println!("");
                for user in result.users {
                    let email = match user.email {
                        Some(email) => format!(" ({})", email),
                        None => "".to_string(),
                    };
                    let details = match user.details {
                        Some(details) => format!(" -- {}", details),
                        None => "".to_string(),
                    };
                    println!("@{}{}{}", user.name, email, details);
                }
                if i + 1 != count {
                    println!("");
                    println!("");
                }
            },
            Err(e) => {
                let mut t = term::stderr().unwrap();
                t.fg(term::color::RED).unwrap();
                writeln!(t, "{}", e.service_name).unwrap();
                writeln!(t, "{}", repeat_char("=", e.service_name.len())).unwrap();
                writeln!(t, "").unwrap();
                writeln!(t, "{}", e.error_message).unwrap();
                t.reset().unwrap();
            }
        }
    }
}

fn repeat_char(c: &'static str, length: usize) -> String {
    return c.chars().cycle().take(length).collect::<String>();
}
<commit_msg>Fixed #8 -- exit code based on whether there are people<commit_after>extern crate getopts;
extern crate term;

extern crate otp_cop;

use std::{env, io, process, thread};
use std::io::Write;
use std::sync::{mpsc, Arc};

use otp_cop::service::{CreateServiceResult, ServiceFactory};

struct ParallelIter<T> {
    count: usize,
    pos: usize,
    rx: mpsc::Receiver<T>
}

impl<T> Iterator for ParallelIter<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if self.pos < self.count {
            self.count += 1;
            return self.rx.recv().ok();
        } else {
            return None;
        }
    }
}

fn parallel<T, U, F1>(objs: Vec<T>, f1: F1) -> ParallelIter<U>
        where F1: 'static + Fn(T) -> U + Send + Sync, T: 'static + Send, U: 'static + Send {
    let (tx, rx) = mpsc::channel();
    let count = objs.len();
    let shared_f1 = Arc::new(f1);
    for o in objs {
        let f1 = shared_f1.clone();
        let tx = tx.clone();
        thread::spawn(move || {
            tx.send(f1(o)).unwrap();
        });
    }

    return ParallelIter{count: count, pos: 0, rx: rx};
}

fn main() {
    let service_factories = vec![
        Box::new(otp_cop::SlackServiceFactory) as Box<ServiceFactory>,
        Box::new(otp_cop::GithubServiceFactory) as Box<ServiceFactory>,
    ];

    let mut opts = getopts::Options::new();

    for factory in service_factories.iter() {
        factory.add_options(&mut opts);
    }

    let matches = match opts.parse(env::args().skip(1)) {
        Ok(matches) => matches,
        Err(e) => {
            writeln!(io::stderr(), "{}", e.to_string()).unwrap();
            process::exit(1);
        }
    };

    let mut services = vec![];

    for factory in service_factories.iter() {
        match factory.create_service(&matches) {
            CreateServiceResult::Service(s) => services.push(s),
            CreateServiceResult::MissingArguments(args) => {
                writeln!(io::stderr(), "Missing arguments: {:?}", args).unwrap();
                process::exit(1);
            }
            CreateServiceResult::None => continue,
        }
    }

    if services.is_empty() {
        writeln!(io::stderr(), "{}", opts.usage("otp-cop: <args>")).unwrap();
        process::exit(1);
    }

    let count = services.len();
    let mut people_found = false;
    for (i, result) in parallel(services, |service| service.get_users()).enumerate() {
        match result {
            Ok(result) => {
                let header = format!("{} ({})", result.service_name, result.users.len());
                println!("{}", header);
                println!("{}", repeat_char("=", header.len()));
                println!("");
                if !result.users.is_empty() {
                    people_found = true;
                }
                for user in result.users {
                    let email = match user.email {
                        Some(email) => format!(" ({})", email),
                        None => "".to_string(),
                    };
                    let details = match user.details {
                        Some(details) => format!(" -- {}", details),
                        None => "".to_string(),
                    };
                    println!("@{}{}{}", user.name, email, details);
                }
                if i + 1 != count {
                    println!("");
                    println!("");
                }
            },
            Err(e) => {
                let mut t = term::stderr().unwrap();
                t.fg(term::color::RED).unwrap();
                writeln!(t, "{}", e.service_name).unwrap();
                writeln!(t, "{}", repeat_char("=", e.service_name.len())).unwrap();
                writeln!(t, "").unwrap();
                writeln!(t, "{}", e.error_message).unwrap();
                t.reset().unwrap();
            }
        }
    }

    if people_found {
        process::exit(2);
    }
}

fn repeat_char(c: &'static str, length: usize) -> String {
    return c.chars().cycle().take(length).collect::<String>();
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate router;
extern crate rustc_serialize;

use std::io::Read;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;

use iron::prelude::*;
use iron::status;
use router::Router;
use rustc_serialize::json::Json;

#[derive(Debug)]
struct Mapping;

impl Mapping {
    fn new() -> Mapping {
        Mapping
    }
}

#[derive(Debug)]
struct Index {
    pub mappings: HashMap<&'static str, Mapping>,
}

impl Index {
    fn new() -> Index {
        Index{
            mappings: HashMap::new(),
        }
    }
}

fn index_not_found_response() -> Response {
    let mut response = Response::with((status::NotFound, "{\"message\": \"Index not found\"}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    return response;
}

fn main() {
    let mut indices = Arc::new(Mutex::new(HashMap::new()));
    let mut wagtail_index = Index::new();
    wagtail_index.mappings.insert("wagtaildocs_document", Mapping::new());
    indices.lock().unwrap().insert("wagtail", wagtail_index);

    let mut router = Router::new();

    router.get("/", |_: &mut Request| -> IronResult<Response> {
        Ok(Response::with((status::Ok, "Hello World!")))
    });

    {
        let indices = indices.clone();

        router.get("/:index/_count", move |req: &mut Request| -> IronResult<Response> {
            // URL parameters
            let ref index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

            // Lock index array
            let mut indices = indices.lock().unwrap();

            // Find index
            let mut index = match indices.get(index_name) {
                Some(index) => index,
                None => {
                    return Ok(index_not_found_response());
                }
            };

            // Load query from body
            let mut payload = String::new();
            req.body.read_to_string(&mut payload).unwrap();

            let data = match Json::from_str(&payload) {
                Ok(data) => data,
                Err(error) => {
                    // TODO: What specifically is bad about the JSON?
                    let mut response = Response::with((status::BadRequest, "{\"message\": \"Couldn't parse JSON\"}"));
                    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                    return Ok(response);
                }
            };

            // TODO: Run query

            let mut response = Response::with((status::Ok, "{\"count\": 0}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            Ok(response)
        });
    }

    {
        let indices = indices.clone();

        router.get("/:index/_search", move |req: &mut Request| -> IronResult<Response> {
            // URL parameters
            let ref index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

            // Lock index array
            let mut indices = indices.lock().unwrap();

            // Find index
            let mut index = match indices.get(index_name) {
                Some(index) => index,
                None => {
                    return Ok(index_not_found_response());
                }
            };

            // Load query from body
            let mut payload = String::new();
            req.body.read_to_string(&mut payload).unwrap();

            let data = match Json::from_str(&payload) {
                Ok(data) => data,
                Err(error) => {
                    // TODO: What specifically is bad about the JSON?
                    let mut response = Response::with((status::BadRequest, "{\"message\": \"Couldn't parse JSON\"}"));
                    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                    return Ok(response);
                }
            };

            // TODO: Run query

            let mut response = Response::with((status::Ok, "{\"hits\": {\"total\": 0, \"hits\": []}}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            Ok(response)
        });
    }

    {
        let indices = indices.clone();

        router.put("/:index/:mapping/:doc", move |req: &mut Request| -> IronResult<Response> {
            // URL parameters
            let ref index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
            let ref mapping_name = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");
            let ref doc_id = req.extensions.get::<Router>().unwrap().find("doc").unwrap_or("");

            // Lock index array
            let mut indices = indices.lock().unwrap();

            // Find index
            let mut index = match indices.get(index_name) {
                Some(index) => index,
                None => {
                    return Ok(index_not_found_response());
                }
            };

            // Find mapping
            let mut mapping = match index.mappings.get(mapping_name) {
                Some(mapping) => mapping,
                None => {
                    let mut response = Response::with((status::NotFound, "{\"message\": \"Mapping not found\"}"));
                    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                    return Ok(response);
                }
            };

            // Load data from body
            let mut payload = String::new();
            req.body.read_to_string(&mut payload).unwrap();

            let data = match Json::from_str(&payload) {
                Ok(data) => data,
                Err(error) => {
                    // TODO: What specifically is bad about the JSON?
                    let mut response = Response::with((status::BadRequest, "{\"message\": \"Couldn't parse JSON\"}"));
                    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                    return Ok(response);
                }
            };

            // TODO: Validate and insert document

            let mut response = Response::with((status::Ok, "{}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            Ok(response)
        });
    }

    Iron::new(router).http("localhost:9200").unwrap();
}
<commit_msg>Added documents and insert code<commit_after>extern crate iron;
extern crate router;
extern crate rustc_serialize;

use std::io::Read;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;

use iron::prelude::*;
use iron::status;
use router::Router;
use rustc_serialize::json::Json;

#[derive(Debug)]
struct Document {
    data: Json,
}

impl Document {
    fn from_json(data: Json) -> Document {
        Document{
            data: data,
        }
    }
}

#[derive(Debug)]
struct Mapping;

impl Mapping {
    fn new() -> Mapping {
        Mapping
    }
}

#[derive(Debug)]
struct Index {
    pub mappings: HashMap<&'static str, Mapping>,
    pub docs: HashMap<String, Document>,
}

impl Index {
    fn new() -> Index {
        Index{
            mappings: HashMap::new(),
            docs: HashMap::new(),
        }
    }
}

fn index_not_found_response() -> Response {
    let mut response = Response::with((status::NotFound, "{\"message\": \"Index not found\"}"));
    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
    return response;
}

fn main() {
    let mut indices = Arc::new(Mutex::new(HashMap::new()));
    let mut wagtail_index = Index::new();
    wagtail_index.mappings.insert("wagtaildocs_document", Mapping::new());
    indices.lock().unwrap().insert("wagtail", wagtail_index);

    let mut router = Router::new();

    router.get("/", |_: &mut Request| -> IronResult<Response> {
        Ok(Response::with((status::Ok, "Hello World!")))
    });

    {
        let indices = indices.clone();

        router.get("/:index/_count", move |req: &mut Request| -> IronResult<Response> {
            // URL parameters
            let ref index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

            // Lock index array
            let mut indices = indices.lock().unwrap();

            // Find index
            let mut index = match indices.get(index_name) {
                Some(index) => index,
                None => {
                    return Ok(index_not_found_response());
                }
            };

            // Load query from body
            let mut payload = String::new();
            req.body.read_to_string(&mut payload).unwrap();

            let data = match Json::from_str(&payload) {
                Ok(data) => data,
                Err(error) => {
                    // TODO: What specifically is bad about the JSON?
                    let mut response = Response::with((status::BadRequest, "{\"message\": \"Couldn't parse JSON\"}"));
                    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                    return Ok(response);
                }
            };

            // TODO: Run query

            let mut response = Response::with((status::Ok, "{\"count\": 0}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            Ok(response)
        });
    }

    {
        let indices = indices.clone();

        router.get("/:index/_search", move |req: &mut Request| -> IronResult<Response> {
            // URL parameters
            let ref index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");

            // Lock index array
            let mut indices = indices.lock().unwrap();

            // Find index
            let mut index = match indices.get(index_name) {
                Some(index) => index,
                None => {
                    return Ok(index_not_found_response());
                }
            };

            // Load query from body
            let mut payload = String::new();
            req.body.read_to_string(&mut payload).unwrap();

            let data = match Json::from_str(&payload) {
                Ok(data) => data,
                Err(error) => {
                    // TODO: What specifically is bad about the JSON?
                    let mut response = Response::with((status::BadRequest, "{\"message\": \"Couldn't parse JSON\"}"));
                    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                    return Ok(response);
                }
            };

            // TODO: Run query

            let mut response = Response::with((status::Ok, "{\"hits\": {\"total\": 0, \"hits\": []}}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            Ok(response)
        });
    }

    {
        let indices = indices.clone();

        router.put("/:index/:mapping/:doc", move |req: &mut Request| -> IronResult<Response> {
            // URL parameters
            let index_name = req.extensions.get::<Router>().unwrap().find("index").unwrap_or("");
            let ref mapping_name = req.extensions.get::<Router>().unwrap().find("mapping").unwrap_or("");
            let ref doc_id = req.extensions.get::<Router>().unwrap().find("doc").unwrap_or("");

            // Lock index array
            let mut indices = indices.lock().unwrap();

            // Find index
            let ref mut index = match indices.get_mut(index_name) {
                Some(index) => index,
                None => {
                    return Ok(index_not_found_response());
                }
            };

            // Find mapping
            let mut mapping = match index.mappings.get(mapping_name) {
                Some(mapping) => mapping,
                None => {
                    let mut response = Response::with((status::NotFound, "{\"message\": \"Mapping not found\"}"));
                    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                    return Ok(response);
                }
            };

            // Load data from body
            let mut payload = String::new();
            req.body.read_to_string(&mut payload).unwrap();

            let data = match Json::from_str(&payload) {
                Ok(data) => data,
                Err(error) => {
                    // TODO: What specifically is bad about the JSON?
                    let mut response = Response::with((status::BadRequest, "{\"message\": \"Couldn't parse JSON\"}"));
                    response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
                    return Ok(response);
                }
            };

            // Create and insert document
            let doc = Document::from_json(data);
            index.docs.insert(doc_id.clone().to_owned(), doc);

            let mut response = Response::with((status::Ok, "{}"));
            response.headers.set_raw("Content-Type", vec![b"application/json".to_vec()]);
            Ok(response)
        });
    }

    Iron::new(router).http("localhost:9200").unwrap();
}
<|endoftext|>