<commit_before>#[cfg(test)]
mod test {
    #[test]
    fn it_works() {
    }
}
<commit_msg>Started with data structures<commit_after>
enum Direction {
    Vertical,
    Horizontal,
}

struct Container {
    temp: i32,
}

struct Split {
    split: Option<Box<Split>>,
    leftTop: Container,
    rightBottom: Container,
    percent: f32,
    direction: Direction,
}

struct Workspace {
    split: Split,
    x: i32,
    y: i32,
    width: i32,
    height: i32,
}

#[cfg(test)]
mod test {
    #[test]
    fn it_works() {
    }
}
<|endoftext|>


<commit_before>extern crate piston_window;
extern crate poke_a_mango;
extern crate rusttype;
extern crate conrod;

use std::io::stderr;
use std::process::exit;
use rusttype::FontCollection;
use conrod::backend::piston_window as conrod_backend;
use piston_window::{PistonWindow, UpdateEvent, Window};

fn main() {
    let result = actual_main();
    exit(result);
}

fn actual_main() -> i32 {
    if let Err(err) = result_main() {
        err.print_error(&mut stderr());
        err.exit_value()
    } else {
        0
    }
}

fn result_main() -> Result<(), poke_a_mango::Error> {
    let opts = poke_a_mango::Options::parse();
    println!("{:#?}", opts);

    let mut window: PistonWindow = try!(poke_a_mango::ops::create_window(opts.desktop_size));
    let mut ui = conrod::UiBuilder::new().build();
    let mut glyph_cache = conrod_backend::GlyphCache::new(&mut window, opts.desktop_size.0, opts.desktop_size.1);
    let image_map = conrod::image::Map::new();

    ui.fonts.insert(FontCollection::from_bytes(&include_bytes!("../assets/DejaVuSansMono.ttf")[..]).into_font().unwrap());

    let mut game_state = poke_a_mango::ops::GameState::MainMenu;
    let widgets = poke_a_mango::ops::Widgets::new(ui.widget_id_generator());

    while let Some(event) = window.next() {
        if let Some(e) = conrod_backend::convert_event(event.clone(), &window) {
            ui.handle_event(e);
        }

        let is_update = event.update(|_| {
                widgets.update(ui.set_widgets(), &mut game_state);
            })
            .is_some();
        if is_update {
            if game_state.should_exit() {
                window.set_should_close(true);
            } else if game_state.should_load_leaderboard() {
                game_state =
                    poke_a_mango::ops::GameState::DisplayLeaderboard(try!(poke_a_mango::ops::Leader::read(&opts.config_dir.1.join("leaderboard.toml"))));
            } else if let poke_a_mango::ops::GameState::GameEnded { name, score } = game_state {
                let leaderboard_p = opts.config_dir.1.join("leaderboard.toml");
                let mut leaderboard = try!(poke_a_mango::ops::Leader::read(&leaderboard_p));
                leaderboard.push(poke_a_mango::ops::Leader::now(name, score));
                leaderboard.sort();
                leaderboard.reverse();
                try!(poke_a_mango::ops::Leader::write(leaderboard, &leaderboard_p));

                game_state = poke_a_mango::ops::GameState::MainMenu;
            }
        }

        window.draw_2d(&event, |ctx, graphics| {
            if let Some(primitives) = ui.draw_if_changed() {
                conrod_backend::draw(ctx, graphics, primitives, &mut glyph_cache, &image_map, |img| img);
            }
        });
    }

    Ok(())
}
<commit_msg>No longer display options<commit_after>extern crate piston_window;
extern crate poke_a_mango;
extern crate rusttype;
extern crate conrod;

use std::io::stderr;
use std::process::exit;
use rusttype::FontCollection;
use conrod::backend::piston_window as conrod_backend;
use piston_window::{PistonWindow, UpdateEvent, Window};

fn main() {
    let result = actual_main();
    exit(result);
}

fn actual_main() -> i32 {
    if let Err(err) = result_main() {
        err.print_error(&mut stderr());
        err.exit_value()
    } else {
        0
    }
}

fn result_main() -> Result<(), poke_a_mango::Error> {
    let opts = poke_a_mango::Options::parse();

    let mut window: PistonWindow = try!(poke_a_mango::ops::create_window(opts.desktop_size));
    let mut ui = conrod::UiBuilder::new().build();
    let mut glyph_cache = conrod_backend::GlyphCache::new(&mut window, opts.desktop_size.0, opts.desktop_size.1);
    let image_map = conrod::image::Map::new();

    ui.fonts.insert(FontCollection::from_bytes(&include_bytes!("../assets/DejaVuSansMono.ttf")[..]).into_font().unwrap());

    let mut game_state = poke_a_mango::ops::GameState::MainMenu;
    let widgets = poke_a_mango::ops::Widgets::new(ui.widget_id_generator());

    while let Some(event) = window.next() {
        if let Some(e) = conrod_backend::convert_event(event.clone(), &window) {
            ui.handle_event(e);
        }

        let is_update = event.update(|_| {
                widgets.update(ui.set_widgets(), &mut game_state);
            })
            .is_some();
        if is_update {
            if game_state.should_exit() {
                window.set_should_close(true);
            } else if game_state.should_load_leaderboard() {
                game_state =
                    poke_a_mango::ops::GameState::DisplayLeaderboard(try!(poke_a_mango::ops::Leader::read(&opts.config_dir.1.join("leaderboard.toml"))));
            } else if let poke_a_mango::ops::GameState::GameEnded { name, score } = game_state {
                let leaderboard_p = opts.config_dir.1.join("leaderboard.toml");
                let mut leaderboard = try!(poke_a_mango::ops::Leader::read(&leaderboard_p));
                leaderboard.push(poke_a_mango::ops::Leader::now(name, score));
                leaderboard.sort();
                leaderboard.reverse();
                try!(poke_a_mango::ops::Leader::write(leaderboard, &leaderboard_p));

                game_state = poke_a_mango::ops::GameState::MainMenu;
            }
        }

        window.draw_2d(&event, |ctx, graphics| {
            if let Some(primitives) = ui.draw_if_changed() {
                conrod_backend::draw(ctx, graphics, primitives, &mut glyph_cache, &image_map, |img| img);
            }
        });
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::ops::Deref;

/// A `Vec` in sorted order without duplicates.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SortedUniqueVec<T: Ord + Clone> {
    vec: Vec<T>,
}

fn is_sorted_unique<T: Ord>(slice: &[T]) -> bool {
    if slice.len() < 2 {
        true
    } else {
        slice.windows(2).all(|win| win[0] < win[1])
    }
}

pub enum LeftOrRight {
    Left,
    Right,
}

impl<T: Ord + Clone> SortedUniqueVec<T> {
    pub fn new() -> Self {
        SortedUniqueVec { vec: Vec::new() }
    }

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

    pub fn contains(&self, element: &T) -> bool {
        self.vec.binary_search(element).is_ok()
    }

    /// Insert `element` into the sorted list.
    ///
    /// Panics if an element with the same key (according to the Eq trait) already exists.
    pub fn insert(&mut self, element: T) {
        match self.vec.binary_search(&element) {
            Ok(_idx) => {
                panic!("Element already exists");
            }
            Err(idx) => {
                self.vec.insert(idx, element);
            }
        }
    }

    /// Merges `self` and `other` into a new SortedVec.
    /// `choose_equal` decides which one of two equal values to take.
    pub fn merge<F>(&self, other: &Self, choose_equal: &F) -> Self
        where F: Fn(&T, &T) -> LeftOrRight
    {
        let mut vec = Vec::with_capacity(self.len() + other.len());

        let mut left_iter = self.vec.iter().peekable();
        let mut right_iter = other.vec.iter().peekable();

        enum Take {
            OneLeft,
            OneRight,
            Both,
            AllLeft,
            AllRight,
        };

        loop {
            let take;

            match (left_iter.peek(), right_iter.peek()) {
                (Some(l), Some(r)) => {
                    if l < r {
                        take = Take::OneLeft;
                    } else if r < l {
                        take = Take::OneRight;
                    } else {
                        take = Take::Both;
                    }
                }
                (Some(_), None) => {
                    take = Take::AllLeft;
                }
                (None, Some(_)) => {
                    take = Take::AllRight;
                }
                (None, None) => {
                    break;
                }
            }
            match take {
                Take::OneLeft => {
                    vec.push((*left_iter.next().unwrap()).clone());
                }
                Take::OneRight => {
                    vec.push((*right_iter.next().unwrap()).clone());
                }
                Take::Both => {
                    // two equal values
                    let left_value = left_iter.next().unwrap();
                    let right_value = right_iter.next().unwrap();
                    match choose_equal(left_value, right_value) {
                        LeftOrRight::Left => {
                            vec.push((*left_value).clone());
                        }
                        LeftOrRight::Right => {
                            vec.push((*right_value).clone());
                        }
                    }
                }
                Take::AllLeft => {
                    for item in left_iter {
                        vec.push((*item).clone());
                    }
                    break;
                }
                Take::AllRight => {
                    for item in right_iter {
                        vec.push((*item).clone());
                    }
                    break;
                }

            }
        }

        debug_assert!(is_sorted_unique(&vec));
        SortedUniqueVec { vec: vec }
    }
}

impl<T: Ord + Clone> AsRef<[T]> for SortedUniqueVec<T> {
    fn as_ref(&self) -> &[T] {
        &self.vec
    }
}

impl<T: Ord + Clone> Deref for SortedUniqueVec<T> {
    type Target = [T];

    fn deref(&self) -> &[T] {
        &self.vec
    }
}

#[test]
fn test_insert() {
    let mut s = SortedUniqueVec::new();
    s.insert(5);
    s.insert(1);
    s.insert(8);
    s.insert(0);
    assert!(is_sorted_unique(&s));
    assert_eq!(&[0, 1, 5, 8][..], s.as_ref());
    assert_eq!(4, s.len());
}

#[test]
fn test_merge() {
    let mut s1 = SortedUniqueVec::new();
    s1.insert(5);
    s1.insert(1);
    s1.insert(8);
    s1.insert(0);
    assert!(is_sorted_unique(&s1));

    let mut s2 = SortedUniqueVec::new();
    s2.insert(55);
    s2.insert(1);
    s2.insert(5);
    s2.insert(7);
    s2.insert(9);
    assert!(is_sorted_unique(&s2));

    let r = s1.merge(&s2, &|_, _| LeftOrRight::Left);
    assert!(is_sorted_unique(&r));
    assert_eq!(&[0, 1, 5, 7, 8, 9, 55][..], r.as_ref());
}
<commit_msg>Implement align(). This can be used for gene alignment in neat-rs.<commit_after>use std::ops::Deref;

/// A `Vec` in sorted order without duplicates.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SortedUniqueVec<T: Ord + Clone> {
    vec: Vec<T>,
}

fn is_sorted_unique<T: Ord>(slice: &[T]) -> bool {
    if slice.len() < 2 {
        true
    } else {
        slice.windows(2).all(|win| win[0] < win[1])
    }
}

pub enum LeftOrRight {
    Left,
    Right,
}

#[derive(Debug)]
pub enum Alignment<'a, T: 'a> {
    Match(&'a T, &'a T),
    ExcessLeftHead(&'a T),
    ExcessRightHead(&'a T),
    ExcessLeftTail(&'a T),
    ExcessRightTail(&'a T),
    DisjointLeft(&'a T),
    DisjointRight(&'a T),
}

impl<T: Ord + Clone> SortedUniqueVec<T> {
    pub fn new() -> Self {
        SortedUniqueVec { vec: Vec::new() }
    }

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

    pub fn contains(&self, element: &T) -> bool {
        self.vec.binary_search(element).is_ok()
    }

    /// Insert `element` into the sorted list.
    ///
    /// Panics if an element with the same key (according to the Eq trait) already exists.
    pub fn insert(&mut self, element: T) {
        match self.vec.binary_search(&element) {
            Ok(_idx) => {
                panic!("Element already exists");
            }
            Err(idx) => {
                self.vec.insert(idx, element);
            }
        }
    }

    /// Align the items of two sorted unique vectors.
    /// Note: The returned value from `f` must have the same order.
    pub fn align<'a, F>(&'a self, other: &'a Self, f: &mut F) -> Self
        where F: FnMut(Alignment<'a, T>) -> Option<T>
    {
        let mut vec = Vec::with_capacity(self.len() + other.len());

        let mut left_iter = self.vec.iter().peekable();
        let mut right_iter = other.vec.iter().peekable();
        let mut left_count = 0;
        let mut right_count = 0;

        enum Take {
            OneLeft,
            OneRight,
            Both,
            AllLeft,
            AllRight,
        };

        loop {
            let take;

            match (left_iter.peek(), right_iter.peek()) {
                (Some(l), Some(r)) => {
                    if l < r {
                        take = Take::OneLeft;
                    } else if r < l {
                        take = Take::OneRight;
                    } else {
                        take = Take::Both;
                    }
                }
                (Some(_), None) => {
                    take = Take::AllLeft;
                }
                (None, Some(_)) => {
                    take = Take::AllRight;
                }
                (None, None) => {
                    break;
                }
            }

            match take {
                Take::OneLeft => {
                    let value = left_iter.next().unwrap();

                    let alignment = if right_count == 0 {
                        Alignment::ExcessLeftHead(value)
                    } else {
                        Alignment::DisjointLeft(value)
                    };

                    if let Some(new) = f(alignment) {
                        assert!(value.eq(&new));
                        vec.push(new);
                    }

                    left_count += 1;
                }
                Take::OneRight => {
                    let value = right_iter.next().unwrap();

                    let alignment = if left_count == 0 {
                        Alignment::ExcessRightHead(value)
                    } else {
                        Alignment::DisjointRight(value)
                    };

                    if let Some(new) = f(alignment) {
                        assert!(value.eq(&new));
                        vec.push(new);
                    }

                    right_count += 1;
                }
                Take::Both => {
                    // two equal values
                    let left_value = left_iter.next().unwrap();
                    let right_value = right_iter.next().unwrap();
                    debug_assert!(left_value.eq(right_value));

                    let alignment = Alignment::Match(left_value, right_value);

                    if let Some(new) = f(alignment) {
                        assert!(left_value.eq(&new));
                        vec.push(new);
                    }

                    left_count += 1;
                    right_count += 1;
                }
                Take::AllLeft => {
                    // There are no items left on the right side, so all items are ExcessLeftTail.
                    for item in left_iter {
                        let alignment = Alignment::ExcessLeftTail(item);
                        if let Some(new) = f(alignment) {
                            assert!(item.eq(&new));
                            vec.push(new);
                        }
                    }
                    break;
                }
                Take::AllRight => {
                    // There are no items left on the right side, so all items are ExcessRightTail.
                    for item in right_iter {
                        let alignment = Alignment::ExcessRightTail(item);
                        if let Some(new) = f(alignment) {
                            assert!(item.eq(&new));
                            vec.push(new);
                        }
                    }
                    break;
                }
            }
        }

        debug_assert!(is_sorted_unique(&vec));
        SortedUniqueVec { vec: vec }
    }

    /// Merges `self` and `other` into a new SortedVec.
    /// `choose_equal` decides which one of two equal values to take.
    pub fn merge<F>(&self, other: &Self, choose_equal: &F) -> Self
        where F: Fn(&T, &T) -> LeftOrRight
    {
        let mut vec = Vec::with_capacity(self.len() + other.len());

        let mut left_iter = self.vec.iter().peekable();
        let mut right_iter = other.vec.iter().peekable();

        enum Take {
            OneLeft,
            OneRight,
            Both,
            AllLeft,
            AllRight,
        };

        loop {
            let take;

            match (left_iter.peek(), right_iter.peek()) {
                (Some(l), Some(r)) => {
                    if l < r {
                        take = Take::OneLeft;
                    } else if r < l {
                        take = Take::OneRight;
                    } else {
                        take = Take::Both;
                    }
                }
                (Some(_), None) => {
                    take = Take::AllLeft;
                }
                (None, Some(_)) => {
                    take = Take::AllRight;
                }
                (None, None) => {
                    break;
                }
            }
            match take {
                Take::OneLeft => {
                    vec.push((*left_iter.next().unwrap()).clone());
                }
                Take::OneRight => {
                    vec.push((*right_iter.next().unwrap()).clone());
                }
                Take::Both => {
                    // two equal values
                    let left_value = left_iter.next().unwrap();
                    let right_value = right_iter.next().unwrap();
                    match choose_equal(left_value, right_value) {
                        LeftOrRight::Left => {
                            vec.push((*left_value).clone());
                        }
                        LeftOrRight::Right => {
                            vec.push((*right_value).clone());
                        }
                    }
                }
                Take::AllLeft => {
                    for item in left_iter {
                        vec.push((*item).clone());
                    }
                    break;
                }
                Take::AllRight => {
                    for item in right_iter {
                        vec.push((*item).clone());
                    }
                    break;
                }

            }
        }

        debug_assert!(is_sorted_unique(&vec));
        SortedUniqueVec { vec: vec }
    }
}

impl<T: Ord + Clone> AsRef<[T]> for SortedUniqueVec<T> {
    fn as_ref(&self) -> &[T] {
        &self.vec
    }
}

impl<T: Ord + Clone> Deref for SortedUniqueVec<T> {
    type Target = [T];

    fn deref(&self) -> &[T] {
        &self.vec
    }
}

#[test]
fn test_insert() {
    let mut s = SortedUniqueVec::new();
    s.insert(5);
    s.insert(1);
    s.insert(8);
    s.insert(0);
    assert!(is_sorted_unique(&s));
    assert_eq!(&[0, 1, 5, 8][..], s.as_ref());
    assert_eq!(4, s.len());
}

#[test]
fn test_merge() {
    let mut s1 = SortedUniqueVec::new();
    s1.insert(5);
    s1.insert(1);
    s1.insert(8);
    s1.insert(0);
    assert!(is_sorted_unique(&s1));

    let mut s2 = SortedUniqueVec::new();
    s2.insert(55);
    s2.insert(1);
    s2.insert(5);
    s2.insert(7);
    s2.insert(9);
    assert!(is_sorted_unique(&s2));

    let r = s1.merge(&s2, &|_, _| LeftOrRight::Left);
    assert!(is_sorted_unique(&r));
    assert_eq!(&[0, 1, 5, 7, 8, 9, 55][..], r.as_ref());
}

#[test]
fn test_align() {
    let mut s1 = SortedUniqueVec::new();
    s1.insert(0);
    s1.insert(1);
    s1.insert(5);
    s1.insert(8);
    assert!(is_sorted_unique(&s1));

    let mut s2 = SortedUniqueVec::new();
    s2.insert(1);
    s2.insert(5);
    s2.insert(7);
    s2.insert(9);
    s2.insert(55);
    assert!(is_sorted_unique(&s2));

    let r = s1.align(&s2,
                     &mut |alignment| {
                         match alignment {
                             Alignment::Match(a, _b) => Some((*a).clone()),
                             Alignment::ExcessLeftHead(a) |
                             Alignment::ExcessRightHead(a) |
                             Alignment::ExcessLeftTail(a) |
                             Alignment::ExcessRightTail(a) |
                             Alignment::DisjointLeft(a) |
                             Alignment::DisjointRight(a) => Some((*a).clone()),
                         }
                     });

    assert!(is_sorted_unique(&r));
    assert_eq!(&[0, 1, 5, 7, 8, 9, 55][..], r.as_ref());
}
<|endoftext|>


<commit_before>#![allow(dead_code, unused_variables, option_unwrap_used)]
#![feature(custom_derive, plugin)]
#![plugin(clippy)]

#[macro_use]
extern crate kafka;
extern crate log;
extern crate openssl;
extern crate toml;

mod flowgger;

const DEFAULT_CONFIG_FILE: &'static str = "flowgger.toml";

fn main() {
    let config_file = std::env::args().skip(1).next().unwrap_or(DEFAULT_CONFIG_FILE.to_owned());
    flowgger::start(&config_file);
}
<commit_msg>Remove unused custom_derive feature<commit_after>#![allow(dead_code, unused_variables, option_unwrap_used)]
#![feature(plugin)]
#![plugin(clippy)]

#[macro_use]
extern crate kafka;
extern crate log;
extern crate openssl;
extern crate toml;

mod flowgger;

const DEFAULT_CONFIG_FILE: &'static str = "flowgger.toml";

fn main() {
    let config_file = std::env::args().skip(1).next().unwrap_or(DEFAULT_CONFIG_FILE.to_owned());
    flowgger::start(&config_file);
}
<|endoftext|>


<commit_before>#![feature(drain_filter, proc_macro, slice_patterns)]

extern crate hyper;
extern crate iron;
extern crate regex;
extern crate router;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate scoped_threadpool;
extern crate tera;

use std::collections::{BTreeMap, HashMap};
use std::env;
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH};

use iron::headers::ContentType;
use iron::prelude::*;
use iron::status;
use router::Router;
use scoped_threadpool::Pool;
use tera::{Context, Tera};

pub mod battle_net_api_client;
pub mod thread_throttler;

use battle_net_api_client::{AuctionListing, BattleNetApiClient, Region};

/// Represents a single option available for sale from the blood vendor.
#[derive(Debug, Deserialize)]
struct VendorItem {
    name: String,
    quantity: f64,
    id: u64,
    vendor_type: String,  // TODO: This should really be an enum populated by a custom deserializer.
    subtext: Option<String>,
    mats: Option<Vec<CraftingComponent>>,
}

/// Some items we can 'buy' are actually crafted or traded with NPCs. This represents
/// an ingredient in the recipe.
#[derive(Debug, Deserialize)]
struct CraftingComponent {
    id: u64,
    quantity: u64,
}

/// Value of an item on a realm.
#[derive(Debug)]
struct ItemValue {
    id: u64,
    value: u64,
}

/// The calculated values for items on a particular realm.
#[derive(Debug)]
struct CurrentRealmValues {
    last_update: u64,  // The last time we got this info, as reported by the Blizzard API.
    auction_values: Arc<Vec<ItemValue>>,  // Should be sorted by value.
}

/// All the data in a single row in our price list for a realm.
#[derive(Debug, Serialize)]
struct PriceRow {
    name: String,
    quantity: f64,
    icon: String,
    subtext: String,
    vendor_type: String,
    value_ratio: u64,
    gold: u64,
    silver: u64,
    copper: u64,
}

/// Number of threads to use when fetching auction house results.
const NUM_AUCTION_DATA_THREADS: u32 = 5;

/// Number of seconds to wait between fetching new auction results.
const RESULT_FETCH_PERIOD: u64 = 60 * 30;

/// Given a vec of auction listings for a realm and a map of the items we care about,
/// returns a vec of (item_id, value) sorted by decreasing value, where value is
/// based on the 5th percentile buyout price.
fn calculate_auction_values(listings: &Vec<AuctionListing>, items: &HashMap<u64, VendorItem>) -> Vec<ItemValue> {
    // Calculate 5th percentiles for the items we care about.
    let mut price_points: BTreeMap<u64, Vec<(u64, u64)>> = BTreeMap::new();
    for listing in listings {
        if items.contains_key(&listing.item) && listing.buyout > 0 {
            price_points.entry(listing.item).or_insert(Vec::new()).push((listing.quantity, listing.buyout / listing.quantity));
        }
    }
    for quantities_and_buyouts in price_points.values_mut() {
        quantities_and_buyouts.sort_by_key(|a| a.1);  // Sort by buyout price.
    }
    let total_item_quantities: BTreeMap<u64, u64> =
        price_points.iter().map(|(k, v)| {
            (*k, v.iter().fold(0, |sum, quantity_and_buyout| sum + quantity_and_buyout.0))
        }).collect();
    let fifth_percentile_price_points: BTreeMap<u64, u64> =
        price_points.iter().map(|(item_id, ref item_listings)| {
            let fifth_percentile_quantity = total_item_quantities.get(item_id).unwrap() / 20;
            let mut running_sum: u64 = 0;
            let fifth_percentile_listing = item_listings.iter().find(|&&(quantity, _)| {
                running_sum += quantity;
                running_sum >= fifth_percentile_quantity
            }).unwrap();
            (*item_id, fifth_percentile_listing.1)
        }).collect();
    let mut item_values: Vec<ItemValue> = items.values().map(|item|
        ItemValue {
            id: item.id,
            value: match item.quantity {
                // A bit of a hack to avoid propagating floating point madness through the codebase:
                // Some items are 10 primal sargerite for 1 gem. All the others are whole numbers. So
                // we special case the former case as integer division.
                0.1 => fifth_percentile_price_points.get(&item.id).unwrap_or(&0u64) / 10,
                _ => fifth_percentile_price_points.get(&item.id).unwrap_or(&0u64) * item.quantity as u64,
            }
        }
    ).collect();
    item_values.sort_by_key(|item_value| !item_value.value);
    item_values
}

/// Given a battle net Region, return the first path of the app's URL.
fn app_url_for_region(region: &Region) -> &'static str {
    match region {
        &Region::US => "blood-money",
        &Region::EU => "blood-money-eu",
    }
}

fn main() {
    let token = match env::args().nth(1) {
        Some(token) => token,
        None => {
            println!("Usage: bloodmoney <api token> (us|eu)");
            return;
        }
    };
    let locale = match env::args().nth(2) {
        Some(ref s) if s == "us" => Region::US,
        Some(ref s) if s == "eu" => Region::EU,
        _ => {
            println!("Usage: bloodmoney <api token> (us|eu)");
            return;
        }
    };
    let client = Arc::new(BattleNetApiClient::new(&token, locale));

    // Process our item options and grab their icon names.
    let items: Vec<VendorItem> = serde_json::from_str(include_str!("../catalog/items.json"))
        .expect("Error reading items.");
    let item_id_map: Arc<HashMap<u64, VendorItem>> = Arc::new(items.into_iter().map(|x| (x.id, x)).collect());
    let item_icons: Arc<HashMap<u64, String>> = Arc::new(item_id_map.keys().map(|&id| (id, client.get_item_info(id).icon)).collect());

    // Get the list of realms and create an empty price map so we can render pages while
    // waiting for the auction results to be retrieved.
    let realms = Arc::new(client.get_realms());
    let connected_realms = BattleNetApiClient::process_connected_realms(&realms);
    let price_map: Arc<BTreeMap<String, RwLock<CurrentRealmValues>>> =
        Arc::new(realms.iter().map(|realm| (realm.slug.clone(), RwLock::new(CurrentRealmValues {
            last_update: 0,
            auction_values: Arc::new(item_id_map.keys().map(|id| ItemValue{id: *id, value: 0u64}).collect()),
        }))).collect());

    // Set up our web-app.
    let tera = Arc::new(Tera::new("templates/**/*"));
    let mut router = Router::new();
    {
        let realms = realms.clone();
        let tera = tera.clone();
        router.get(format!("/{}", app_url_for_region(&locale)), move |_: &mut Request| {
            let mut context = Context::new();
            context.add("realms", &realms);
            context.add("is_eu", &(locale == Region::EU));
            Ok(Response::with((ContentType::html().0, status::Ok, tera.render("index.html", context).unwrap())))
        }, "index");
    }
    {
        let price_map = price_map.clone();
        let item_id_map = item_id_map.clone();
        let realms = realms.clone();
        let tera = tera.clone();
        router.get(format!("/{}/:realm", app_url_for_region(&locale)), move |req : &mut Request| {
            let realm = req.extensions.get::<Router>().unwrap().find("realm").unwrap();
            if let Some(realm_prices_lock) = price_map.get(realm) {
                let mut context = Context::new();
                let realm_prices = realm_prices_lock.read().unwrap();
                // Build up a list of entries.
                let highest_value = realm_prices.auction_values.get(0).unwrap().value;
                let mut price_rows: Vec<PriceRow> = realm_prices.auction_values.iter().map(|&ItemValue{id, value}| {
                    let item_info = item_id_map.get(&id).unwrap();
                    let gold = value / (10_000);
                    let silver = (value - gold * 10_000) / 100;
                    let copper = value - gold * 10_000 - silver * 100;
                    let value_ratio = match highest_value {
                        0u64 => 0u64,
                        _ => value*100/highest_value,  // Percentile!
                    };
                    PriceRow {
                        name: item_info.name.clone(),
                        quantity: item_info.quantity,
                        icon: item_icons.get(&id).unwrap().clone(),
                        subtext: item_info.subtext.clone().unwrap_or(String::new()),
                        vendor_type: item_info.vendor_type.clone(),
                        value_ratio: value_ratio,
                        gold: gold,
                        silver: silver,
                        copper: copper,
                    }
                }).collect();
                // NOTE: drain_filter() is a nightly only experimental API call that might break.
                let blood_price_rows = price_rows.drain_filter(|x| x.vendor_type.eq("blood")).collect::<Vec<_>>();
                let sargerite_price_rows = price_rows.drain_filter(|x| x.vendor_type.eq("sargerite")).collect::<Vec<_>>();
                context.add("realm_name", &realms.iter().find(|&realm_info| &realm_info.slug == realm).unwrap().name);
                context.add("blood_price_rows", &blood_price_rows);
                context.add("sargerite_price_rows", &sargerite_price_rows);
                // TODO: Change this to something more human readable.
                if realm_prices.last_update == 0 {
                    context.add("update_age", &-1);
                } else {
                    context.add("update_age",
                        &((SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - realm_prices.last_update / 1000) / 60));
                }
                context.add("is_eu", &(locale == Region::EU));
                Ok(Response::with((ContentType::html().0, status::Ok, tera.render("prices.html", context).unwrap())))
            } else {
                return Ok(Response::with(status::NotFound));
            }
        }, "realm-prices");
    }
    let http_result = Iron::new(router).http(format!("localhost:{}", match locale {
        Region::US => 3000,
        Region::EU => 3001,
    }).as_str());
    println!("Ready for web traffic.");

    // Now that the webserver is up, periodically fetch
    // new auction house data.
    let mut pool = Pool::new(NUM_AUCTION_DATA_THREADS);
    loop {
        let download_start = Instant::now();
        let next_download_time = download_start + Duration::from_secs(RESULT_FETCH_PERIOD);
        println!("Starting download of auction data.");
        pool.scoped(|scope| {
            for realm_list in &connected_realms {
                // We have to move realm_list into the closure.
                // Clone other values.
                let client = client.clone();
                let price_map = price_map.clone();
                let item_id_map = item_id_map.clone();
                scope.execute(move || {
                    let lead_realm = realm_list.get(0).unwrap();
                    let update_time: u64;
                    let auction_listings: Vec<AuctionListing>;
                    println!("Downloading {}", lead_realm);
                    {
                        let current_realm_values =
                            price_map.get(lead_realm).unwrap().read().unwrap();
                        match client.get_auction_listings(lead_realm, current_realm_values.last_update) {
                            Some((ts, al)) => {
                                update_time = ts;
                                auction_listings = al;
                            },
                            None => return,
                        }
                    }
                    let auction_values = Arc::new(calculate_auction_values(&auction_listings, &item_id_map));
                    for realm in realm_list {
                        println!("Updating {}", realm);
                        let mut current_realm_values =
                            price_map.get(realm).unwrap().write().unwrap();
                        current_realm_values.auction_values = auction_values.clone();
                        current_realm_values.last_update = update_time;
                    }
                })
            }
            scope.join_all();
        });
        let download_end_time = Instant::now();
        println!("Downloading all realms took {} seconds.", download_end_time.duration_since(download_start).as_secs());
        if download_end_time < next_download_time {
            println!("Sleeping for {}", next_download_time.duration_since(download_end_time).as_secs());
            sleep(next_download_time.duration_since(download_end_time));
        }
    }
}
<commit_msg>Don't consider reagents for highest value item<commit_after>#![feature(drain_filter, proc_macro, slice_patterns)]

extern crate hyper;
extern crate iron;
extern crate regex;
extern crate router;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate scoped_threadpool;
extern crate tera;

use std::collections::{BTreeMap, HashMap};
use std::env;
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH};

use iron::headers::ContentType;
use iron::prelude::*;
use iron::status;
use router::Router;
use scoped_threadpool::Pool;
use tera::{Context, Tera};

pub mod battle_net_api_client;
pub mod thread_throttler;

use battle_net_api_client::{AuctionListing, BattleNetApiClient, Region};

/// Represents a single option available for sale from the blood vendor.
#[derive(Debug, Deserialize)]
struct VendorItem {
    name: String,
    quantity: f64,
    id: u64,
    vendor_type: String,  // TODO: This should really be an enum populated by a custom deserializer.
    subtext: Option<String>,
    mats: Option<Vec<CraftingComponent>>,
}

/// Some items we can 'buy' are actually crafted or traded with NPCs. This represents
/// an ingredient in the recipe.
#[derive(Debug, Deserialize)]
struct CraftingComponent {
    id: u64,
    quantity: u64,
}

/// Value of an item on a realm.
#[derive(Debug)]
struct ItemValue {
    id: u64,
    value: u64,
}

/// The calculated values for items on a particular realm.
#[derive(Debug)]
struct CurrentRealmValues {
    last_update: u64,  // The last time we got this info, as reported by the Blizzard API.
    auction_values: Arc<Vec<ItemValue>>,  // Should be sorted by value.
}

/// All the data in a single row in our price list for a realm.
#[derive(Debug, Serialize)]
struct PriceRow {
    name: String,
    quantity: f64,
    icon: String,
    subtext: String,
    vendor_type: String,
    value_ratio: u64,
    gold: u64,
    silver: u64,
    copper: u64,
}

/// Number of threads to use when fetching auction house results.
const NUM_AUCTION_DATA_THREADS: u32 = 5;

/// Number of seconds to wait between fetching new auction results.
const RESULT_FETCH_PERIOD: u64 = 60 * 30;

/// Given a vec of auction listings for a realm and a map of the items we care about,
/// returns a vec of (item_id, value) sorted by decreasing value, where value is
/// based on the 5th percentile buyout price.
fn calculate_auction_values(listings: &Vec<AuctionListing>, items: &HashMap<u64, VendorItem>) -> Vec<ItemValue> {
    // Calculate 5th percentiles for the items we care about.
    let mut price_points: BTreeMap<u64, Vec<(u64, u64)>> = BTreeMap::new();
    for listing in listings {
        if items.contains_key(&listing.item) && listing.buyout > 0 {
            price_points.entry(listing.item).or_insert(Vec::new()).push((listing.quantity, listing.buyout / listing.quantity));
        }
    }
    for quantities_and_buyouts in price_points.values_mut() {
        quantities_and_buyouts.sort_by_key(|a| a.1);  // Sort by buyout price.
    }
    let total_item_quantities: BTreeMap<u64, u64> =
        price_points.iter().map(|(k, v)| {
            (*k, v.iter().fold(0, |sum, quantity_and_buyout| sum + quantity_and_buyout.0))
        }).collect();
    let fifth_percentile_price_points: BTreeMap<u64, u64> =
        price_points.iter().map(|(item_id, ref item_listings)| {
            let fifth_percentile_quantity = total_item_quantities.get(item_id).unwrap() / 20;
            let mut running_sum: u64 = 0;
            let fifth_percentile_listing = item_listings.iter().find(|&&(quantity, _)| {
                running_sum += quantity;
                running_sum >= fifth_percentile_quantity
            }).unwrap();
            (*item_id, fifth_percentile_listing.1)
        }).collect();
    let mut item_values: Vec<ItemValue> = items.values().map(|item|
        ItemValue {
            id: item.id,
            value: match item.quantity {
                // A bit of a hack to avoid propagating floating point madness through the codebase:
                // Some items are 10 primal sargerite for 1 gem. All the others are whole numbers. So
                // we special case the former case as integer division.
                0.1 => fifth_percentile_price_points.get(&item.id).unwrap_or(&0u64) / 10,
                _ => fifth_percentile_price_points.get(&item.id).unwrap_or(&0u64) * item.quantity as u64,
            }
        }
    ).collect();
    item_values.sort_by_key(|item_value| !item_value.value);
    item_values
}

/// Given a battle net Region, return the first path of the app's URL.
fn app_url_for_region(region: &Region) -> &'static str {
    match region {
        &Region::US => "blood-money",
        &Region::EU => "blood-money-eu",
    }
}

fn main() {
    let token = match env::args().nth(1) {
        Some(token) => token,
        None => {
            println!("Usage: bloodmoney <api token> (us|eu)");
            return;
        }
    };
    let locale = match env::args().nth(2) {
        Some(ref s) if s == "us" => Region::US,
        Some(ref s) if s == "eu" => Region::EU,
        _ => {
            println!("Usage: bloodmoney <api token> (us|eu)");
            return;
        }
    };
    let client = Arc::new(BattleNetApiClient::new(&token, locale));

    // Process our item options and grab their icon names.
    let items: Vec<VendorItem> = serde_json::from_str(include_str!("../catalog/items.json"))
        .expect("Error reading items.");
    let item_id_map: Arc<HashMap<u64, VendorItem>> = Arc::new(items.into_iter().map(|x| (x.id, x)).collect());
    let item_icons: Arc<HashMap<u64, String>> = Arc::new(item_id_map.keys().map(|&id| (id, client.get_item_info(id).icon)).collect());

    // Get the list of realms and create an empty price map so we can render pages while
    // waiting for the auction results to be retrieved.
    let realms = Arc::new(client.get_realms());
    let connected_realms = BattleNetApiClient::process_connected_realms(&realms);
    let price_map: Arc<BTreeMap<String, RwLock<CurrentRealmValues>>> =
        Arc::new(realms.iter().map(|realm| (realm.slug.clone(), RwLock::new(CurrentRealmValues {
            last_update: 0,
            auction_values: Arc::new(item_id_map.keys().map(|id| ItemValue{id: *id, value: 0u64}).collect()),
        }))).collect());

    // Set up our web-app.
    let tera = Arc::new(Tera::new("templates/**/*"));
    let mut router = Router::new();
    {
        let realms = realms.clone();
        let tera = tera.clone();
        router.get(format!("/{}", app_url_for_region(&locale)), move |_: &mut Request| {
            let mut context = Context::new();
            context.add("realms", &realms);
            context.add("is_eu", &(locale == Region::EU));
            Ok(Response::with((ContentType::html().0, status::Ok, tera.render("index.html", context).unwrap())))
        }, "index");
    }
    {
        let price_map = price_map.clone();
        let item_id_map = item_id_map.clone();
        let realms = realms.clone();
        let tera = tera.clone();
        router.get(format!("/{}/:realm", app_url_for_region(&locale)), move |req : &mut Request| {
            let realm = req.extensions.get::<Router>().unwrap().find("realm").unwrap();
            if let Some(realm_prices_lock) = price_map.get(realm) {
                let mut context = Context::new();
                let realm_prices = realm_prices_lock.read().unwrap();
                // Build up a list of entries.
                // Find the most valuable item that isn't a reagent.
                // TODO: This calculation is getting a little awkward. Do it at template time instead?
                let highest_value = realm_prices.auction_values.iter()
                    .find(|&x| item_id_map.get(&x.id).unwrap().vendor_type != "reagent")
                    .unwrap().value;
                let mut price_rows: Vec<PriceRow> = realm_prices.auction_values.iter().map(|&ItemValue{id, value}| {
                    let item_info = item_id_map.get(&id).unwrap();
                    let gold = value / (10_000);
                    let silver = (value - gold * 10_000) / 100;
                    let copper = value - gold * 10_000 - silver * 100;
                    let value_ratio = match highest_value {
                        0u64 => 0u64,
                        _ => value*100/highest_value,  // Percentile!
                    };
                    PriceRow {
                        name: item_info.name.clone(),
                        quantity: item_info.quantity,
                        icon: item_icons.get(&id).unwrap().clone(),
                        subtext: item_info.subtext.clone().unwrap_or(String::new()),
                        vendor_type: item_info.vendor_type.clone(),
                        value_ratio: value_ratio,
                        gold: gold,
                        silver: silver,
                        copper: copper,
                    }
                }).collect();
                // NOTE: drain_filter() is a nightly only experimental API call that might break.
                let blood_price_rows = price_rows.drain_filter(|x| x.vendor_type.eq("blood")).collect::<Vec<_>>();
                let sargerite_price_rows = price_rows.drain_filter(|x| x.vendor_type.eq("sargerite")).collect::<Vec<_>>();
                context.add("realm_name", &realms.iter().find(|&realm_info| &realm_info.slug == realm).unwrap().name);
                context.add("blood_price_rows", &blood_price_rows);
                context.add("sargerite_price_rows", &sargerite_price_rows);
                // TODO: Change this to something more human readable.
                if realm_prices.last_update == 0 {
                    context.add("update_age", &-1);
                } else {
                    context.add("update_age",
                        &((SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() - realm_prices.last_update / 1000) / 60));
                }
                context.add("is_eu", &(locale == Region::EU));
                Ok(Response::with((ContentType::html().0, status::Ok, tera.render("prices.html", context).unwrap())))
            } else {
                return Ok(Response::with(status::NotFound));
            }
        }, "realm-prices");
    }
    let http_result = Iron::new(router).http(format!("localhost:{}", match locale {
        Region::US => 3000,
        Region::EU => 3001,
    }).as_str());
    println!("Ready for web traffic.");

    // Now that the webserver is up, periodically fetch
    // new auction house data.
    let mut pool = Pool::new(NUM_AUCTION_DATA_THREADS);
    loop {
        let download_start = Instant::now();
        let next_download_time = download_start + Duration::from_secs(RESULT_FETCH_PERIOD);
        println!("Starting download of auction data.");
        pool.scoped(|scope| {
            for realm_list in &connected_realms {
                // We have to move realm_list into the closure.
                // Clone other values.
                let client = client.clone();
                let price_map = price_map.clone();
                let item_id_map = item_id_map.clone();
                scope.execute(move || {
                    let lead_realm = realm_list.get(0).unwrap();
                    let update_time: u64;
                    let auction_listings: Vec<AuctionListing>;
                    println!("Downloading {}", lead_realm);
                    {
                        let current_realm_values =
                            price_map.get(lead_realm).unwrap().read().unwrap();
                        match client.get_auction_listings(lead_realm, current_realm_values.last_update) {
                            Some((ts, al)) => {
                                update_time = ts;
                                auction_listings = al;
                            },
                            None => return,
                        }
                    }
                    let auction_values = Arc::new(calculate_auction_values(&auction_listings, &item_id_map));
                    for realm in realm_list {
                        println!("Updating {}", realm);
                        let mut current_realm_values =
                            price_map.get(realm).unwrap().write().unwrap();
                        current_realm_values.auction_values = auction_values.clone();
                        current_realm_values.last_update = update_time;
                    }
                })
            }
            scope.join_all();
        });
        let download_end_time = Instant::now();
        println!("Downloading all realms took {} seconds.", download_end_time.duration_since(download_start).as_secs());
        if download_end_time < next_download_time {
            println!("Sleeping for {}", next_download_time.duration_since(download_end_time).as_secs());
            sleep(next_download_time.duration_since(download_end_time));
        }
    }
}
<|endoftext|>


<commit_before>#![feature(box_syntax)]

extern crate bins as lib;
extern crate hyper;
extern crate hyper_openssl;
extern crate url;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate clap;
extern crate toml;
extern crate flate2;
#[macro_use]
extern crate log;
extern crate time;

macro_rules! option {
  ($e: expr) => {{
    match $e {
      Some(x) => x,
      None => return None
    }
  }}
}

mod bins;
mod config;
mod logger;

use config::*;

use lib::*;
use lib::error::*;
use lib::files::{Paste, UploadFile};

use clap::{App, Arg, ArgMatches};
use flate2::read::GzDecoder;

use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::{Read, Write};
use std::io::Result as IoResult;
use std::error::Error;
use std::collections::HashMap;
use std::sync::Arc;

use log::LogLevel;

use url::Url;

fn main() {
  std::process::exit(inner());
}

fn inner() -> i32 {
  let config = match get_config() {
    Ok(c) => c,
    Err(e) => {
      println!("could not create or load bins config file: {}", e);
      for error in error_parents(&e) {
        println!("{}", error);
      }
      return 1;
    }
  };

  let matches = App::new("bins")
    .about("A tool for pasting from the terminal")
    .author(crate_authors!())
    .version(crate_version!())
    .version_message("print version information and exit")
    .version_short("v")
    .help_message("print help information and exit")
    .arg(Arg::with_name("inputs")
      .help("inputs to the program, either files or URLs")
      .takes_value(true)
      .value_name("input")
      .multiple(true))
    .arg(Arg::with_name("debug")
      .long("debug")
      .short("d")
      .help("enable debug output"))
    .arg(Arg::with_name("bin")
      .long("bin")
      .short("b")
      .help("specify the upload bin")
      .required(config.defaults.bin.is_none())
      .takes_value(true)
      .value_name("bin")
      .possible_values(&["hastebin", "sprunge", "gist"]))
    .arg(Arg::with_name("public")
      .long("public")
      .short("P")
      .help("set the paste to be public")
      .conflicts_with("private"))
    .arg(Arg::with_name("private")
      .long("private")
      .short("p")
      .help("set the paste to be private"))
    .arg(Arg::with_name("authed")
      .long("authed")
      .short("a")
      .help("set the paste to be uploaded while authenticated")
      .conflicts_with("anonymous"))
    .arg(Arg::with_name("anonymous")
      .long("anonymous")
      .short("A")
      .help("set the paste to be uploaded while not authenticated"))
    .arg(Arg::with_name("json")
      .long("json")
      .short("j")
      .help("make bins output JSON information"))
    .get_matches();

  let level = if matches.is_present("debug") {
    LogLevel::Debug
  } else {
    LogLevel::Info
  };
  if let Err(e) = logger::Logger::new(level).init() {
    println!("could not initialize logger: {}", e);
  }

  let mut cli_options = CommandLineOptions::default();

  if matches.is_present("public") {
    cli_options.private = Some(false);
  } else if matches.is_present("private") {
    cli_options.private = Some(true);
  }

  if matches.is_present("authed") {
    cli_options.authed = Some(true);
  } else if matches.is_present("anonymous") {
    cli_options.authed = Some(false);
  }

  if matches.is_present("json") {
    cli_options.json = Some(true);
  }

  let config = Arc::new(config);
  let cli_options = Arc::new(cli_options);

  let bins: HashMap<&str, Box<Bin>> = {
    let mut map: HashMap<&str, Box<Bin>> = HashMap::new();
    map.insert("sprunge", box bins::Sprunge::new(config.clone(), cli_options.clone()));
    map.insert("hastebin", box bins::Hastebin::new(config.clone(), cli_options.clone()));
    map.insert("gist", box bins::Gist::new(config.clone(), cli_options.clone()));
    map
  };

  let b = Bins {
    bins: bins,
    config: config,
    cli_options: cli_options,
    matches: matches
  };

  b.main()
}

struct Bins<'a> {
  bins: HashMap<&'static str, Box<Bin>>,
  config: Arc<Config>,
  cli_options: Arc<CommandLineOptions>,
  matches: ArgMatches<'a>
}

impl<'a> Bins<'a> {
  fn main(&self) -> i32 {
    let inputs: Option<Vec<&str>> = self.matches.values_of("inputs").map(|x| x.collect());
    if let Some(ref is) = inputs {
      if !is.is_empty() {
        let url: Result<Url> = Url::parse(&is[0]).map_err(BinsError::UrlParse);
        if let Ok(u) = url {
          return self.download(u, if is.len() > 1 { Some(&is[1..]) } else { None });
        }
      }
    }
    self.upload(inputs)
  }

  fn upload(&self, inputs: Option<Vec<&str>>) -> i32 {
    let bin_name = self.matches.value_of("bin").map(|x| x.to_owned()).or_else(|| self.config.defaults.bin.clone()).expect("no bin specified");
    let possible_bin = self.bins.get(bin_name.as_str());
    let bin = match possible_bin {
      Some(b) => b,
      None => {
        error!("there is no bin called \"{}\"", bin_name);
        return 1;
      }
    };

    let bin_features = bin.features();
    let features = {
      let mut map = HashMap::new();
      map.insert(BinFeature::Private, self.cli_options.private);
      map.insert(BinFeature::Public, self.cli_options.private.map(|x| !x));
      map.insert(BinFeature::Authed, self.cli_options.authed);
      map.insert(BinFeature::Anonymous, self.cli_options.authed.map(|x| !x));
      map
    };
    for (feature, status) in features {
      if let Some(true) = status {
        if !bin_features.contains(&feature) {
          if let Some(true) = self.config.safety.warn_on_unsupported {
            warn!("{} does not support {} pastes", bin.name(), feature);
          }
          if let Some(true) = self.config.safety.cancel_on_unsupported {
            error!("bins stopped because an unsupported feature was used with {}", bin.name());
            return 1;
          }
        }
      }
    }

    let upload_files = match inputs {
      Some(v) => get_upload_files(v),
      None => get_stdin().map(|x| vec![x])
    };
    let upload_files = match upload_files {
      Ok(u) => u,
      Err(e) => {
        error!("could not get input: {}", e);
        return 1;
      }
    };
    match bin.upload(&upload_files) {
      Err(e) => {
        error!("error uploading to {}: {}", bin.name(), e);
        for error in error_parents(&e) {
          error!("{}", error);
        }
        return 1;
      },
      Ok(u) => println!("{}", u)
    }
    0
  }

  fn download(&self, url: Url, names: Option<&[&str]>) -> i32 {
    let host = match url.host_str() {
      Some(h) => h,
      None => {
        error!("invalid url (no host): {}", url.as_str());
        return 1;
      }
    };
    let (is_html_url, bin) = match self.bins.iter().find(|&(_, b)| b.raw_host() == host) {
      Some(b) => (false, b.1),
      None => {
        match self.bins.iter().find(|&(_, b)| b.html_host() == host) {
          Some(b) => (true, b.1),
          None => {
            error!("no bin uses the hostname {}", host);
            return 1;
          }
        }
      }
    };
    let id = if is_html_url {
      bin.id_from_html_url(url.as_str())
    } else {
      bin.id_from_raw_url(url.as_str())
    };
    let id = match id {
      Some(i) => i,
      None => {
        error!("could not extract paste ID from {}", url.as_str());
        return 1;
      }
    };
    let download = match bin.download(&id, names) {
      Ok(d) => d,
      Err(e) => {
        error!("could not download ID {}: {}", id, e);
        return 1;
      }
    };
    if let Some(true) = self.cli_options.json {
      match serde_json::to_string(&download) {
        Ok(j) => println!("{}", j),
        Err(e) => {
          error!("error converting download to json: {}", e);
          return 1;
        }
      }
    } else {
      match download {
        Paste::Single(f) => {
          println!("{}", f.content);
        },
        Paste::MultiDownloaded(fs) => {
          for f in fs {
            println!("==> {} <==\n\n{}", f.name.name(), f.content);
          }
        }
      }
    }
    0
  }
}

fn get_stdin() -> Result<UploadFile> {
  let mut content = String::new();
  let mut stdin = std::io::stdin();
  stdin.read_to_string(&mut content).map_err(BinsError::Io)?;
  Ok(UploadFile::new("stdin".to_owned(), content))
}

fn get_upload_files(inputs: Vec<&str>) -> Result<Vec<UploadFile>> {
  let files: Vec<(&str, File)> = inputs.into_iter()
    .map(|f| File::open(f).map(|x| (f, x)))
    .collect::<IoResult<_>>()
    .map_err(BinsError::Io)?;
  let contents: Vec<(&str, String)> = files.into_iter()
    .map(|(n, mut f)| {
      let mut c = String::new();
      f.read_to_string(&mut c).map(|_| (n, c))
    })
    .collect::<IoResult<_>>()
    .map_err(BinsError::Io)?;
  Ok(contents.into_iter().map(|(n, c)| UploadFile::new(n.to_owned(), c)).collect())
}

fn error_parents(error: &Error) -> Vec<&Error> {
  let mut parents = Vec::new();
  let mut last_error = error;
  loop {
    match last_error.cause() {
      None => break,
      Some(e) => {
        parents.push(e);
        last_error = e;
      }
    }
  }
  parents
}

fn get_config() -> Result<Config> {
  let mut f = match find_config_path() {
    Some(p) => File::open(p).map_err(BinsError::Io)?,
    None => create_config_file()?
  };
  let mut content = String::new();
  f.read_to_string(&mut content).map_err(BinsError::Io)?;
  toml::from_str(&content).map_err(BinsError::Toml)
}

fn create_xdg_config_file() -> Result<File> {
  if let Ok(xdg_dir) = std::env::var("XDG_CONFIG_DIR") {
    let xdg_path = Path::new(&xdg_dir);
    let xdg_config_path = xdg_path.join("bins.cfg");
    if xdg_path.exists() && xdg_path.is_dir() && !xdg_config_path.exists() {
      return File::create(xdg_config_path).map_err(BinsError::Io);
    }
  }
  Err(BinsError::Config)
}

fn create_home_config_file() -> Result<File> {
  if let Ok(home_dir) = std::env::var("HOME") {
    let home = Path::new(&home_dir);
    let home_folder = home.join(".config");
    let home_folder_config = home_folder.join("bins.cfg");
    if home_folder.exists() && home_folder.is_dir() && !home_folder_config.exists() {
      return File::create(home_folder_config).map_err(BinsError::Io);
    }
    let home_config = Path::new(&home_dir).join(".bins.cfg");
    if home.exists() && home.is_dir() && !home_config.exists() {
      return File::create(home_config).map_err(BinsError::Io);
    }
  }
  Err(BinsError::Config)
}

fn create_config_file() -> Result<File> {
  let mut f = match create_xdg_config_file() {
    Ok(f) => f,
    Err(_) => match create_home_config_file() {
      Ok(hf) => hf,
      Err(_) => return Err(BinsError::Config)
    }
  };
  let mut default_config = String::new();
  GzDecoder::new(config::DEFAULT_CONFIG_GZIP)
    .map_err(BinsError::Io)?
    .read_to_string(&mut default_config)
    .map_err(BinsError::Io)?;
  f.write_all(default_config.as_bytes()).map_err(BinsError::Io)?;
  Ok(f)
}

fn find_config_path() -> Option<PathBuf> {
  if let Ok(xdg_dir) = std::env::var("XDG_CONFIG_DIR") {
    let xdg_config_path = Path::new(&xdg_dir).join("bins.cfg");
    if xdg_config_path.exists() {
      return Some(xdg_config_path.to_owned());
    }
  }
  if let Ok(home_dir) = std::env::var("HOME") {
    let home_config_folder = Path::new(&home_dir).join(".config").join("bins.cfg");
    if home_config_folder.exists() {
      return Some(home_config_folder.to_owned());
    }
    let home_config = Path::new(&home_dir).join(".bins.cfg");
    if home_config.exists() {
      return Some(home_config.to_owned());
    }
  }
  None
}
<commit_msg>fix: open proper file descriptor<commit_after>#![feature(box_syntax)]

extern crate bins as lib;
extern crate hyper;
extern crate hyper_openssl;
extern crate url;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate clap;
extern crate toml;
extern crate flate2;
#[macro_use]
extern crate log;
extern crate time;

macro_rules! option {
  ($e: expr) => {{
    match $e {
      Some(x) => x,
      None => return None
    }
  }}
}

mod bins;
mod config;
mod logger;

use config::*;

use lib::*;
use lib::error::*;
use lib::files::{Paste, UploadFile};

use clap::{App, Arg, ArgMatches};
use flate2::read::GzDecoder;

use std::path::{Path, PathBuf};
use std::fs::{File, OpenOptions};
use std::io::{Seek, SeekFrom};
use std::io::{Read, Write};
use std::io::Result as IoResult;
use std::error::Error;
use std::collections::HashMap;
use std::sync::Arc;

use log::LogLevel;

use url::Url;

fn main() {
  std::process::exit(inner());
}

fn inner() -> i32 {
  let config = match get_config() {
    Ok(c) => c,
    Err(e) => {
      println!("could not create or load bins config file: {}", e);
      for error in error_parents(&e) {
        println!("{}", error);
      }
      return 1;
    }
  };

  let matches = App::new("bins")
    .about("A tool for pasting from the terminal")
    .author(crate_authors!())
    .version(crate_version!())
    .version_message("print version information and exit")
    .version_short("v")
    .help_message("print help information and exit")
    .arg(Arg::with_name("inputs")
      .help("inputs to the program, either files or URLs")
      .takes_value(true)
      .value_name("input")
      .multiple(true))
    .arg(Arg::with_name("debug")
      .long("debug")
      .short("d")
      .help("enable debug output"))
    .arg(Arg::with_name("bin")
      .long("bin")
      .short("b")
      .help("specify the upload bin")
      .required(config.defaults.bin.is_none())
      .takes_value(true)
      .value_name("bin")
      .possible_values(&["hastebin", "sprunge", "gist"]))
    .arg(Arg::with_name("public")
      .long("public")
      .short("P")
      .help("set the paste to be public")
      .conflicts_with("private"))
    .arg(Arg::with_name("private")
      .long("private")
      .short("p")
      .help("set the paste to be private"))
    .arg(Arg::with_name("authed")
      .long("authed")
      .short("a")
      .help("set the paste to be uploaded while authenticated")
      .conflicts_with("anonymous"))
    .arg(Arg::with_name("anonymous")
      .long("anonymous")
      .short("A")
      .help("set the paste to be uploaded while not authenticated"))
    .arg(Arg::with_name("json")
      .long("json")
      .short("j")
      .help("make bins output JSON information"))
    .get_matches();

  let level = if matches.is_present("debug") {
    LogLevel::Debug
  } else {
    LogLevel::Info
  };
  if let Err(e) = logger::Logger::new(level).init() {
    println!("could not initialize logger: {}", e);
  }

  let mut cli_options = CommandLineOptions::default();

  if matches.is_present("public") {
    cli_options.private = Some(false);
  } else if matches.is_present("private") {
    cli_options.private = Some(true);
  }

  if matches.is_present("authed") {
    cli_options.authed = Some(true);
  } else if matches.is_present("anonymous") {
    cli_options.authed = Some(false);
  }

  if matches.is_present("json") {
    cli_options.json = Some(true);
  }

  let config = Arc::new(config);
  let cli_options = Arc::new(cli_options);

  let bins: HashMap<&str, Box<Bin>> = {
    let mut map: HashMap<&str, Box<Bin>> = HashMap::new();
    map.insert("sprunge", box bins::Sprunge::new(config.clone(), cli_options.clone()));
    map.insert("hastebin", box bins::Hastebin::new(config.clone(), cli_options.clone()));
    map.insert("gist", box bins::Gist::new(config.clone(), cli_options.clone()));
    map
  };

  let b = Bins {
    bins: bins,
    config: config,
    cli_options: cli_options,
    matches: matches
  };

  b.main()
}

struct Bins<'a> {
  bins: HashMap<&'static str, Box<Bin>>,
  config: Arc<Config>,
  cli_options: Arc<CommandLineOptions>,
  matches: ArgMatches<'a>
}

impl<'a> Bins<'a> {
  fn main(&self) -> i32 {
    let inputs: Option<Vec<&str>> = self.matches.values_of("inputs").map(|x| x.collect());
    if let Some(ref is) = inputs {
      if !is.is_empty() {
        let url: Result<Url> = Url::parse(&is[0]).map_err(BinsError::UrlParse);
        if let Ok(u) = url {
          return self.download(u, if is.len() > 1 { Some(&is[1..]) } else { None });
        }
      }
    }
    self.upload(inputs)
  }

  fn upload(&self, inputs: Option<Vec<&str>>) -> i32 {
    let bin_name = self.matches.value_of("bin").map(|x| x.to_owned()).or_else(|| self.config.defaults.bin.clone()).expect("no bin specified");
    let possible_bin = self.bins.get(bin_name.as_str());
    let bin = match possible_bin {
      Some(b) => b,
      None => {
        error!("there is no bin called \"{}\"", bin_name);
        return 1;
      }
    };

    let bin_features = bin.features();
    let features = {
      let mut map = HashMap::new();
      map.insert(BinFeature::Private, self.cli_options.private);
      map.insert(BinFeature::Public, self.cli_options.private.map(|x| !x));
      map.insert(BinFeature::Authed, self.cli_options.authed);
      map.insert(BinFeature::Anonymous, self.cli_options.authed.map(|x| !x));
      map
    };
    for (feature, status) in features {
      if let Some(true) = status {
        if !bin_features.contains(&feature) {
          if let Some(true) = self.config.safety.warn_on_unsupported {
            warn!("{} does not support {} pastes", bin.name(), feature);
          }
          if let Some(true) = self.config.safety.cancel_on_unsupported {
            error!("bins stopped because an unsupported feature was used with {}", bin.name());
            return 1;
          }
        }
      }
    }

    let upload_files = match inputs {
      Some(v) => get_upload_files(v),
      None => get_stdin().map(|x| vec![x])
    };
    let upload_files = match upload_files {
      Ok(u) => u,
      Err(e) => {
        error!("could not get input: {}", e);
        return 1;
      }
    };
    match bin.upload(&upload_files) {
      Err(e) => {
        error!("error uploading to {}: {}", bin.name(), e);
        for error in error_parents(&e) {
          error!("{}", error);
        }
        return 1;
      },
      Ok(u) => println!("{}", u)
    }
    0
  }

  fn download(&self, url: Url, names: Option<&[&str]>) -> i32 {
    let host = match url.host_str() {
      Some(h) => h,
      None => {
        error!("invalid url (no host): {}", url.as_str());
        return 1;
      }
    };
    let (is_html_url, bin) = match self.bins.iter().find(|&(_, b)| b.raw_host() == host) {
      Some(b) => (false, b.1),
      None => {
        match self.bins.iter().find(|&(_, b)| b.html_host() == host) {
          Some(b) => (true, b.1),
          None => {
            error!("no bin uses the hostname {}", host);
            return 1;
          }
        }
      }
    };
    let id = if is_html_url {
      bin.id_from_html_url(url.as_str())
    } else {
      bin.id_from_raw_url(url.as_str())
    };
    let id = match id {
      Some(i) => i,
      None => {
        error!("could not extract paste ID from {}", url.as_str());
        return 1;
      }
    };
    let download = match bin.download(&id, names) {
      Ok(d) => d,
      Err(e) => {
        error!("could not download ID {}: {}", id, e);
        return 1;
      }
    };
    if let Some(true) = self.cli_options.json {
      match serde_json::to_string(&download) {
        Ok(j) => println!("{}", j),
        Err(e) => {
          error!("error converting download to json: {}", e);
          return 1;
        }
      }
    } else {
      match download {
        Paste::Single(f) => {
          println!("{}", f.content);
        },
        Paste::MultiDownloaded(fs) => {
          for f in fs {
            println!("==> {} <==\n\n{}", f.name.name(), f.content);
          }
        }
      }
    }
    0
  }
}

fn get_stdin() -> Result<UploadFile> {
  let mut content = String::new();
  let mut stdin = std::io::stdin();
  stdin.read_to_string(&mut content).map_err(BinsError::Io)?;
  Ok(UploadFile::new("stdin".to_owned(), content))
}

fn get_upload_files(inputs: Vec<&str>) -> Result<Vec<UploadFile>> {
  let files: Vec<(&str, File)> = inputs.into_iter()
    .map(|f| File::open(f).map(|x| (f, x)))
    .collect::<IoResult<_>>()
    .map_err(BinsError::Io)?;
  let contents: Vec<(&str, String)> = files.into_iter()
    .map(|(n, mut f)| {
      let mut c = String::new();
      f.read_to_string(&mut c).map(|_| (n, c))
    })
    .collect::<IoResult<_>>()
    .map_err(BinsError::Io)?;
  Ok(contents.into_iter().map(|(n, c)| UploadFile::new(n.to_owned(), c)).collect())
}

fn error_parents(error: &Error) -> Vec<&Error> {
  let mut parents = Vec::new();
  let mut last_error = error;
  loop {
    match last_error.cause() {
      None => break,
      Some(e) => {
        parents.push(e);
        last_error = e;
      }
    }
  }
  parents
}

fn get_config() -> Result<Config> {
  let mut f = match find_config_path() {
    Some(p) => File::open(p).map_err(BinsError::Io)?,
    None => create_config_file()?
  };
  let mut content = String::new();
  f.read_to_string(&mut content).map_err(BinsError::Io)?;
  toml::from_str(&content).map_err(BinsError::Toml)
}

fn create_xdg_config_file() -> Result<File> {
  if let Ok(xdg_dir) = std::env::var("XDG_CONFIG_DIR") {
    let xdg_path = Path::new(&xdg_dir);
    let xdg_config_path = xdg_path.join("bins.cfg");
    if xdg_path.exists() && xdg_path.is_dir() && !xdg_config_path.exists() {
      return OpenOptions::new()
        .create(true)
        .read(true)
        .write(true)
        .open(xdg_config_path)
        .map_err(BinsError::Io);
    }
  }
  Err(BinsError::Config)
}

fn create_home_config_file() -> Result<File> {
  if let Ok(home_dir) = std::env::var("HOME") {
    let home = Path::new(&home_dir);
    let home_folder = home.join(".config");
    let home_folder_config = home_folder.join("bins.cfg");
    if home_folder.exists() && home_folder.is_dir() && !home_folder_config.exists() {
      return OpenOptions::new()
        .create(true)
        .read(true)
        .write(true)
        .open(home_folder_config)
        .map_err(BinsError::Io);
    }
    let home_config = Path::new(&home_dir).join(".bins.cfg");
    if home.exists() && home.is_dir() && !home_config.exists() {
      return OpenOptions::new()
        .create(true)
        .read(true)
        .write(true)
        .open(home_config)
        .map_err(BinsError::Io);
    }
  }
  Err(BinsError::Config)
}

fn create_config_file() -> Result<File> {
  let mut f = match create_xdg_config_file() {
    Ok(f) => f,
    Err(_) => match create_home_config_file() {
      Ok(hf) => hf,
      Err(_) => return Err(BinsError::Config)
    }
  };
  let mut default_config = String::new();
  GzDecoder::new(config::DEFAULT_CONFIG_GZIP)
    .map_err(BinsError::Io)?
    .read_to_string(&mut default_config)
    .map_err(BinsError::Io)?;
  f.write_all(default_config.as_bytes()).map_err(BinsError::Io)?;
  f.seek(SeekFrom::Start(0)).map_err(BinsError::Io)?;
  Ok(f)
}

fn find_config_path() -> Option<PathBuf> {
  if let Ok(xdg_dir) = std::env::var("XDG_CONFIG_DIR") {
    let xdg_config_path = Path::new(&xdg_dir).join("bins.cfg");
    if xdg_config_path.exists() {
      return Some(xdg_config_path.to_owned());
    }
  }
  if let Ok(home_dir) = std::env::var("HOME") {
    let home_config_folder = Path::new(&home_dir).join(".config").join("bins.cfg");
    if home_config_folder.exists() {
      return Some(home_config_folder.to_owned());
    }
    let home_config = Path::new(&home_dir).join(".bins.cfg");
    if home_config.exists() {
      return Some(home_config.to_owned());
    }
  }
  None
}
<|endoftext|>


<commit_before>// #![allow(dead_code)]
// #![allow(unused_imports)]
extern crate labyrinth as library;
extern crate num_cpus;
extern crate threadpool;

use std::env;
use threadpool::ThreadPool;
#[allow(unused_imports)]
use std::sync::mpsc::{channel, sync_channel};

use library::{Int, Uint, Wall};

fn main() {
    let mut args = env::args();
    args.next();
    let cols     = if let Some(arg) = args.next() { arg.parse::<Int>() }   else { Ok(10) };
    let rows     = if let Some(arg) = args.next() { arg.parse::<Int>() }   else { Ok(10) };
    let deep     = if let Some(arg) = args.next() { arg.parse::<Uint>() }  else { Ok(1u32) };
    let poolsize = if let Some(arg) = args.next() { arg.parse::<usize>() } else { Ok(num_cpus::get()) };

    match (rows, cols, deep, poolsize) {
        (Ok(rows), Ok(cols), Ok(deep), Ok(poolsize)) => {
            if rows > 0 && cols > 0 {

                /* Linear implementation */
                // for _ in 0..deep {
                //     let mut wall = Wall::new(cols, rows);
                //     wall.carve();
                //     wall.print();
                //     println!("");
                // }

                /* Parallel implementation */
                let pool = ThreadPool::new(poolsize);
                let port = {
                    let (chan, port) = channel(); // sync_channel((deep as usize + poolsize - 1) / poolsize);
                    for _ in 0..deep {
                        let chan = chan.clone();
                        pool.execute(move || {
                            let mut wall = Wall::new(cols, rows);
                            wall.carve();
                            chan.send(wall).unwrap();
                        });
                    }
                    port
                };
                for _ in 0..deep {
                    let wall = port.recv().unwrap();
                    wall.print();
                }

            } else {
                println!("Hey, give me numbers grater than 0");
            }
        }
        _ => println!("Hey, give me a number.")
    }
}<commit_msg>do not use threads for concurrency=1 and move parallel implementation to a function<commit_after>// #![allow(dead_code)]
// #![allow(unused_imports)]
extern crate labyrinth as library;
extern crate num_cpus;
extern crate threadpool;

use std::env;
use threadpool::ThreadPool;
#[allow(unused_imports)]
use std::sync::mpsc::{channel, sync_channel};

use library::{Int, Uint, Wall};

fn main() {
    let mut args = env::args();
    args.next();
    let cols = if let Some(arg) = args.next() { arg.parse::<Int>() }   else { Ok(10) };
    let rows = if let Some(arg) = args.next() { arg.parse::<Int>() }   else { Ok(10) };
    let deep = if let Some(arg) = args.next() { arg.parse::<Uint>() }  else { Ok(1u32) };
    let concurrency = if let Some(arg) = args.next() { arg.parse::<usize>() } else { Ok(num_cpus::get()) };

    match (rows, cols, deep, concurrency) {
        (Ok(rows), Ok(cols), Ok(deep), Ok(concurrency)) => {
            if rows > 0 && cols > 0 {

                if concurrency <= 1 {
                    /* Linear implementation */
                    for _ in 0..deep {
                        let mut wall = Wall::new(cols, rows);
                        wall.carve();
                        wall.print();
                    }
                } else {
                    /* Parallel implementation */
                    parallel(cols, rows, deep, concurrency);
                }

            } else {
                println!("Hey, give me numbers bigger than 0");
            }
        }
        _ => println!("Hey, give me a number.")
    }
}

fn parallel(cols: Int, rows: Int, deep: Uint, concurrency: usize) {
    let pool = ThreadPool::new(concurrency);
    let port = {
        let (chan, port) = channel(); // sync_channel((deep as usize + concurrency - 1) / concurrency);
        for _ in 0..deep {
            let chan = chan.clone();
            pool.execute(move || {
                let mut wall = Wall::new(cols, rows);
                wall.carve();
                chan.send(wall).unwrap();
            });
        }
        port
    };
    for _ in 0..deep {
        let wall = port.recv().unwrap();
        wall.print();
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate crc16;

use std::io::{self, Read, Write};
use std::convert::From;

// TODO: Send CAN byte after too many errors
// TODO: Handle CAN bytes while sending
// TODO: Implement Error for Error

const SOH: u8 = 0x01;
const STX: u8 = 0x02;
const EOT: u8 = 0x04;
const ACK: u8 = 0x06;
const NAK: u8 = 0x15;
const CAN: u8 = 0x18;
const CRC: u8 = 0x43;

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

#[derive(Debug)]
pub enum Error {
    Io(io::Error),

    /// The number of communications errors exceeded `max_errors` in a single
    /// transmission.
    ExhaustedRetries,

    /// The transmission was canceled by the other end of the channel.
    Canceled,
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

#[derive(Copy, Clone, Debug)]
pub enum Checksum {
    Standard,
    CRC16,
}

#[derive(Copy, Clone, Debug)]
pub enum BlockLength {
    Standard = 128,
    OneK = 1024,
}

/// Configuration for the XMODEM transfer.
#[derive(Copy, Clone, Debug)]
pub struct Xmodem {
    /// The number of errors that can occur before the communication is
    /// considered a failure. Errors include unexpected bytes and timeouts waiting for bytes.
    pub max_errors: u32,

    /// The byte used to pad the last block. XMODEM can only send blocks of a certain size,
    /// so if the message is not a multiple of that size the last block needs to be padded.
    pub pad_byte: u8,

    /// The length of each block. There are only two options: 128-byte blocks (standard
    ///  XMODEM) or 1024-byte blocks (XMODEM-1k).
    pub block_length: BlockLength,

    /// The checksum mode used by XMODEM. This is determined by the receiver.
    checksum_mode: Checksum,
    errors: u32,
}

impl Xmodem {
    /// Creates the XMODEM config with default parameters.
    pub fn new() -> Self {
        Xmodem {
            max_errors: 16,
            pad_byte: 0x1a,
            block_length: BlockLength::Standard,
            checksum_mode: Checksum::Standard,
            errors: 0,
        }
    }

    /// Starts the XMODEM transmission.
    ///
    /// `dev` should be the serial communication channel (e.g. the serial device).
    /// `stream` should be the message to send (e.g. a file).
    ///
    /// # Timeouts
    /// This method has no way of setting the timeout of `dev`, so it's up to the caller
    /// to set the timeout of the device before calling this method. Timeouts on receiving
    /// bytes will be counted against `max_errors`, but timeouts on transmitting bytes
    /// will be considered a fatal error.
    pub fn send<D: Read + Write, R: Read>(&mut self, dev: &mut D, stream: &mut R) -> Result<()> {
        self.errors = 0;

        debug!("Starting XMODEM transfer");
        try!(self.start_send(dev));
        debug!("First byte received. Sending stream.");
        try!(self.send_stream(dev, stream));
        debug!("Sending EOT");
        try!(self.finish_send(dev));

        Ok(())
    }

    /// Receive an XMODEM transmission.
    ///
    /// `dev` should be the serial communication channel (e.g. the serial device).
    /// The received data will be written to `outstream`.
    /// `checksum` indicates which checksum mode should be used; Checksum::Standard is
    /// a reasonable default.
    ///
    /// # Timeouts
    /// This method has no way of setting the timeout of `dev`, so it's up to the caller
    /// to set the timeout of the device before calling this method. Timeouts on receiving
    /// bytes will be counted against `max_errors`, but timeouts on transmitting bytes
    /// will be considered a fatal error.
    pub fn recv<D: Read + Write, W: Write>(&mut self,
                                           dev: &mut D,
                                           outstream: &mut W,
                                           checksum : Checksum) -> Result<()> {
        self.errors = 0;
        self.checksum_mode = checksum;
        debug!("Starting XMODEM receive");
        try!(dev.write(&[match self.checksum_mode {
            Checksum::Standard => NAK,
            Checksum::CRC16 => CRC }]));
        debug!("NCG sent. Receiving stream.");
        let mut packet_num : u8 = 1;
        loop {
            match try!(get_byte_timeout(dev)) {
                Some(SOH) => { // Handle next packet
                    let pnum = try!(get_byte(dev)); // specified packet number
                    let pnum_1c = try!(get_byte(dev)); // same, 1's complemented
                    // We'll respond with cancel later if the packet number is wrong
                    let cancel_packet = packet_num != pnum ||  (255-pnum) != pnum_1c;
                    let mut data : [u8; 128] = [0; 128];
                    try!(dev.read_exact(&mut data));
                    let success = match self.checksum_mode {
                        Checksum::Standard => {
                            let recv_checksum = try!(get_byte(dev));
                            calc_checksum(&data) == recv_checksum },
                        Checksum::CRC16 => {
                            let recv_checksum =
                                ( (try!(get_byte(dev)) as u16) << 8) +
                                try!(get_byte(dev)) as u16;
                            calc_crc(&data) == recv_checksum },
                    };

                    if cancel_packet {
                        try!(dev.write(&[CAN]));
                            try!(dev.write(&[CAN]));
                        return Err(Error::Canceled)
                    }
                    if success {
                        packet_num = packet_num.wrapping_add(1);
                        try!(dev.write(&[ACK]));
                        try!(outstream.write_all(&data));
                    } else {
                        try!(dev.write(&[NAK]));
                        self.errors += 1;
                    }
                },
                Some(EOT) => { // End of file
                    try!(dev.write(&[ACK]));
                    break
                },
                Some(_) => {
                    warn!("Unrecognized symbol!");
                },
                None => warn!("Timeout!"),
            }
        }
        Ok(())
    }

    fn start_send<D: Read + Write>(&mut self, dev: &mut D) -> Result<()> {
        let mut cancels = 0u32;
        loop {
            match try!(get_byte_timeout(dev)) {
                Some(c) => {
                    match c {
                        NAK => {
                            debug!("Standard checksum requested");
                            self.checksum_mode = Checksum::Standard;
                            return Ok(());
                        }
                        CRC => {
                            debug!("16-bit CRC requested");
                            self.checksum_mode = Checksum::CRC16;
                            return Ok(());
                        }
                        CAN => {
                            warn!("Cancel (CAN) byte received");
                            cancels += 1;
                        },
                        c => warn!("Unknown byte received at start of XMODEM transfer: {}", c),
                    }
                },
                None => warn!("Timed out waiting for start of XMODEM transfer."),
            }

            self.errors += 1;

            if cancels >= 2 {
                error!("Transmission canceled: received two cancel (CAN) bytes \
                        at start of XMODEM transfer");
                return Err(Error::Canceled);
            }

            if self.errors >= self.max_errors {
                error!("Exhausted max retries ({}) at start of XMODEM transfer.", self.max_errors);
                if let Err(err) = dev.write_all(&[CAN]) {
                    warn!("Error sending CAN byte: {}", err);
                }
                return Err(Error::ExhaustedRetries);
            }
        }
    }

    fn send_stream<D: Read + Write, R: Read>(&mut self, dev: &mut D, stream: &mut R) -> Result<()> {
        let mut block_num = 0u32;
        loop {
            let mut buff = vec![self.pad_byte; self.block_length as usize + 3];
            let n = try!(stream.read(&mut buff[3..]));
            if n == 0 {
                debug!("Reached EOF");
                return Ok(());
            }

            block_num += 1;
            buff[0] = match self.block_length {
                BlockLength::Standard => SOH,
                BlockLength::OneK => STX,
            };
            buff[1] = (block_num & 0xFF) as u8;
            buff[2] = 0xFF - buff[1];

            match self.checksum_mode {
                Checksum::Standard => {
                    let checksum = calc_checksum(&buff[3..]);
                    buff.push(checksum);
                },
                Checksum::CRC16 => {
                    let crc = calc_crc(&buff[3..]);
                    buff.push(((crc >> 8) & 0xFF) as u8);
                    buff.push((crc & 0xFF) as u8);
                }
            }

            debug!("Sending block {}", block_num);
            try!(dev.write_all(&buff));

            match try!(get_byte_timeout(dev)) {
                Some(c) => {
                    if c == ACK {
                        debug!("Received ACK for block {}", block_num);
                        continue
                    } else {
                        warn!("Expected ACK, got {}", c);
                    }
                    // TODO handle CAN bytes
                },
                None => warn!("Timeout waiting for ACK for block {}", block_num),
            }

            self.errors += 1;

            if self.errors >= self.max_errors {
                error!("Exhausted max retries ({}) while sending block {} in XMODEM transfer",
                       self.max_errors, block_num);
                return Err(Error::ExhaustedRetries);
            }
        }
    }

    fn finish_send<D: Read + Write>(&mut self, dev: &mut D) -> Result<()> {
        loop {
            try!(dev.write_all(&[EOT]));

            match try!(get_byte_timeout(dev)) {
                Some(c) => {
                    if c == ACK {
                        info!("XMODEM transmission successful");
                        return Ok(());
                    } else {
                        warn!("Expected ACK, got {}", c);
                    }
                },
                None => warn!("Timeout waiting for ACK for EOT"),
            }

            self.errors += 1;

            if self.errors >= self.max_errors {
                error!("Exhausted max retries ({}) while waiting for ACK for EOT", self.max_errors);
                return Err(Error::ExhaustedRetries);
            }
        }
    }
}

fn calc_checksum(data: &[u8]) -> u8 {
    data.iter().fold(0, |x, &y| x.wrapping_add(y))
}

fn calc_crc(data: &[u8]) -> u16 {
    crc16::State::<crc16::XMODEM>::calculate(data)
}

fn get_byte<R: Read>(reader: &mut R) -> std::io::Result<u8> {
    let mut buff = [0];
    try!(reader.read_exact(&mut buff));
    Ok(buff[0])
}

/// Turns timeout errors into `Ok(None)`
fn get_byte_timeout<R: Read>(reader: &mut R) -> std::io::Result<Option<u8>> {
    match get_byte(reader) {
        Ok(c) => Ok(Some(c)),
        Err(err) => {
            if err.kind() == io::ErrorKind::TimedOut {
                Ok(None)
            } else {
                Err(err)
            }
        }
    }
}
<commit_msg>check for excessive errors in recv<commit_after>#[macro_use]
extern crate log;
extern crate crc16;

use std::io::{self, Read, Write};
use std::convert::From;

// TODO: Send CAN byte after too many errors
// TODO: Handle CAN bytes while sending
// TODO: Implement Error for Error

const SOH: u8 = 0x01;
const STX: u8 = 0x02;
const EOT: u8 = 0x04;
const ACK: u8 = 0x06;
const NAK: u8 = 0x15;
const CAN: u8 = 0x18;
const CRC: u8 = 0x43;

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

#[derive(Debug)]
pub enum Error {
    Io(io::Error),

    /// The number of communications errors exceeded `max_errors` in a single
    /// transmission.
    ExhaustedRetries,

    /// The transmission was canceled by the other end of the channel.
    Canceled,
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

#[derive(Copy, Clone, Debug)]
pub enum Checksum {
    Standard,
    CRC16,
}

#[derive(Copy, Clone, Debug)]
pub enum BlockLength {
    Standard = 128,
    OneK = 1024,
}

/// Configuration for the XMODEM transfer.
#[derive(Copy, Clone, Debug)]
pub struct Xmodem {
    /// The number of errors that can occur before the communication is
    /// considered a failure. Errors include unexpected bytes and timeouts waiting for bytes.
    pub max_errors: u32,

    /// The byte used to pad the last block. XMODEM can only send blocks of a certain size,
    /// so if the message is not a multiple of that size the last block needs to be padded.
    pub pad_byte: u8,

    /// The length of each block. There are only two options: 128-byte blocks (standard
    ///  XMODEM) or 1024-byte blocks (XMODEM-1k).
    pub block_length: BlockLength,

    /// The checksum mode used by XMODEM. This is determined by the receiver.
    checksum_mode: Checksum,
    errors: u32,
}

impl Xmodem {
    /// Creates the XMODEM config with default parameters.
    pub fn new() -> Self {
        Xmodem {
            max_errors: 16,
            pad_byte: 0x1a,
            block_length: BlockLength::Standard,
            checksum_mode: Checksum::Standard,
            errors: 0,
        }
    }

    /// Starts the XMODEM transmission.
    ///
    /// `dev` should be the serial communication channel (e.g. the serial device).
    /// `stream` should be the message to send (e.g. a file).
    ///
    /// # Timeouts
    /// This method has no way of setting the timeout of `dev`, so it's up to the caller
    /// to set the timeout of the device before calling this method. Timeouts on receiving
    /// bytes will be counted against `max_errors`, but timeouts on transmitting bytes
    /// will be considered a fatal error.
    pub fn send<D: Read + Write, R: Read>(&mut self, dev: &mut D, stream: &mut R) -> Result<()> {
        self.errors = 0;

        debug!("Starting XMODEM transfer");
        try!(self.start_send(dev));
        debug!("First byte received. Sending stream.");
        try!(self.send_stream(dev, stream));
        debug!("Sending EOT");
        try!(self.finish_send(dev));

        Ok(())
    }

    /// Receive an XMODEM transmission.
    ///
    /// `dev` should be the serial communication channel (e.g. the serial device).
    /// The received data will be written to `outstream`.
    /// `checksum` indicates which checksum mode should be used; Checksum::Standard is
    /// a reasonable default.
    ///
    /// # Timeouts
    /// This method has no way of setting the timeout of `dev`, so it's up to the caller
    /// to set the timeout of the device before calling this method. Timeouts on receiving
    /// bytes will be counted against `max_errors`, but timeouts on transmitting bytes
    /// will be considered a fatal error.
    pub fn recv<D: Read + Write, W: Write>(&mut self,
                                           dev: &mut D,
                                           outstream: &mut W,
                                           checksum : Checksum) -> Result<()> {
        self.errors = 0;
        self.checksum_mode = checksum;
        debug!("Starting XMODEM receive");
        try!(dev.write(&[match self.checksum_mode {
            Checksum::Standard => NAK,
            Checksum::CRC16 => CRC }]));
        debug!("NCG sent. Receiving stream.");
        let mut packet_num : u8 = 1;
        loop {
            match try!(get_byte_timeout(dev)) {
                Some(SOH) => { // Handle next packet
                    let pnum = try!(get_byte(dev)); // specified packet number
                    let pnum_1c = try!(get_byte(dev)); // same, 1's complemented
                    // We'll respond with cancel later if the packet number is wrong
                    let cancel_packet = packet_num != pnum ||  (255-pnum) != pnum_1c;
                    let mut data : [u8; 128] = [0; 128];
                    try!(dev.read_exact(&mut data));
                    let success = match self.checksum_mode {
                        Checksum::Standard => {
                            let recv_checksum = try!(get_byte(dev));
                            calc_checksum(&data) == recv_checksum },
                        Checksum::CRC16 => {
                            let recv_checksum =
                                ( (try!(get_byte(dev)) as u16) << 8) +
                                try!(get_byte(dev)) as u16;
                            calc_crc(&data) == recv_checksum },
                    };

                    if cancel_packet {
                        try!(dev.write(&[CAN]));
                            try!(dev.write(&[CAN]));
                        return Err(Error::Canceled)
                    }
                    if success {
                        packet_num = packet_num.wrapping_add(1);
                        try!(dev.write(&[ACK]));
                        try!(outstream.write_all(&data));
                    } else {
                        try!(dev.write(&[NAK]));
                        self.errors += 1;
                    }
                },
                Some(EOT) => { // End of file
                    try!(dev.write(&[ACK]));
                    break
                },
                Some(_) => {
                    warn!("Unrecognized symbol!");
                },
                None => { self.errors += 1; warn!("Timeout!") },
            }
            if self.errors >= self.max_errors {
                error!("Exhausted max retries ({}) while waiting for ACK for EOT", self.max_errors);
                return Err(Error::ExhaustedRetries);
            }
        }
        Ok(())
    }

    fn start_send<D: Read + Write>(&mut self, dev: &mut D) -> Result<()> {
        let mut cancels = 0u32;
        loop {
            match try!(get_byte_timeout(dev)) {
                Some(c) => {
                    match c {
                        NAK => {
                            debug!("Standard checksum requested");
                            self.checksum_mode = Checksum::Standard;
                            return Ok(());
                        }
                        CRC => {
                            debug!("16-bit CRC requested");
                            self.checksum_mode = Checksum::CRC16;
                            return Ok(());
                        }
                        CAN => {
                            warn!("Cancel (CAN) byte received");
                            cancels += 1;
                        },
                        c => warn!("Unknown byte received at start of XMODEM transfer: {}", c),
                    }
                },
                None => warn!("Timed out waiting for start of XMODEM transfer."),
            }

            self.errors += 1;

            if cancels >= 2 {
                error!("Transmission canceled: received two cancel (CAN) bytes \
                        at start of XMODEM transfer");
                return Err(Error::Canceled);
            }

            if self.errors >= self.max_errors {
                error!("Exhausted max retries ({}) at start of XMODEM transfer.", self.max_errors);
                if let Err(err) = dev.write_all(&[CAN]) {
                    warn!("Error sending CAN byte: {}", err);
                }
                return Err(Error::ExhaustedRetries);
            }
        }
    }

    fn send_stream<D: Read + Write, R: Read>(&mut self, dev: &mut D, stream: &mut R) -> Result<()> {
        let mut block_num = 0u32;
        loop {
            let mut buff = vec![self.pad_byte; self.block_length as usize + 3];
            let n = try!(stream.read(&mut buff[3..]));
            if n == 0 {
                debug!("Reached EOF");
                return Ok(());
            }

            block_num += 1;
            buff[0] = match self.block_length {
                BlockLength::Standard => SOH,
                BlockLength::OneK => STX,
            };
            buff[1] = (block_num & 0xFF) as u8;
            buff[2] = 0xFF - buff[1];

            match self.checksum_mode {
                Checksum::Standard => {
                    let checksum = calc_checksum(&buff[3..]);
                    buff.push(checksum);
                },
                Checksum::CRC16 => {
                    let crc = calc_crc(&buff[3..]);
                    buff.push(((crc >> 8) & 0xFF) as u8);
                    buff.push((crc & 0xFF) as u8);
                }
            }

            debug!("Sending block {}", block_num);
            try!(dev.write_all(&buff));

            match try!(get_byte_timeout(dev)) {
                Some(c) => {
                    if c == ACK {
                        debug!("Received ACK for block {}", block_num);
                        continue
                    } else {
                        warn!("Expected ACK, got {}", c);
                    }
                    // TODO handle CAN bytes
                },
                None => warn!("Timeout waiting for ACK for block {}", block_num),
            }

            self.errors += 1;

            if self.errors >= self.max_errors {
                error!("Exhausted max retries ({}) while sending block {} in XMODEM transfer",
                       self.max_errors, block_num);
                return Err(Error::ExhaustedRetries);
            }
        }
    }

    fn finish_send<D: Read + Write>(&mut self, dev: &mut D) -> Result<()> {
        loop {
            try!(dev.write_all(&[EOT]));

            match try!(get_byte_timeout(dev)) {
                Some(c) => {
                    if c == ACK {
                        info!("XMODEM transmission successful");
                        return Ok(());
                    } else {
                        warn!("Expected ACK, got {}", c);
                    }
                },
                None => warn!("Timeout waiting for ACK for EOT"),
            }

            self.errors += 1;

            if self.errors >= self.max_errors {
                error!("Exhausted max retries ({}) while waiting for ACK for EOT", self.max_errors);
                return Err(Error::ExhaustedRetries);
            }
        }
    }
}

fn calc_checksum(data: &[u8]) -> u8 {
    data.iter().fold(0, |x, &y| x.wrapping_add(y))
}

fn calc_crc(data: &[u8]) -> u16 {
    crc16::State::<crc16::XMODEM>::calculate(data)
}

fn get_byte<R: Read>(reader: &mut R) -> std::io::Result<u8> {
    let mut buff = [0];
    try!(reader.read_exact(&mut buff));
    Ok(buff[0])
}

/// Turns timeout errors into `Ok(None)`
fn get_byte_timeout<R: Read>(reader: &mut R) -> std::io::Result<Option<u8>> {
    match get_byte(reader) {
        Ok(c) => Ok(Some(c)),
        Err(err) => {
            if err.kind() == io::ErrorKind::TimedOut {
                Ok(None)
            } else {
                Err(err)
            }
        }
    }
}
<|endoftext|>