<commit_before>//#![deny(missing_docs)]

//! Language tags can be used identify human languages, scripts e.g. Latin script, countries and
//! other regions.
//!
//! Language tags are defined in [BCP47](http://tools.ietf.org/html/bcp47), an introduction is
//! ["Language tags in HTML and XML"](http://www.w3.org/International/articles/language-tags/) by
//! the W3C. They are commonly used in HTML and HTTP `Content-Language` and `Accept-Language`
//! header fields.

use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use std::default::Default;
use std::ascii::AsciiExt;
use std::collections::BTreeMap;

#[derive(Debug, Eq, PartialEq)]
pub struct Error;

fn is_alphabetic(s: &str) -> bool {
    s.chars().all(|x| x >= 'A' && x <= 'Z' || x >= 'a' && x <= 'z')
}

fn is_numeric(s: &str) -> bool {
    s.chars().all(|x| x >= '0' && x <= '9')
}

fn is_alphanumeric_or_dash(s: &str) -> bool {
    s.chars().all(|x| x >= 'A' && x <= 'Z' || x >= 'a' && x <= 'z' || x >= '0' && x <= '9' || x == '-')
}

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

pub const GRANDFATHERED_IRREGULAR: [&'static str; 17] = [
    "en-GB-oed",
    "i-ami",
    "i-bnn",
    "i-default",
    "i-enochian",
    "i-hak",
    "i-klingon",
    "i-lux",
    "i-mingo",
    "i-navajo",
    "i-pwn",
    "i-tao",
    "i-tay",
    "i-tsu",
    "sgn-BE-FR",
    "sgn-BE-NL",
    "sgn-CH-DE"];

pub const GRANDFATHERED_REGULAR: [&'static str; 9] = [
    "art-lojban",
    "cel-gaulish",
    "no-bok",
    "no-nyn",
    "zh-guoyu",
    "zh-hakka",
    "zh-min",
    "zh-min-nan",
    "zh-xiang"];

/// A language tag as desribed in [BCP47](http://tools.ietf.org/html/bcp47)
#[derive(Debug, Eq, Clone)]
pub struct LanguageTag {
    pub language: Option<String>,
    pub extlang: Option<String>,
    pub script: Option<String>,
    pub region: Option<String>,
    pub variants: Vec<String>,
    pub extensions: BTreeMap<u8, Vec<String>>,
    pub privateuse: Vec<String>
}

impl LanguageTag {
    /// Matches language tags like described in
    /// [rfc4647#Extended filtering](https://tools.ietf.org/html/rfc4647#section-3.3.2)
    ///
    /// For example `en-GB` matches only `en-GB` and `en-Arab-GB` but not `en`. While `en` matches
    /// all of `en`, `en-GB` ,`en-Arab` and `en-Arab-GB`.
    pub fn matches(&self, other: &LanguageTag) -> bool {
        return matches_option_ignore_ascii_case(&self.language, &other.language) &&
        matches_option_ignore_ascii_case(&self.extlang, &other.extlang) &&
        matches_option_ignore_ascii_case(&self.script, &other.script) &&
        matches_option_ignore_ascii_case(&self.region, &other.region) &&
        self.variants.iter().all(|x| other.variants.iter().all(|y| x.eq_ignore_ascii_case(y))) &&
        self.privateuse.len() == other.privateuse.len() &&
        self.privateuse.iter().zip(other.privateuse.iter()).all(|(x, y)| x.eq_ignore_ascii_case(y));

        fn matches_option_ignore_ascii_case(a: &Option<String>, b: &Option<String>) -> bool {
            match (a.is_some(), b.is_some()) {
                (true, true) => a.as_ref().unwrap().eq_ignore_ascii_case(b.as_ref().unwrap()),
                (false, false) => true,
                (true, false) => false,
                (false, true) => true,
            }

        }
    }
}

impl PartialEq for LanguageTag {
    fn eq(&self, other: &LanguageTag) -> bool {
        return eq_option_ignore_ascii_case(&self.language, &other.language) &&
        eq_option_ignore_ascii_case(&self.extlang, &other.extlang) &&
        eq_option_ignore_ascii_case(&self.script, &other.script) &&
        eq_option_ignore_ascii_case(&self.region, &other.region) &&
        self.variants.iter().all(|x| other.variants.iter().all(|y| x.eq_ignore_ascii_case(y))) &&
        self.privateuse.len() == other.privateuse.len() &&
        self.privateuse.iter().zip(other.privateuse.iter()).all(|(x, y)| x.eq_ignore_ascii_case(y));

        fn eq_option_ignore_ascii_case(a: &Option<String>, b: &Option<String>) -> bool {
            match (a.is_some(), b.is_some()) {
                (true, true) => a.as_ref().unwrap().eq_ignore_ascii_case(b.as_ref().unwrap()),
                (false, false) => true,
                _ => false,
            }

        }
    }
}

impl Default for LanguageTag {
    fn default() -> LanguageTag {
        LanguageTag {
            language: None,
            extlang: None,
            script: None,
            region: None,
            variants: Vec::new(),
            extensions: BTreeMap::new(),
            privateuse: Vec::new(),
        }
    }
}

impl std::str::FromStr for LanguageTag {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self> {
        let t = s.trim();
        if !is_alphanumeric_or_dash(t)  {
            return Err(Error);
        }
        // Handle grandfathered tags
        if let Some(tag) = GRANDFATHERED_IRREGULAR.iter().find(|x| x.eq_ignore_ascii_case(t)) {
            return Ok(simple_langtag(tag))
        }
        if let Some(tag) = GRANDFATHERED_REGULAR.iter().find(|x| x.eq_ignore_ascii_case(t)) {
            return Ok(simple_langtag(tag))
        }
        // Handle normal tags
        // The parser has a position from 0 to 6. Bigger positions reepresent the ASCII codes of
        // single character extensions
        // language-extlang-script-region-variant-extension-privateuse
        // --- 0 -- -- 1 -- -- 2 - -- 3 - -- 4 -- --- x --- ---- 6 ---
        let mut langtag: LanguageTag = Default::default();
        let mut position: u8 = 0;
        for subtag in t.split('-') {
            if subtag.len() > 8 {
                // > All subtags have a maximum length of eight characters.
                return Err(Error);
            }
            if position == 6 {
                langtag.privateuse.push(subtag.to_owned());
            } else if subtag.eq_ignore_ascii_case("x") {
                position = 6;
            } else if position == 0 {
                // Primary language
                if subtag.len() < 2 || !is_alphabetic(subtag) {
                    return Err(Error)
                }
                langtag.language = Some(subtag.to_owned());
                if subtag.len() < 4 {
                    // Extlangs are only allowed for short language tags
                    position = 1;
                } else {
                    position = 2;
                }
            } else if position == 1 && subtag.len() == 3 && is_alphabetic(subtag) {
                // Extlang
                langtag.extlang = Some(subtag.to_owned());
                position = 2;
            } else if position == 2 && subtag.len() == 3 && is_alphabetic(subtag)
                    && langtag.extlang.is_some() {
                // Multiple extlangs
                let x = [langtag.extlang.unwrap(), subtag.to_owned()].connect("-");
                if x.len() > 11 {
                    // maximum 3 extlangs
                    return Err(Error);
                }
                langtag.extlang = Some(x);
            } else if position <= 2 && subtag.len() == 4 && is_alphabetic(subtag) {
                // Script
                langtag.script = Some(subtag.to_owned());
                position = 3;
            } else if position <= 3 && (subtag.len() == 2 && is_alphabetic(subtag) ||
                    subtag.len() == 3 && is_numeric(subtag)) {
                langtag.region = Some(subtag.to_owned());
                position = 4;
            } else if position <= 4 && (subtag.len() >= 5 && is_alphabetic(&subtag[0..1]) ||
                    subtag.len() >= 4 && is_numeric(&subtag[0..1])) {
                // Variant
                langtag.variants.push(subtag.to_owned());
                position = 4;
            } else if subtag.len() == 1 {
                position = subtag.chars().next().unwrap() as u8;
                if langtag.extensions.contains_key(&position) {
                    return Err(Error);
                }
                langtag.extensions.insert(position, Vec::new());
            } else if position > 6 {
                langtag.extensions.get_mut(&position).unwrap().push(subtag.to_owned());
            } else {
                return Err(Error);
            }
        }
        if langtag.extensions.values().any(|x| x.is_empty()) || position == 6 && langtag.privateuse.is_empty() {
            // Extensions and privateuse must not be empty if present
            return Err(Error);
        }
        return Ok(langtag);

        fn simple_langtag(s: &str) -> LanguageTag {
            let mut x: LanguageTag = Default::default();
            x.language = Some(s.to_owned());
            x
        }
    }
}

impl fmt::Display for LanguageTag {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref x) = self.language {
            try!(Display::fmt(x, f))
        }
        if let Some(ref x) = self.extlang {
            try!(write!(f, "-{}", x));
        }
        if let Some(ref x) = self.script {
            try!(write!(f, "-{}", x));
        }
        if let Some(ref x) = self.region {
            try!(write!(f, "-{}", x));
        }
        for x in self.variants.iter() {
            try!(write!(f, "-{}", x));
        }
        for (raw_key, values) in self.extensions.iter() {
            let mut key = String::new();
            key.push(*raw_key as char);
            try!(write!(f, "-{}", key));
            //parts.push(key);
            for value in values {
                try!(write!(f, "-{}", value));
            }
        }
        if !self.privateuse.is_empty() {
            if self.language.is_none() {
                try!(f.write_str("x"));
            } else {
                try!(f.write_str("-x"));
            }
            for value in self.privateuse.iter() {
                try!(write!(f, "-{}", value));
            }
        }
        Ok(())
    }
}
<commit_msg>docs(all): add docs for all items<commit_after>#![deny(missing_docs)]

//! Language tags can be used identify human languages, scripts e.g. Latin script, countries and
//! other regions.
//!
//! Language tags are defined in [BCP47](http://tools.ietf.org/html/bcp47), an introduction is
//! ["Language tags in HTML and XML"](http://www.w3.org/International/articles/language-tags/) by
//! the W3C. They are commonly used in HTML and HTTP `Content-Language` and `Accept-Language`
//! header fields.
//!
//! # Examples
//! Create a simple language tag representing the French language as spoken
//! in Belgium and print it:
//!
//! ```rust
//! use language_tags::LanguageTag;
//! let mut langtag: LanguageTag = Default::default();
//! langtag.language = Some("fr".to_owned());
//! langtag.region = Some("BE".to_owned());
//! assert_eq!(format!("{}", langtag), "fr-BE");
//! ```
//!
//! Parse a tag representing a special type of English specified by private agreement:
//!
//! ```rust
//! use language_tags::LanguageTag;
//! let langtag: LanguageTag = "en-x-twain".parse().unwrap();
//! assert_eq!(format!("{}", langtag.language.unwrap()), "en");
//! assert_eq!(format!("{:?}", langtag.privateuse), "[\"twain\"]");
//! ```
//!
//! You can check for equality, but more often you should test if two tags match.
//! In this example we check if the resource in German language is suitable for
//! a user from Austria. While people speaking Austrian German normally understand
//! standard German the opposite is not always true. So the resource can be presented
//! to the user but if the resource was in `de-AT` and a user asked for a representation
//! in `de` the request should be rejected.
//!
//! ```rust
//! use language_tags::LanguageTag;
//! let mut langtag1: LanguageTag = Default::default();
//! langtag1.language = Some("de".to_owned());
//! langtag1.region = Some("AT".to_owned());
//! let mut langtag2: LanguageTag = Default::default();
//! langtag2.language = Some("de".to_owned());
//! assert!(langtag2.matches(&langtag1));
//! ```

use std::fmt::{self, Display, Formatter};
use std::str::FromStr;
use std::default::Default;
use std::ascii::AsciiExt;
use std::collections::BTreeMap;

fn is_alphabetic(s: &str) -> bool {
    s.chars().all(|x| x >= 'A' && x <= 'Z' || x >= 'a' && x <= 'z')
}

fn is_numeric(s: &str) -> bool {
    s.chars().all(|x| x >= '0' && x <= '9')
}

fn is_alphanumeric_or_dash(s: &str) -> bool {
    s.chars().all(|x| x >= 'A' && x <= 'Z' || x >= 'a' && x <= 'z' || x >= '0' && x <= '9' || x == '-')
}

/// Defines an Error type for langtags.
// TODO: Extend this to be more specific.
#[derive(Debug, Eq, PartialEq)]
pub struct Error;

/// Result type used for this library.
pub type Result<T> = ::std::result::Result<T, Error>;

/// Contains the 17 irregular old language tags not matching the standard grammer of tags.
pub const GRANDFATHERED_IRREGULAR: [&'static str; 17] = [
    "en-GB-oed",
    "i-ami",
    "i-bnn",
    "i-default",
    "i-enochian",
    "i-hak",
    "i-klingon",
    "i-lux",
    "i-mingo",
    "i-navajo",
    "i-pwn",
    "i-tao",
    "i-tay",
    "i-tsu",
    "sgn-BE-FR",
    "sgn-BE-NL",
    "sgn-CH-DE"];

/// Contains the 9 regular grandfathered tags having special semantics.
pub const GRANDFATHERED_REGULAR: [&'static str; 9] = [
    "art-lojban",
    "cel-gaulish",
    "no-bok",
    "no-nyn",
    "zh-guoyu",
    "zh-hakka",
    "zh-min",
    "zh-min-nan",
    "zh-xiang"];

/// A language tag as described in [BCP47](http://tools.ietf.org/html/bcp47).
///
/// Language tags are used to help identify languages, whether spoken,
/// written, signed, or otherwise signaled, for the purpose of
/// communication.  This includes constructed and artificial languages
/// but excludes languages not intended primarily for human
/// communication, such as programming languages.
#[derive(Debug, Eq, Clone)]
pub struct LanguageTag {
    /// Language subtags are used to indicate the language, ignoring all
    /// other aspects such as script, region or spefic invariants.
    pub language: Option<String>,
    /// Extended language subtags are used to identify certain specially
    /// selected languages that, for various historical and compatibility
    /// reasons, are closely identified with or tagged using an existing
    /// primary language subtag.
    pub extlang: Option<String>,
    /// Script subtags are used to indicate the script or writing system
    /// variations that distinguish the written forms of a language or its
    /// dialects.
    pub script: Option<String>,
    /// Region subtags are used to indicate linguistic variations associated
    /// with or appropriate to a specific country, territory, or region.
    /// Typically, a region subtag is used to indicate variations such as
    /// regional dialects or usage, or region-specific spelling conventions.
    /// It can also be used to indicate that content is expressed in a way
    /// that is appropriate for use throughout a region, for instance,
    /// Spanish content tailored to be useful throughout Latin America.
    pub region: Option<String>,
    /// Variant subtags are used to indicate additional, well-recognized
    /// variations that define a language or its dialects that are not
    /// covered by other available subtags.
    pub variants: Vec<String>,
    /// Extensions provide a mechanism for extending language tags for use in
    /// various applications.  They are intended to identify information that
    /// is commonly used in association with languages or language tags but
    /// that is not part of language identification.
    pub extensions: BTreeMap<u8, Vec<String>>,
    /// Private use subtags are used to indicate distinctions in language
    /// that are important in a given context by private agreement.
    pub privateuse: Vec<String>
}

impl LanguageTag {
    /// Matches language tags like described in
    /// [RFC4647#Extended filtering](https://tools.ietf.org/html/rfc4647#section-3.3.2)
    ///
    /// For example `en-GB` matches only `en-GB` and `en-Arab-GB` but not `en`. While `en` matches
    /// all of `en`, `en-GB` ,`en-Arab` and `en-Arab-GB`.
    pub fn matches(&self, other: &LanguageTag) -> bool {
        return matches_option_ignore_ascii_case(&self.language, &other.language) &&
        matches_option_ignore_ascii_case(&self.extlang, &other.extlang) &&
        matches_option_ignore_ascii_case(&self.script, &other.script) &&
        matches_option_ignore_ascii_case(&self.region, &other.region) &&
        self.variants.iter().all(|x| other.variants.iter().all(|y| x.eq_ignore_ascii_case(y))) &&
        self.privateuse.len() == other.privateuse.len() &&
        self.privateuse.iter().zip(other.privateuse.iter()).all(|(x, y)| x.eq_ignore_ascii_case(y));

        fn matches_option_ignore_ascii_case(a: &Option<String>, b: &Option<String>) -> bool {
            match (a.is_some(), b.is_some()) {
                (true, true) => a.as_ref().unwrap().eq_ignore_ascii_case(b.as_ref().unwrap()),
                (false, false) => true,
                (true, false) => false,
                (false, true) => true,
            }

        }
    }
}

impl PartialEq for LanguageTag {
    fn eq(&self, other: &LanguageTag) -> bool {
        return eq_option_ignore_ascii_case(&self.language, &other.language) &&
        eq_option_ignore_ascii_case(&self.extlang, &other.extlang) &&
        eq_option_ignore_ascii_case(&self.script, &other.script) &&
        eq_option_ignore_ascii_case(&self.region, &other.region) &&
        self.variants.iter().all(|x| other.variants.iter().all(|y| x.eq_ignore_ascii_case(y))) &&
        self.privateuse.len() == other.privateuse.len() &&
        self.privateuse.iter().zip(other.privateuse.iter()).all(|(x, y)| x.eq_ignore_ascii_case(y));

        fn eq_option_ignore_ascii_case(a: &Option<String>, b: &Option<String>) -> bool {
            match (a.is_some(), b.is_some()) {
                (true, true) => a.as_ref().unwrap().eq_ignore_ascii_case(b.as_ref().unwrap()),
                (false, false) => true,
                _ => false,
            }

        }
    }
}

impl Default for LanguageTag {
    fn default() -> LanguageTag {
        LanguageTag {
            language: None,
            extlang: None,
            script: None,
            region: None,
            variants: Vec::new(),
            extensions: BTreeMap::new(),
            privateuse: Vec::new(),
        }
    }
}

impl std::str::FromStr for LanguageTag {
    type Err = Error;
    fn from_str(s: &str) -> Result<Self> {
        let t = s.trim();
        if !is_alphanumeric_or_dash(t)  {
            return Err(Error);
        }
        // Handle grandfathered tags
        if let Some(tag) = GRANDFATHERED_IRREGULAR.iter().find(|x| x.eq_ignore_ascii_case(t)) {
            return Ok(simple_langtag(tag))
        }
        if let Some(tag) = GRANDFATHERED_REGULAR.iter().find(|x| x.eq_ignore_ascii_case(t)) {
            return Ok(simple_langtag(tag))
        }
        // Handle normal tags
        // The parser has a position from 0 to 6. Bigger positions reepresent the ASCII codes of
        // single character extensions
        // language-extlang-script-region-variant-extension-privateuse
        // --- 0 -- -- 1 -- -- 2 - -- 3 - -- 4 -- --- x --- ---- 6 ---
        let mut langtag: LanguageTag = Default::default();
        let mut position: u8 = 0;
        for subtag in t.split('-') {
            if subtag.len() > 8 {
                // > All subtags have a maximum length of eight characters.
                return Err(Error);
            }
            if position == 6 {
                langtag.privateuse.push(subtag.to_owned());
            } else if subtag.eq_ignore_ascii_case("x") {
                position = 6;
            } else if position == 0 {
                // Primary language
                if subtag.len() < 2 || !is_alphabetic(subtag) {
                    return Err(Error)
                }
                langtag.language = Some(subtag.to_owned());
                if subtag.len() < 4 {
                    // Extlangs are only allowed for short language tags
                    position = 1;
                } else {
                    position = 2;
                }
            } else if position == 1 && subtag.len() == 3 && is_alphabetic(subtag) {
                // Extlang
                langtag.extlang = Some(subtag.to_owned());
                position = 2;
            } else if position == 2 && subtag.len() == 3 && is_alphabetic(subtag)
                    && langtag.extlang.is_some() {
                // Multiple extlangs
                let x = [langtag.extlang.unwrap(), subtag.to_owned()].connect("-");
                if x.len() > 11 {
                    // maximum 3 extlangs
                    return Err(Error);
                }
                langtag.extlang = Some(x);
            } else if position <= 2 && subtag.len() == 4 && is_alphabetic(subtag) {
                // Script
                langtag.script = Some(subtag.to_owned());
                position = 3;
            } else if position <= 3 && (subtag.len() == 2 && is_alphabetic(subtag) ||
                    subtag.len() == 3 && is_numeric(subtag)) {
                langtag.region = Some(subtag.to_owned());
                position = 4;
            } else if position <= 4 && (subtag.len() >= 5 && is_alphabetic(&subtag[0..1]) ||
                    subtag.len() >= 4 && is_numeric(&subtag[0..1])) {
                // Variant
                langtag.variants.push(subtag.to_owned());
                position = 4;
            } else if subtag.len() == 1 {
                position = subtag.chars().next().unwrap() as u8;
                if langtag.extensions.contains_key(&position) {
                    return Err(Error);
                }
                langtag.extensions.insert(position, Vec::new());
            } else if position > 6 {
                langtag.extensions.get_mut(&position).unwrap().push(subtag.to_owned());
            } else {
                return Err(Error);
            }
        }
        if langtag.extensions.values().any(|x| x.is_empty()) || position == 6 && langtag.privateuse.is_empty() {
            // Extensions and privateuse must not be empty if present
            return Err(Error);
        }
        return Ok(langtag);

        fn simple_langtag(s: &str) -> LanguageTag {
            let mut x: LanguageTag = Default::default();
            x.language = Some(s.to_owned());
            x
        }
    }
}

impl fmt::Display for LanguageTag {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref x) = self.language {
            try!(Display::fmt(x, f))
        }
        if let Some(ref x) = self.extlang {
            try!(write!(f, "-{}", x));
        }
        if let Some(ref x) = self.script {
            try!(write!(f, "-{}", x));
        }
        if let Some(ref x) = self.region {
            try!(write!(f, "-{}", x));
        }
        for x in self.variants.iter() {
            try!(write!(f, "-{}", x));
        }
        for (raw_key, values) in self.extensions.iter() {
            let mut key = String::new();
            key.push(*raw_key as char);
            try!(write!(f, "-{}", key));
            for value in values {
                try!(write!(f, "-{}", value));
            }
        }
        if !self.privateuse.is_empty() {
            if self.language.is_none() {
                try!(f.write_str("x"));
            } else {
                try!(f.write_str("-x"));
            }
            for value in self.privateuse.iter() {
                try!(write!(f, "-{}", value));
            }
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>#![feature(collections,io)]

/// Implements the XTEA block cipher, whose reference source is public
/// domain.  This code is also public domain.
///
/// Also implements a CBC-mode block cipher with padding.  I'm not
/// good at crypto so don't use this.

/// A key is 128 bits.  We don't seem to need SIMD anywhere so it's
/// just an array.
pub type Key = [u32; 4];

/// XTEA uses 64-bit blocks; for simplicity and to match the reference
/// source, we use an array here too.
pub type Block = [u32; 2];

static NUM_ROUNDS: u32 = 32;

/// Encrypts 64 bits of input using the key.
pub fn encipher(key: &Key, input: &Block) -> Block {
    // Reference source:
    // unsigned int i;
    // uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
    // for (i=0; i < num_rounds; i++) {
    //     v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    //     sum += delta;
    //     v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    // }
    // v[0]=v0; v[1]=v1;
    let [mut v0, mut v1] = *input;
    let delta = 0x9E3779B9;
    let mut sum: u32 = 0;
    for _ in 0..NUM_ROUNDS {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[(sum & 3) as usize]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[((sum>>11) & 3) as usize])
    }
    [v0, v1]
}

/// Decrypts 64 bits of input using the key.
pub fn decipher(key: &Key, input: &Block) -> Block {
    // Reference source:
    // unsigned int i;
    // uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
    // for (i=0; i < num_rounds; i++) {
    //     v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    //     sum -= delta;
    //     v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    // }
    // v[0]=v0; v[1]=v1;
    let [mut v0, mut v1] = *input;
    let delta = 0x9E3779B9;
    let mut sum = delta * NUM_ROUNDS;
    for _ in 0..NUM_ROUNDS {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[((sum>>11) & 3) as usize]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[(sum & 3) as usize]);
    }
    [v0, v1]
}

use std::io;
use std::mem;
use std::ptr;

fn read_block(chunk: &[u8]) -> Block {
    assert_eq!(chunk.len(), 8);
    unsafe {
        let mut block: Block = mem::uninitialized();
        let b0 = block.get_unchecked_mut(0) as *mut u32;
        let input = chunk.get_unchecked(0) as *const u8;
        ptr::copy_nonoverlapping(b0 as *mut u8, input, 8);
        block
    }
}

fn write_block(block: &Block) -> [u8; 8] {
    unsafe { mem::transmute(*block) }
}

/// Adapts encipher and decipher into a block cipher using CBC.
pub struct BlockCipher {
    key: Key,
    prev: Block,
}

impl BlockCipher {

    /// Creates an XTEA BlockCipher with a key and an initialization
    /// vector.
    pub fn new(key: Key, iv: Block) -> BlockCipher {
        BlockCipher{key: key, prev: iv}
    }

    fn encrypt_chunk(&mut self, chunk: &[u8], output: &mut io::Write) {
        let mut input_block: Block = read_block(chunk);
        input_block[0] ^= self.prev[0];
        input_block[1] ^= self.prev[1];
        self.prev = encipher(&self.key, &input_block);
        output.write_all(&write_block(&self.prev)).ok().unwrap();
    }

    /// Reads bytes from `input` until it's consumed, and writes the
    /// encrypted bytes to `output`.  Will pad to an 8-byte boundary.
    pub fn encrypt(&mut self, input: &mut io::BufRead, output: &mut io::Write) {
        let mut consumed;
        loop {
            consumed = 0;
            {
                let bytes = input.fill_buf().ok().unwrap();
                if bytes.len() < 8 {
                    let mut vec = bytes.to_vec();
                    vec.resize(8, 8 - bytes.len() as u8);
                    self.encrypt_chunk(&vec, output);
                    consumed = bytes.len();
                    break;
                }
                for chunk in bytes.chunks(8) {
                    if chunk.len() < 8 {
                        break;
                    }
                    self.encrypt_chunk(&chunk, output);
                    consumed += 8;
                }
            }
            input.consume(consumed);
        }
        input.consume(consumed);
    }

    fn decrypt_chunk(&mut self, chunk: &[u8]) -> [u8; 8] {
        let input_block: Block = read_block(chunk);
        let mut decrypted_block = decipher(&self.key, &input_block);
        decrypted_block[0] ^= self.prev[0];
        decrypted_block[1] ^= self.prev[1];
        self.prev = input_block;
        write_block(&decrypted_block)
    }

    /// Reads encrypted bytes from `input` until it's consumed, and
    /// writes the decrypted bytes to `output`.  If the input needed
    /// to be padded, the result will have trailing zeroes.
    pub fn decrypt(&mut self, input: &mut io::BufRead, output: &mut io::Write) {
        let mut last_chunk: [u8; 8] = [0u8; 8];
        let mut last_chunk_initialized = false;
        loop {
            let mut consumed = 0;
            {
                let bytes = input.fill_buf().ok().unwrap();
                if bytes.len() == 0 {
                    break;
                }
                assert!(bytes.len() >= 8);
                for chunk in bytes.chunks(8) {
                    if chunk.len() < 8 {
                        break;
                    }
                    if last_chunk_initialized {
                        output.write_all(&last_chunk).ok().unwrap();
                    }
                    last_chunk = self.decrypt_chunk(&chunk);
                    last_chunk_initialized = true;
                    consumed += 8;
                }
            };
            input.consume(consumed);
        }
        if last_chunk_initialized {
            let padded = last_chunk[7];
            if padded < 8 {
                output.write_all(&last_chunk[..(8 - padded) as usize]).ok().unwrap();
            }
        }
    }

}

#[test]
fn tea_works() {
    let key: Key = [10, 20, 30, 42];
    let plaintext: Block = [300, 400];
    let ciphertext = encipher(&key, &plaintext);
    assert!(plaintext != ciphertext);
    assert_eq!(plaintext, decipher(&key, &ciphertext));
}

#[test]
fn cipher_works() {
    let mut cipher = BlockCipher::new([123, 456, 789, 1011], [867, 5309]);
    let input: Vec<u8> = (0u8..128).collect();

    let mut crypted_cursor: io::Cursor<Vec<u8>> = io::Cursor::new(Vec::new());
    cipher.encrypt(&mut io::Cursor::new(input.clone()), &mut crypted_cursor);
    let crypted = crypted_cursor.into_inner();

    assert_eq!(input.len() + 8, crypted.len());
    assert!(input != crypted);

    // reset cipher iv
    cipher = BlockCipher::new([123, 456, 789, 1011], [867, 5309]);

    let mut decrypted_cursor: io::Cursor<Vec<u8>> = io::Cursor::new(Vec::new());
    cipher.decrypt(&mut io::Cursor::new(crypted.clone()), &mut decrypted_cursor);
    let decrypted = decrypted_cursor.into_inner();
    assert_eq!(input, decrypted);
}

#[test]
fn cipher_padding() {
    let mut cipher = BlockCipher::new([123, 456, 789, 1011], [867, 5309]);
    let input: Vec<u8> = (0u8..123).collect();

    let mut crypted_cursor: io::Cursor<Vec<u8>> = io::Cursor::new(Vec::new());
    cipher.encrypt(&mut io::Cursor::new(input.clone()), &mut crypted_cursor);
    let crypted = crypted_cursor.into_inner();

    assert_eq!(input.len() + 5, crypted.len());
    assert!(input != crypted);

    // reset cipher iv
    cipher = BlockCipher::new([123, 456, 789, 1011], [867, 5309]);

    let mut decrypted_cursor: io::Cursor<Vec<u8>> = io::Cursor::new(Vec::new());
    cipher.decrypt(&mut io::Cursor::new(crypted.clone()), &mut decrypted_cursor);
    let decrypted = decrypted_cursor.into_inner();
    assert_eq!(input, decrypted);
}
<commit_msg>fixed comments about padding<commit_after>#![feature(collections,io)]

/// Implements the XTEA block cipher, whose reference source is public
/// domain.  This code is also public domain.
///
/// Also implements a CBC-mode block cipher with padding.  I'm not
/// good at crypto so don't use this.

/// A key is 128 bits.  We don't seem to need SIMD anywhere so it's
/// just an array.
pub type Key = [u32; 4];

/// XTEA uses 64-bit blocks; for simplicity and to match the reference
/// source, we use an array here too.
pub type Block = [u32; 2];

static NUM_ROUNDS: u32 = 32;

/// Encrypts 64 bits of input using the key.
pub fn encipher(key: &Key, input: &Block) -> Block {
    // Reference source:
    // unsigned int i;
    // uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
    // for (i=0; i < num_rounds; i++) {
    //     v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    //     sum += delta;
    //     v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    // }
    // v[0]=v0; v[1]=v1;
    let [mut v0, mut v1] = *input;
    let delta = 0x9E3779B9;
    let mut sum: u32 = 0;
    for _ in 0..NUM_ROUNDS {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[(sum & 3) as usize]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[((sum>>11) & 3) as usize])
    }
    [v0, v1]
}

/// Decrypts 64 bits of input using the key.
pub fn decipher(key: &Key, input: &Block) -> Block {
    // Reference source:
    // unsigned int i;
    // uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
    // for (i=0; i < num_rounds; i++) {
    //     v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
    //     sum -= delta;
    //     v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    // }
    // v[0]=v0; v[1]=v1;
    let [mut v0, mut v1] = *input;
    let delta = 0x9E3779B9;
    let mut sum = delta * NUM_ROUNDS;
    for _ in 0..NUM_ROUNDS {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[((sum>>11) & 3) as usize]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[(sum & 3) as usize]);
    }
    [v0, v1]
}

use std::io;
use std::mem;
use std::ptr;

fn read_block(chunk: &[u8]) -> Block {
    assert_eq!(chunk.len(), 8);
    unsafe {
        let mut block: Block = mem::uninitialized();
        let b0 = block.get_unchecked_mut(0) as *mut u32;
        let input = chunk.get_unchecked(0) as *const u8;
        ptr::copy_nonoverlapping(b0 as *mut u8, input, 8);
        block
    }
}

fn write_block(block: &Block) -> [u8; 8] {
    unsafe { mem::transmute(*block) }
}

/// Adapts encipher and decipher into a block cipher using CBC.
pub struct BlockCipher {
    key: Key,
    prev: Block,
}

impl BlockCipher {

    /// Creates an XTEA BlockCipher with a key and an initialization
    /// vector.
    pub fn new(key: Key, iv: Block) -> BlockCipher {
        BlockCipher{key: key, prev: iv}
    }

    fn encrypt_chunk(&mut self, chunk: &[u8], output: &mut io::Write) {
        let mut input_block: Block = read_block(chunk);
        input_block[0] ^= self.prev[0];
        input_block[1] ^= self.prev[1];
        self.prev = encipher(&self.key, &input_block);
        output.write_all(&write_block(&self.prev)).ok().unwrap();
    }

    /// Reads bytes from `input` until it's consumed, and writes the
    /// encrypted bytes to `output`.  Will pad to an 8-byte boundary
    /// with PKCS#7.
    pub fn encrypt(&mut self, input: &mut io::BufRead, output: &mut io::Write) {
        let mut consumed;
        loop {
            consumed = 0;
            {
                let bytes = input.fill_buf().ok().unwrap();
                if bytes.len() < 8 {
                    let mut vec = bytes.to_vec();
                    vec.resize(8, 8 - bytes.len() as u8);
                    self.encrypt_chunk(&vec, output);
                    consumed = bytes.len();
                    break;
                }
                for chunk in bytes.chunks(8) {
                    if chunk.len() < 8 {
                        break;
                    }
                    self.encrypt_chunk(&chunk, output);
                    consumed += 8;
                }
            }
            input.consume(consumed);
        }
        input.consume(consumed);
    }

    fn decrypt_chunk(&mut self, chunk: &[u8]) -> [u8; 8] {
        let input_block: Block = read_block(chunk);
        let mut decrypted_block = decipher(&self.key, &input_block);
        decrypted_block[0] ^= self.prev[0];
        decrypted_block[1] ^= self.prev[1];
        self.prev = input_block;
        write_block(&decrypted_block)
    }

    /// Reads encrypted bytes from `input` until it's consumed, and
    /// writes the decrypted bytes to `output`.
    pub fn decrypt(&mut self, input: &mut io::BufRead, output: &mut io::Write) {
        let mut last_chunk: [u8; 8] = [0u8; 8];
        let mut last_chunk_initialized = false;
        loop {
            let mut consumed = 0;
            {
                let bytes = input.fill_buf().ok().unwrap();
                if bytes.len() == 0 {
                    break;
                }
                assert!(bytes.len() >= 8);
                for chunk in bytes.chunks(8) {
                    if chunk.len() < 8 {
                        break;
                    }
                    if last_chunk_initialized {
                        output.write_all(&last_chunk).ok().unwrap();
                    }
                    last_chunk = self.decrypt_chunk(&chunk);
                    last_chunk_initialized = true;
                    consumed += 8;
                }
            };
            input.consume(consumed);
        }
        if last_chunk_initialized {
            let padded = last_chunk[7];
            if padded < 8 {
                output.write_all(&last_chunk[..(8 - padded) as usize]).ok().unwrap();
            }
        }
    }

}

#[test]
fn tea_works() {
    let key: Key = [10, 20, 30, 42];
    let plaintext: Block = [300, 400];
    let ciphertext = encipher(&key, &plaintext);
    assert!(plaintext != ciphertext);
    assert_eq!(plaintext, decipher(&key, &ciphertext));
}

#[test]
fn cipher_works() {
    let mut cipher = BlockCipher::new([123, 456, 789, 1011], [867, 5309]);
    let input: Vec<u8> = (0u8..128).collect();

    let mut crypted_cursor: io::Cursor<Vec<u8>> = io::Cursor::new(Vec::new());
    cipher.encrypt(&mut io::Cursor::new(input.clone()), &mut crypted_cursor);
    let crypted = crypted_cursor.into_inner();

    assert_eq!(input.len() + 8, crypted.len());
    assert!(input != crypted);

    // reset cipher iv
    cipher = BlockCipher::new([123, 456, 789, 1011], [867, 5309]);

    let mut decrypted_cursor: io::Cursor<Vec<u8>> = io::Cursor::new(Vec::new());
    cipher.decrypt(&mut io::Cursor::new(crypted.clone()), &mut decrypted_cursor);
    let decrypted = decrypted_cursor.into_inner();
    assert_eq!(input, decrypted);
}

#[test]
fn cipher_padding() {
    let mut cipher = BlockCipher::new([123, 456, 789, 1011], [867, 5309]);
    let input: Vec<u8> = (0u8..123).collect();

    let mut crypted_cursor: io::Cursor<Vec<u8>> = io::Cursor::new(Vec::new());
    cipher.encrypt(&mut io::Cursor::new(input.clone()), &mut crypted_cursor);
    let crypted = crypted_cursor.into_inner();

    assert_eq!(input.len() + 5, crypted.len());
    assert!(input != crypted);

    // reset cipher iv
    cipher = BlockCipher::new([123, 456, 789, 1011], [867, 5309]);

    let mut decrypted_cursor: io::Cursor<Vec<u8>> = io::Cursor::new(Vec::new());
    cipher.decrypt(&mut io::Cursor::new(crypted.clone()), &mut decrypted_cursor);
    let decrypted = decrypted_cursor.into_inner();
    assert_eq!(input, decrypted);
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}

pub mod houselights {
    extern crate sacn;
    use self::sacn::DmxSource;

    const UNIVERSE_SIZE: usize = 510;

    const GAMMA: f32 = 2.2;
    const GC_BOTTOM_THRESHOLD: u16 = 20;

    #[derive(Clone,Debug)]
    pub struct RGB {
        pub red:   u8,
        pub green: u8,
        pub blue:  u8
    }

    #[derive(Clone,Debug)]
    pub struct Zone  {
        pub head: u8,
        pub body: u8,
        pub tail: u8,
        pub name: String
    }

    // return RGB for a given color temperature
    pub fn kelvin (mut temp: u16) -> RGB {
        // http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
        temp /= 100;

        let mut rgb: RGB = RGB { red: 0, green: 0, blue: 0 };
        // calculate red
        if temp <= 66 {
            rgb.red = 255;
        } else {
            let red: f32 = (temp - 60) as f32;
            rgb.red = _normalize_value((329.698727446 * red.powf(-0.1332047592)).round());
        }
        // calculate green
        if temp <= 66 {
            let green: f32 = temp as f32;
            rgb.green = _normalize_value((99.4708025861 * green.ln() - 161.1195681661).round());
        } else {
            let green: f32 = (temp - 60) as f32;
            rgb.green = _normalize_value((288.1221695283 * green.powf(-0.0755148492)).round())
        }
        // calculate blue
        if temp >= 66 {
            rgb.blue = 255;
        } else {
            if temp <= 19 {
                rgb.blue = 0;
            } else {
                let blue: f32 = (temp - 10) as f32;
                rgb.blue = _normalize_value((138.5177312231 * blue.ln() - 305.0447927307).round());
            }
        }
        return rgb;
    }

    fn _normalize_value (value: f32) -> u8 {
        if value < 0_f32 {
            return 0_u8;
        }
        if value > 255_f32 {
            return 255_u8;
        }
        return value as u8;
    }

    pub fn scale_rgb(rgb: RGB, intensity: f32, max_intensity: f32) -> RGB {
        let i: f32 = intensity * max_intensity as f32;
        let scaled: RGB = RGB {
            red:   (rgb.red   as f32 * i).round() as u8,
            green: (rgb.green as f32 * i).round() as u8,
            blue:  (rgb.blue  as f32 * i).round() as u8
        };
        return scaled;
    }

    pub fn gamma_correct(rgb: &RGB) -> RGB {
        let mut c: RGB = RGB {
            red:   (255_f32 * (rgb.red   as f32 / 255_f32).powf(GAMMA)) as u8,
            green: (255_f32 * (rgb.green as f32 / 255_f32).powf(GAMMA)) as u8,
            blue:  (255_f32 * (rgb.blue  as f32 / 255_f32).powf(GAMMA)) as u8
        };
        // drop to dark if sum of all thre falls below threshold
        // trying to avoid bottoming out to dim red or green when I want white
        if (c.red as u16 + c.green as u16 + c.blue as u16) < GC_BOTTOM_THRESHOLD {
            c.red   = 0;
            c.green = 0;
            c.blue  = 0;
        }
        return c;
    }

    pub fn render( lights: &[RGB], zones: &[Zone], dmx: &DmxSource ) {
        let spliced = splice_null_pixels(lights, zones);
        let mut out: Vec<u8> = vec![];
        for rgb in spliced.iter() {
            let gc = gamma_correct(rgb);
            out.push(gc.red);
            out.push(gc.green);
            out.push(gc.blue);
        }
        let mut universes = Vec::new();
        while out.len() > UNIVERSE_SIZE {
            let u = out.split_off(UNIVERSE_SIZE);
            universes.push(out);
            out = u;
        }
        universes.push(out);
        let mut universe: u16 = 1;
        for u in universes {
            let _res = dmx.send(universe, &u);
            universe += 1;
        }
    }

    fn splice_null_pixels( lights: &[RGB], zones: &[Zone] ) -> Vec<RGB> {
        let mut copy: Vec<RGB> = vec![];
        copy.extend_from_slice(lights);
        let mut idx: usize = 0;
        for zone in zones {
            // null pixels at the head of the zone
            for _i in 0..zone.head {
                copy.insert(idx, RGB { red: 0, green: 0, blue: 0 });
            }
            // and at the tail
            idx += zone.head as usize + zone.body as usize;
            for _i in 0..zone.tail {
                copy.insert(idx, RGB { red: 0, green: 0, blue: 0 });
            }
            idx += zone.tail as usize;
        }
        return copy;
    }
}
<commit_msg>working on encapsulation<commit_after>#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
    }
}

pub mod houselights {
    extern crate sacn;
    use self::sacn::DmxSource;

    const UNIVERSE_SIZE: usize = 510;

    const GAMMA: f32 = 2.2;
    const GC_BOTTOM_THRESHOLD: u16 = 20;

    #[derive(Clone,Debug)]
    pub struct RGB {
        pub red:   u8,
        pub green: u8,
        pub blue:  u8
    }
    impl RGB {
        pub fn null() -> RGB {
            RGB { red: 0, green: 0, blue: 0 }
        }
    }

    #[derive(Clone,Debug)]
    pub struct Zone  {
        pub head: u8,
        pub body: u8,
        pub tail: u8,
        pub name: String
    }

    pub struct Dmx {
        source: DmxSource
    }
    impl Dmx {
        pub fn new() -> Dmx {
            Dmx { source: DmxSource::new("Controller").unwrap() }
        }
    }
    impl Drop for Dmx {
        fn drop(&mut self) {
            let _res = self.source.terminate_stream(1);
        }
    }

    // return RGB for a given color temperature
    pub fn kelvin (mut temp: u16) -> RGB {
        // http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
        temp /= 100;

        let mut rgb: RGB = RGB::null();
        // calculate red
        if temp <= 66 {
            rgb.red = 255;
        } else {
            let red: f32 = (temp - 60) as f32;
            rgb.red = _normalize_value((329.698727446 * red.powf(-0.1332047592)).round());
        }
        // calculate green
        if temp <= 66 {
            let green: f32 = temp as f32;
            rgb.green = _normalize_value((99.4708025861 * green.ln() - 161.1195681661).round());
        } else {
            let green: f32 = (temp - 60) as f32;
            rgb.green = _normalize_value((288.1221695283 * green.powf(-0.0755148492)).round())
        }
        // calculate blue
        if temp >= 66 {
            rgb.blue = 255;
        } else {
            if temp <= 19 {
                rgb.blue = 0;
            } else {
                let blue: f32 = (temp - 10) as f32;
                rgb.blue = _normalize_value((138.5177312231 * blue.ln() - 305.0447927307).round());
            }
        }
        return rgb;
    }

    fn _normalize_value (value: f32) -> u8 {
        if value < 0_f32 {
            return 0_u8;
        }
        if value > 255_f32 {
            return 255_u8;
        }
        return value as u8;
    }

    pub fn scale_rgb(rgb: RGB, intensity: f32, max_intensity: f32) -> RGB {
        let i: f32 = intensity * max_intensity as f32;
        let scaled: RGB = RGB {
            red:   (rgb.red   as f32 * i).round() as u8,
            green: (rgb.green as f32 * i).round() as u8,
            blue:  (rgb.blue  as f32 * i).round() as u8
        };
        return scaled;
    }

    pub fn gamma_correct(rgb: &RGB) -> RGB {
        let mut c: RGB = RGB {
            red:   (255_f32 * (rgb.red   as f32 / 255_f32).powf(GAMMA)) as u8,
            green: (255_f32 * (rgb.green as f32 / 255_f32).powf(GAMMA)) as u8,
            blue:  (255_f32 * (rgb.blue  as f32 / 255_f32).powf(GAMMA)) as u8
        };
        // drop to dark if sum of all thre falls below threshold
        // trying to avoid bottoming out to dim red or green when I want white
        if (c.red as u16 + c.green as u16 + c.blue as u16) < GC_BOTTOM_THRESHOLD {
            c.red   = 0;
            c.green = 0;
            c.blue  = 0;
        }
        return c;
    }

    pub fn render( lights: &[RGB], zones: &[Zone], dmx: &Dmx ) {
        let spliced = splice_null_pixels(lights, zones);
        let mut out: Vec<u8> = vec![];
        for rgb in spliced.iter() {
            let gc = gamma_correct(rgb);
            out.push(gc.red);
            out.push(gc.green);
            out.push(gc.blue);
        }
        let mut universes = Vec::new();
        while out.len() > UNIVERSE_SIZE {
            let u = out.split_off(UNIVERSE_SIZE);
            universes.push(out);
            out = u;
        }
        universes.push(out);
        let mut universe: u16 = 1;
        for u in universes {
            let _res = dmx.source.send(universe, &u);
            universe += 1;
        }
    }

    fn splice_null_pixels( lights: &[RGB], zones: &[Zone] ) -> Vec<RGB> {
        let mut copy: Vec<RGB> = vec![];
        copy.extend_from_slice(lights);
        let mut idx: usize = 0;
        for zone in zones {
            // null pixels at the head of the zone
            for _i in 0..zone.head {
                copy.insert(idx, RGB::null());
            }
            // and at the tail
            idx += zone.head as usize + zone.body as usize;
            for _i in 0..zone.tail {
                copy.insert(idx, RGB::null());
            }
            idx += zone.tail as usize;
        }
        return copy;
    }
}
<|endoftext|>


<commit_before>/* vim: set et: */

#![feature(macro_rules)]

extern crate url;
extern crate "rustc-serialize" as rustc_serialize;
<commit_msg>Added mods back to lib.rs to actually run the tests<commit_after>/* vim: set et: */

#![feature(macro_rules)]

extern crate url;
extern crate "rustc-serialize" as rustc_serialize;

mod types;
mod urls;
<|endoftext|>


<commit_before>extern crate serde;
extern crate serde_json;
extern crate serde_yaml;

use serde_json::Value as JsonValue;
use serde_json::Error as JsonError;
use serde_yaml::Value as YamlValue;
use serde_yaml::Error as YamlError;
use std::num::ParseFloatError;
use std::collections::BTreeMap;
use serde::de::Deserialize;

pub enum Error {
    ParseFloat(ParseFloatError),
    InvalidValue,
    Json(JsonError),
    Yaml(YamlError),
}

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

impl From<YamlError> for Error {
    fn from(error: YamlError) -> Error {
        Error::Yaml(error)
    }
}

impl From<JsonError> for Error {
    fn from(error: JsonError) -> Error {
        Error::Json(error)
    }
}
impl From<ParseFloatError> for Error {
    fn from(error: ParseFloatError) -> Error {
        Error::ParseFloat(error)
    }
}

pub fn from_str<T>(s: &str) -> Result<T>
    where T: Deserialize
{
    if s.starts_with("{") || s.starts_with("[") {
        let value = try!(serde_json::from_str(s));
        Ok(value)
    } else {
        let value = try!(serde_yaml::from_str(s));
        Ok(value)
    }

}

pub fn to_yaml(json: &JsonValue) -> Result<YamlValue> {
    match json {
        &JsonValue::Null => Ok(YamlValue::Null),
        &JsonValue::Bool(ref value) => Ok(YamlValue::Boolean(value.clone())),
        &JsonValue::I64(ref value) => Ok(YamlValue::Integer(value.clone())),
        &JsonValue::U64(ref value) => Ok(YamlValue::Integer(value.clone() as i64)),
        &JsonValue::F64(ref value) => Ok(YamlValue::Real(value.to_string())),
        &JsonValue::String(ref value) => Ok(YamlValue::String(value.clone())),
        &JsonValue::Array(ref values) => {
            let mut yaml_values = vec![];
            for value in values {
                yaml_values.push(try!(to_yaml(&value)));
            }
            Ok(YamlValue::Array(yaml_values))
        }
        &JsonValue::Object(ref value) => {
            let mut yaml_value = BTreeMap::new();
            for (k, v) in value {
                let yaml = try!(to_yaml(&v));
                yaml_value.insert(YamlValue::String(k.clone()), yaml);
            }
            Ok(YamlValue::Hash(yaml_value))
        }
    }
}

pub fn to_json(yaml: &YamlValue) -> Result<JsonValue> {
    match yaml {
        &YamlValue::Real(ref value) => Ok(JsonValue::F64(try!(value.parse::<f64>()))),
        &YamlValue::Integer(ref value) => Ok(JsonValue::I64(value.clone())),
        &YamlValue::String(ref value) => Ok(JsonValue::String(value.clone())),
        &YamlValue::Boolean(ref value) => Ok(JsonValue::Bool(value.clone())),
        &YamlValue::Array(ref values) => {
            let mut json_values = vec![];
            for value in values {
                let json = try!(to_json(&value));
                json_values.push(json);
            }
            Ok(JsonValue::Array(json_values))
        }
        &YamlValue::Hash(ref value) => {
            let mut json_value = BTreeMap::new();
            for (k, v) in value {
                match k {
                    &YamlValue::String(ref key) => {
                        let json = try!(to_json(&v));
                        json_value.insert(key.clone(), json);
                    }
                    _ => return Err(Error::InvalidValue),
                }
            }
            Ok(JsonValue::Object(json_value))
        }
        &YamlValue::Alias(_) => Err(Error::InvalidValue), // not supported yet
        &YamlValue::Null => Ok(JsonValue::Null),
        &YamlValue::BadValue => Err(Error::InvalidValue),

    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {}
}
<commit_msg>simple test<commit_after>extern crate serde;
extern crate serde_json;
extern crate serde_yaml;

use serde_json::Value as JsonValue;
use serde_json::Error as JsonError;
use serde_yaml::Value as YamlValue;
use serde_yaml::Error as YamlError;
use std::num::ParseFloatError;
use std::collections::BTreeMap;
use serde::de::Deserialize;

#[derive(Debug)]
pub enum Error {
    ParseFloat(ParseFloatError),
    InvalidValue,
    Json(JsonError),
    Yaml(YamlError),
}

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

impl From<YamlError> for Error {
    fn from(error: YamlError) -> Error {
        Error::Yaml(error)
    }
}

impl From<JsonError> for Error {
    fn from(error: JsonError) -> Error {
        Error::Json(error)
    }
}

impl From<ParseFloatError> for Error {
    fn from(error: ParseFloatError) -> Error {
        Error::ParseFloat(error)
    }
}

pub fn to_yaml(json: &JsonValue) -> Result<YamlValue> {
    match json {
        &JsonValue::Null => Ok(YamlValue::Null),
        &JsonValue::Bool(ref value) => Ok(YamlValue::Boolean(value.clone())),
        &JsonValue::I64(ref value) => Ok(YamlValue::Integer(value.clone())),
        &JsonValue::U64(ref value) => Ok(YamlValue::Integer(value.clone() as i64)),
        &JsonValue::F64(ref value) => Ok(YamlValue::Real(value.to_string())),
        &JsonValue::String(ref value) => Ok(YamlValue::String(value.clone())),
        &JsonValue::Array(ref values) => {
            let mut yaml_values = vec![];
            for value in values {
                yaml_values.push(try!(to_yaml(&value)));
            }
            Ok(YamlValue::Array(yaml_values))
        }
        &JsonValue::Object(ref value) => {
            let mut yaml_value = BTreeMap::new();
            for (k, v) in value {
                let yaml = try!(to_yaml(&v));
                yaml_value.insert(YamlValue::String(k.clone()), yaml);
            }
            Ok(YamlValue::Hash(yaml_value))
        }
    }
}

pub fn to_json(yaml: &YamlValue) -> Result<JsonValue> {
    match yaml {
        &YamlValue::Real(ref value) => Ok(JsonValue::F64(try!(value.parse::<f64>()))),
        &YamlValue::Integer(ref value) => Ok(JsonValue::I64(value.clone())),
        &YamlValue::String(ref value) => Ok(JsonValue::String(value.clone())),
        &YamlValue::Boolean(ref value) => Ok(JsonValue::Bool(value.clone())),
        &YamlValue::Array(ref values) => {
            let mut json_values = vec![];
            for value in values {
                let json = try!(to_json(&value));
                json_values.push(json);
            }
            Ok(JsonValue::Array(json_values))
        }
        &YamlValue::Hash(ref value) => {
            let mut json_value = BTreeMap::new();
            for (k, v) in value {
                match k {
                    &YamlValue::String(ref key) => {
                        let json = try!(to_json(&v));
                        json_value.insert(key.clone(), json);
                    }
                    _ => return Err(Error::InvalidValue),
                }
            }
            Ok(JsonValue::Object(json_value))
        }
        &YamlValue::Alias(_) => Err(Error::InvalidValue), // not supported yet
        &YamlValue::Null => Ok(JsonValue::Null),
        &YamlValue::BadValue => Err(Error::InvalidValue),
    }
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;
    use super::serde_json;
    use super::serde_yaml;
    use super::{to_json, to_yaml};

    #[test]
    fn json_to_yaml() {
        let input = serde_json::to_value(&vec!["foo"]);
        let output = serde_yaml::Value::Array(vec![serde_yaml::Value::String("foo".to_owned())]);
        assert_eq!(to_yaml(&input).unwrap(), output);
    }

    #[test]
    fn yaml_to_json() {
        let input = serde_yaml::to_value(&vec!["foo"]);
        let output = serde_json::Value::Array(vec![serde_json::Value::String("foo".to_owned())]);
        assert_eq!(to_json(&input).unwrap(), output);
    }
}
<|endoftext|>


<commit_before>extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;
use quote::Ident;
use syn::Field;

#[proc_macro_derive(OptionalStruct, attributes(optional_name, optional_derive))]
pub fn optional_struct(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_derive_input(&s).unwrap();
    let gen = create_optional_struct(&ast);
    gen.parse().unwrap()
}

fn create_optional_struct(ast: &syn::DeriveInput) -> quote::Tokens {
    let (struct_name, derives) = parse_attributes(&ast);

    if let syn::Body::Struct(ref variant_data) = ast.body {
        if let &syn::VariantData::Struct(ref fields) = variant_data {
            return create_non_tuple_struct(fields, struct_name, derives);
        }
    }

    panic!("OptionalStruct only supports non-tuple structs for now");
}

fn parse_attributes(ast: &syn::DeriveInput) -> (syn::Ident, quote::Tokens) {
    let mut struct_name = ast.ident.clone();
    let mut derives = quote!{};

    for attribute in &ast.attrs {
        match &attribute.value {
            &syn::MetaItem::Word(_) => panic!("No word attribute is supported"),
            &syn::MetaItem::NameValue(ref name, ref value) => {
                if name != "optional_name" {
                    panic!("Only optional_name is supported");
                }

                match value {
                    &syn::Lit::Str(ref name_value, _) => {
                        struct_name = syn::Ident::new(name_value.clone())
                    }
                    _ => panic!("optional_name should be a string"),
                }
            }
            &syn::MetaItem::List(ref name, ref values) => {
                if name != "optional_derive" {
                    panic!("Only optional_derive are supported");
                }

                for value in values {
                    match value {
                        &syn::NestedMetaItem::MetaItem(ref item) => {
                            match item {
                                &syn::MetaItem::Word(ref derive_name) => {
                                    derives = quote!{ #derives, #derive_name }
                                }
                                _ => {
                                    panic!("Only traits name are supported inside optional_struct")
                                }
                            }
                        }
                        &syn::NestedMetaItem::Literal(_) => {
                            panic!("Only traits name are supported inside optional_struct")
                        }
                    }
                }
            }
        }
    }

    derives = quote!{ #[derive(#derives)] };
    (struct_name, derives)
}

fn create_non_tuple_struct(
    fields: &Vec<Field>,
    struct_name: syn::Ident,
    derives: quote::Tokens,
) -> quote::Tokens {
    let struct_name_string = quote!{#struct_name}.to_string();
    let mut optional_struct_name = String::from("Optional");
    optional_struct_name.push_str(&struct_name_string);
    let optional_struct_name = Ident::new(optional_struct_name);

    let mut attributes = quote!{};
    let mut assigners = quote!{};
    for field in fields {
        let ref type_name = &field.ty;
        let ref field_name = &field.ident.clone().unwrap();
        let next_attribute;
        let next_assigner;

        if field_name.to_string().starts_with("Option<") {
            next_attribute = quote!{ pub #field_name: #type_name>, };
            next_assigner = quote!{ self.#field_name = optional_struct.#field_name };
        } else {
            next_attribute = quote! { pub #field_name: Option<#type_name>, };
            next_assigner =
                quote!{
                    if let Some(attribute) = optional_struct.#field_name {
                        self.#field_name = attribute;
                    }
                };
        }

        assigners = quote!{ #assigners #next_assigner };
        attributes = quote!{ #attributes #next_attribute };
    }

    quote!{
        #derives
        pub struct #optional_struct_name {
            #attributes
        }

        impl #struct_name {
            pub fn apply_options(&mut self, optional_struct: &#optional_struct_name) {
                #assigners
            }
        }
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}
<commit_msg>add attrbutes names in macro def<commit_after>#![feature(custom_attribute)]

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;
use quote::Ident;
use syn::Field;

#[proc_macro_derive(OptionalStruct, attributes(optional_name, optional_derive))]
pub fn optional_struct(input: TokenStream) -> TokenStream {
    let s = input.to_string();
    let ast = syn::parse_derive_input(&s).unwrap();
    let gen = create_optional_struct(&ast);
    gen.parse().unwrap()
}

fn create_optional_struct(ast: &syn::DeriveInput) -> quote::Tokens {
    let (struct_name, derives) = parse_attributes(&ast);

    if let syn::Body::Struct(ref variant_data) = ast.body {
        if let &syn::VariantData::Struct(ref fields) = variant_data {
            return create_non_tuple_struct(fields, struct_name, derives);
        }
    }

    panic!("OptionalStruct only supports non-tuple structs for now");
}

fn parse_attributes(ast: &syn::DeriveInput) -> (syn::Ident, quote::Tokens) {
    let mut struct_name = ast.ident.clone();
    let mut derives = quote!{};

    for attribute in &ast.attrs {
        match &attribute.value {
            &syn::MetaItem::Word(_) => panic!("No word attribute is supported"),
            &syn::MetaItem::NameValue(ref name, ref value) => {
                if name != "optional_name" {
                    panic!("Only optional_name is supported");
                }

                match value {
                    &syn::Lit::Str(ref name_value, _) => {
                        struct_name = syn::Ident::new(name_value.clone())
                    }
                    _ => panic!("optional_name should be a string"),
                }
            }
            &syn::MetaItem::List(ref name, ref values) => {
                if name != "optional_derive" {
                    panic!("Only optional_derive are supported");
                }

                for value in values {
                    match value {
                        &syn::NestedMetaItem::MetaItem(ref item) => {
                            match item {
                                &syn::MetaItem::Word(ref derive_name) => {
                                    derives = quote!{ #derives, #derive_name }
                                }
                                _ => {
                                    panic!("Only traits name are supported inside optional_struct")
                                }
                            }
                        }
                        &syn::NestedMetaItem::Literal(_) => {
                            panic!("Only traits name are supported inside optional_struct")
                        }
                    }
                }
            }
        }
    }

    derives = quote!{ #[derive(#derives)] };
    (struct_name, derives)
}

fn create_non_tuple_struct(
    fields: &Vec<Field>,
    struct_name: syn::Ident,
    derives: quote::Tokens,
) -> quote::Tokens {
    let struct_name_string = quote!{#struct_name}.to_string();
    let mut optional_struct_name = String::from("Optional");
    optional_struct_name.push_str(&struct_name_string);
    let optional_struct_name = Ident::new(optional_struct_name);

    let mut attributes = quote!{};
    let mut assigners = quote!{};
    for field in fields {
        let ref type_name = &field.ty;
        let ref field_name = &field.ident.clone().unwrap();
        let next_attribute;
        let next_assigner;

        if field_name.to_string().starts_with("Option<") {
            next_attribute = quote!{ pub #field_name: #type_name>, };
            next_assigner = quote!{ self.#field_name = optional_struct.#field_name };
        } else {
            next_attribute = quote! { pub #field_name: Option<#type_name>, };
            next_assigner =
                quote!{
                    if let Some(attribute) = optional_struct.#field_name {
                        self.#field_name = attribute;
                    }
                };
        }

        assigners = quote!{ #assigners #next_assigner };
        attributes = quote!{ #attributes #next_attribute };
    }

    quote!{
        #derives
        pub struct #optional_struct_name {
            #attributes
        }

        impl #struct_name {
            pub fn apply_options(&mut self, optional_struct: &#optional_struct_name) {
                #assigners
            }
        }
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}
<|endoftext|>


<commit_before>extern crate chrono;

use chrono::UTC;
use std::net::UdpSocket;

mod error;
use self::error::DogstatsdError;

mod metrics;
use self::metrics::*;

#[derive(Debug, PartialEq)]
pub struct DogstatsdOptions {
    host: String,
    port: i32,
}

impl DogstatsdOptions {
    pub fn default() -> Self {
        DogstatsdOptions{
            host: "127.0.0.1".into(),
            port: 8125,
        }
    }
}

#[derive(Debug, PartialEq)]
pub struct Dogstatsd {
    udp_socket: String,
}

impl Dogstatsd {
    /// Spawn a new Dogstatsd handle
    pub fn new(options: DogstatsdOptions) -> Self {
        let socket_addr = format!("{}:{}", options.host, options.port);

        Dogstatsd {
            udp_socket: socket_addr,
        }
    }

    /// Increment a counter
    pub fn incr(&self, stat: &str) -> Result<(), DogstatsdError> {
        self.send(CountMetric::Incr(stat.into()))
    }

    /// Decrement a counter
    pub fn decr(&self, stat: &str) -> Result<(), DogstatsdError> {
        self.send(CountMetric::Decr(stat.into()))
    }

    /// Time a block of code
    pub fn time<F: FnOnce()>(&self, stat: &str, block: F) -> Result<(), DogstatsdError> {
        let start_time = UTC::now();
        block();
        let end_time = UTC::now();

        self.send(TimeMetric::new(stat, start_time, end_time))
    }

    /// Send a timing metric
    pub fn timing(&self, stat: &str, ms: i64) -> Result<(), DogstatsdError> {
        self.send(TimingMetric::new(stat, ms))
    }

    /// Report an arbitrary value (a gauge)
    pub fn gauge(&self, stat: &str, val: &str) -> Result<(), DogstatsdError> {
        self.send(GaugeMetric::new(stat, val))
    }

    /// Report a value in a histogram
    pub fn histogram(&self, stat: &str, val: &str) -> Result<(), DogstatsdError> {
        self.send(HistogramMetric::new(stat, val))
    }

    /// Report a value in a set
    pub fn set(&self, stat: &str, val: &str) -> Result<(), DogstatsdError> {
        self.send(SetMetric::new(stat, val))
    }

    /// Send a custom event
    pub fn event(&self, title: &str, text: &str) -> Result<(), DogstatsdError> {
        self.send(Event::new(title, text))
    }

    fn send<M: Metric>(&self, metric: M) -> Result<(), DogstatsdError> {
        let socket = try!(self.socket());
        try!(socket.send_to(metric.format_for_send().as_bytes(), try!(socket.local_addr())));
        Ok(())
    }

    fn socket(&self) -> Result<UdpSocket, DogstatsdError> {
        let socket = try!(UdpSocket::bind(&self.udp_socket[..]));
        Ok(socket)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::net::UdpSocket;

    #[test]
    fn test_options_default() {
        let options = DogstatsdOptions::default();
        let expected_options = DogstatsdOptions {
            host: "127.0.0.1".into(),
            port: 8125,
        };

        assert_eq!(expected_options, options)
    }

    #[test]
    fn test_new() {
        let client = Dogstatsd::new(DogstatsdOptions::default());

        assert_eq!(Dogstatsd { udp_socket: "127.0.0.1:8125".into() }, client)
    }
}
<commit_msg>Make Options fields public<commit_after>extern crate chrono;

use chrono::UTC;
use std::net::UdpSocket;

mod error;
use self::error::DogstatsdError;

mod metrics;
use self::metrics::*;

#[derive(Debug, PartialEq)]
pub struct DogstatsdOptions {
    pub host: String,
    pub port: i32,
}

impl DogstatsdOptions {
    pub fn default() -> Self {
        DogstatsdOptions{
            host: "127.0.0.1".into(),
            port: 8125,
        }
    }
}

#[derive(Debug, PartialEq)]
pub struct Dogstatsd {
    udp_socket: String,
}

impl Dogstatsd {
    /// Spawn a new Dogstatsd handle
    pub fn new(options: DogstatsdOptions) -> Self {
        let socket_addr = format!("{}:{}", options.host, options.port);

        Dogstatsd {
            udp_socket: socket_addr,
        }
    }

    /// Increment a counter
    pub fn incr(&self, stat: &str) -> Result<(), DogstatsdError> {
        self.send(CountMetric::Incr(stat.into()))
    }

    /// Decrement a counter
    pub fn decr(&self, stat: &str) -> Result<(), DogstatsdError> {
        self.send(CountMetric::Decr(stat.into()))
    }

    /// Time a block of code
    pub fn time<F: FnOnce()>(&self, stat: &str, block: F) -> Result<(), DogstatsdError> {
        let start_time = UTC::now();
        block();
        let end_time = UTC::now();

        self.send(TimeMetric::new(stat, start_time, end_time))
    }

    /// Send a timing metric
    pub fn timing(&self, stat: &str, ms: i64) -> Result<(), DogstatsdError> {
        self.send(TimingMetric::new(stat, ms))
    }

    /// Report an arbitrary value (a gauge)
    pub fn gauge(&self, stat: &str, val: &str) -> Result<(), DogstatsdError> {
        self.send(GaugeMetric::new(stat, val))
    }

    /// Report a value in a histogram
    pub fn histogram(&self, stat: &str, val: &str) -> Result<(), DogstatsdError> {
        self.send(HistogramMetric::new(stat, val))
    }

    /// Report a value in a set
    pub fn set(&self, stat: &str, val: &str) -> Result<(), DogstatsdError> {
        self.send(SetMetric::new(stat, val))
    }

    /// Send a custom event
    pub fn event(&self, title: &str, text: &str) -> Result<(), DogstatsdError> {
        self.send(Event::new(title, text))
    }

    fn send<M: Metric>(&self, metric: M) -> Result<(), DogstatsdError> {
        let socket = try!(self.socket());
        try!(socket.send_to(metric.format_for_send().as_bytes(), try!(socket.local_addr())));
        Ok(())
    }

    fn socket(&self) -> Result<UdpSocket, DogstatsdError> {
        let socket = try!(UdpSocket::bind(&self.udp_socket[..]));
        Ok(socket)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::net::UdpSocket;

    #[test]
    fn test_options_default() {
        let options = DogstatsdOptions::default();
        let expected_options = DogstatsdOptions {
            host: "127.0.0.1".into(),
            port: 8125,
        };

        assert_eq!(expected_options, options)
    }

    #[test]
    fn test_new() {
        let client = Dogstatsd::new(DogstatsdOptions::default());

        assert_eq!(Dogstatsd { udp_socket: "127.0.0.1:8125".into() }, client)
    }
}
<|endoftext|>


<commit_before>
use std::hash::{Hash, Hasher,SipHasher};
use std::any::Any;
use std::rc::Rc;

#[derive (Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SiphashID {
data: [u64; 2],
}
pub struct SiphashID_Generator {
data: [SipHasher; 2],
}
impl Hasher for SiphashID_Generator {
fn finish (& self)->u64 {panic!()}//Hack: this is actually for generating SiphashID, not 64-bit
fn write (&mut self, bytes: & [u8]) {self.data [0].write (bytes);self.data [1].write (bytes);}
//TODO: should we implement the others for efficiency?
}
impl SiphashID_Generator {
fn generate (& self)->SiphashID {SiphashID {data: [self.data [0].finish (), self.data [1].finish ()]}}
}

//TODO: Shouldn't "ordered type with minimum and maximum values" be a trait common to other situations as well? Find out if there's a standard name for that.
pub trait BaseTime: Ord {
  fn min_time ()->Self;
  fn max_time ()->Self;
}
impl BaseTime for i64 {
  fn min_time ()->i64 {i64::min_value ()}
  fn max_time ()->i64 {i64::max_value ()}
}

pub trait Field {
  type Data: Any;// = Self;

  /**
  Returns a constant identifier for the type, which must be 64 bits of random data.

  Thanks to the [birthday problem](https://en.wikipedia.org/wiki/Birthday_problem), this would have a >1% chance of a collision with a mere 700 million Field implementors. I don't think we need to worry about this. 128 bit IDs are necessary for entities, because computers can generate billions of them easily, but this isn't the same situation.

  It might seem desirable to default to a hash of the TypeId of Self, for the convenience of some implementations. However, Rust does not guarantee that the TypeId remains the same across different compilations or different compiler versions. And it certainly doesn't remain the same when you add or remove types from your code, which would be desirable for compatibility between different versions of your program. Also, the Rust interface for getting the TypeId is currently unstable. Using an explicit constant is simply better.

  TODO: change this into an associated constant once associated constants become stable.
  */
  fn unique_identifier ()->u64;

  /**
  Implementors MAY return true if first and second are indistinguishable.

  This is labeled "unsafe" because imperfect implementations can easily cause nondeterminism in the TimeSteward. Using the default is fine, and implementing it is only an optimization. Don't implement this unless you know what you're doing.

  This is similar to requiring Data to be PartialEq, but with an important difference. PartialEq only requires the comparison to be an equivalence relation, but does NOT require that (first == second) guarantee that there is no observable difference between the values. Therefore, trusting arbitrary implementations of PartialEq would be unsafe (in the sense that it would allow the TimeSteward to behave non-deterministically).

  TODO: perhaps we can create default implementations of this for POD types.
  TODO: can we create automated tests for implementations of this function?
  */
  fn guaranteed_equal__unsafe (first: & Self::Data, second: & Self::Data)->bool {false}
}

#[derive (Clone, PartialEq, Eq, Hash)]
struct FieldID {
  entity: SiphashID,
  field: u64,
}

#[derive (Clone, PartialEq, Eq, PartialOrd, Ord)]
struct GenericExtendedTime <Base: BaseTime> {
  base: Base,
  iteration: u64,
  id: SiphashID,
}

pub trait Basics {
  type Time: BaseTime;
  type Constants;
}
type ExtendedTime <B: Basics> = GenericExtendedTime <B::Time>;

//Note: in the future, we expect we might use a custom hash table type that knows it can rely on SiphashID to already be random, so we don't need to hash it again. This also applies to FieldID, although there may be some complications in that case.
impl Hash for SiphashID {
  fn hash <H: Hasher> (& self, state: &mut H) {self.data [0].hash (state);}
}
impl <Base: BaseTime> Hash for GenericExtendedTime <Base>{
  fn hash <H: Hasher> (& self, state: &mut H) {self.id.hash (state);}
}

pub trait Accessor <B: Basics> {
  fn get <F: Field> (&mut self, id: SiphashID)-> Option <& F::Data>;
  fn constants (& self)->& B::Constants;
}

pub trait Mutator <B: Basics>: Accessor <B> {
  fn get_mut <F: Field> (&mut self, id: SiphashID)->Option <&mut F::Data> where F::Data: Clone;
  fn set <F: Field> (&mut self, id: SiphashID, data: Option <F::Data>);
  fn random_bits (&mut self, num_bits:u32)->u64;
  fn random_id (&mut self)->SiphashID;
  fn now (& self)->B::Time;
}
pub trait PredictorAccessor <B: Basics>: Accessor <B> {

}
pub trait Snapshot <B: Basics>: Accessor <B> {

}

enum FiatEventOperationResult {
  Success,
  InvalidInput,
  InvalidTime,
}

pub trait TimeSteward <'a, B: Basics> {
  type S: Snapshot <B>;
  type M: Mutator <B>;
  type P: PredictorAccessor <B>;
  type Event;
  type Prediction;
  type Predictor;

  /**
  TimeSteward implementors are permitted, but not required, to discard old data in order to save memory. This may make the TimeSteward unusable at some points in its history.

  Newly created stewards should generally have valid_from() == B::Time::min_time().

  All implementors must obey certain restrictions on how other TimeSteward methods may change the result of valid_from(). Implementors may have their own methods that can alter this in customized ways, which should be documented with those individual methods.
  */
  fn valid_from (& self)->B::Time;

  /**
  Inserts a fiat event at some point in the history.

  If time < valid_from(), this does nothing and returns InvalidTime. If there is already a fiat event with the same time and distinguisher, this does nothing and returns InvalidInput. Otherwise, it inserts the event and returns Success.

  steward.insert_fiat_event(time, _) must not return InvalidTime if time >= steward.valid_from().
  steward.insert_fiat_event() may not change steward.valid_from().
  */
  fn insert_fiat_event (&mut self, time: B::Time, distinguisher: u64, event: Self::Event)->FiatEventOperationResult ;

  /**
  Erases a fiat event that has been inserted previously.

  If time < valid_from(), this does nothing and returns InvalidTime. If there is no fiat event with the specified time and distinguisher, this does nothing and returns InvalidInput. Otherwise, it erases the event and returns Success.

  steward.erase_fiat_event(time, _) must not return InvalidTime if time >= steward.valid_from().
  steward.erase_fiat_event() may not change steward.valid_from().
  */
  fn erase_fiat_event (&mut self, time: B::Time, distinguisher: u64)->FiatEventOperationResult ;

  /**
  Returns a "snapshot" into the TimeSteward.

  The snapshot is guaranteed to be valid and unchanging for the full lifetime of the TimeSteward. It is specific to both the time argument, and the current collection of fiat events. Callers may freely call mutable methods of the same TimeSteward after taking a snapshot, without changing the contents of the snapshot.

  Each TimeSteward implementor determines exactly how to provide these guarantees. Implementors should provide individual guarantees about the processor-time bounds of snapshot operations.

  steward.snapshot_before(time) must return Some if time >= steward.valid_from().
  steward.snapshot_before(time) may not increase steward.valid_from() beyond time.
  */
  //note: we implement "before" and not "after" because we might be banning events that happen during max_time
  fn snapshot_before (&mut self, time: B::Time)->Option <Self::S>;
}

pub trait FlatTimeSteward <'a, B: Basics>:TimeSteward <'a, B> {
  fn valid_strictly_between (& self)->(B::Time, B::Time);
}

type Event <B: Basics, M: Mutator <B>> =Rc <Fn (&mut M)>;
enum Prediction <B: Basics, M: Mutator <B>> {
  Nothing,
  Immediately (Event <B, M>),
  At (B::Time, Event <B, M>),
}
type Predictor <B: Basics, M: Mutator <B>, PA: PredictorAccessor <B>> =Rc <for <'b, 'c> Fn (& 'b mut PA)->Prediction <B, M>>;

mod StandardFlatTimeSteward {
use super::{SiphashID, FieldID, Field, ExtendedTime, Basics, TimeSteward, FiatEventOperationResult};
use std::collections::{HashMap, BinaryHeap};
use std::any::Any;
use std::borrow::Borrow;
use std::rc::Rc;

pub struct Steward <'a, B: Basics> {
  entity_states: HashMap< FieldID, Box <Any>>,
  last_change: B::Time,
  fiat_events: HashMap< ExtendedTime <B>, Event <'a, B>>,
  predictions: HashMap<ExtendedTime <B>, Prediction <'a, B>>,
  upcoming_event_times: BinaryHeap <ExtendedTime <B>>,

}
pub struct Snapshot <'a, B: Basics + 'a> {
  steward: &'a Steward <'a, B>,
}
pub struct Mutator <'a, B: Basics + 'a> {
  steward: & 'a mut Steward <'a, B>,
}
pub struct PredictorAccessor <'a, B: Basics + 'a> {
  steward: & 'a mut Steward <'a, B>,
}
type Event <'a, B: Basics + 'a> = super::Event <B, Mutator <'a, B>>;
type Prediction <'a, B: Basics + 'a> = super::Prediction <B, Mutator <'a, B>>;
type Predictor <'a, B: Basics + 'a> = super::Predictor <B, Mutator <'a, B>, PredictorAccessor <'a, B>>;

impl <'a, B: Basics>Steward <'a, B> {
  fn new (constants: B::Constants, predictors: Vec<Predictor <'a, B>>)->Self {}
}
impl <'a, B: Basics> super::Accessor <B> for Snapshot <'a, B> {
  fn get <F: Field> (&mut self, id: SiphashID)-> Option <& F::Data> {
    self.steward.entity_states.get (& FieldID {entity: id, field: F::unique_identifier ()}).map (| something | something.downcast ::<F::Data> ().unwrap ().borrow ())
  }
}
impl <'a, B: Basics> super::Accessor <B> for Mutator <'a, B> {
  fn get <F: Field> (&mut self, id: SiphashID)-> Option <& F::Data> {
    self.steward.entity_states.get (& FieldID {entity: id, field: F::unique_identifier ()}).map (| something | something.downcast ::<F::Data> ().unwrap ().borrow ())
  }
}

impl <'a, B: Basics> super::TimeSteward <'a, B> for Steward <'a, B> {
  type S = Snapshot <'a, B>;
  type M =Mutator <'a, B>;
  type P =PredictorAccessor <'a, B>;

  type Event = Event <'a, B>;
  type Prediction = super::Prediction <B, Self::Event>;
  type Predictor = super::Predictor <B, Self::P, Self::Prediction>;

  fn insert_fiat_event (&mut self, time: B::Time, distinguisher: u64, event: Self::Event)->FiatEventOperationResult {}
  fn erase_fiat_event (&mut self, time: B::Time, distinguisher: u64)->FiatEventOperationResult {}
  fn snapshot_before (&mut self, time: B::Time)->Option <Self::S> {}
}

}

 #[test]
fn it_works() {
}
<commit_msg>More implementing<commit_after>use std::hash::{Hash, Hasher,SipHasher};
use std::any::Any;
use std::rc::Rc;

#[derive (Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct SiphashID {
data: [u64; 2],
}
pub struct SiphashID_Generator {
data: [SipHasher; 2],
}
impl Hasher for SiphashID_Generator {
fn finish (& self)->u64 {panic!()}//Hack: this is actually for generating SiphashID, not 64-bit
fn write (&mut self, bytes: & [u8]) {self.data [0].write (bytes);self.data [1].write (bytes);}
//TODO: should we implement the others for efficiency?
}
impl SiphashID_Generator {
fn generate (& self)->SiphashID {SiphashID {data: [self.data [0].finish (), self.data [1].finish ()]}}
}

//TODO: Shouldn't "ordered type with minimum and maximum values" be a trait common to other situations as well? Find out if there's a standard name for that.
pub trait BaseTime: Clone + Ord {
  fn min_time ()->Self;
  fn max_time ()->Self;
}
impl BaseTime for i64 {
  fn min_time ()->i64 {i64::min_value ()}
  fn max_time ()->i64 {i64::max_value ()}
}

pub trait Field {
  type Data: Any;// = Self;

  /**
  Returns a constant identifier for the type, which must be 64 bits of random data.

  Thanks to the [birthday problem](https://en.wikipedia.org/wiki/Birthday_problem), this would have a >1% chance of a collision with a mere 700 million Field implementors. I don't think we need to worry about this. 128 bit IDs are necessary for entities, because computers can generate billions of them easily, but this isn't the same situation.

  It might seem desirable to default to a hash of the TypeId of Self, for the convenience of some implementations. However, Rust does not guarantee that the TypeId remains the same across different compilations or different compiler versions. And it certainly doesn't remain the same when you add or remove types from your code, which would be desirable for compatibility between different versions of your program. Also, the Rust interface for getting the TypeId is currently unstable. Using an explicit constant is simply better.

  TODO: change this into an associated constant once associated constants become stable.
  */
  fn unique_identifier ()->u64;

  /**
  Implementors MAY return true if first and second are indistinguishable.

  This is labeled "unsafe" because imperfect implementations can easily cause nondeterminism in the TimeSteward. Using the default is fine, and implementing it is only an optimization. Don't implement this unless you know what you're doing.

  This is similar to requiring Data to be PartialEq, but with an important difference. PartialEq only requires the comparison to be an equivalence relation, but does NOT require that (first == second) guarantee that there is no observable difference between the values. Therefore, trusting arbitrary implementations of PartialEq would be unsafe (in the sense that it would allow the TimeSteward to behave non-deterministically).

  TODO: perhaps we can create default implementations of this for POD types.
  TODO: can we create automated tests for implementations of this function?
  */
  fn guaranteed_equal__unsafe (first: & Self::Data, second: & Self::Data)->bool {false}
}

#[derive (Clone, PartialEq, Eq, Hash)]
struct FieldID {
  entity: SiphashID,
  field: u64,
}

#[derive (Clone, PartialEq, Eq, PartialOrd, Ord)]
struct GenericExtendedTime <Base: BaseTime> {
  base: Base,
  iteration: u64,
  id: SiphashID,
}

pub trait Basics {
  type Time: BaseTime;
  type Constants;
}
type ExtendedTime <B: Basics> = GenericExtendedTime <B::Time>;

//Note: in the future, we expect we might use a custom hash table type that knows it can rely on SiphashID to already be random, so we don't need to hash it again. This also applies to FieldID, although there may be some complications in that case.
impl Hash for SiphashID {
  fn hash <H: Hasher> (& self, state: &mut H) {self.data [0].hash (state);}
}
impl <Base: BaseTime> Hash for GenericExtendedTime <Base>{
  fn hash <H: Hasher> (& self, state: &mut H) {self.id.hash (state);}
}

pub trait Accessor <B: Basics> {
  fn get <F: Field> (&mut self, id: SiphashID)-> Option <& F::Data>;
  fn constants (& self)->& B::Constants;
}

pub trait MomentaryAccessor <B: Basics>: Accessor <B> {
  fn now (& self)->B::Time;
}

pub trait Mutator <B: Basics>: MomentaryAccessor <B> {
  fn get_mut <F: Field> (&mut self, id: SiphashID)->Option <&mut F::Data> where F::Data: Clone;
  fn set <F: Field> (&mut self, id: SiphashID, data: Option <F::Data>);
  fn random_bits (&mut self, num_bits:u32)->u64;
  fn random_id (&mut self)->SiphashID;
}
pub trait PredictorAccessor <B: Basics>: Accessor <B> {

}
pub trait Snapshot <B: Basics>: MomentaryAccessor <B> {
}

enum FiatEventOperationResult {
  Success,
  InvalidInput,
  InvalidTime,
}

pub trait TimeSteward <'a, B: Basics> {
  type S: Snapshot <B>;
  type M: Mutator <B>;
  type P: PredictorAccessor <B>;
  type Event;
  type Prediction;
  type Predictor;

  /**
  TimeSteward implementors are permitted, but not required, to discard old data in order to save memory. This may make the TimeSteward unusable at some points in its history.

  Newly created stewards should generally have valid_from() == B::Time::min_time().

  All implementors must obey certain restrictions on how other TimeSteward methods may change the result of valid_from(). Implementors may have their own methods that can alter this in customized ways, which should be documented with those individual methods.
  */
  fn valid_from (& self)->B::Time;

  /**
  Inserts a fiat event at some point in the history.

  If time < valid_from(), this does nothing and returns InvalidTime. If there is already a fiat event with the same time and distinguisher, this does nothing and returns InvalidInput. Otherwise, it inserts the event and returns Success.

  steward.insert_fiat_event(time, _) must not return InvalidTime if time >= steward.valid_from().
  steward.insert_fiat_event() may not change steward.valid_from().
  */
  fn insert_fiat_event (&mut self, time: B::Time, distinguisher: u64, event: Self::Event)->FiatEventOperationResult ;

  /**
  Erases a fiat event that has been inserted previously.

  If time < valid_from(), this does nothing and returns InvalidTime. If there is no fiat event with the specified time and distinguisher, this does nothing and returns InvalidInput. Otherwise, it erases the event and returns Success.

  steward.erase_fiat_event(time, _) must not return InvalidTime if time >= steward.valid_from().
  steward.erase_fiat_event() may not change steward.valid_from().
  */
  fn erase_fiat_event (&mut self, time: B::Time, distinguisher: u64)->FiatEventOperationResult ;

  /**
  Returns a "snapshot" into the TimeSteward.

  The snapshot is guaranteed to be valid and unchanging for the full lifetime of the TimeSteward. It is specific to both the time argument, and the current collection of fiat events. Callers may freely call mutable methods of the same TimeSteward after taking a snapshot, without changing the contents of the snapshot.

  Each TimeSteward implementor determines exactly how to provide these guarantees. Implementors should provide individual guarantees about the processor-time bounds of snapshot operations.

  steward.snapshot_before(time) must return Some if time >= steward.valid_from().
  steward.snapshot_before(time) may not increase steward.valid_from() beyond time.
  */
  //note: we implement "before" and not "after" because we might be banning events that happen during max_time
  fn snapshot_before (&mut self, time: B::Time)->Option <Self::S>;
}

pub trait FlatTimeSteward <'a, B: Basics>:TimeSteward <'a, B> {
  fn valid_strictly_between (& self)->(B::Time, B::Time);
}

type Event <B: Basics, M: Mutator <B>> =Rc <Fn (&mut M)>;
enum Prediction <B: Basics, M: Mutator <B>> {
  Nothing,
  Immediately (Event <B, M>),
  At (B::Time, Event <B, M>),
}
type Predictor <B: Basics, M: Mutator <B>, PA: PredictorAccessor <B>> =Rc <for <'b, 'c> Fn (& 'b mut PA)->Prediction <B, M>>;

mod SimpleFlatTimeSteward {
use super::{SiphashID, FieldID, Field, ExtendedTime, Basics, TimeSteward, FiatEventOperationResult};
use std::collections::{HashMap, BTreeMap};
use std::any::Any;
use std::borrow::{Borrow, Cow};
use std::rc::Rc;

struct ConstantPart <'a, B: Basics> {
  predictors: Vec<Predictor <'a, B>>,
  constants: B::Constants,
}
#[derive (Clone)]
struct BothParts <'a, B: Basics> {
  entity_states: HashMap< FieldID, Box <Any>>,
  last_change: B::Time,
  fiat_events: BTreeMap<ExtendedTime <B>, Event <'a, B>>,
  predictions: BTreeMap<ExtendedTime <B>, Prediction <'a, B>>,
  prediction_dependencies: HashMap<FieldID, Vec<ExtendedTime <B>>>,
  constant_part: Rc< ConstantPart <'a, B>>,
}

pub struct Steward <'a, B: Basics + 'a> {
  data: Cow <'a, BothParts <'a, B>>,
}
pub struct Snapshot <'a, B: Basics + 'a> {
  now: B::Time,
  data: &'a BothParts <'a, B>,
}
pub struct Mutator <'a, B: Basics + 'a> {
  now: B::Time,
  steward: & 'a mut BothParts <'a, B>,
}
pub struct PredictorAccessor <'a, B: Basics + 'a> {
  steward: & 'a mut BothParts <'a, B>,
}
type Event <'a, B: Basics + 'a> = super::Event <B, Mutator <'a, B>>;
type Prediction <'a, B: Basics + 'a> = super::Prediction <B, Mutator <'a, B>>;
type Predictor <'a, B: Basics + 'a> = super::Predictor <B, Mutator <'a, B>, PredictorAccessor <'a, B>>;

impl <'a, B: Basics> super::Accessor <B> for Snapshot <'a, B> {
  fn get <F: Field> (&mut self, id: SiphashID)-> Option <& F::Data> {
    self. data.get ::<F> (id)
  }
}
impl <'a, B: Basics> super::Accessor <B> for Mutator <'a, B> {
  fn get <F: Field> (&mut self, id: SiphashID)-> Option <& F::Data> {
    self. data.get::<F> (id)
  }
}
impl <'a, B: Basics> super::Accessor <B> for PredictorAccessor <'a, B> {
  fn get <F: Field> (&mut self, id: SiphashID)-> Option <& F::Data> {
    self. data.get::<F> (id)
  }
}

impl <'a, B: Basics> super::MomentaryAccessor <B> for Snapshot <'a, B> {
  fn now (& self)->B::Time {self.now}
}
impl <'a, B: Basics> super::MomentaryAccessor <B> for Mutator <'a, B> {
  fn now (& self)->B::Time {self.now}
}
impl <'a, B: Basics> super::PredictorAccessor <B> for PredictorAccessor <'a, B> {}
impl <'a, B: Basics> super::Snapshot <B> for Snapshot <'a, B> {}

impl <'a, B: Basics> super::Mutator <B> for Mutator <'a, B> {
  fn get_mut <F: Field> (&mut self, id: SiphashID)->Option <&mut F::Data> where F::Data: Clone {}
  fn set <F: Field> (&mut self, id: SiphashID, data: Option <F::Data>) {}
  fn random_bits (&mut self, num_bits:u32)->u64 {}
  fn random_id (&mut self)->SiphashID {}
}

impl <'a, B: Basics>BothParts <'a, B> {
  fn get <F: Field> (&mut self, id: SiphashID)-> Option <& F::Data> {
    self.entity_states.get (& FieldID {entity: id, field: F::unique_identifier ()}).map (| something | something.downcast ::<F::Data> ().unwrap ().borrow ())
  }
}

impl <'a, B: Basics>Steward <'a, B> {
  fn new (constants: B::Constants, predictors: Vec<Predictor <'a, B>>)->Self {}
}
impl <'a, B: Basics> super::TimeSteward <'a, B> for Steward <'a, B> {
  type S = Snapshot <'a, B>;
  type M =Mutator <'a, B>;
  type P =PredictorAccessor <'a, B>;

  type Event = Event <'a, B>;
  type Prediction = super::Prediction <B, Self::Event>;
  type Predictor = super::Predictor <B, Self::P, Self::Prediction>;

  fn insert_fiat_event (&mut self, time: B::Time, distinguisher: u64, event: Self::Event)->FiatEventOperationResult {}
  fn erase_fiat_event (&mut self, time: B::Time, distinguisher: u64)->FiatEventOperationResult {}
  fn snapshot_before (&mut self, time: B::Time)->Option <Self::S> {}
}

}

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


<commit_before>use std::cmp::PartialEq;
use std::fmt::Debug;

mod option;
mod result;
mod vec;

#[derive(Debug)]
pub struct SpecDescription<'r> {
    value: &'r str,
}

#[derive(Debug)]
pub struct Spec<'s, S: 's> {
    subject: &'s S,
    description: Option<&'s str>,
    expected: Option<String>,
    actual: Option<String>,
}

pub fn assert_that<'s, S>(subject: &'s S) -> Spec<'s, S> {
    Spec {
        subject: subject,
        description: None,
        expected: None,
        actual: None,
    }
}

pub fn asserting<'r>(description: &'r str) -> SpecDescription {
    SpecDescription { value: description }
}

fn build_expectation_string(expected: &str, actual: &str) -> String {
    format!("expected {} but was {}", expected, actual)
}

impl<'r> SpecDescription<'r> {
    pub fn that<S>(self, subject: &'r S) -> Spec<'r, S> {
        Spec {
            subject: subject,
            description: Some(self.value),
            expected: None,
            actual: None,
        }
    }
}

impl<'s, S> Spec<'s, S> {
    fn with_expected(self, expected: String) -> Self {
        let mut spec = self;
        spec.expected = Some(expected);
        spec
    }

    fn with_actual(self, actual: String) -> Self {
        let mut spec = self;
        spec.actual = Some(actual);
        spec
    }

    fn fail(self) {
        if !self.expected.is_some() || !self.actual.is_some() {
            panic!("invalid assertion");
        }

        match self.description {
            Some(description) => {
                panic!(format!("{}: expected {} but was {}",
                               description,
                               self.expected.unwrap(),
                               self.actual.unwrap()))
            }
            None => {
                panic!(format!("expected {} but was {}",
                               self.expected.unwrap(),
                               self.actual.unwrap()))
            }
        }
    }
}

impl<'s, S> Spec<'s, S>
    where S: Debug + PartialEq
{
    pub fn is_equal_to(self, expected: &S) {
        let subject = self.subject;

        if !subject.eq(expected) {
            self.with_expected(format!("<{:?}>", expected))
                .with_actual(format!("<{:?}>", subject))
                .fail();
        }
    }
}

impl<'s, S> Spec<'s, S>
    where S: Debug
{
    pub fn matches<F>(&self, matching_function: F)
        where F: Fn(&'s S) -> bool
    {
        if !matching_function(self.subject) {
            panic!(format!("expected failed for value <{:?}>", self.subject));
        }
    }

    pub fn map<F, T>(self, mapping_function: F) -> Spec<'s, T>
        where F: Fn(&'s S) -> &'s T
    {
        Spec {
            subject: mapping_function(self.subject),
            description: self.description,
            expected: self.expected,
            actual: self.actual,
        }
    }
}
<commit_msg>fixed up search and replace fail<commit_after>use std::cmp::PartialEq;
use std::fmt::Debug;

mod option;
mod result;
mod vec;

#[derive(Debug)]
pub struct SpecDescription<'r> {
    value: &'r str,
}

#[derive(Debug)]
pub struct Spec<'s, S: 's> {
    subject: &'s S,
    description: Option<&'s str>,
    expected: Option<String>,
    actual: Option<String>,
}

pub fn assert_that<'s, S>(subject: &'s S) -> Spec<'s, S> {
    Spec {
        subject: subject,
        description: None,
        expected: None,
        actual: None,
    }
}

pub fn asserting<'r>(description: &'r str) -> SpecDescription {
    SpecDescription { value: description }
}

fn build_expectation_string(expected: &str, actual: &str) -> String {
    format!("expected {} but was {}", expected, actual)
}

impl<'r> SpecDescription<'r> {
    pub fn that<S>(self, subject: &'r S) -> Spec<'r, S> {
        Spec {
            subject: subject,
            description: Some(self.value),
            expected: None,
            actual: None,
        }
    }
}

impl<'s, S> Spec<'s, S> {
    fn with_expected(self, expected: String) -> Self {
        let mut spec = self;
        spec.expected = Some(expected);
        spec
    }

    fn with_actual(self, actual: String) -> Self {
        let mut spec = self;
        spec.actual = Some(actual);
        spec
    }

    fn fail(self) {
        if !self.expected.is_some() || !self.actual.is_some() {
            panic!("invalid assertion");
        }

        match self.description {
            Some(description) => {
                panic!(format!("{}: expected {} but was {}",
                               description,
                               self.expected.unwrap(),
                               self.actual.unwrap()))
            }
            None => {
                panic!(format!("expected {} but was {}",
                               self.expected.unwrap(),
                               self.actual.unwrap()))
            }
        }
    }
}

impl<'s, S> Spec<'s, S>
    where S: Debug + PartialEq
{
    pub fn is_equal_to(self, expected: &S) {
        let subject = self.subject;

        if !subject.eq(expected) {
            self.with_expected(format!("<{:?}>", expected))
                .with_actual(format!("<{:?}>", subject))
                .fail();
        }
    }
}

impl<'s, S> Spec<'s, S>
    where S: Debug
{
    pub fn matches<F>(&self, matching_function: F)
        where F: Fn(&'s S) -> bool
    {
        if !matching_function(self.subject) {
            panic!(format!("expectation failed for value <{:?}>", self.subject));
        }
    }

    pub fn map<F, T>(self, mapping_function: F) -> Spec<'s, T>
        where F: Fn(&'s S) -> &'s T
    {
        Spec {
            subject: mapping_function(self.subject),
            description: self.description,
            expected: self.expected,
            actual: self.actual,
        }
    }
}
<|endoftext|>