<commit_before>extern crate rustc_serialize;

use std::collections::BTreeMap;

use rustc_serialize::json::{Json, ToJson};

/// An Optional value can contain Optional::Some<T> or Optional::Absent.
/// It is similar to an Option, but Optional::Absent means it will be
/// omitted when serialized.
pub enum Optional<T> {
    Value(T),
    Absent,
}

impl<T> Optional<T> {
    pub fn map_or<U, F: FnOnce(T) -> U>(self, def: U, f: F) -> U {
        match self {
            Optional::Value(v) => f(v),
            Optional::Absent => def,
        }
    }
}

pub struct Location {
    pub address: Optional<String>,
    pub lat: f64,
    pub lon: f64,
}

pub struct Spacefed {
    pub spacenet: bool,
    pub spacesaml: bool,
    pub spacephone: bool,
}

pub struct Icon {
    pub open: String,
    pub close: String,
}

pub struct State {
    pub open: Option<bool>,
    pub lastchange: Optional<u64>,
    pub trigger_person: Optional<String>,
    pub message: Optional<String>,
    pub icon: Optional<Icon>,
}

pub struct Event {
    pub name: String,
    pub _type: String,
    pub timestamp: u64,
    pub extra: Optional<String>,
}

pub struct Contact {
    pub irc: Optional<String>,
    pub twitter: Optional<String>,
    pub foursquare: Optional<String>,
    pub email: Optional<String>,
}

pub struct Feed {
    pub _type: Optional<String>,
    pub url: String,
}

pub struct Feeds {
    pub blog: Optional<Feed>,
    pub wiki: Optional<Feed>,
    pub calendar: Optional<Feed>,
    pub flickr: Optional<Feed>,
}

pub struct Sensors {
    pub people_now_present: Optional<Vec<PeopleNowPresentSensor>>,
    pub temperature: Optional<Vec<TemperatureSensor>>,
}

pub struct PeopleNowPresentSensor {
    pub value: u32,
    pub location: Optional<String>,
    pub name: Optional<String>,
    pub names: Optional<Vec<String>>,
    pub description: Optional<String>,
}

pub struct TemperatureSensor {
    pub value: f32,
    pub unit: String,
    pub location: String,
    pub name: Optional<String>,
    pub description: Optional<String>,
}

pub struct Cache {
    pub schedule: String,
}

pub struct RadioShow {
    pub name: String,
    pub url: String,
    pub _type: String,
    pub start: String,
    pub end: String,
}

pub struct Status {

    // Hackerspace properties
    pub api: String,
    pub space: String,
    pub logo: String,
    pub url: String,
    pub location: Location,
    pub contact: Contact,

    // Hackerspace features / projects
    pub spacefed: Optional<Spacefed>,
    pub projects: Optional<Vec<String>>,
    pub cam: Optional<Vec<String>>,
    pub feeds: Optional<Feeds>,
    pub events: Optional<Vec<Event>>,
    pub radio_show: Optional<Vec<RadioShow>>,

    // SpaceAPI internal usage
    pub cache: Optional<Cache>,
    pub issue_report_channels: Vec<String>,

    // Mutable data
    pub state: State,
    pub sensors: Optional<Sensors>,

}

impl Status {

    /// Create a new Status object with only the minimum amount of fields
    pub fn new(space: String, logo: String, url: String, location: Location, contact: Contact, issue_report_channels: Vec<String>) -> Status {
        Status {
            api: "0.13".to_string(),
            space: space,
            logo: logo,
            url: url,
            location: location,
            contact: contact,

            spacefed: Optional::Absent,
            projects: Optional::Absent,
            cam: Optional::Absent,
            feeds: Optional::Absent,
            events: Optional::Absent,
            radio_show: Optional::Absent,

            cache: Optional::Absent,
            issue_report_channels: issue_report_channels,

            state: State {
                open: None,
                lastchange: Optional::Absent,
                trigger_person: Optional::Absent,
                message: Optional::Absent,
                icon: Optional::Absent,
            },
            sensors: Optional::Absent,
        }
    }

}

impl ToJson for Status {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();

        d.insert("api".to_string(), self.api.to_json());
        d.insert("space".to_string(), self.space.to_json());
        d.insert("logo".to_string(), self.logo.to_json());
        d.insert("url".to_string(), self.url.to_json());
        d.insert("location".to_string(), self.location.to_json());
        d.insert("contact".to_string(), self.contact.to_json());

        match self.spacefed {
            Optional::Value(ref spacefed) => { d.insert("spacefed".to_string(), spacefed.to_json()); },
            Optional::Absent => (),
        };
        match self.projects {
            Optional::Value(ref projects) => { d.insert("projects".to_string(), projects.to_json()); },
            Optional::Absent => (),
        };
        match self.cam {
            Optional::Value(ref cam) => { d.insert("cam".to_string(), cam.to_json()); },
            Optional::Absent => (),
        };
        match self.feeds {
            Optional::Value(ref feeds) => { d.insert("feeds".to_string(), feeds.to_json()); },
            Optional::Absent => (),
        };
        match self.events {
            Optional::Value(ref events) => { d.insert("events".to_string(), events.to_json()); },
            Optional::Absent => (),
        };
        match self.radio_show {
            Optional::Value(ref radio_show) => { d.insert("radio_show".to_string(), radio_show.to_json()); },
            Optional::Absent => (),
        };

        match self.cache {
            Optional::Value(ref cache) => { d.insert("cache".to_string(), cache.to_json()); },
            Optional::Absent => (),
        };
        d.insert("issue_report_channels".to_string(), self.issue_report_channels.to_json());

        d.insert("state".to_string(), self.state.to_json());
        match self.sensors {
            Optional::Value(ref sensors) => { d.insert("sensors".to_string(), sensors.to_json()); },
            Optional::Absent => (),
        };

        Json::Object(d)
    }
}

impl ToJson for Location {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("lat".to_string(), self.lat.to_json());
        d.insert("lon".to_string(), self.lon.to_json());
        match self.address {
            Optional::Value(ref address) => {
                d.insert("address".to_string(), address.to_json());
            },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Spacefed {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("spacenet".to_string(), self.spacenet.to_json());
        d.insert("spacesaml".to_string(), self.spacesaml.to_json());
        d.insert("spacephone".to_string(), self.spacephone.to_json());
        Json::Object(d)
    }
}

impl ToJson for Icon {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("open".to_string(), self.open.to_json());
        d.insert("close".to_string(), self.close.to_json());
        Json::Object(d)
    }
}

impl ToJson for State {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("open".to_string(), self.open.to_json());
        match self.lastchange {
            Optional::Value(ref lastchange) => { d.insert("lastchange".to_string(), lastchange.to_json()); },
            Optional::Absent => (),
        };
        match self.trigger_person {
            Optional::Value(ref trigger_person) => { d.insert("trigger_person".to_string(), trigger_person.to_json()); },
            Optional::Absent => (),
        };
        match self.message {
            Optional::Value(ref message) => { d.insert("message".to_string(), message.to_json()); },
            Optional::Absent => (),
        };
        match self.icon {
            Optional::Value(ref icon) => { d.insert("icon".to_string(), icon.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Event {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("name".to_string(), self.name.to_json());
        d.insert("type".to_string(), self._type.to_json());
        d.insert("timestamp".to_string(), self.timestamp.to_json());
        match self.extra {
            Optional::Value(ref extra) => { d.insert("extra".to_string(), extra.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Contact {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self.irc {
            Optional::Value(ref irc) => { d.insert("irc".to_string(), irc.to_json()); },
            Optional::Absent => (),
        };
        match self.twitter {
            Optional::Value(ref twitter) => { d.insert("twitter".to_string(), twitter.to_json()); },
            Optional::Absent => (),
        };
        match self.foursquare {
            Optional::Value(ref foursquare) => { d.insert("foursquare".to_string(), foursquare.to_json()); },
            Optional::Absent => (),
        };
        match self.email {
            Optional::Value(ref email) => { d.insert("email".to_string(), email.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Feed {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("url".to_string(), self.url.to_json());
        match self._type {
            Optional::Value(ref _type) => { d.insert("type".to_string(), _type.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Feeds {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self.blog {
            Optional::Value(ref blog) => { d.insert("blog".to_string(), blog.to_json()); },
            Optional::Absent => (),
        };
        match self.wiki {
            Optional::Value(ref wiki) => { d.insert("wiki".to_string(), wiki.to_json()); },
            Optional::Absent => (),
        };
        match self.calendar {
            Optional::Value(ref calendar) => { d.insert("calendar".to_string(), calendar.to_json()); },
            Optional::Absent => (),
        };
        match self.flickr {
            Optional::Value(ref flickr) => { d.insert("flickr".to_string(), flickr.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Sensors {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self.people_now_present {
            Optional::Value(ref people_now_present) => { d.insert("people_now_present".to_string(), people_now_present.to_json()); },
            Optional::Absent => (),
        };
        match self.temperature {
            Optional::Value(ref temperature) => { d.insert("temperature".to_string(), temperature.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for PeopleNowPresentSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        match self.location {
            Optional::Value(ref location) => { d.insert("location".to_string(), location.to_json()); },
            Optional::Absent => (),
        };
        match self.name {
            Optional::Value(ref name) => { d.insert("name".to_string(), name.to_json()); },
            Optional::Absent => (),
        };
        match self.names {
            Optional::Value(ref names) => { d.insert("names".to_string(), names.to_json()); },
            Optional::Absent => (),
        };
        match self.description {
            Optional::Value(ref description) => { d.insert("description".to_string(), description.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for TemperatureSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        d.insert("unit".to_string(), self.unit.to_json());
        d.insert("location".to_string(), self.location.to_json());
        match self.name {
            Optional::Value(ref name) => { d.insert("name".to_string(), name.to_json()); },
            Optional::Absent => (),
        };
        match self.description {
            Optional::Value(ref description) => { d.insert("description".to_string(), description.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Cache {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("schedule".to_string(), self.schedule.to_json());
        Json::Object(d)
    }
}

impl ToJson for RadioShow {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("name".to_string(), self.name.to_json());
        d.insert("url".to_string(), self.url.to_json());
        d.insert("type".to_string(), self._type.to_json());
        d.insert("start".to_string(), self.start.to_json());
        d.insert("end".to_string(), self.end.to_json());
        Json::Object(d)
    }
}
<commit_msg>spaceapi: derive Debug, Copy and Clone for Optional<commit_after>extern crate rustc_serialize;

use std::collections::BTreeMap;

use rustc_serialize::json::{Json, ToJson};

/// An Optional value can contain Optional::Some<T> or Optional::Absent.
/// It is similar to an Option, but Optional::Absent means it will be
/// omitted when serialized.
#[derive(Debug, Copy, Clone)]
pub enum Optional<T> {
    Value(T),
    Absent,
}

impl<T> Optional<T> {
    pub fn map_or<U, F: FnOnce(T) -> U>(self, def: U, f: F) -> U {
        match self {
            Optional::Value(v) => f(v),
            Optional::Absent => def,
        }
    }
}

pub struct Location {
    pub address: Optional<String>,
    pub lat: f64,
    pub lon: f64,
}

pub struct Spacefed {
    pub spacenet: bool,
    pub spacesaml: bool,
    pub spacephone: bool,
}

pub struct Icon {
    pub open: String,
    pub close: String,
}

pub struct State {
    pub open: Option<bool>,
    pub lastchange: Optional<u64>,
    pub trigger_person: Optional<String>,
    pub message: Optional<String>,
    pub icon: Optional<Icon>,
}

pub struct Event {
    pub name: String,
    pub _type: String,
    pub timestamp: u64,
    pub extra: Optional<String>,
}

pub struct Contact {
    pub irc: Optional<String>,
    pub twitter: Optional<String>,
    pub foursquare: Optional<String>,
    pub email: Optional<String>,
}

pub struct Feed {
    pub _type: Optional<String>,
    pub url: String,
}

pub struct Feeds {
    pub blog: Optional<Feed>,
    pub wiki: Optional<Feed>,
    pub calendar: Optional<Feed>,
    pub flickr: Optional<Feed>,
}

pub struct Sensors {
    pub people_now_present: Optional<Vec<PeopleNowPresentSensor>>,
    pub temperature: Optional<Vec<TemperatureSensor>>,
}

pub struct PeopleNowPresentSensor {
    pub value: u32,
    pub location: Optional<String>,
    pub name: Optional<String>,
    pub names: Optional<Vec<String>>,
    pub description: Optional<String>,
}

pub struct TemperatureSensor {
    pub value: f32,
    pub unit: String,
    pub location: String,
    pub name: Optional<String>,
    pub description: Optional<String>,
}

pub struct Cache {
    pub schedule: String,
}

pub struct RadioShow {
    pub name: String,
    pub url: String,
    pub _type: String,
    pub start: String,
    pub end: String,
}

pub struct Status {

    // Hackerspace properties
    pub api: String,
    pub space: String,
    pub logo: String,
    pub url: String,
    pub location: Location,
    pub contact: Contact,

    // Hackerspace features / projects
    pub spacefed: Optional<Spacefed>,
    pub projects: Optional<Vec<String>>,
    pub cam: Optional<Vec<String>>,
    pub feeds: Optional<Feeds>,
    pub events: Optional<Vec<Event>>,
    pub radio_show: Optional<Vec<RadioShow>>,

    // SpaceAPI internal usage
    pub cache: Optional<Cache>,
    pub issue_report_channels: Vec<String>,

    // Mutable data
    pub state: State,
    pub sensors: Optional<Sensors>,

}

impl Status {

    /// Create a new Status object with only the minimum amount of fields
    pub fn new(space: String, logo: String, url: String, location: Location, contact: Contact, issue_report_channels: Vec<String>) -> Status {
        Status {
            api: "0.13".to_string(),
            space: space,
            logo: logo,
            url: url,
            location: location,
            contact: contact,

            spacefed: Optional::Absent,
            projects: Optional::Absent,
            cam: Optional::Absent,
            feeds: Optional::Absent,
            events: Optional::Absent,
            radio_show: Optional::Absent,

            cache: Optional::Absent,
            issue_report_channels: issue_report_channels,

            state: State {
                open: None,
                lastchange: Optional::Absent,
                trigger_person: Optional::Absent,
                message: Optional::Absent,
                icon: Optional::Absent,
            },
            sensors: Optional::Absent,
        }
    }

}

impl ToJson for Status {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();

        d.insert("api".to_string(), self.api.to_json());
        d.insert("space".to_string(), self.space.to_json());
        d.insert("logo".to_string(), self.logo.to_json());
        d.insert("url".to_string(), self.url.to_json());
        d.insert("location".to_string(), self.location.to_json());
        d.insert("contact".to_string(), self.contact.to_json());

        match self.spacefed {
            Optional::Value(ref spacefed) => { d.insert("spacefed".to_string(), spacefed.to_json()); },
            Optional::Absent => (),
        };
        match self.projects {
            Optional::Value(ref projects) => { d.insert("projects".to_string(), projects.to_json()); },
            Optional::Absent => (),
        };
        match self.cam {
            Optional::Value(ref cam) => { d.insert("cam".to_string(), cam.to_json()); },
            Optional::Absent => (),
        };
        match self.feeds {
            Optional::Value(ref feeds) => { d.insert("feeds".to_string(), feeds.to_json()); },
            Optional::Absent => (),
        };
        match self.events {
            Optional::Value(ref events) => { d.insert("events".to_string(), events.to_json()); },
            Optional::Absent => (),
        };
        match self.radio_show {
            Optional::Value(ref radio_show) => { d.insert("radio_show".to_string(), radio_show.to_json()); },
            Optional::Absent => (),
        };

        match self.cache {
            Optional::Value(ref cache) => { d.insert("cache".to_string(), cache.to_json()); },
            Optional::Absent => (),
        };
        d.insert("issue_report_channels".to_string(), self.issue_report_channels.to_json());

        d.insert("state".to_string(), self.state.to_json());
        match self.sensors {
            Optional::Value(ref sensors) => { d.insert("sensors".to_string(), sensors.to_json()); },
            Optional::Absent => (),
        };

        Json::Object(d)
    }
}

impl ToJson for Location {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("lat".to_string(), self.lat.to_json());
        d.insert("lon".to_string(), self.lon.to_json());
        match self.address {
            Optional::Value(ref address) => {
                d.insert("address".to_string(), address.to_json());
            },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Spacefed {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("spacenet".to_string(), self.spacenet.to_json());
        d.insert("spacesaml".to_string(), self.spacesaml.to_json());
        d.insert("spacephone".to_string(), self.spacephone.to_json());
        Json::Object(d)
    }
}

impl ToJson for Icon {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("open".to_string(), self.open.to_json());
        d.insert("close".to_string(), self.close.to_json());
        Json::Object(d)
    }
}

impl ToJson for State {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("open".to_string(), self.open.to_json());
        match self.lastchange {
            Optional::Value(ref lastchange) => { d.insert("lastchange".to_string(), lastchange.to_json()); },
            Optional::Absent => (),
        };
        match self.trigger_person {
            Optional::Value(ref trigger_person) => { d.insert("trigger_person".to_string(), trigger_person.to_json()); },
            Optional::Absent => (),
        };
        match self.message {
            Optional::Value(ref message) => { d.insert("message".to_string(), message.to_json()); },
            Optional::Absent => (),
        };
        match self.icon {
            Optional::Value(ref icon) => { d.insert("icon".to_string(), icon.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Event {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("name".to_string(), self.name.to_json());
        d.insert("type".to_string(), self._type.to_json());
        d.insert("timestamp".to_string(), self.timestamp.to_json());
        match self.extra {
            Optional::Value(ref extra) => { d.insert("extra".to_string(), extra.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Contact {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self.irc {
            Optional::Value(ref irc) => { d.insert("irc".to_string(), irc.to_json()); },
            Optional::Absent => (),
        };
        match self.twitter {
            Optional::Value(ref twitter) => { d.insert("twitter".to_string(), twitter.to_json()); },
            Optional::Absent => (),
        };
        match self.foursquare {
            Optional::Value(ref foursquare) => { d.insert("foursquare".to_string(), foursquare.to_json()); },
            Optional::Absent => (),
        };
        match self.email {
            Optional::Value(ref email) => { d.insert("email".to_string(), email.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Feed {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("url".to_string(), self.url.to_json());
        match self._type {
            Optional::Value(ref _type) => { d.insert("type".to_string(), _type.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Feeds {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self.blog {
            Optional::Value(ref blog) => { d.insert("blog".to_string(), blog.to_json()); },
            Optional::Absent => (),
        };
        match self.wiki {
            Optional::Value(ref wiki) => { d.insert("wiki".to_string(), wiki.to_json()); },
            Optional::Absent => (),
        };
        match self.calendar {
            Optional::Value(ref calendar) => { d.insert("calendar".to_string(), calendar.to_json()); },
            Optional::Absent => (),
        };
        match self.flickr {
            Optional::Value(ref flickr) => { d.insert("flickr".to_string(), flickr.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Sensors {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        match self.people_now_present {
            Optional::Value(ref people_now_present) => { d.insert("people_now_present".to_string(), people_now_present.to_json()); },
            Optional::Absent => (),
        };
        match self.temperature {
            Optional::Value(ref temperature) => { d.insert("temperature".to_string(), temperature.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for PeopleNowPresentSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        match self.location {
            Optional::Value(ref location) => { d.insert("location".to_string(), location.to_json()); },
            Optional::Absent => (),
        };
        match self.name {
            Optional::Value(ref name) => { d.insert("name".to_string(), name.to_json()); },
            Optional::Absent => (),
        };
        match self.names {
            Optional::Value(ref names) => { d.insert("names".to_string(), names.to_json()); },
            Optional::Absent => (),
        };
        match self.description {
            Optional::Value(ref description) => { d.insert("description".to_string(), description.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for TemperatureSensor {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("value".to_string(), self.value.to_json());
        d.insert("unit".to_string(), self.unit.to_json());
        d.insert("location".to_string(), self.location.to_json());
        match self.name {
            Optional::Value(ref name) => { d.insert("name".to_string(), name.to_json()); },
            Optional::Absent => (),
        };
        match self.description {
            Optional::Value(ref description) => { d.insert("description".to_string(), description.to_json()); },
            Optional::Absent => (),
        };
        Json::Object(d)
    }
}

impl ToJson for Cache {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("schedule".to_string(), self.schedule.to_json());
        Json::Object(d)
    }
}

impl ToJson for RadioShow {
    fn to_json(&self) -> Json {
        let mut d = BTreeMap::new();
        d.insert("name".to_string(), self.name.to_json());
        d.insert("url".to_string(), self.url.to_json());
        d.insert("type".to_string(), self._type.to_json());
        d.insert("start".to_string(), self.start.to_json());
        d.insert("end".to_string(), self.end.to_json());
        Json::Object(d)
    }
}
<|endoftext|>


<commit_before>extern crate uuid;
extern crate proc_macro2;
extern crate quote;

#[cfg(feature = "native_coin_cbc")]
extern crate coin_cbc;
extern crate minilp;

pub mod util;

pub mod dsl {
    pub mod variables;
    pub use self::variables::*;
    pub mod operations;
    pub use self::operations::*;
    pub mod problem;
    pub use self::problem::*;
}

pub mod format {
   pub mod lp_format;
}

pub mod solvers;
<commit_msg>Add missing feature gate<commit_after>extern crate uuid;
extern crate proc_macro2;
extern crate quote;

#[cfg(feature = "native_coin_cbc")]
extern crate coin_cbc;
#[cfg(feature = "minilp")]
extern crate minilp;

pub mod util;

pub mod dsl {
    pub mod variables;
    pub use self::variables::*;
    pub mod operations;
    pub use self::operations::*;
    pub mod problem;
    pub use self::problem::*;
}

pub mod format {
   pub mod lp_format;
}

pub mod solvers;
<|endoftext|>


<commit_before>//! The `gettext` crate provides functionality for
//! parsing and using Gettext catalogs stored in MO files.

// https://pascalhertleif.de/artikel/good-practices-for-writing-rust-libraries/
#![deny(missing_docs, missing_debug_implementations,
        trivial_casts, trivial_numeric_casts,
        unstable_features, unused_import_braces)]

mod parser;

use std::collections::HashMap;
use std::ops::Deref;

pub use parser::Error;

/// Returns the number of the appropriate plural form
/// for the given count `n` of objects for germanic languages.
fn plural_form(n: usize) -> usize {
    if n != 1 {
        1
    } else {
        0
    }
}

/// Catalog represents a set of translation strings
/// parsed out of one MO file.
#[derive(Debug)]
pub struct Catalog {
    strings: HashMap<String, Message>,
}

impl Catalog {
    fn new() -> Self {
        Catalog { strings: HashMap::new() }
    }

    fn insert(&mut self, msg: Message) {
        let key = match msg.context {
            Some(ref ctxt) => [&**ctxt, &*msg.id].join("\x04"),
            None => msg.id.clone(),
        };
        self.strings.insert(key, msg);
    }

    /// Returns the singular translation of `msg_id` from the given catalog
    /// or `msg_id` itself if a translation does not exist.
    pub fn gettext<'a>(&'a self, msg_id: &'a str) -> &'a str {
        self.strings.get(&msg_id.to_owned()).and_then(|msg| msg.get_translated(0)).unwrap_or(msg_id)
    }

    /// Returns the plural translation of `msg_id` from the given catalog
    /// with the correct plural form for the number `n` of objects.
    /// Returns msg_id if a translation does not exist and `n == 1`,
    /// msg_id_plural otherwise.
    ///
    /// Currently, the only supported plural formula is `n != 1`.
    pub fn ngettext<'a>(&'a self, msg_id: &'a str, msg_id_plural: &'a str, n: usize) -> &'a str {
        let form_no = plural_form(n);

        match self.strings.get(&msg_id.to_owned()) {
            Some(msg) => {
                msg.get_translated(form_no).unwrap_or_else(|| [msg_id, msg_id_plural][form_no])
            }
            None if form_no == 0 => msg_id,
            None if form_no == 1 => msg_id_plural,
            _ => unreachable!(),
        }
    }

    /// Returns the singular translation of `msg_id`
    /// in the context `msg_context`
    /// or `msg_id` itself if a translation does not exist.
    // TODO: DRY gettext/pgettext
    pub fn pgettext<'a>(&'a self, msg_context: &'a str, msg_id: &'a str) -> &'a str {
        let key = [msg_context, msg_id].join("\x04");
        self.strings.get(&key).and_then(|msg| msg.get_translated(0)).unwrap_or(msg_id)
    }

    /// Returns the plural translation of `msg_id`
    /// in the context `msg_context`
    /// with the correct plural form for the number `n` of objects.
    /// Returns msg_id if a translation does not exist and `n == 1`,
    /// msg_id_plural otherwise.
    ///
    /// Currently, the only supported plural formula is `n != 1`.
    // TODO: DRY ngettext/npgettext
    pub fn npgettext<'a>(&'a self,
                         msg_context: &'a str,
                         msg_id: &'a str,
                         msg_id_plural: &'a str,
                         n: usize)
                         -> &'a str {
        let key = [msg_context, msg_id].join("\x04");
        let form_no = plural_form(n);
        match self.strings.get(&key) {
            Some(msg) => {
                msg.get_translated(form_no).unwrap_or_else(|| [msg_id, msg_id_plural][form_no])
            }
            None if form_no == 0 => msg_id,
            None if form_no == 1 => msg_id_plural,
            _ => unreachable!(),
        }
    }
}

#[derive(Debug, Eq, PartialEq)]
struct Message {
    id: String,
    context: Option<String>,
    translated: Vec<String>,
}

impl Message {
    fn new<T: Into<String>>(id: T, context: Option<T>, translated: Vec<T>) -> Self {
        Message {
            id: id.into(),
            context: context.map(Into::into),
            translated: translated.into_iter().map(Into::into).collect(),
        }
    }

    fn get_translated(&self, form_no: usize) -> Option<&str> {
        self.translated.get(form_no).map(|s| s.deref())
    }
}

#[test]
fn catalog_insert() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("thisisid", None, vec![]));
    cat.insert(Message::new("anotherid", Some("context"), vec![]));
    let mut keys = cat.strings.keys().collect::<Vec<_>>();
    keys.sort();
    assert_eq!(keys, &["context\x04anotherid", "thisisid"])
}

#[test]
fn catalog_gettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", None, vec!["Tekstas"]));
    cat.insert(Message::new("Image", Some("context"), vec!["Paveikslelis"]));
    assert_eq!(cat.gettext("Text"), "Tekstas");
    assert_eq!(cat.gettext("Image"), "Image");
}

#[test]
fn catalog_ngettext() {
    let mut cat = Catalog::new();
    {
        // n == 1, no translation
        assert_eq!(cat.ngettext("Text", "Texts", 1), "Text");
        // n != 1, no translation
        assert_eq!(cat.ngettext("Text", "Texts", 0), "Texts");
        assert_eq!(cat.ngettext("Text", "Texts", 2), "Texts");
    }
    {
        cat.insert(Message::new("Text", None, vec!["Tekstas", "Tekstai"]));
        // n == 1, translation available
        assert_eq!(cat.ngettext("Text", "Texts", 1), "Tekstas");
        // n != 1, translation available
        assert_eq!(cat.ngettext("Text", "Texts", 0), "Tekstai");
        assert_eq!(cat.ngettext("Text", "Texts", 2), "Tekstai");
    }
}

#[test]
fn catalog_pgettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", Some("unit test"), vec!["Tekstas"]));
    assert_eq!(cat.pgettext("unit test", "Text"), "Tekstas");
    assert_eq!(cat.pgettext("integration test", "Text"), "Text");
}

#[test]
fn catalog_npgettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", Some("unit test"), vec!["Tekstas", "Tekstai"]));

    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 1), "Tekstas");
    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 0), "Tekstai");
    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 2), "Tekstai");

    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 1),
               "Text");
    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 0),
               "Texts");
    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 2),
               "Texts");
}
<commit_msg>Compatibility with Rust <= 1.2<commit_after>//! The `gettext` crate provides functionality for
//! parsing and using Gettext catalogs stored in MO files.

// https://pascalhertleif.de/artikel/good-practices-for-writing-rust-libraries/
#![deny(missing_docs, missing_debug_implementations,
        trivial_casts, trivial_numeric_casts, unused_import_braces)]

mod parser;

use std::collections::HashMap;
use std::ops::Deref;

pub use parser::Error;

/// Returns the number of the appropriate plural form
/// for the given count `n` of objects for germanic languages.
fn plural_form(n: usize) -> usize {
    if n != 1 {
        1
    } else {
        0
    }
}

fn key_with_context(context: &str, key: &str) -> String {
    let mut result = context.to_owned();
    result.push('\x04');
    result.push_str(key);
    result
}

/// Catalog represents a set of translation strings
/// parsed out of one MO file.
#[derive(Debug)]
pub struct Catalog {
    strings: HashMap<String, Message>,
}

impl Catalog {
    fn new() -> Self {
        Catalog { strings: HashMap::new() }
    }

    fn insert(&mut self, msg: Message) {
        let key = match msg.context {
            Some(ref ctxt) => key_with_context(ctxt, &msg.id),
            None => msg.id.clone(),
        };
        self.strings.insert(key, msg);
    }

    /// Returns the singular translation of `msg_id` from the given catalog
    /// or `msg_id` itself if a translation does not exist.
    pub fn gettext<'a>(&'a self, msg_id: &'a str) -> &'a str {
        self.strings.get(&msg_id.to_owned()).and_then(|msg| msg.get_translated(0)).unwrap_or(msg_id)
    }

    /// Returns the plural translation of `msg_id` from the given catalog
    /// with the correct plural form for the number `n` of objects.
    /// Returns msg_id if a translation does not exist and `n == 1`,
    /// msg_id_plural otherwise.
    ///
    /// Currently, the only supported plural formula is `n != 1`.
    pub fn ngettext<'a>(&'a self, msg_id: &'a str, msg_id_plural: &'a str, n: usize) -> &'a str {
        let form_no = plural_form(n);

        match self.strings.get(&msg_id.to_owned()) {
            Some(msg) => {
                msg.get_translated(form_no).unwrap_or_else(|| [msg_id, msg_id_plural][form_no])
            }
            None if form_no == 0 => msg_id,
            None if form_no == 1 => msg_id_plural,
            _ => unreachable!(),
        }
    }

    /// Returns the singular translation of `msg_id`
    /// in the context `msg_context`
    /// or `msg_id` itself if a translation does not exist.
    // TODO: DRY gettext/pgettext
    pub fn pgettext<'a>(&'a self, msg_context: &'a str, msg_id: &'a str) -> &'a str {
        let key = key_with_context(msg_context, &msg_id);
        self.strings.get(&key).and_then(|msg| msg.get_translated(0)).unwrap_or(msg_id)
    }

    /// Returns the plural translation of `msg_id`
    /// in the context `msg_context`
    /// with the correct plural form for the number `n` of objects.
    /// Returns msg_id if a translation does not exist and `n == 1`,
    /// msg_id_plural otherwise.
    ///
    /// Currently, the only supported plural formula is `n != 1`.
    // TODO: DRY ngettext/npgettext
    pub fn npgettext<'a>(&'a self,
                         msg_context: &'a str,
                         msg_id: &'a str,
                         msg_id_plural: &'a str,
                         n: usize)
                         -> &'a str {
        let key = key_with_context(msg_context, &msg_id);
        let form_no = plural_form(n);
        match self.strings.get(&key) {
            Some(msg) => {
                msg.get_translated(form_no).unwrap_or_else(|| [msg_id, msg_id_plural][form_no])
            }
            None if form_no == 0 => msg_id,
            None if form_no == 1 => msg_id_plural,
            _ => unreachable!(),
        }
    }
}

#[derive(Debug, Eq, PartialEq)]
struct Message {
    id: String,
    context: Option<String>,
    translated: Vec<String>,
}

impl Message {
    fn new<T: Into<String>>(id: T, context: Option<T>, translated: Vec<T>) -> Self {
        Message {
            id: id.into(),
            context: context.map(Into::into),
            translated: translated.into_iter().map(Into::into).collect(),
        }
    }

    fn get_translated(&self, form_no: usize) -> Option<&str> {
        self.translated.get(form_no).map(|s| s.deref())
    }
}

#[test]
fn catalog_insert() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("thisisid", None, vec![]));
    cat.insert(Message::new("anotherid", Some("context"), vec![]));
    let mut keys = cat.strings.keys().collect::<Vec<_>>();
    keys.sort();
    assert_eq!(keys, &["context\x04anotherid", "thisisid"])
}

#[test]
fn catalog_gettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", None, vec!["Tekstas"]));
    cat.insert(Message::new("Image", Some("context"), vec!["Paveikslelis"]));
    assert_eq!(cat.gettext("Text"), "Tekstas");
    assert_eq!(cat.gettext("Image"), "Image");
}

#[test]
fn catalog_ngettext() {
    let mut cat = Catalog::new();
    {
        // n == 1, no translation
        assert_eq!(cat.ngettext("Text", "Texts", 1), "Text");
        // n != 1, no translation
        assert_eq!(cat.ngettext("Text", "Texts", 0), "Texts");
        assert_eq!(cat.ngettext("Text", "Texts", 2), "Texts");
    }
    {
        cat.insert(Message::new("Text", None, vec!["Tekstas", "Tekstai"]));
        // n == 1, translation available
        assert_eq!(cat.ngettext("Text", "Texts", 1), "Tekstas");
        // n != 1, translation available
        assert_eq!(cat.ngettext("Text", "Texts", 0), "Tekstai");
        assert_eq!(cat.ngettext("Text", "Texts", 2), "Tekstai");
    }
}

#[test]
fn catalog_pgettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", Some("unit test"), vec!["Tekstas"]));
    assert_eq!(cat.pgettext("unit test", "Text"), "Tekstas");
    assert_eq!(cat.pgettext("integration test", "Text"), "Text");
}

#[test]
fn catalog_npgettext() {
    let mut cat = Catalog::new();
    cat.insert(Message::new("Text", Some("unit test"), vec!["Tekstas", "Tekstai"]));

    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 1), "Tekstas");
    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 0), "Tekstai");
    assert_eq!(cat.npgettext("unit test", "Text", "Texts", 2), "Tekstai");

    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 1),
               "Text");
    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 0),
               "Texts");
    assert_eq!(cat.npgettext("integration test", "Text", "Texts", 2),
               "Texts");
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

#![crate_type = "lib"]
#![allow(unused_features)]
#![feature(test)]
#![feature(optin_builtin_traits)]
#![feature(btree_range, collections_bound)]
#![feature(recover)]
#![feature(fnbox)]
#![feature(plugin)]
#![feature(box_syntax)]
#![feature(const_fn)]
#![feature(panic_handler)]
#![feature(static_rwlock)]
#![feature(iter_arith)]
#![cfg_attr(feature = "dev", plugin(clippy))]
#![cfg_attr(not(feature = "dev"), allow(unknown_lints))]
#![recursion_limit="100"]

#[macro_use]
extern crate log;
#[macro_use]
extern crate quick_error;
extern crate test;
extern crate protobuf;
extern crate bytes;
extern crate byteorder;
extern crate rand;
extern crate mio;
extern crate tempdir;
extern crate rocksdb;
extern crate uuid;
extern crate kvproto;
extern crate time;
extern crate tipb;
extern crate threadpool;
extern crate num;
extern crate cadence;
extern crate libc;
extern crate crc;
extern crate rustc_serialize;
extern crate hyper;

#[macro_use]
pub mod util;
pub mod raft;
#[allow(clippy)]
pub mod storage;

pub use storage::{Storage, Dsn};
pub mod raftstore;
pub mod pd;
pub mod server;
<commit_msg>*: remove stale features (#766)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

#![crate_type = "lib"]
#![feature(test)]
#![feature(optin_builtin_traits)]
#![feature(btree_range, collections_bound)]
#![feature(fnbox)]
#![feature(plugin)]
#![feature(box_syntax)]
#![feature(const_fn)]
#![feature(iter_arith)]
#![cfg_attr(feature = "dev", plugin(clippy))]
#![cfg_attr(not(feature = "dev"), allow(unknown_lints))]
#![recursion_limit="100"]

#[macro_use]
extern crate log;
#[macro_use]
extern crate quick_error;
extern crate test;
extern crate protobuf;
extern crate bytes;
extern crate byteorder;
extern crate rand;
extern crate mio;
extern crate tempdir;
extern crate rocksdb;
extern crate uuid;
extern crate kvproto;
extern crate time;
extern crate tipb;
extern crate threadpool;
extern crate num;
extern crate cadence;
extern crate libc;
extern crate crc;
extern crate rustc_serialize;
extern crate hyper;

#[macro_use]
pub mod util;
pub mod raft;
#[allow(clippy)]
pub mod storage;

pub use storage::{Storage, Dsn};
pub mod raftstore;
pub mod pd;
pub mod server;
<|endoftext|>


<commit_before>#![feature(zero_one,box_syntax)]

extern crate asexp;
extern crate expression;

use expression::{ExpressionError, Expression};
use std::ops::{Add, Sub, Mul, Div};
use std::num::{Zero, One};
use std::fmt::Debug;
use asexp::Sexp;

pub trait NumType: Debug + Copy + Clone + PartialEq + PartialOrd + Default + Zero + One +
Add<Output=Self> + Sub<Output=Self> + Mul<Output=Self> + Div<Output=Self> {}

impl NumType for f32 {}
impl NumType for f64 {}
impl NumType for u32 {}
impl NumType for u64 {}

/// An expression evaluates to a numeric value of type `NumType`.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum NumExpr<T: NumType> {
    /// A constant value.
    Const(T),

    /// References a variable by position
    Var(usize),

    Add(Box<NumExpr<T>>, Box<NumExpr<T>>),
    Sub(Box<NumExpr<T>>, Box<NumExpr<T>>),
    Mul(Box<NumExpr<T>>, Box<NumExpr<T>>),
    Div(Box<NumExpr<T>>, Box<NumExpr<T>>),

    /// Safe division with x/0 = 0.0
    Divz(Box<NumExpr<T>>, Box<NumExpr<T>>),

    /// Reciprocal (1 / x).
    Recip(Box<NumExpr<T>>),

    /// Reciprocal using safe division
    Recipz(Box<NumExpr<T>>),
}

impl<T: NumType> Expression for NumExpr<T> {
    type Element = T;

    fn evaluate(&self, variables: &[Self::Element]) -> Result<Self::Element, ExpressionError> {
        Ok(match self {
            &NumExpr::Var(n) => {
                try!(variables.get(n).ok_or(ExpressionError::InvalidVariable)).clone()
            }
            &NumExpr::Const(val) => val,
            &NumExpr::Add(ref e1, ref e2) => {
                try!(e1.evaluate(variables)) + try!(e2.evaluate(variables))
            }
            &NumExpr::Sub(ref e1, ref e2) => {
                try!(e1.evaluate(variables)) - try!(e2.evaluate(variables))
            }
            &NumExpr::Mul(ref e1, ref e2) => {
                try!(e1.evaluate(variables)) * try!(e2.evaluate(variables))
            }
            &NumExpr::Div(ref e1, ref e2) => {
                let a = try!(e1.evaluate(variables));
                let div = try!(e2.evaluate(variables));
                if div == T::zero() {
                    return Err(ExpressionError::DivByZero);
                }
                a / div
            }
            &NumExpr::Divz(ref e1, ref e2) => {
                let a = try!(e1.evaluate(variables));
                let div = try!(e2.evaluate(variables));
                if div == T::zero() {
                    div
                } else {
                    a / div
                }
            }
            &NumExpr::Recip(ref e1) => {
                let div = try!(e1.evaluate(variables));
                if div == T::zero() {
                    return Err(ExpressionError::DivByZero);
                } else {
                    T::one() / div
                }
            }
            &NumExpr::Recipz(ref e1) => {
                let div = try!(e1.evaluate(variables));
                if div == T::zero() {
                    div
                } else {
                    T::one() / div
                }
            }
        })
    }
}

impl<'a, T: NumType + Into<Sexp>> Into<Sexp> for &'a NumExpr<T> {
    fn into(self) -> Sexp {
        match self {
            &NumExpr::Const(n) => n.into(),
            &NumExpr::Var(n) => Sexp::from(format!("${}", n)),
            &NumExpr::Add(ref a, ref b) => {
                Sexp::from(("+",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Sub(ref a, ref b) => {
                Sexp::from(("-",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Mul(ref a, ref b) => {
                Sexp::from(("*",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Div(ref a, ref b) => {
                Sexp::from(("/",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Divz(ref a, ref b) => {
                Sexp::from(("divz",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Recip(ref a) => Sexp::from(("recip", Into::<Sexp>::into(a.as_ref()))),
            &NumExpr::Recipz(ref a) => Sexp::from(("recipz", Into::<Sexp>::into(a.as_ref()))),
        }
    }
}

#[cfg(test)]
const NO_VARS: [f32; 0] = [];

#[test]
fn test_expr_divz() {
    let expr = NumExpr::Divz(box NumExpr::Const(1.0), box NumExpr::Const(0.0));
    assert_eq!(Ok(0.0), expr.evaluate(&NO_VARS));
}

#[test]
fn test_expr_recipz() {
    let expr = NumExpr::Recipz(box NumExpr::Const(0.0));
    assert_eq!(Ok(0.0), expr.evaluate(&NO_VARS));

    let expr = NumExpr::Recipz(box NumExpr::Const(1.0));
    assert_eq!(Ok(1.0), expr.evaluate(&NO_VARS));

    let expr = NumExpr::Recipz(box NumExpr::Const(0.5));
    assert_eq!(Ok(2.0), expr.evaluate(&NO_VARS));
}

#[test]
fn test_expr() {
    let expr =
        NumExpr::Sub(box NumExpr::Const(0.0),
                     box NumExpr::Div(box NumExpr::Mul(box NumExpr::Add(box NumExpr::Const(1.0),
                                                                        box NumExpr::Var(0)),
                                                       box NumExpr::Var(1)),
                                      box NumExpr::Const(2.0)));

    fn fun(a: f32, b: f32) -> f32 {
        0.0 - ((1.0 + a) * b) / 2.0
    }

    fn check(expr: &NumExpr<f32>, a: f32, b: f32) {
        assert_eq!(Ok(fun(a, b)), expr.evaluate(&[a, b]))
    }

    check(&expr, 123.0, 4444.0);
    check(&expr, 0.0, -12.0);
}
<commit_msg>Add op_* functions that perform constant folding if possible.<commit_after>#![feature(zero_one,box_syntax)]

extern crate asexp;
extern crate expression;

use expression::{ExpressionError, Expression};
use std::ops::{Add, Sub, Mul, Div};
use std::num::{Zero, One};
use std::fmt::Debug;
use asexp::Sexp;

pub trait NumType: Debug + Copy + Clone + PartialEq + PartialOrd + Default + Zero + One +
Add<Output=Self> + Sub<Output=Self> + Mul<Output=Self> + Div<Output=Self> {}

impl NumType for f32 {}
impl NumType for f64 {}
impl NumType for u32 {}
impl NumType for u64 {}

/// An expression evaluates to a numeric value of type `NumType`.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum NumExpr<T: NumType> {
    /// A constant value.
    Const(T),

    /// References a variable by position
    Var(usize),

    Add(Box<NumExpr<T>>, Box<NumExpr<T>>),
    Sub(Box<NumExpr<T>>, Box<NumExpr<T>>),
    Mul(Box<NumExpr<T>>, Box<NumExpr<T>>),
    Div(Box<NumExpr<T>>, Box<NumExpr<T>>),

    /// Safe division with x/0 = 0.0
    Divz(Box<NumExpr<T>>, Box<NumExpr<T>>),

    /// Reciprocal (1 / x).
    Recip(Box<NumExpr<T>>),

    /// Reciprocal using safe division
    Recipz(Box<NumExpr<T>>),
}

impl<T: NumType> NumExpr<T> {
    pub fn op_add(self, other: NumExpr<T>) -> NumExpr<T> {
        match (self, other) {
            (NumExpr::Const(a), NumExpr::Const(b)) => NumExpr::Const(a + b),
            (a, b) => NumExpr::Add(Box::new(a), Box::new(b)),
        }
    }

    pub fn op_sub(self, other: NumExpr<T>) -> NumExpr<T> {
        match (self, other) {
            (NumExpr::Const(a), NumExpr::Const(b)) => NumExpr::Const(a - b),
            (a, b) => NumExpr::Sub(Box::new(a), Box::new(b)),
        }
    }

    pub fn op_mul(self, other: NumExpr<T>) -> NumExpr<T> {
        match (self, other) {
            (NumExpr::Const(a), NumExpr::Const(b)) => NumExpr::Const(a * b),
            (a, b) => NumExpr::Mul(Box::new(a), Box::new(b)),
        }
    }

    pub fn op_div(self, other: NumExpr<T>) -> NumExpr<T> {
        match (self, other) {
            (NumExpr::Const(a), NumExpr::Const(b)) if b != T::zero() => NumExpr::Const(a * b),
            (a, b) => NumExpr::Div(Box::new(a), Box::new(b)),
        }
    }

    pub fn op_divz(self, other: NumExpr<T>) -> NumExpr<T> {
        match (self, other) {
            (NumExpr::Const(a), NumExpr::Const(b)) => {
                if b == T::zero() {
                    NumExpr::Const(T::zero())
                } else {
                    NumExpr::Const(a * b)
                }
            }
            (a, b) => NumExpr::Divz(Box::new(a), Box::new(b)),
        }
    }

    pub fn op_recip(self) -> NumExpr<T> {
        match self {
            NumExpr::Const(a) if a != T::zero() => NumExpr::Const(T::one() / a),
            a => NumExpr::Recip(Box::new(a)),
        }
    }

    pub fn op_recipz(self) -> NumExpr<T> {
        match self {
            NumExpr::Const(a) => {
                if a == T::zero() {
                    NumExpr::Const(T::zero())
                } else {
                    NumExpr::Const(T::one() / a)
                }
            }
            a => NumExpr::Recipz(Box::new(a)),
        }
    }
}

impl<T: NumType> Expression for NumExpr<T> {
    type Element = T;

    fn evaluate(&self, variables: &[Self::Element]) -> Result<Self::Element, ExpressionError> {
        Ok(match self {
            &NumExpr::Var(n) => {
                try!(variables.get(n).ok_or(ExpressionError::InvalidVariable)).clone()
            }
            &NumExpr::Const(val) => val,
            &NumExpr::Add(ref e1, ref e2) => {
                try!(e1.evaluate(variables)) + try!(e2.evaluate(variables))
            }
            &NumExpr::Sub(ref e1, ref e2) => {
                try!(e1.evaluate(variables)) - try!(e2.evaluate(variables))
            }
            &NumExpr::Mul(ref e1, ref e2) => {
                try!(e1.evaluate(variables)) * try!(e2.evaluate(variables))
            }
            &NumExpr::Div(ref e1, ref e2) => {
                let a = try!(e1.evaluate(variables));
                let div = try!(e2.evaluate(variables));
                if div == T::zero() {
                    return Err(ExpressionError::DivByZero);
                }
                a / div
            }
            &NumExpr::Divz(ref e1, ref e2) => {
                let a = try!(e1.evaluate(variables));
                let div = try!(e2.evaluate(variables));
                if div == T::zero() {
                    div
                } else {
                    a / div
                }
            }
            &NumExpr::Recip(ref e1) => {
                let div = try!(e1.evaluate(variables));
                if div == T::zero() {
                    return Err(ExpressionError::DivByZero);
                } else {
                    T::one() / div
                }
            }
            &NumExpr::Recipz(ref e1) => {
                let div = try!(e1.evaluate(variables));
                if div == T::zero() {
                    div
                } else {
                    T::one() / div
                }
            }
        })
    }
}

impl<'a, T: NumType + Into<Sexp>> Into<Sexp> for &'a NumExpr<T> {
    fn into(self) -> Sexp {
        match self {
            &NumExpr::Const(n) => n.into(),
            &NumExpr::Var(n) => Sexp::from(format!("${}", n)),
            &NumExpr::Add(ref a, ref b) => {
                Sexp::from(("+",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Sub(ref a, ref b) => {
                Sexp::from(("-",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Mul(ref a, ref b) => {
                Sexp::from(("*",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Div(ref a, ref b) => {
                Sexp::from(("/",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Divz(ref a, ref b) => {
                Sexp::from(("divz",
                            Into::<Sexp>::into(a.as_ref()),
                            Into::<Sexp>::into(b.as_ref())))
            }
            &NumExpr::Recip(ref a) => Sexp::from(("recip", Into::<Sexp>::into(a.as_ref()))),
            &NumExpr::Recipz(ref a) => Sexp::from(("recipz", Into::<Sexp>::into(a.as_ref()))),
        }
    }
}

#[cfg(test)]
const NO_VARS: [f32; 0] = [];

#[test]
fn test_expr_divz() {
    let expr = NumExpr::Divz(box NumExpr::Const(1.0), box NumExpr::Const(0.0));
    assert_eq!(Ok(0.0), expr.evaluate(&NO_VARS));
}

#[test]
fn test_expr_recipz() {
    let expr = NumExpr::Recipz(box NumExpr::Const(0.0));
    assert_eq!(Ok(0.0), expr.evaluate(&NO_VARS));

    let expr = NumExpr::Recipz(box NumExpr::Const(1.0));
    assert_eq!(Ok(1.0), expr.evaluate(&NO_VARS));

    let expr = NumExpr::Recipz(box NumExpr::Const(0.5));
    assert_eq!(Ok(2.0), expr.evaluate(&NO_VARS));
}

#[test]
fn test_expr() {
    let expr =
        NumExpr::Sub(box NumExpr::Const(0.0),
                     box NumExpr::Div(box NumExpr::Mul(box NumExpr::Add(box NumExpr::Const(1.0),
                                                                        box NumExpr::Var(0)),
                                                       box NumExpr::Var(1)),
                                      box NumExpr::Const(2.0)));

    fn fun(a: f32, b: f32) -> f32 {
        0.0 - ((1.0 + a) * b) / 2.0
    }

    fn check(expr: &NumExpr<f32>, a: f32, b: f32) {
        assert_eq!(Ok(fun(a, b)), expr.evaluate(&[a, b]))
    }

    check(&expr, 123.0, 4444.0);
    check(&expr, 0.0, -12.0);
}

#[test]
fn test_constant_folding() {
    let expr = NumExpr::Const(1.0);
    let expr2 = expr.op_add(NumExpr::Const(2.0));
    assert_eq!(NumExpr::Const(1.0 + 2.0), expr2);

    let expr = NumExpr::Var(1);
    let expr2 = expr.op_add(NumExpr::Const(2.0));
    assert_eq!(NumExpr::Add(box NumExpr::Var(1), box NumExpr::Const(2.0)),
               expr2);

}
<|endoftext|>


<commit_before>use std::convert::From;

use std::str::Utf8Error;
use std::string::FromUtf8Error;

use std::io;
use std::io::BufReader;
use std::net::IpAddr;

extern crate byteorder;

use byteorder::{LittleEndian, ByteOrder, ReadBytesExt};

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

#[derive(Debug, Clone)]
pub enum FormatError {
    UnknownBlock(u32),
    UnknownOption(u16),
    Utf8Error(Utf8Error)
}

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

impl From<FromUtf8Error> for Error {
    fn from(err: FromUtf8Error) -> Error {
        Error::FormatError(FormatError::Utf8Error(err.utf8_error()))
    }
}

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

#[derive(Debug)]
pub enum Block {
    SectionHeader(SectionHeaderBlock),
    InterfaceDescription(InterfaceDescriptionBlock),
    InterfaceStatistics(InterfaceStatisticsBlock),
    EnhancedPacket(EnhancedPacketBlock)
}

#[derive(Debug)]
pub struct SectionHeaderBlock {
    /// Magic number equal to 0x1A2B3C4D. This field can be used to detect
    /// sections saved on systems with different endianness.
    pub magic: u32,

    pub major_version: u16,
    pub minor_version: u16,

    /// Length in bytes of the following section excluding this block.
    ///
    /// This block can be used to skip the section for faster navigation in
    /// large files. Length of -1u64 means that the length is unspecified.
    pub section_length: u64,
    pub options: Vec<SectionHeaderOption>
}

#[derive(Debug)]
pub enum SectionHeaderOption {
    /// Comment associated with the current block
    Comment(String),

    /// Description of the hardware used to create this section
    Hardware(String),

    /// Name of the operating system used to create this section
    OS(String),

    /// Name of the application used to create this section
    UserApplication(String)
}

/// Block type 1
#[derive(Debug)]
pub struct InterfaceDescriptionBlock {
    /// Link layer type of the interface
    pub link_type: u16,

    /// Maximum number of bytes stored from each packet
    pub snap_len: u32,

    pub options: Vec<InterfaceDescriptionOption>
}

#[derive(Debug)]
pub enum InterfaceDescriptionOption {
    /// Comment associated with the current block
    Comment(String),

    /// Name of the device used to capture data
    Name(String),

    /// Description of the device used to capture data
    Description(String),

    /// IPv4 interface address and netmask
    Ipv4Addr(u32, u32),

    /// IPv6 interface address and prefix length
    Ipv6Addr(IpAddr, u8),

    /// Hardware MAC address (48 bits)
    MacAddr(u64),

    /// Hardware EUI address
    EuiAddr(u64),

    /// Interface speed in bps
    Speed(u64),

    /// Resolution of timestamps.
    ///
    /// If the MSB is equal to zero, the remaining bits indicate the resolution
    /// as a negative power of 10 (e.g. 6 means microsecond resolution,
    /// timestamps are teh number of microseconds since 1/1/1970).
    ///
    /// If the MSB is equal to one, the remaining bits indicate the resolution
    /// as a negative power of 2 (e.g. 10 means 1/1024 of a second).
    ///
    /// If this options is not present, microsecond resolution is assumed.
    TsResolution(u8),

    /// Timezone for GMT support (?)
    Timezone(u32),

    /// The filter used to capture traffic. The first byte specifies the type
    /// of filter used (e.g. libpcap string, BPF bytecode, etc)
    Filter(u8, Vec<u8>),

    /// Operating system of the machine in which this interface is installed.
    /// This can be different from the Section Header Block if the capture
    /// was done remotely.
    OS(String),

    /// Length of Frame Check Sequence in bits for this interface. For link
    /// layers whose FCS length can change during time, the Packet Block
    /// flags be used.
    FcsLen(u8),

    /// Offset pf the timestamp in seconds
    TsOffset(u64)
}

/// Block type 5
#[derive(Debug)]
pub struct InterfaceStatisticsBlock {
    pub interface_id: u32,
    pub timestamp: u64,

    pub options: Vec<InterfaceStatisticsOption>
}

#[derive(Debug)]
pub enum InterfaceStatisticsOption {
    /// Comment associated with the current block
    Comment(String),

    /// Time when capture started
    StartTime(u64),

    /// Time when capture ended
    EndTime(u64),

    /// Number of packets received from the physical interface
    Received(u64),

    /// Number of packets dropped by the interface due to the lack of resources
    Dropped(u64),

    /// Number of packets accepted by the filter
    FilterAccepted(u64),

    /// Number of packets dropped by the operating system
    OSDropped(u64),

    /// Number of packets delivered to the user starting. The value in this
    /// field can be different from 'Accepted - Dropped' because some packets
    /// could still be in the OS buffers when the capture ended.
    Delivered(u64)
}

#[derive(Debug)]
pub struct EnhancedPacketBlock {
    pub interface_id: u32,
    pub timestamp: u64,

    /// Actual length of the packet when it was transmitted on the network
    pub len: u32,

    pub data: Vec<u8>,
    pub options: Vec<EnhancedPacketBlockOption>
}

#[derive(Debug)]
pub enum EnhancedPacketBlockOption {
    Comment(String),
    Flags(u32),
    Hash,

    /// Number of packets lost between the last packet
    DropCount(u64)
}

#[inline(always)]
fn dword_aligned(n: usize) -> usize {
    (n + 3) & !3
}

fn read_exact(r: &mut io::Read, n: usize) -> io::Result<Vec<u8>> {
    let mut buf = Vec::with_capacity(n);
    unsafe { buf.set_len(n); }

    let mut i = 0;
    while i < n {
        i += try!(r.read(&mut buf[i..]));
    }

    Ok(buf)
}

pub fn read_raw_block<BO: ByteOrder>(r: &mut io::Read) -> Result<(u32, Vec<u8>), io::Error> {
    let block_type = try!(r.read_u32::<BO>());

    let total_len = try!(r.read_u32::<BO>()) as usize;
    let data_len = total_len - 12; // 12 = type + 2*length

    let mut data = try!(read_exact(r, dword_aligned(data_len)));
    data.truncate(data_len);

    assert!(total_len == try!(r.read_u32::<BO>()) as usize);

    Ok((block_type, data))
}

fn read_option<BO: ByteOrder>(r: &mut io::Read) -> Result<(u16, Vec<u8>), io::Error> {
    let code = try!(r.read_u16::<BO>());
    let len = try!(r.read_u16::<BO>()) as usize;

    let mut data = try!(read_exact(r, dword_aligned(len)));
    data.truncate(len);

    Ok((code, data))
}

pub fn read_block(r: &mut io::Read) -> Result<Block, Error> {
    use Block::*;
    type BO = LittleEndian;

    let (block_type, data) = try!(read_raw_block::<BO>(r));
    let mut r = BufReader::new(&data[..]);

    let r = match block_type {
        0x0A0D0D0A => SectionHeader(try!(SectionHeaderBlock::read::<BO>(&mut r))),
        1 => InterfaceDescription(try!(InterfaceDescriptionBlock::read::<BO>(&mut r))),
        5 => InterfaceStatistics(try!(InterfaceStatisticsBlock::read::<BO>(&mut r))),
        6 => EnhancedPacket(try!(EnhancedPacketBlock::read::<BO>(&mut r))),
        _ => return Err(From::from(FormatError::UnknownBlock(block_type)))
    };

    Ok(r)
}

pub struct BlockIter<'a> {
    r: &'a mut (io::Read + 'a)
}

pub struct PacketIter<'a> {
    r: &'a mut SimpleReader<'a>
}

pub struct SimpleReader<'a> {
    r: &'a mut (io::Read + 'a),

    interfaces: Vec<InterfaceDescriptionBlock>,
    if_offset: usize
}

impl<'a> Iterator for BlockIter<'a> {
    type Item = Block;

    fn next(&mut self) -> Option<Block> {
        match read_block(self.r) {
            Ok(block) => Some(block),
            Err(_) => None
        }
    }
}

pub type IterPacket<'a> = (&'a InterfaceDescriptionBlock, EnhancedPacketBlock);

impl<'a> Iterator for PacketIter<'a> {
    type Item = IterPacket<'a>;

    fn next(&mut self) -> Option<IterPacket<'a>> {
        while let Ok(block) = read_block(self.r.r) {
            match block {
                Block::SectionHeader(_) => {
                    // Each section is independent from another,
                    // however we might still have live references
                    // to the old interface descriptions so we
                    // can't just clear the vector
                    self.r.if_offset = self.r.interfaces.len()
                },

                Block::InterfaceDescription(id) => self.r.interfaces.push(id),

                Block::EnhancedPacket(ep) => {
                    let iface = &self.r.interfaces[self.r.if_offset + ep.interface_id as usize];
                    unsafe {
                        // The interface description should live as long as
                        // SimpleBufReader, so this should be safe.
                        let iface = std::mem::transmute(iface);

                        return Some((iface, ep))
                    }
                },
                _ => {}
            }
        }

        None
    }
}

impl<'a> SimpleReader<'a> {
    pub fn new(r: &mut io::Read) -> SimpleReader {
        SimpleReader { r: r, interfaces: Vec::new(), if_offset: 0}
    }

    pub fn blocks(&mut self) -> BlockIter {
        BlockIter { r: self.r }
    }

    pub fn packets(&'a mut self) -> PacketIter<'a> {
        PacketIter { r: self }
    }
}

impl SectionHeaderBlock {
    pub fn read<BO: ByteOrder>(r: &mut io::Read) -> Result<SectionHeaderBlock, Error> {
        let magic = try!(r.read_u32::<BO>());

        assert!(magic == 0x1A2B3C4D, "unsupported endianness");

        let major_version = try!(r.read_u16::<BO>());
        let minor_version = try!(r.read_u16::<BO>());
        let section_length = try!(r.read_u64::<BO>());

        let mut options = Vec::new();

        loop {
            use SectionHeaderOption::*;

            let (code, data) = match read_option::<BO>(r) {
                Ok(x) => x,
                Err(_) => break
            };

            let opt = match code {
                0 => break,

                1...4 => {
                    let s = try!(String::from_utf8(data));

                    match code {
                        1 => Comment(s),
                        2 => Hardware(s),
                        3 => OS(s),
                        4 => UserApplication(s),
                        _ => unreachable!()
                    }
                }

                _ => return Err(From::from(FormatError::UnknownOption(code)))
            };

            options.push(opt);
        }

        Ok(SectionHeaderBlock {
            magic: magic,
            major_version: major_version,
            minor_version: minor_version,
            section_length: section_length,

            options: options
        })
    }
}

impl InterfaceDescriptionBlock {
    pub fn read<BO: ByteOrder>(r: &mut io::Read) -> Result<InterfaceDescriptionBlock, Error> {
        let link_type = try!(r.read_u16::<BO>());
        try!(r.read_u16::<BO>()); // Reserved
        let snap_len = try!(r.read_u32::<BO>());

        let mut options = Vec::new();

        loop {
            use InterfaceDescriptionOption::*;

            let (code, data) = match read_option::<BO>(r) {
                Ok(x) => x,
                Err(_) => break
            };

            let mut d = &data[..];

            let opt = match code {
                0 => break,
                1...3 | 12 => {
                    let s = try!(String::from_utf8(data.clone()));

                    match code {
                        1 => Comment(s),
                        2 => Name(s),
                        3 => Description(s),
                        12 => OS(s),
                        _ => unreachable!()
                    }
                }

                4 => {
                    let ip = try!(d.read_u32::<BO>());
                    let mask = try!(d.read_u32::<BO>());

                    Ipv4Addr(ip, mask)
                }

                6 => MacAddr(try!(d.read_uint::<BO>(6))),
                9 => TsResolution(try!(d.read_u8())),

                _ => return Err(From::from(FormatError::UnknownOption(code)))
            };

            options.push(opt);
        }

        Ok(InterfaceDescriptionBlock {
            link_type: link_type,
            snap_len: snap_len,
            options: options
        })
    }
}

impl EnhancedPacketBlock {
    pub fn read<BO: ByteOrder>(r: &mut io::Read) -> Result<EnhancedPacketBlock, Error> {
        let interface_id = try!(r.read_u32::<BO>());
        let ts = try!(r.read_u64::<BO>());
        let cap_len = try!(r.read_u32::<BO>()) as usize;
        let len = try!(r.read_u32::<BO>());

        let aligned_len = dword_aligned(cap_len);

        let mut packet_data = try!(read_exact(r, aligned_len));
        packet_data.truncate(cap_len);

        let mut options = Vec::new();

        loop {
            use EnhancedPacketBlockOption::*;

            let (code, data) = match read_option::<BO>(r) {
                Ok(x) => x,
                Err(_) => break
            };

            let opt = match code {
                0 => break,
                1 => {
                    match String::from_utf8(data.clone()) {
                        Ok(r) => Comment(r),
                        Err(err) => return Err(From::from(err))
                    }
                }

                // TODO: rest of the options
                _ => return Err(From::from(FormatError::UnknownOption(code)))
            };

            options.push(opt);
        }

        Ok(EnhancedPacketBlock {
            interface_id: interface_id,
            timestamp: ts,
            len: len,
            data: packet_data,
            options: options
        })
    }
}

impl InterfaceStatisticsBlock {
    pub fn read<BO: ByteOrder>(r: &mut io::Read) -> Result<InterfaceStatisticsBlock, Error> {
        let interface_id = try!(r.read_u32::<BO>());
        let ts = try!(r.read_u64::<BO>());

        let mut options = Vec::new();

        loop {
            use InterfaceStatisticsOption::*;

            let (code, data) = match read_option::<BO>(r) {
                Ok(x) => x,
                Err(_) => break
            };

            let opt = match code {
                0 => break,
                1 => {
                    match String::from_utf8(data.clone()) {
                        Ok(r) => Comment(r),
                        Err(err) => return Err(From::from(err))
                    }
                }

                2...8 => {
                    let data = try!((&*data).read_u64::<BO>());

                    match code {
                        2 => StartTime(data),
                        3 => EndTime(data),
                        4 => Received(data),
                        5 => Dropped(data),
                        6 => FilterAccepted(data),
                        7 => OSDropped(data),
                        8 => Delivered(data),
                        _ => unreachable!()
                    }
                }

                _ => return Err(From::from(FormatError::UnknownOption(code)))
            };

            options.push(opt);
        }

        Ok(InterfaceStatisticsBlock {
            interface_id: interface_id,
            timestamp: ts,
            options: options
        })
    }
}
<commit_msg>Use Read::read_exact<commit_after>use std::convert::From;

use std::str::Utf8Error;
use std::string::FromUtf8Error;

use std::io;
use std::io::BufReader;
use std::net::IpAddr;

extern crate byteorder;

use byteorder::{LittleEndian, ByteOrder, ReadBytesExt};

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

#[derive(Debug, Clone)]
pub enum FormatError {
    UnknownBlock(u32),
    UnknownOption(u16),
    Utf8Error(Utf8Error)
}

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

impl From<FromUtf8Error> for Error {
    fn from(err: FromUtf8Error) -> Error {
        Error::FormatError(FormatError::Utf8Error(err.utf8_error()))
    }
}

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

#[derive(Debug)]
pub enum Block {
    SectionHeader(SectionHeaderBlock),
    InterfaceDescription(InterfaceDescriptionBlock),
    InterfaceStatistics(InterfaceStatisticsBlock),
    EnhancedPacket(EnhancedPacketBlock)
}

#[derive(Debug)]
pub struct SectionHeaderBlock {
    /// Magic number equal to 0x1A2B3C4D. This field can be used to detect
    /// sections saved on systems with different endianness.
    pub magic: u32,

    pub major_version: u16,
    pub minor_version: u16,

    /// Length in bytes of the following section excluding this block.
    ///
    /// This block can be used to skip the section for faster navigation in
    /// large files. Length of -1u64 means that the length is unspecified.
    pub section_length: u64,
    pub options: Vec<SectionHeaderOption>
}

#[derive(Debug)]
pub enum SectionHeaderOption {
    /// Comment associated with the current block
    Comment(String),

    /// Description of the hardware used to create this section
    Hardware(String),

    /// Name of the operating system used to create this section
    OS(String),

    /// Name of the application used to create this section
    UserApplication(String)
}

/// Block type 1
#[derive(Debug)]
pub struct InterfaceDescriptionBlock {
    /// Link layer type of the interface
    pub link_type: u16,

    /// Maximum number of bytes stored from each packet
    pub snap_len: u32,

    pub options: Vec<InterfaceDescriptionOption>
}

#[derive(Debug)]
pub enum InterfaceDescriptionOption {
    /// Comment associated with the current block
    Comment(String),

    /// Name of the device used to capture data
    Name(String),

    /// Description of the device used to capture data
    Description(String),

    /// IPv4 interface address and netmask
    Ipv4Addr(u32, u32),

    /// IPv6 interface address and prefix length
    Ipv6Addr(IpAddr, u8),

    /// Hardware MAC address (48 bits)
    MacAddr(u64),

    /// Hardware EUI address
    EuiAddr(u64),

    /// Interface speed in bps
    Speed(u64),

    /// Resolution of timestamps.
    ///
    /// If the MSB is equal to zero, the remaining bits indicate the resolution
    /// as a negative power of 10 (e.g. 6 means microsecond resolution,
    /// timestamps are teh number of microseconds since 1/1/1970).
    ///
    /// If the MSB is equal to one, the remaining bits indicate the resolution
    /// as a negative power of 2 (e.g. 10 means 1/1024 of a second).
    ///
    /// If this options is not present, microsecond resolution is assumed.
    TsResolution(u8),

    /// Timezone for GMT support (?)
    Timezone(u32),

    /// The filter used to capture traffic. The first byte specifies the type
    /// of filter used (e.g. libpcap string, BPF bytecode, etc)
    Filter(u8, Vec<u8>),

    /// Operating system of the machine in which this interface is installed.
    /// This can be different from the Section Header Block if the capture
    /// was done remotely.
    OS(String),

    /// Length of Frame Check Sequence in bits for this interface. For link
    /// layers whose FCS length can change during time, the Packet Block
    /// flags be used.
    FcsLen(u8),

    /// Offset pf the timestamp in seconds
    TsOffset(u64)
}

/// Block type 5
#[derive(Debug)]
pub struct InterfaceStatisticsBlock {
    pub interface_id: u32,
    pub timestamp: u64,

    pub options: Vec<InterfaceStatisticsOption>
}

#[derive(Debug)]
pub enum InterfaceStatisticsOption {
    /// Comment associated with the current block
    Comment(String),

    /// Time when capture started
    StartTime(u64),

    /// Time when capture ended
    EndTime(u64),

    /// Number of packets received from the physical interface
    Received(u64),

    /// Number of packets dropped by the interface due to the lack of resources
    Dropped(u64),

    /// Number of packets accepted by the filter
    FilterAccepted(u64),

    /// Number of packets dropped by the operating system
    OSDropped(u64),

    /// Number of packets delivered to the user starting. The value in this
    /// field can be different from 'Accepted - Dropped' because some packets
    /// could still be in the OS buffers when the capture ended.
    Delivered(u64)
}

#[derive(Debug)]
pub struct EnhancedPacketBlock {
    pub interface_id: u32,
    pub timestamp: u64,

    /// Actual length of the packet when it was transmitted on the network
    pub len: u32,

    pub data: Vec<u8>,
    pub options: Vec<EnhancedPacketBlockOption>
}

#[derive(Debug)]
pub enum EnhancedPacketBlockOption {
    Comment(String),
    Flags(u32),
    Hash,

    /// Number of packets lost between the last packet
    DropCount(u64)
}

#[inline(always)]
fn dword_aligned(n: usize) -> usize {
    (n + 3) & !3
}

pub fn read_raw_block<BO: ByteOrder>(r: &mut io::Read) -> io::Result<(u32, Vec<u8>)> {
    let block_type = try!(r.read_u32::<BO>());

    let total_len = try!(r.read_u32::<BO>()) as usize;
    let data_len = total_len - 12; // 12 = type + 2*length

    let mut data = vec![0; dword_aligned(data_len)];
    try!(r.read_exact(&mut data[..]));
    data.truncate(data_len);

    assert!(total_len == try!(r.read_u32::<BO>()) as usize);

    Ok((block_type, data))
}

fn read_option<BO: ByteOrder>(r: &mut io::Read) -> io::Result<(u16, Vec<u8>)> {
    let code = try!(r.read_u16::<BO>());
    let len = try!(r.read_u16::<BO>()) as usize;

    let mut data = vec![0; dword_aligned(len)];
    try!(r.read_exact(&mut data[..]));
    data.truncate(len);

    Ok((code, data))
}

pub fn read_block(r: &mut io::Read) -> Result<Block, Error> {
    use Block::*;
    type BO = LittleEndian;

    let (block_type, data) = try!(read_raw_block::<BO>(r));
    let mut r = BufReader::new(&data[..]);

    let r = match block_type {
        0x0A0D0D0A => SectionHeader(try!(SectionHeaderBlock::read::<BO>(&mut r))),
        1 => InterfaceDescription(try!(InterfaceDescriptionBlock::read::<BO>(&mut r))),
        5 => InterfaceStatistics(try!(InterfaceStatisticsBlock::read::<BO>(&mut r))),
        6 => EnhancedPacket(try!(EnhancedPacketBlock::read::<BO>(&mut r))),
        _ => return Err(From::from(FormatError::UnknownBlock(block_type)))
    };

    Ok(r)
}

pub struct BlockIter<'a> {
    r: &'a mut (io::Read + 'a)
}

pub struct PacketIter<'a> {
    r: &'a mut SimpleReader<'a>
}

pub struct SimpleReader<'a> {
    r: &'a mut (io::Read + 'a),

    interfaces: Vec<InterfaceDescriptionBlock>,
    if_offset: usize
}

impl<'a> Iterator for BlockIter<'a> {
    type Item = Block;

    fn next(&mut self) -> Option<Block> {
        match read_block(self.r) {
            Ok(block) => Some(block),
            Err(_) => None
        }
    }
}

pub type IterPacket<'a> = (&'a InterfaceDescriptionBlock, EnhancedPacketBlock);

impl<'a> Iterator for PacketIter<'a> {
    type Item = IterPacket<'a>;

    fn next(&mut self) -> Option<IterPacket<'a>> {
        while let Ok(block) = read_block(self.r.r) {
            match block {
                Block::SectionHeader(_) => {
                    // Each section is independent from another,
                    // however we might still have live references
                    // to the old interface descriptions so we
                    // can't just clear the vector
                    self.r.if_offset = self.r.interfaces.len()
                },

                Block::InterfaceDescription(id) => self.r.interfaces.push(id),

                Block::EnhancedPacket(ep) => {
                    let iface = &self.r.interfaces[self.r.if_offset + ep.interface_id as usize];
                    unsafe {
                        // The interface description should live as long as
                        // SimpleBufReader, so this should be safe.
                        let iface = std::mem::transmute(iface);

                        return Some((iface, ep))
                    }
                },
                _ => {}
            }
        }

        None
    }
}

impl<'a> SimpleReader<'a> {
    pub fn new(r: &mut io::Read) -> SimpleReader {
        SimpleReader { r: r, interfaces: Vec::new(), if_offset: 0}
    }

    pub fn blocks(&mut self) -> BlockIter {
        BlockIter { r: self.r }
    }

    pub fn packets(&'a mut self) -> PacketIter<'a> {
        PacketIter { r: self }
    }
}

impl SectionHeaderBlock {
    pub fn read<BO: ByteOrder>(r: &mut io::Read) -> Result<SectionHeaderBlock, Error> {
        let magic = try!(r.read_u32::<BO>());

        assert!(magic == 0x1A2B3C4D, "unsupported endianness");

        let major_version = try!(r.read_u16::<BO>());
        let minor_version = try!(r.read_u16::<BO>());
        let section_length = try!(r.read_u64::<BO>());

        let mut options = Vec::new();

        loop {
            use SectionHeaderOption::*;

            let (code, data) = match read_option::<BO>(r) {
                Ok(x) => x,
                Err(_) => break
            };

            let opt = match code {
                0 => break,

                1...4 => {
                    let s = try!(String::from_utf8(data));

                    match code {
                        1 => Comment(s),
                        2 => Hardware(s),
                        3 => OS(s),
                        4 => UserApplication(s),
                        _ => unreachable!()
                    }
                }

                _ => return Err(From::from(FormatError::UnknownOption(code)))
            };

            options.push(opt);
        }

        Ok(SectionHeaderBlock {
            magic: magic,
            major_version: major_version,
            minor_version: minor_version,
            section_length: section_length,

            options: options
        })
    }
}

impl InterfaceDescriptionBlock {
    pub fn read<BO: ByteOrder>(r: &mut io::Read) -> Result<InterfaceDescriptionBlock, Error> {
        let link_type = try!(r.read_u16::<BO>());
        try!(r.read_u16::<BO>()); // Reserved
        let snap_len = try!(r.read_u32::<BO>());

        let mut options = Vec::new();

        loop {
            use InterfaceDescriptionOption::*;

            let (code, data) = match read_option::<BO>(r) {
                Ok(x) => x,
                Err(_) => break
            };

            let mut d = &data[..];

            let opt = match code {
                0 => break,
                1...3 | 12 => {
                    let s = try!(String::from_utf8(data.clone()));

                    match code {
                        1 => Comment(s),
                        2 => Name(s),
                        3 => Description(s),
                        12 => OS(s),
                        _ => unreachable!()
                    }
                }

                4 => {
                    let ip = try!(d.read_u32::<BO>());
                    let mask = try!(d.read_u32::<BO>());

                    Ipv4Addr(ip, mask)
                }

                6 => MacAddr(try!(d.read_uint::<BO>(6))),
                9 => TsResolution(try!(d.read_u8())),

                _ => return Err(From::from(FormatError::UnknownOption(code)))
            };

            options.push(opt);
        }

        Ok(InterfaceDescriptionBlock {
            link_type: link_type,
            snap_len: snap_len,
            options: options
        })
    }
}

impl EnhancedPacketBlock {
    pub fn read<BO: ByteOrder>(r: &mut io::Read) -> Result<EnhancedPacketBlock, Error> {
        let interface_id = try!(r.read_u32::<BO>());
        let ts = try!(r.read_u64::<BO>());
        let cap_len = try!(r.read_u32::<BO>()) as usize;
        let len = try!(r.read_u32::<BO>());

        let aligned_len = dword_aligned(cap_len);

        let mut packet_data = vec![0; aligned_len];
        try!(r.read_exact(&mut packet_data[..]));
        packet_data.truncate(cap_len);

        let mut options = Vec::new();

        loop {
            use EnhancedPacketBlockOption::*;

            let (code, data) = match read_option::<BO>(r) {
                Ok(x) => x,
                Err(_) => break
            };

            let opt = match code {
                0 => break,
                1 => {
                    match String::from_utf8(data.clone()) {
                        Ok(r) => Comment(r),
                        Err(err) => return Err(From::from(err))
                    }
                }

                // TODO: rest of the options
                _ => return Err(From::from(FormatError::UnknownOption(code)))
            };

            options.push(opt);
        }

        Ok(EnhancedPacketBlock {
            interface_id: interface_id,
            timestamp: ts,
            len: len,
            data: packet_data,
            options: options
        })
    }
}

impl InterfaceStatisticsBlock {
    pub fn read<BO: ByteOrder>(r: &mut io::Read) -> Result<InterfaceStatisticsBlock, Error> {
        let interface_id = try!(r.read_u32::<BO>());
        let ts = try!(r.read_u64::<BO>());

        let mut options = Vec::new();

        loop {
            use InterfaceStatisticsOption::*;

            let (code, data) = match read_option::<BO>(r) {
                Ok(x) => x,
                Err(_) => break
            };

            let opt = match code {
                0 => break,
                1 => {
                    match String::from_utf8(data.clone()) {
                        Ok(r) => Comment(r),
                        Err(err) => return Err(From::from(err))
                    }
                }

                2...8 => {
                    let data = try!((&*data).read_u64::<BO>());

                    match code {
                        2 => StartTime(data),
                        3 => EndTime(data),
                        4 => Received(data),
                        5 => Dropped(data),
                        6 => FilterAccepted(data),
                        7 => OSDropped(data),
                        8 => Delivered(data),
                        _ => unreachable!()
                    }
                }

                _ => return Err(From::from(FormatError::UnknownOption(code)))
            };

            options.push(opt);
        }

        Ok(InterfaceStatisticsBlock {
            interface_id: interface_id,
            timestamp: ts,
            options: options
        })
    }
}
<|endoftext|>


<commit_before>pub mod aes;

pub fn ecb_encrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
    let ekey = aes::expand_key(key);
    let mut output = Vec::new();
    for block in data.chunks(16) {
        if block.len() != 16 { panic!("Size must be multiple of 16 bytes"); }
        let encrypted = aes::encrypt(&ekey, block);
        output.extend(encrypted.iter().cloned());
    }
    output
}

pub fn ecb_decrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
    let ekey = aes::expand_key(key);
    let mut output = Vec::new();
    for block in data.chunks(16) {
        if block.len() != 16 { panic!("Size must be multiple of 16 bytes"); }
        let decrypted = aes::decrypt(&ekey, block);
        output.extend(decrypted.iter().cloned());
    }
    output
}

#[test]
fn ecb_test() {
    let data = [1; 128];
    let key = [2; 16];
    let encrypted = ecb_encrypt(&key, &data);
    let decrypted = ecb_decrypt(&key, &encrypted);
    assert_eq!(&data[..], &decrypted[..]);
}

pub fn cbc_encrypt(key: &[u8], data: &[u8], iv: &[u8]) -> Vec<u8> {
    let ekey = aes::expand_key(key);
    let mut output = Vec::new();
    let mut previous = [0; 16];
    for i in 0..16 { previous[i] = iv[i]; }
    for block in data.chunks(16) {
        if block.len() != 16 { panic!("Size must be multiple of 16 bytes"); }
        for i in 0..16 {
            previous[i] ^= block[i];
        }
        let encrypted = aes::encrypt(&ekey, &previous);
        previous = encrypted;
        output.extend(encrypted.iter().cloned());
    }
    output
}

pub fn cbc_decrypt(key: &[u8], data: &[u8], iv: &[u8]) -> Vec<u8> {
    let ekey = aes::expand_key(key);
    let mut output = Vec::new();
    let mut previous = [0; 16];
    for i in 0..16 { previous[i] = iv[i]; }
    for block in data.chunks(16) {
        if block.len() != 16 { panic!("Size must be multiple of 16 bytes"); }
        let mut decrypted = aes::decrypt(&ekey, block);
        for i in 0..16 {
            decrypted[i] ^= previous[i];
            previous[i] = block[i];
        }
        output.extend(decrypted.iter().cloned());
    }
    output
}

#[test]
fn cbc_test() {
    let data = [3; 128];
    let key = [4; 16];
    let iv = [5; 16];
    let encrypted = cbc_encrypt(&key, &data, &iv);
    let decrypted = cbc_decrypt(&key, &encrypted, &iv);
    assert_eq!(&data[..], &decrypted[..]);
}

pub fn pad(data: &[u8]) -> Vec<u8> {
    let length = data.len();
    let target = length - length % 16 + 16;
    let padding = target - length;
    let mut output = data.to_vec();
    for _ in 0..padding {
        output.push(padding as u8);
    }
    output
}

#[test]
fn pad_test() {
    let data = [7;11];
    let padded = pad(&data);
    assert_eq!([7,7,7,7,7,7,7,7,7,7,7,5,5,5,5,5], padded[..]);
    let data2 = [4;16];
    let padded2 = pad(&data2);
    assert_eq!([4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16], padded2[..]);
    // ^ allowing for no padding at all creates ambiguity if the unpadded data has an ending that
    // looks like it could be padding so we pad for a full block
}
<commit_msg>Added unpadding<commit_after>pub mod aes;

pub fn ecb_encrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
    let ekey = aes::expand_key(key);
    let mut output = Vec::new();
    for block in data.chunks(16) {
        if block.len() != 16 { panic!("Size must be multiple of 16 bytes"); }
        let encrypted = aes::encrypt(&ekey, block);
        output.extend(encrypted.iter().cloned());
    }
    output
}

pub fn ecb_decrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
    let ekey = aes::expand_key(key);
    let mut output = Vec::new();
    for block in data.chunks(16) {
        if block.len() != 16 { panic!("Size must be multiple of 16 bytes"); }
        let decrypted = aes::decrypt(&ekey, block);
        output.extend(decrypted.iter().cloned());
    }
    output
}

#[test]
fn ecb_test() {
    let data = [1; 128];
    let key = [2; 16];
    let encrypted = ecb_encrypt(&key, &data);
    let decrypted = ecb_decrypt(&key, &encrypted);
    assert_eq!(&data[..], &decrypted[..]);
}

pub fn cbc_encrypt(key: &[u8], data: &[u8], iv: &[u8]) -> Vec<u8> {
    let ekey = aes::expand_key(key);
    let mut output = Vec::new();
    let mut previous = [0; 16];
    for i in 0..16 { previous[i] = iv[i]; }
    for block in data.chunks(16) {
        if block.len() != 16 { panic!("Size must be multiple of 16 bytes"); }
        for i in 0..16 {
            previous[i] ^= block[i];
        }
        let encrypted = aes::encrypt(&ekey, &previous);
        previous = encrypted;
        output.extend(encrypted.iter().cloned());
    }
    output
}

pub fn cbc_decrypt(key: &[u8], data: &[u8], iv: &[u8]) -> Vec<u8> {
    let ekey = aes::expand_key(key);
    let mut output = Vec::new();
    let mut previous = [0; 16];
    for i in 0..16 { previous[i] = iv[i]; }
    for block in data.chunks(16) {
        if block.len() != 16 { panic!("Size must be multiple of 16 bytes"); }
        let mut decrypted = aes::decrypt(&ekey, block);
        for i in 0..16 {
            decrypted[i] ^= previous[i];
            previous[i] = block[i];
        }
        output.extend(decrypted.iter().cloned());
    }
    output
}

#[test]
fn cbc_test() {
    let data = [3; 128];
    let key = [4; 16];
    let iv = [5; 16];
    let encrypted = cbc_encrypt(&key, &data, &iv);
    let decrypted = cbc_decrypt(&key, &encrypted, &iv);
    assert_eq!(&data[..], &decrypted[..]);
}

pub fn pad(data: &[u8]) -> Vec<u8> {
    let length = data.len();
    let target = length - length % 16 + 16;
    let padding = target - length;
    let mut output = data.to_vec();
    for _ in 0..padding {
        output.push(padding as u8);
    }
    output
}

pub fn unpad(padded: &[u8]) -> Vec<u8> {
    let last = padded[padded.len() - 1];
    let mut output = padded.to_vec();
    while output[output.len() - 1] == last {
        let end = output.len() -1;
        output.remove(end);
    }
    output
}

#[test]
fn pad_test() {
    let data = [7;11];
    let padded = pad(&data);
    assert_eq!([7,7,7,7,7,7,7,7,7,7,7,5,5,5,5,5], padded[..]);
    let data2 = [4;16];
    let padded2 = pad(&data2);
    assert_eq!([4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16], padded2[..]);
    // ^ allowing for no padding at all creates ambiguity if the unpadded data has an ending that
    // looks like it could be padding so we pad for a full block
    assert_eq!(data, unpad(&padded)[..]);
    assert_eq!(data2, unpad(&padded2)[..]);
}

<|endoftext|>