<commit_before>extern crate stemmer;
extern crate unicode_normalization;
extern crate unicode_segmentation;

use self::stemmer::Stemmer;
use self::unicode_normalization::UnicodeNormalization;
use self::unicode_segmentation::UnicodeSegmentation;

pub struct Stems<'a> {
    words: unicode_segmentation::UnicodeWordIndices<'a>,
    stemmer: Stemmer,
}

#[derive(Debug, PartialEq)]
pub struct StemmedWord {
    // Where the stemmed word starts
    pub stemmed_offset: usize,
    // Where the suffix starts
    pub suffix_offset: usize,
    // The stemmed word
    pub stemmed: String,
    // The difference between the stemmed word and the original lowercased one. It can be
    // used to recontruct the original word (for exact match searches)
    pub suffix: String,
}

impl<'a> Stems<'a> {
    pub fn new(text: &str) -> Stems {
        Stems{
            words: text.unicode_word_indices(),
            stemmer: Stemmer::new("english").unwrap(),
        }
    }

    /// Return the *byte* length of the common prefix between two strings
    fn common_prefix_len(aa: &str, bb: &str) -> usize {
        aa.chars()
            .zip(bb.chars())
            .take_while(|&(a, b)| a == b)
            .fold(0, |acc, (a, _)| acc + a.len_utf8())
    }
}

impl<'a> Iterator for Stems<'a> {
    type Item = StemmedWord;

    fn next(&mut self) -> Option<StemmedWord> {
        match self.words.next() {
            Some((pos, word)) => {
                let lowercased = word.to_lowercase();
                let normalized = lowercased.nfkc().collect::<String>();
                let stemmed = self.stemmer.stem(&normalized);
                let prefix_len = Stems::common_prefix_len(&stemmed, &normalized);
                let ret = StemmedWord {
                    stemmed_offset: pos,
                    suffix_offset: pos + prefix_len,
                    stemmed: stemmed,
                    suffix: (&normalized[prefix_len..normalized.len()]).to_string(),
                };
                Some(ret)
            },
            None => None
        }
    }

}

#[cfg(test)]
mod tests {
    use super::{StemmedWord, Stems};

    #[test]
    fn test_stems_lowercase() {
        let input = "These words deeply test smoothly that stemming";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 5,
                          stemmed: String::from("these"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 6, suffix_offset: 10,
                          stemmed: String::from("word"), suffix: String::from("s") },
            // "deeply" stems to "deepli"
            StemmedWord { stemmed_offset: 12, suffix_offset: 17,
                          stemmed: String::from("deepli"), suffix: String::from("y") },
            StemmedWord { stemmed_offset: 19, suffix_offset: 23,
                          stemmed: String::from("test"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 24, suffix_offset: 30,
                          stemmed: String::from("smooth"), suffix: String::from("ly") },
            StemmedWord { stemmed_offset: 33, suffix_offset: 37,
                          stemmed: String::from("that"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 38, suffix_offset: 42,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_mixedcase() {
        let input = "THEse Words deeplY test smOOthly that stemmING";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 5,
                          stemmed: String::from("these"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 6, suffix_offset: 10,
                          stemmed: String::from("word"), suffix: String::from("s") },
            // "deeply" stems to "deepli"
            StemmedWord { stemmed_offset: 12, suffix_offset: 17,
                          stemmed: String::from("deepli"), suffix: String::from("y") },
            StemmedWord { stemmed_offset: 19, suffix_offset: 23,
                          stemmed: String::from("test"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 24, suffix_offset: 30,
                          stemmed: String::from("smooth"), suffix: String::from("ly") },
            StemmedWord { stemmed_offset: 33, suffix_offset: 37,
                          stemmed: String::from("that"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 38, suffix_offset: 42,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_nonchars() {
        let input = "  @#$!== \t+-";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        assert_eq!(result.len(), 0);
    }

    #[test]
    fn test_stems_some_nonchars() {
        let input = "@!?   Let's seeing...";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 6, suffix_offset: 9,
                          stemmed: String::from("let"), suffix: String::from("'s") },
            StemmedWord { stemmed_offset: 12, suffix_offset: 15,
                          stemmed: String::from("see"), suffix: String::from("ing") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode() {
        let input = "Ünicöde stemming";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 8,
                          stemmed: String::from("ünicöd"), suffix: String::from("e") },
            StemmedWord { stemmed_offset: 10, suffix_offset: 14,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode_lowercase_has_more_bytes() {
        let input = "İ";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 3,
                          stemmed: String::from("i̇"), suffix: String::from("") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    // Some characters don't have a corresponding uppercase chararacter with the same byte size,
    // but when those uppercase letters are transformed to lowercase, they are transformed to the
    // same number of bytes combining several characters (although there is a single character for
    // it).
    // Here's an example in Python:
    //
    //     >>> u"Ρ̓".lower() == u"ῤ"
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".lower()
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".upper().lower()
    //     True
    // Or Rust:
    //
    //    let upper = "Ρ̓";
    //    let lower = "ῤ";
    //    println!("lower({}) == {}: {}", upper, lower, upper.to_lowercase() == lower);
    //    println!("lower({}) == lower(upper({})): {}", upper, lower, upper.to_lowercase() == lower.to_uppercase().to_lowercase());
    //    lower(Ρ̓) == ῤ: false
    //    lower(Ρ̓) == lower(upper(ῤ)): true
    #[test]
    fn test_stems_unicode_lowercase_has_less_bytes() {
        // The input is: Ρ̓ῤῤ (11 bytes), lowercases is ῤῤῤ (9 bytes)
        let input = "\u{03A1}\u{0313}\u{03C1}\u{0313}\u{1FE4}";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 9,
                          stemmed: String::from("\u{1FE4}\u{1FE4}\u{1FE4}"), suffix: String::from("") },
           ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_common_prefix_len() {
        let tests = vec![
            ("a", "a", 1),
            ("ab", "a", 1),
            ("a", "ab", 1),
            ("ab", "ab", 2),
            ("a", "b", 0),
            ("b", "a", 0),
            ("ab", "cd", 0),
            ("ab", "bc", 0),
            ("abc", "abd", 2),
            ("ac", "abcd", 1),
            (" a", "a", 0),
            ("a", "a ", 1),
            ("xyzabc", "xyz", 3),
            ("xyz", "xyzabc", 3),
            ("öxyz", "öx", 3),
            ];
        for (aa, bb, expected) in tests {
            let prefix_len = Stems::common_prefix_len(aa, bb);
            assert_eq!(prefix_len, expected);
        }
    }
}
<commit_msg>Changes to word stemming<commit_after>extern crate stemmer;
extern crate unicode_normalization;
extern crate unicode_segmentation;

use std::iter::Peekable;

use self::stemmer::Stemmer;
use self::unicode_normalization::UnicodeNormalization;
use self::unicode_segmentation::UnicodeSegmentation;

pub struct Stems<'a> {
    words: Peekable<unicode_segmentation::UWordBoundIndices<'a>>,
    stemmer: Stemmer,
}

#[derive(Debug, PartialEq)]
pub struct StemmedWord {
    // Where the stemmed word starts
    pub stemmed_offset: usize,
    // Where the suffix starts
    pub suffix_offset: usize,
    // The stemmed word
    pub stemmed: String,
    // The difference between the stemmed word and the original lowercased one. It can be
    // used to recontruct the original word (for exact match searches)
    pub suffix: String,
}

impl<'a> Stems<'a> {
    pub fn new(text: &str) -> Stems {
        Stems{
            words: text.split_word_bound_indices().peekable(),
            stemmer: Stemmer::new("english").unwrap(),
        }
    }

    /// Return the *byte* length of the common prefix between two strings
    fn common_prefix_len(aa: &str, bb: &str) -> usize {
        aa.chars()
            .zip(bb.chars())
            .take_while(|&(a, b)| a == b)
            .fold(0, |acc, (a, _)| acc + a.len_utf8())
    }
}

impl<'a> Iterator for Stems<'a> {
    type Item = StemmedWord;

    fn next(&mut self) -> Option<StemmedWord> {
        let mut word_to_stem = String::new();
        let mut stemmed_offset = 0;
        let mut normalized = String::new();
        loop {
            match self.words.peek() {
                Some(&(pos, word)) => {
                    normalized = word.nfkc().collect::<String>();
                    if word.chars().next().unwrap().is_alphabetic() {
                        stemmed_offset = pos;
                        break;
                    } else {
                        word_to_stem.push_str(&normalized);
                        self.words.next();
                    }
                },
                None => {
                    if word_to_stem.is_empty() {
                        return None;
                    } else {
                        break;
                    }
                },
            }
        }

        if !word_to_stem.is_empty() {
            // we found the begining of the string is not a stemmable word.
            // Return the accumulated string as the stemmed word
            return Some(StemmedWord {
                            stemmed_offset: 0,
                            suffix_offset: word_to_stem.len(),
                            stemmed: word_to_stem,
                            suffix: String::new(),
                });
        }
        // normalized contains our stemmable word. advance the iter since we only peeked.
        self.words.next();
        word_to_stem = normalized;
        let mut suffix = word_to_stem.clone();
        loop {
            // loop through all non-alphabetic chars and add to suffix (which means the suffix starts
            // before the stemmed word)
            match self.words.peek() {
                Some(&(_pos, word)) => {
                    normalized = word.nfkc().collect::<String>();
                    if normalized.chars().next().unwrap().is_alphabetic() {
                        break;
                    } else {
                        suffix.push_str(&normalized);
                        self.words.next();
                    }
                },
                None => break,
            }
        }
        let stemmed = self.stemmer.stem(&word_to_stem.to_lowercase());
        let prefix_len = Stems::common_prefix_len(&stemmed, &suffix);
        Some(StemmedWord {
            stemmed_offset: stemmed_offset,
            suffix_offset: stemmed_offset + prefix_len,
            stemmed: stemmed,
            suffix: (&suffix[prefix_len..]).to_string(),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::{StemmedWord, Stems};

    #[test]
    fn test_stems_lowercase() {
        let input = "These words deeply test smoothly that stemming";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 5,
                          stemmed: String::from("these"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 6, suffix_offset: 10,
                          stemmed: String::from("word"), suffix: String::from("s") },
            // "deeply" stems to "deepli"
            StemmedWord { stemmed_offset: 12, suffix_offset: 17,
                          stemmed: String::from("deepli"), suffix: String::from("y") },
            StemmedWord { stemmed_offset: 19, suffix_offset: 23,
                          stemmed: String::from("test"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 24, suffix_offset: 30,
                          stemmed: String::from("smooth"), suffix: String::from("ly") },
            StemmedWord { stemmed_offset: 33, suffix_offset: 37,
                          stemmed: String::from("that"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 38, suffix_offset: 42,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_mixedcase() {
        let input = "THEse Words deeplY test smOOthly that stemmING";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 5,
                          stemmed: String::from("these"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 6, suffix_offset: 10,
                          stemmed: String::from("word"), suffix: String::from("s") },
            // "deeply" stems to "deepli"
            StemmedWord { stemmed_offset: 12, suffix_offset: 17,
                          stemmed: String::from("deepli"), suffix: String::from("y") },
            StemmedWord { stemmed_offset: 19, suffix_offset: 23,
                          stemmed: String::from("test"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 24, suffix_offset: 30,
                          stemmed: String::from("smooth"), suffix: String::from("ly") },
            StemmedWord { stemmed_offset: 33, suffix_offset: 37,
                          stemmed: String::from("that"), suffix: String::from("") },
            StemmedWord { stemmed_offset: 38, suffix_offset: 42,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_nonchars() {
        let input = "  @#$!== \t+-";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        assert_eq!(result.len(), 0);
    }

    #[test]
    fn test_stems_some_nonchars() {
        let input = "@!?   Let's seeing...";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 6, suffix_offset: 9,
                          stemmed: String::from("let"), suffix: String::from("'s") },
            StemmedWord { stemmed_offset: 12, suffix_offset: 15,
                          stemmed: String::from("see"), suffix: String::from("ing") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode() {
        let input = "Ünicöde stemming";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 8,
                          stemmed: String::from("ünicöd"), suffix: String::from("e") },
            StemmedWord { stemmed_offset: 10, suffix_offset: 14,
                          stemmed: String::from("stem"), suffix: String::from("ming") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_stems_unicode_lowercase_has_more_bytes() {
        let input = "İ";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 3,
                          stemmed: String::from("i̇"), suffix: String::from("") },
            ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    // Some characters don't have a corresponding uppercase chararacter with the same byte size,
    // but when those uppercase letters are transformed to lowercase, they are transformed to the
    // same number of bytes combining several characters (although there is a single character for
    // it).
    // Here's an example in Python:
    //
    //     >>> u"Ρ̓".lower() == u"ῤ"
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".lower()
    //     False
    //     >>> u"Ρ̓".lower() == u"ῤ".upper().lower()
    //     True
    // Or Rust:
    //
    //    let upper = "Ρ̓";
    //    let lower = "ῤ";
    //    println!("lower({}) == {}: {}", upper, lower, upper.to_lowercase() == lower);
    //    println!("lower({}) == lower(upper({})): {}", upper, lower, upper.to_lowercase() == lower.to_uppercase().to_lowercase());
    //    lower(Ρ̓) == ῤ: false
    //    lower(Ρ̓) == lower(upper(ῤ)): true
    #[test]
    fn test_stems_unicode_lowercase_has_less_bytes() {
        // The input is: Ρ̓ῤῤ (11 bytes), lowercases is ῤῤῤ (9 bytes)
        let input = "\u{03A1}\u{0313}\u{03C1}\u{0313}\u{1FE4}";
        let result = Stems::new(input).collect::<Vec<StemmedWord>>();
        let expected = vec![
            StemmedWord { stemmed_offset: 0, suffix_offset: 9,
                          stemmed: String::from("\u{1FE4}\u{1FE4}\u{1FE4}"), suffix: String::from("") },
           ];
        assert_eq!(result.len(), expected.len());
        for (stem, expected_stem) in result.iter().zip(expected.iter()) {
            assert_eq!(stem, expected_stem);
        }
    }

    #[test]
    fn test_common_prefix_len() {
        let tests = vec![
            ("a", "a", 1),
            ("ab", "a", 1),
            ("a", "ab", 1),
            ("ab", "ab", 2),
            ("a", "b", 0),
            ("b", "a", 0),
            ("ab", "cd", 0),
            ("ab", "bc", 0),
            ("abc", "abd", 2),
            ("ac", "abcd", 1),
            (" a", "a", 0),
            ("a", "a ", 1),
            ("xyzabc", "xyz", 3),
            ("xyz", "xyzabc", 3),
            ("öxyz", "öx", 3),
            ];
        for (aa, bb, expected) in tests {
            let prefix_len = Stems::common_prefix_len(aa, bb);
            assert_eq!(prefix_len, expected);
        }
    }
}
<|endoftext|>


<commit_before>//! Shared types (e.g. for IP address and domain name)

/// A salted and cryptographically hashed string
///
/// A `Hash` represents a string that was salted and cryptographically hashed using the bcrypt
/// algorithm. The salt is stored alongside the hash. A `Hash` is well suited to store encrypted
/// passwords.
///
/// Since `Hash` implements `From<&str>`, the preferred method to obtain a `Hash` is to convert a
/// string slice using `Into<Hash>` as shown in the example below.
///
/// A `Hash` can be compared to a given string slice (== verified) with the method [`is`].
///
/// Because of different, randomly chosen salts, two hashes are (almost) never equal, even if
/// obtained from the exact same plain text.
///
/// [`is`]: #method.is
///
/// # Example
///
/// ```
/// use dynonym::types::Hash;
///
/// // Create
/// let h: Hash = "foo".into();
///
/// // Verify
/// assert!( h.is("foo"));
/// assert!(!h.is("bar"));
/// ```
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Hash(String);

impl Hash {
    /// Verifies whether `self` is a hashed version of the given string slice.
    pub fn is(&self, plain: &str) -> bool {
        use bcrypt::verify;
        verify(plain, &self.0).unwrap()
    }
}

impl<'a> From<&'a str> for Hash {
    fn from(plain: &'a str) -> Self {
        use bcrypt::{hash, DEFAULT_COST};
        let hash = hash(plain, DEFAULT_COST).unwrap();
        Hash(hash)
    }
}

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

    #[test]
    fn hash_eq() {
        let h: Hash = "foo".into();
        assert!(h.is("foo"));
    }

    #[test]
    fn hash_ne() {
        let h: Hash = "foo".into();
        assert!(!h.is("bar"));
    }

    #[test]
    fn hash_salt() {
        let h1: Hash = "foo".into();
        let h2: Hash = "foo".into();
        assert!(h1 != h2);  // different salts!
    }
}
<commit_msg>Add types::Domain<commit_after>//! Shared types (e.g. for IP address and domain name)

/// A domain name
///
/// A `Domain` represents a domain name as used in the Domain Name System (DNS).
///
/// Warning: At the moment, a `Domain` is merely a tuple struct wrapping a `String`. The API even
/// provides public access to the inner field. However, this is not considered to be stable and
/// may change in the future without being considered to be a breaking change.
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Domain(pub String);

/// A salted and cryptographically hashed string
///
/// A `Hash` represents a string that was salted and cryptographically hashed using the bcrypt
/// algorithm. The salt is stored alongside the hash. A `Hash` is well suited to store encrypted
/// passwords.
///
/// Since `Hash` implements `From<&str>`, the preferred method to obtain a `Hash` is to convert a
/// string slice using `Into<Hash>` as shown in the example below.
///
/// A `Hash` can be compared to a given string slice (== verified) with the method [`is`].
///
/// Because of different, randomly chosen salts, two hashes are (almost) never equal, even if
/// obtained from the exact same plain text.
///
/// [`is`]: #method.is
///
/// # Example
///
/// ```
/// use dynonym::types::Hash;
///
/// // Create
/// let h: Hash = "foo".into();
///
/// // Verify
/// assert!( h.is("foo"));
/// assert!(!h.is("bar"));
/// ```
#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct Hash(String);

impl Hash {
    /// Verifies whether `self` is a hashed version of the given string slice.
    pub fn is(&self, plain: &str) -> bool {
        use bcrypt::verify;
        verify(plain, &self.0).unwrap()
    }
}

impl<'a> From<&'a str> for Hash {
    fn from(plain: &'a str) -> Self {
        use bcrypt::{hash, DEFAULT_COST};
        let hash = hash(plain, DEFAULT_COST).unwrap();
        Hash(hash)
    }
}

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

    #[test]
    fn hash_eq() {
        let h: Hash = "foo".into();
        assert!(h.is("foo"));
    }

    #[test]
    fn hash_ne() {
        let h: Hash = "foo".into();
        assert!(!h.is("bar"));
    }

    #[test]
    fn hash_salt() {
        let h1: Hash = "foo".into();
        let h2: Hash = "foo".into();
        assert!(h1 != h2);  // different salts!
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::string::raw;

use cass_internal_api;
use error::Error as CassError;
use libc::c_char;
use std::vec::Vec;
use std::num::Int;

use std::io::net::ip::IpAddr;
use std::io::net::ip::Ipv4Addr;
use std::io::net::ip::Ipv6Addr;

//use cassandra_internal_api::CASS_COLLECTION_TYPE_LIST as TYPE_LIST;
//use cassandra_internal_api::CASS_COLLECTION_TYPE_MAP as TYPE_MAP;
//use cassandra_internal_api::CASS_COLLECTION_TYPE_SET as TYPE_SET;

use CollectionIterator;

#[allow(dead_code)]
pub struct CassValue {
  pub cass_value:*const cass_internal_api::CassValue
}

#[allow(dead_code)]
pub struct CassUuid {
  pub cass_uuid:cass_internal_api::CassUuid,
}

#[allow(dead_code)]
pub struct CassInet {
  pub cass_inet:cass_internal_api::CassInet,
}

#[allow(dead_code)]
pub struct CassBytes {
  pub cass_bytes:cass_internal_api::CassBytes,
}

#[allow(dead_code)]
pub struct CassDecimal {
  pub cass_decimal:cass_internal_api::CassDecimal,
}

#[allow(dead_code)]
pub struct CassValueType {
  cass_value_type:cass_internal_api::CassValueType,
}

#[allow(dead_code)]
impl CassValue {

  pub fn get_collection_iterator(&self) -> CollectionIterator {unsafe{
    CollectionIterator{cass_iterator:cass_internal_api::cass_iterator_from_collection(self.cass_value)}
  }}

  pub fn get_string(self) -> Result<String,CassError> {unsafe{
    let ref mut output:cass_internal_api::Struct_CassString_=cass_internal_api::cass_string_init(self.cass_value as *const i8);
    let ref mut output = *output;
    let err_string = cass_internal_api::cass_value_get_string(self.cass_value,&mut*output);
    let err = CassError{cass_error:err_string};
    let ref mut output = *output;
    if err.cass_error == cass_internal_api::CASS_OK {
      let length=output.length as uint;
      println!("item length: {}", length);
      println!("raw: {}", raw::from_parts(output.data as *mut u8, length, length));
      Ok(raw::from_parts(output.data as *mut u8, length, length))
    } else {Err(err)}
  }}

  pub fn get_int32(self) ->  Result<cass_internal_api::cass_int32_t,CassError> {unsafe{
    let ref mut output:cass_internal_api::cass_int32_t=0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_int32(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output)} else {return Err(err)}
  }}

  pub fn get_int64(self) ->  Result<cass_internal_api::cass_int64_t,CassError> {unsafe{
    let ref mut output:cass_internal_api::cass_int64_t=0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_int64(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output)} else {return Err(err)}
    }}

  pub fn get_float(self) ->  Result<cass_internal_api::cass_float_t,CassError> {unsafe{
    let ref mut output:cass_internal_api::cass_float_t=0.0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_float(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output)} else {return Err(err)}
  }}

  pub fn get_double(self) -> Result<f64,CassError> {unsafe{
    let ref mut output:f64=0.0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_double(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output)} else {return Err(err)}
  }}

  pub fn get_bool(self) -> Result<bool,CassError> {unsafe{
    let ref mut output:u32=0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_bool(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output> 0)} else {return Err(err)}
  }}

  pub fn get_uuid(self, output: CassUuid) -> CassError {unsafe{
    CassError{cass_error:cass_internal_api::cass_value_get_uuid(self.cass_value,output.cass_uuid)}
  }}

  pub fn get_inet(self, mut output: CassInet) -> CassError {unsafe{
    let ref mut cass_inet = output.cass_inet;
    CassError{cass_error:cass_internal_api::cass_value_get_inet(self.cass_value,cass_inet)}
  }}

  pub fn get_bytes(self, mut output: CassBytes) -> CassError {unsafe{
    let ref mut my_bytes = output.cass_bytes;
    CassError{cass_error:cass_internal_api::cass_value_get_bytes(self.cass_value,my_bytes)}
  }}

  pub fn get_decimal(self, mut output: CassDecimal) -> CassError {unsafe{
    let ref mut my_decimal = output.cass_decimal;
    CassError{cass_error:cass_internal_api::cass_value_get_decimal(self.cass_value,my_decimal)}
  }}

  pub fn is_null(self) -> bool {unsafe{
    !cass_internal_api::cass_value_is_null(self.cass_value) == Int::zero()
  }}

  pub fn is_collection(self) -> bool {unsafe{
    !cass_internal_api::cass_value_is_collection(self.cass_value) == Int::zero()
  }}

  pub fn item_count(self) -> u64 {unsafe{
    cass_internal_api::cass_value_item_count(self.cass_value)
  }}

  pub fn primary_sub_type(self) -> CassValueType {unsafe{
    CassValueType{cass_value_type:cass_internal_api::cass_value_primary_sub_type(self.cass_value)}
  }}

  pub fn secondary_sub_type(self) -> CassValueType {unsafe{
    CassValueType{cass_value_type:cass_internal_api::cass_value_secondary_sub_type(self.cass_value)}
  }}

  //FIXME segfaults
  pub fn generate_timeuuid() -> CassUuid {unsafe{
    let output:cass_internal_api::CassUuid = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    cass_internal_api::cass_uuid_generate_time(output);
    CassUuid{cass_uuid:output}
  }}

  fn cass_uuid_from_time(time: cass_internal_api::cass_uint64_t, output: CassUuid) {unsafe{
    cass_internal_api::cass_uuid_from_time(time,output.cass_uuid);
  }}

  pub fn cass_uuid_min_from_time(time: cass_internal_api::cass_uint64_t, output: CassUuid) {unsafe{
    cass_internal_api::cass_uuid_min_from_time(time,output.cass_uuid)
  }}

  pub fn cass_uuid_max_from_time(time: cass_internal_api::cass_uint64_t, output: CassUuid) {unsafe{
    cass_internal_api::cass_uuid_max_from_time(time,output.cass_uuid)
  }}

  pub fn cass_uuid_generate_random(output: CassUuid) {unsafe{
    cass_internal_api::cass_uuid_generate_random(output.cass_uuid)
  }}

  pub fn cass_uuid_timestamp(uuid: CassUuid) -> cass_internal_api::cass_uint64_t {unsafe{
    cass_internal_api::cass_uuid_timestamp(uuid.cass_uuid)
  }}

  pub fn cass_uuid_version(uuid: CassUuid) -> cass_internal_api::cass_uint8_t {unsafe{
    cass_internal_api::cass_uuid_version(uuid.cass_uuid)
  }}

  pub fn cass_uuid_string(uuid: CassUuid, output: *mut c_char) {unsafe{
    cass_internal_api::cass_uuid_string(uuid.cass_uuid,output)
  }}

  fn cass_inet_init_v4(address: *const cass_internal_api::cass_uint8_t) -> CassInet {unsafe{
    CassInet{cass_inet:cass_internal_api::cass_inet_init_v4(address)}
  }}

  pub fn build_cass_inet(addr: IpAddr) -> CassInet {
    match addr {
      Ipv4Addr(oct1,oct2,oct3,oct4) => {
        let mut v:Vec<u8> = Vec::with_capacity(4);
        v.push(oct1);v.push(oct2);v.push(oct3);v.push(oct4);
        CassValue::cass_inet_init_v4(v.as_ptr())
      },
      Ipv6Addr(_,_,_,_,_,_,_,_) => {
        //let v:Vec<u16> = Vec::with_capacity(8);
        panic!("FIXME: can't handle v6 addresses yet");
        //~ v.push(seg1);v.push(seg2);v.push(seg3);v.push(seg4);
        //~ v.push(seg5);v.push(seg6);v.push(seg7);v.push(seg8);
        //~ CassValue::cass_inet_init_v4(v.as_ptr())
      },
    }
  }

  pub fn build_cass_bytes(data: Vec<u8>) -> CassBytes {
    CassValue::cass_bytes_init(data.as_slice().as_ptr(), data.len() as u64)
  }

  pub fn build_cass_decimal(int_value: i32, scale:i32) -> CassDecimal {
    let varint = CassValue::build_cass_bytes(int_value.to_string().into_bytes());
    CassValue::cass_decimal_init(scale,varint)
  }

  pub fn str_to_cass_string(string:&String) -> cass_internal_api::CassString {unsafe{
     cass_internal_api::cass_string_init2(string.as_bytes().as_ptr() as *const i8,string.as_bytes().len() as u64)
  }}

  pub fn cass_string_to_str(cass_str:cass_internal_api::CassString) -> String {unsafe{
    raw::from_buf_len(cass_str.data as *const u8,cass_str.length as uint)
  }}

  //PRIVATE METHODS
  fn cass_inet_init_v6(address: *const cass_internal_api::cass_uint8_t) -> CassInet {unsafe{
    CassInet{cass_inet:cass_internal_api::cass_inet_init_v6(address)}
  }}

  fn cass_decimal_init(scale: cass_internal_api::cass_int32_t, varint: CassBytes) -> CassDecimal {unsafe{
    CassDecimal{cass_decimal:cass_internal_api::cass_decimal_init(scale,varint.cass_bytes)}
  }}

  fn cass_bytes_init(data: *const cass_internal_api::cass_byte_t, size: cass_internal_api::cass_size_t) -> CassBytes {unsafe{
    CassBytes{cass_bytes:cass_internal_api::cass_bytes_init(data,size)}
  }}
}

#[cfg(test)]
  mod tests {
    use cass_internal_api;
    #[test]
    fn string_wrapping() {
      let test_string = "test_string2345678".to_string();
      let cass_string:cassandra_internal_api::CassString = super::CassValue::str_to_cass_string(&test_string);
      //println!("cassstr: {}", cass_string);
      let reconstituted:String = super::CassValue::cass_string_to_str(cass_string);
      println!("reconstituted: {}", reconstituted);
      assert!(test_string == reconstituted);
    }
}
<commit_msg>fixed typo breaking tests<commit_after>extern crate libc;

use std::string::raw;

use cass_internal_api;
use error::Error as CassError;
use libc::c_char;
use std::vec::Vec;
use std::num::Int;

use std::io::net::ip::IpAddr;
use std::io::net::ip::Ipv4Addr;
use std::io::net::ip::Ipv6Addr;

//use cassandra_internal_api::CASS_COLLECTION_TYPE_LIST as TYPE_LIST;
//use cassandra_internal_api::CASS_COLLECTION_TYPE_MAP as TYPE_MAP;
//use cassandra_internal_api::CASS_COLLECTION_TYPE_SET as TYPE_SET;

use CollectionIterator;

#[allow(dead_code)]
pub struct CassValue {
  pub cass_value:*const cass_internal_api::CassValue
}

#[allow(dead_code)]
pub struct CassUuid {
  pub cass_uuid:cass_internal_api::CassUuid,
}

#[allow(dead_code)]
pub struct CassInet {
  pub cass_inet:cass_internal_api::CassInet,
}

#[allow(dead_code)]
pub struct CassBytes {
  pub cass_bytes:cass_internal_api::CassBytes,
}

#[allow(dead_code)]
pub struct CassDecimal {
  pub cass_decimal:cass_internal_api::CassDecimal,
}

#[allow(dead_code)]
pub struct CassValueType {
  cass_value_type:cass_internal_api::CassValueType,
}

#[allow(dead_code)]
impl CassValue {

  pub fn get_collection_iterator(&self) -> CollectionIterator {unsafe{
    CollectionIterator{cass_iterator:cass_internal_api::cass_iterator_from_collection(self.cass_value)}
  }}

  pub fn get_string(self) -> Result<String,CassError> {unsafe{
    let ref mut output:cass_internal_api::Struct_CassString_=cass_internal_api::cass_string_init(self.cass_value as *const i8);
    let ref mut output = *output;
    let err_string = cass_internal_api::cass_value_get_string(self.cass_value,&mut*output);
    let err = CassError{cass_error:err_string};
    let ref mut output = *output;
    if err.cass_error == cass_internal_api::CASS_OK {
      let length=output.length as uint;
      println!("item length: {}", length);
      println!("raw: {}", raw::from_parts(output.data as *mut u8, length, length));
      Ok(raw::from_parts(output.data as *mut u8, length, length))
    } else {Err(err)}
  }}

  pub fn get_int32(self) ->  Result<cass_internal_api::cass_int32_t,CassError> {unsafe{
    let ref mut output:cass_internal_api::cass_int32_t=0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_int32(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output)} else {return Err(err)}
  }}

  pub fn get_int64(self) ->  Result<cass_internal_api::cass_int64_t,CassError> {unsafe{
    let ref mut output:cass_internal_api::cass_int64_t=0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_int64(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output)} else {return Err(err)}
    }}

  pub fn get_float(self) ->  Result<cass_internal_api::cass_float_t,CassError> {unsafe{
    let ref mut output:cass_internal_api::cass_float_t=0.0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_float(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output)} else {return Err(err)}
  }}

  pub fn get_double(self) -> Result<f64,CassError> {unsafe{
    let ref mut output:f64=0.0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_double(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output)} else {return Err(err)}
  }}

  pub fn get_bool(self) -> Result<bool,CassError> {unsafe{
    let ref mut output:u32=0;
    let err = CassError{cass_error:cass_internal_api::cass_value_get_bool(self.cass_value,output)};
    if err.cass_error == cass_internal_api::CASS_OK {return Ok(*output> 0)} else {return Err(err)}
  }}

  pub fn get_uuid(self, output: CassUuid) -> CassError {unsafe{
    CassError{cass_error:cass_internal_api::cass_value_get_uuid(self.cass_value,output.cass_uuid)}
  }}

  pub fn get_inet(self, mut output: CassInet) -> CassError {unsafe{
    let ref mut cass_inet = output.cass_inet;
    CassError{cass_error:cass_internal_api::cass_value_get_inet(self.cass_value,cass_inet)}
  }}

  pub fn get_bytes(self, mut output: CassBytes) -> CassError {unsafe{
    let ref mut my_bytes = output.cass_bytes;
    CassError{cass_error:cass_internal_api::cass_value_get_bytes(self.cass_value,my_bytes)}
  }}

  pub fn get_decimal(self, mut output: CassDecimal) -> CassError {unsafe{
    let ref mut my_decimal = output.cass_decimal;
    CassError{cass_error:cass_internal_api::cass_value_get_decimal(self.cass_value,my_decimal)}
  }}

  pub fn is_null(self) -> bool {unsafe{
    !cass_internal_api::cass_value_is_null(self.cass_value) == Int::zero()
  }}

  pub fn is_collection(self) -> bool {unsafe{
    !cass_internal_api::cass_value_is_collection(self.cass_value) == Int::zero()
  }}

  pub fn item_count(self) -> u64 {unsafe{
    cass_internal_api::cass_value_item_count(self.cass_value)
  }}

  pub fn primary_sub_type(self) -> CassValueType {unsafe{
    CassValueType{cass_value_type:cass_internal_api::cass_value_primary_sub_type(self.cass_value)}
  }}

  pub fn secondary_sub_type(self) -> CassValueType {unsafe{
    CassValueType{cass_value_type:cass_internal_api::cass_value_secondary_sub_type(self.cass_value)}
  }}

  //FIXME segfaults
  pub fn generate_timeuuid() -> CassUuid {unsafe{
    let output:cass_internal_api::CassUuid = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    cass_internal_api::cass_uuid_generate_time(output);
    CassUuid{cass_uuid:output}
  }}

  fn cass_uuid_from_time(time: cass_internal_api::cass_uint64_t, output: CassUuid) {unsafe{
    cass_internal_api::cass_uuid_from_time(time,output.cass_uuid);
  }}

  pub fn cass_uuid_min_from_time(time: cass_internal_api::cass_uint64_t, output: CassUuid) {unsafe{
    cass_internal_api::cass_uuid_min_from_time(time,output.cass_uuid)
  }}

  pub fn cass_uuid_max_from_time(time: cass_internal_api::cass_uint64_t, output: CassUuid) {unsafe{
    cass_internal_api::cass_uuid_max_from_time(time,output.cass_uuid)
  }}

  pub fn cass_uuid_generate_random(output: CassUuid) {unsafe{
    cass_internal_api::cass_uuid_generate_random(output.cass_uuid)
  }}

  pub fn cass_uuid_timestamp(uuid: CassUuid) -> cass_internal_api::cass_uint64_t {unsafe{
    cass_internal_api::cass_uuid_timestamp(uuid.cass_uuid)
  }}

  pub fn cass_uuid_version(uuid: CassUuid) -> cass_internal_api::cass_uint8_t {unsafe{
    cass_internal_api::cass_uuid_version(uuid.cass_uuid)
  }}

  pub fn cass_uuid_string(uuid: CassUuid, output: *mut c_char) {unsafe{
    cass_internal_api::cass_uuid_string(uuid.cass_uuid,output)
  }}

  fn cass_inet_init_v4(address: *const cass_internal_api::cass_uint8_t) -> CassInet {unsafe{
    CassInet{cass_inet:cass_internal_api::cass_inet_init_v4(address)}
  }}

  pub fn build_cass_inet(addr: IpAddr) -> CassInet {
    match addr {
      Ipv4Addr(oct1,oct2,oct3,oct4) => {
        let mut v:Vec<u8> = Vec::with_capacity(4);
        v.push(oct1);v.push(oct2);v.push(oct3);v.push(oct4);
        CassValue::cass_inet_init_v4(v.as_ptr())
      },
      Ipv6Addr(_,_,_,_,_,_,_,_) => {
        //let v:Vec<u16> = Vec::with_capacity(8);
        panic!("FIXME: can't handle v6 addresses yet");
        //~ v.push(seg1);v.push(seg2);v.push(seg3);v.push(seg4);
        //~ v.push(seg5);v.push(seg6);v.push(seg7);v.push(seg8);
        //~ CassValue::cass_inet_init_v4(v.as_ptr())
      },
    }
  }

  pub fn build_cass_bytes(data: Vec<u8>) -> CassBytes {
    CassValue::cass_bytes_init(data.as_slice().as_ptr(), data.len() as u64)
  }

  pub fn build_cass_decimal(int_value: i32, scale:i32) -> CassDecimal {
    let varint = CassValue::build_cass_bytes(int_value.to_string().into_bytes());
    CassValue::cass_decimal_init(scale,varint)
  }

  pub fn str_to_cass_string(string:&String) -> cass_internal_api::CassString {unsafe{
     cass_internal_api::cass_string_init2(string.as_bytes().as_ptr() as *const i8,string.as_bytes().len() as u64)
  }}

  pub fn cass_string_to_str(cass_str:cass_internal_api::CassString) -> String {unsafe{
    raw::from_buf_len(cass_str.data as *const u8,cass_str.length as uint)
  }}

  //PRIVATE METHODS
  fn cass_inet_init_v6(address: *const cass_internal_api::cass_uint8_t) -> CassInet {unsafe{
    CassInet{cass_inet:cass_internal_api::cass_inet_init_v6(address)}
  }}

  fn cass_decimal_init(scale: cass_internal_api::cass_int32_t, varint: CassBytes) -> CassDecimal {unsafe{
    CassDecimal{cass_decimal:cass_internal_api::cass_decimal_init(scale,varint.cass_bytes)}
  }}

  fn cass_bytes_init(data: *const cass_internal_api::cass_byte_t, size: cass_internal_api::cass_size_t) -> CassBytes {unsafe{
    CassBytes{cass_bytes:cass_internal_api::cass_bytes_init(data,size)}
  }}
}

#[cfg(test)]
  mod tests {
    use cass_internal_api;
    #[test]
    fn string_wrapping() {
      let test_string = "test_string2345678".to_string();
      let cass_string:cass_internal_api::CassString = super::CassValue::str_to_cass_string(&test_string);
      //println!("cassstr: {}", cass_string);
      let reconstituted:String = super::CassValue::cass_string_to_str(cass_string);
      println!("reconstituted: {}", reconstituted);
      assert!(test_string == reconstituted);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread::{self, sleep, JoinHandle};
use std::time::Duration;

use metrics::*;

/// Internal storage of store data.
pub struct BaseStore {
    counts: HashMap<String, u64>,
    measures: HashMap<String, Vec<f64>>,
    samples: HashMap<String, f64>,
}

impl BaseStore {
    pub fn new() -> BaseStore {
        BaseStore {
            counts: HashMap::new(),
            measures: HashMap::new(),
            samples: HashMap::new(),
        }
    }

    fn record(&mut self, metrics: Vec<Metric>) {
        for metric in metrics {
            match metric {
                Count(name, value) => {
                    let count = self.counts.entry(name).or_insert(0);
                    *count += value;
                },
                Measure(name, value) => {
                    let values = self.measures.entry(name).or_insert(Vec::new());
                    values.push(value);
                },
                Sample(name, value) => {
                    let entry = self.samples.entry(name).or_insert(0.0);
                    *entry = value;
                }
            }
        }
    } // fn record

    fn flush(&mut self) -> AggregatedMetrics {
        let mut aggregated = AggregatedMetrics::new();

        aggregated.aggregate_counts(self.counts.iter());
        self.counts = HashMap::new();

        aggregated.aggregate_measures(self.measures.iter());
        self.measures = HashMap::new();

        aggregated.aggregate_samples(self.samples.iter());
        self.samples = HashMap::new();

        aggregated
    } // fn flush
}

/// Thread-safe interface to the store. In most cases this is what you
/// want to use.
#[derive(Clone)]
pub struct SharedStore {
    shared: Arc<Mutex<BaseStore>>,
}

impl SharedStore {
    pub fn new() -> SharedStore {
        SharedStore {
            shared: Arc::new(Mutex::new(BaseStore::new())),
        }
    }

    pub fn record(&self, metrics: Vec<Metric>) {
        let mut store = self.shared.lock().unwrap();
        store.record(metrics)
    }

    pub fn flush(&self) -> AggregatedMetrics {
        let mut store = self.shared.lock().unwrap();
        store.flush()
    }

    pub fn flush_every<F>(&self, interval: Duration, callback: F) -> JoinHandle<()>
        where F: Fn(AggregatedMetrics) + Send + 'static {

        let shared = self.shared.clone();

        thread::spawn(move || {
            loop {
                sleep(interval);

                let mut store = shared.lock().unwrap();
                let aggregated = store.flush();

                callback(aggregated);
            }
        })
    }
}

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

    use super::BaseStore;
    use super::super::metrics::*;

    fn get_store_with_metrics() -> BaseStore {
        let metrics = vec![
            Count("foo".to_owned(), 1),
            Count("foo".to_owned(), 2),
            Measure("bar".to_owned(), 3.4),
            Measure("bar".to_owned(), 5.6),
            Sample("baz".to_owned(), 7.8),
            Sample("baz".to_owned(), 9.0),
        ];

        let mut store = BaseStore::new();

        store.record(metrics);

        store
    }

    #[test]
    fn it_records_count() {
        let store = get_store_with_metrics();

        let mut expected_counts = HashMap::new();
        expected_counts.insert("foo".to_owned(), 3);

        assert_eq!(store.counts, expected_counts)
    }

    #[test]
    fn it_records_measure() {
        let store = get_store_with_metrics();

        let mut expected_measures: HashMap<String, Vec<f64>> = HashMap::new();
        expected_measures.insert("bar".to_owned(), vec![3.4, 5.6]);

        assert_eq!(store.measures, expected_measures)
    }

    #[test]
    fn it_records_sample() {
        let store = get_store_with_metrics();

        let mut expected_samples = HashMap::new();
        expected_samples.insert("baz".to_owned(), 9.0);

        assert_eq!(store.samples, expected_samples)
    }
}
<commit_msg>Clear hash-maps instead of de-allocating and re-allocating them<commit_after>use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread::{self, sleep, JoinHandle};
use std::time::Duration;

use metrics::*;

/// Internal storage of store data.
pub struct BaseStore {
    counts: HashMap<String, u64>,
    measures: HashMap<String, Vec<f64>>,
    samples: HashMap<String, f64>,
}

impl BaseStore {
    pub fn new() -> BaseStore {
        BaseStore {
            counts: HashMap::new(),
            measures: HashMap::new(),
            samples: HashMap::new(),
        }
    }

    fn record(&mut self, metrics: Vec<Metric>) {
        for metric in metrics {
            match metric {
                Count(name, value) => {
                    let count = self.counts.entry(name).or_insert(0);
                    *count += value;
                },
                Measure(name, value) => {
                    let values = self.measures.entry(name).or_insert(Vec::new());
                    values.push(value);
                },
                Sample(name, value) => {
                    let entry = self.samples.entry(name).or_insert(0.0);
                    *entry = value;
                }
            }
        }
    } // fn record

    fn flush(&mut self) -> AggregatedMetrics {
        let mut aggregated = AggregatedMetrics::new();

        aggregated.aggregate_counts(self.counts.iter());
        self.counts.clear();

        aggregated.aggregate_measures(self.measures.iter());
        self.measures.clear();

        aggregated.aggregate_samples(self.samples.iter());
        self.samples.clear();

        aggregated
    } // fn flush
}

/// Thread-safe interface to the store. In most cases this is what you
/// want to use.
#[derive(Clone)]
pub struct SharedStore {
    shared: Arc<Mutex<BaseStore>>,
}

impl SharedStore {
    pub fn new() -> SharedStore {
        SharedStore {
            shared: Arc::new(Mutex::new(BaseStore::new())),
        }
    }

    pub fn record(&self, metrics: Vec<Metric>) {
        let mut store = self.shared.lock().unwrap();
        store.record(metrics)
    }

    pub fn flush(&self) -> AggregatedMetrics {
        let mut store = self.shared.lock().unwrap();
        store.flush()
    }

    pub fn flush_every<F>(&self, interval: Duration, callback: F) -> JoinHandle<()>
        where F: Fn(AggregatedMetrics) + Send + 'static {

        let shared = self.shared.clone();

        thread::spawn(move || {
            loop {
                sleep(interval);

                let mut store = shared.lock().unwrap();
                let aggregated = store.flush();

                callback(aggregated);
            }
        })
    }
}

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

    use super::BaseStore;
    use super::super::metrics::*;

    fn get_store_with_metrics() -> BaseStore {
        let metrics = vec![
            Count("foo".to_owned(), 1),
            Count("foo".to_owned(), 2),
            Measure("bar".to_owned(), 3.4),
            Measure("bar".to_owned(), 5.6),
            Sample("baz".to_owned(), 7.8),
            Sample("baz".to_owned(), 9.0),
        ];

        let mut store = BaseStore::new();

        store.record(metrics);

        store
    }

    #[test]
    fn it_records_count() {
        let store = get_store_with_metrics();

        let mut expected_counts = HashMap::new();
        expected_counts.insert("foo".to_owned(), 3);

        assert_eq!(store.counts, expected_counts)
    }

    #[test]
    fn it_records_measure() {
        let store = get_store_with_metrics();

        let mut expected_measures: HashMap<String, Vec<f64>> = HashMap::new();
        expected_measures.insert("bar".to_owned(), vec![3.4, 5.6]);

        assert_eq!(store.measures, expected_measures)
    }

    #[test]
    fn it_records_sample() {
        let store = get_store_with_metrics();

        let mut expected_samples = HashMap::new();
        expected_samples.insert("baz".to_owned(), 9.0);

        assert_eq!(store.samples, expected_samples)
    }
}
<|endoftext|>


<commit_before>#![doc(html_root_url = "https://urschrei.github.io/lonlat_bng/")]
//! This module provides utilities to the conversions module
use ostn15_phf::ostn15_lookup;
use std;
use std::fmt;

// fn helmert(lon_vec: [&f64], lat_vec: [&f64]) -> (Vec<f64>, Vec<f64>) {
//     let t_array = Vec3::new(TX, TY, TZ);
//     let params = Mat3::new(1. + s, RZS, -RYS, -RZS, 1. + s, RXS, RYS, -RXS, 1. + s);
//     // let zeros: Vec<&f64> = vec![&1.; lon_vec.len()];
//     let test = vec![1, 2, 3, 4, 5, 6];
//     // let mut combined: Vec<&f64> = lon_vec.extend(lat_vec.iter()).collect();
//     // combined.extend(zeros.iter().cloned());
//     // let tmat = DMat::from_row_vec(3, zeros.len(), &combined);
//     let tmat = DMat::from_row_vec(3, 2, &test);
//     // build input vector of x, y, and z columns
//     // let inp = DMat::from_row_vec(3, 1, vec![lon_vec, lat_vec, h_vec]);
//     (vec![1.], vec![2.])
// }

/// Bounds checking for input values
pub fn check<T>(to_check: T, bounds: (T, T)) -> Result<T, ()>
where
    T: std::cmp::PartialOrd + fmt::Display + Copy,
{
    match to_check {
        to_check if bounds.0 <= to_check && to_check <= bounds.1 => Ok(to_check),
        _ => Err(()),
    }
}

/// Round an Easting or Northing coordinate to the nearest millimetre
pub trait ToMm {
    fn round_to_mm(self) -> f64;
}

impl ToMm for f64 {
    fn round_to_mm(self) -> f64 {
        (self * 1000.).round() as f64 / 1000.
    }
}

/// Round a float to eight decimal places
pub fn round_to_eight(x: f64, y: f64) -> (f64, f64) {
    let new_x = (x * 100000000.).round() as f64 / 100000000.;
    let new_y = (y * 100000000.).round() as f64 / 100000000.;
    (new_x, new_y)
}

/// Try to get OSTN15 shift parameters, and calculate offsets
pub fn get_ostn_ref(x: i32, y: i32) -> Result<(f64, f64, f64), ()> {
    let key = x + (y * 701) + 1;
    // Some or None, so convert to Result, which we can try!
    let result = ostn15_lookup(&key).ok_or(())?;
    Ok((result.0, result.1, result.2))
}

// Input values must be valid ETRS89 grid references
// See p20 of the transformation user guide at
// https://www.ordnancesurvey.co.uk/business-and-government/help-and-support/navigation-technology/os-net/formats-for-developers.html
/// Calculate OSTN15 shifts for a given coordinate
pub fn ostn15_shifts(x: f64, y: f64) -> Result<(f64, f64, f64), ()> {
    let e_index = (x / 1000.) as i32;
    let n_index = (y / 1000.) as i32;

    // eastings and northings of the south-west corner of the cell
    let x0 = e_index as i32 * 1000;
    let y0 = n_index as i32 * 1000;

    // The easting, northing and geoid shifts for the four corners of the cell
    // any of these could be Err, so use try!

    // bottom-left grid intersection
    let s0: (f64, f64, f64) = get_ostn_ref(e_index, n_index)?;
    // bottom-right
    let s1: (f64, f64, f64) = get_ostn_ref(e_index + 1, n_index)?;
    // top-left
    let s2: (f64, f64, f64) = get_ostn_ref(e_index, n_index + 1)?;
    // top-right
    let s3: (f64, f64, f64) = get_ostn_ref(e_index + 1, n_index + 1)?;

    // offset within square
    let dx = x - f64::from(x0);
    let dy = y - f64::from(y0);

    let t = dx / 1000.;
    let u = dy / 1000.;

    let f0 = (1. - t) * (1. - u);
    let f1 = t * (1. - u);
    let f2 = (1. - t) * u;
    let f3 = t * u;

    // bilinear interpolation, to obtain the actual shifts
    let se = f0 * s0.0 + f1 * s1.0 + f2 * s2.0 + f3 * s3.0;
    let sn = f0 * s0.1 + f1 * s1.1 + f2 * s2.1 + f3 * s3.1;
    let sg = f0 * s0.2 + f1 * s1.2 + f2 * s2.2 + f3 * s3.2;

    Ok((se.round_to_mm(), sn.round_to_mm(), sg.round_to_mm()))
}

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

    #[test]
    // original coordinates are 651307.003, 313255.686
    fn test_ostn_hashmap_retrieval() {
        let eastings = 651;
        let northings = 313;
        let expected = (102.787, -78.242, 44.236);
        assert_eq!(expected, get_ostn_ref(eastings, northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_failed_ostn_hashmap_retrieval() {
        // we're try!ing this in the shift calculation function, so an Err is fine
        let eastings = 999;
        let northings = 999;
        let expected = (1., 1., 1.);
        assert_eq!(expected, get_ostn_ref(eastings, northings).unwrap());
    }

    #[test]
    fn test_ostn15_shift_incorporation() {
        // these are the input values and corrections on p20-21
        let eastings = 651307.003;
        let northings = 313255.686;
        let expected = (102.801, -78.236, 44.228);
        assert_eq!(expected, ostn15_shifts(eastings, northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_min_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // below min_lon
        check(&-6.379881, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_min_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // below min lat
        check(&49.871158, (&min_lat, &max_lat)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // above max lon
        check(&1.768961, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // above max lat
        check(&55.811742, (&min_lat, &max_lat)).unwrap();
    }
}
<commit_msg>Added some comments to the interpolation function<commit_after>#![doc(html_root_url = "https://urschrei.github.io/lonlat_bng/")]
//! This module provides utilities to the conversions module
use ostn15_phf::ostn15_lookup;
use std;
use std::fmt;

// fn helmert(lon_vec: [&f64], lat_vec: [&f64]) -> (Vec<f64>, Vec<f64>) {
//     let t_array = Vec3::new(TX, TY, TZ);
//     let params = Mat3::new(1. + s, RZS, -RYS, -RZS, 1. + s, RXS, RYS, -RXS, 1. + s);
//     // let zeros: Vec<&f64> = vec![&1.; lon_vec.len()];
//     let test = vec![1, 2, 3, 4, 5, 6];
//     // let mut combined: Vec<&f64> = lon_vec.extend(lat_vec.iter()).collect();
//     // combined.extend(zeros.iter().cloned());
//     // let tmat = DMat::from_row_vec(3, zeros.len(), &combined);
//     let tmat = DMat::from_row_vec(3, 2, &test);
//     // build input vector of x, y, and z columns
//     // let inp = DMat::from_row_vec(3, 1, vec![lon_vec, lat_vec, h_vec]);
//     (vec![1.], vec![2.])
// }

/// Bounds checking for input values
pub fn check<T>(to_check: T, bounds: (T, T)) -> Result<T, ()>
where
    T: std::cmp::PartialOrd + fmt::Display + Copy,
{
    match to_check {
        to_check if bounds.0 <= to_check && to_check <= bounds.1 => Ok(to_check),
        _ => Err(()),
    }
}

/// Round an Easting or Northing coordinate to the nearest millimetre
pub trait ToMm {
    fn round_to_mm(self) -> f64;
}

impl ToMm for f64 {
    fn round_to_mm(self) -> f64 {
        (self * 1000.).round() as f64 / 1000.
    }
}

/// Round a float to eight decimal places
pub fn round_to_eight(x: f64, y: f64) -> (f64, f64) {
    let new_x = (x * 100000000.).round() as f64 / 100000000.;
    let new_y = (y * 100000000.).round() as f64 / 100000000.;
    (new_x, new_y)
}

/// Try to get OSTN15 shift parameters, and calculate offsets
pub fn get_ostn_ref(x: i32, y: i32) -> Result<(f64, f64, f64), ()> {
    let key = x + (y * 701) + 1;
    // Some or None, so convert to Result, which we can try!
    let result = ostn15_lookup(&key).ok_or(())?;
    Ok((result.0, result.1, result.2))
}

// Input values must be valid ETRS89 grid references
// See p20 of the transformation user guide at
// https://www.ordnancesurvey.co.uk/business-and-government/help-and-support/navigation-technology/os-net/formats-for-developers.html
/// Calculate OSTN15 shifts for a given coordinate
pub fn ostn15_shifts(x: f64, y: f64) -> Result<(f64, f64, f64), ()> {
    let e_index = (x / 1000.) as i32;
    let n_index = (y / 1000.) as i32;

    // eastings and northings of the south-west corner of the cell
    let x0 = e_index as i32 * 1000;
    let y0 = n_index as i32 * 1000;

    // The easting, northing and geoid shifts for the four corners of the cell
    // any of these could be Err, so use try!

    // intersections
    // this is a 3 x 4 matrix (using column-major order)
    // bottom-left grid intersection
    let s0: (f64, f64, f64) = get_ostn_ref(e_index, n_index)?;
    // bottom-right
    let s1: (f64, f64, f64) = get_ostn_ref(e_index + 1, n_index)?;
    // top-left
    let s2: (f64, f64, f64) = get_ostn_ref(e_index, n_index + 1)?;
    // top-right
    let s3: (f64, f64, f64) = get_ostn_ref(e_index + 1, n_index + 1)?;

    // offset within square
    let dx = x - f64::from(x0);
    let dy = y - f64::from(y0);

    let t = dx / 1000.;
    let u = dy / 1000.;

    // Calculation of the weights for each intersection (W)
    // this is a 4 x 1 matrix
    let f0 = (1. - t) * (1. - u);
    let f1 = t * (1. - u);
    let f2 = (1. - t) * u;
    let f3 = t * u;

    // bilinear interpolation, to obtain the actual shifts
    // We could also do a dot product:
    // weights.dot(offsets)
    let se = f0 * s0.0 + f1 * s1.0 + f2 * s2.0 + f3 * s3.0;
    let sn = f0 * s0.1 + f1 * s1.1 + f2 * s2.1 + f3 * s3.1;
    // this isn't needed for this library, since it's a height offset
    let sg = f0 * s0.2 + f1 * s1.2 + f2 * s2.2 + f3 * s3.2;

    Ok((se.round_to_mm(), sn.round_to_mm(), sg.round_to_mm()))
}

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

    #[test]
    // original coordinates are 651307.003, 313255.686
    fn test_ostn_hashmap_retrieval() {
        let eastings = 651;
        let northings = 313;
        let expected = (102.787, -78.242, 44.236);
        assert_eq!(expected, get_ostn_ref(eastings, northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_failed_ostn_hashmap_retrieval() {
        // we're try!ing this in the shift calculation function, so an Err is fine
        let eastings = 999;
        let northings = 999;
        let expected = (1., 1., 1.);
        assert_eq!(expected, get_ostn_ref(eastings, northings).unwrap());
    }

    #[test]
    fn test_ostn15_shift_incorporation() {
        // these are the input values and corrections on p20-21
        let eastings = 651307.003;
        let northings = 313255.686;
        let expected = (102.801, -78.236, 44.228);
        assert_eq!(expected, ostn15_shifts(eastings, northings).unwrap());
    }

    #[test]
    #[should_panic]
    fn test_min_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // below min_lon
        check(&-6.379881, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_min_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // below min lat
        check(&49.871158, (&min_lat, &max_lat)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lon_extents() {
        let max_lon = 1.768960;
        let min_lon = -6.379880;
        // above max lon
        check(&1.768961, (&min_lon, &max_lon)).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_max_lat_extents() {
        let max_lat = 55.811741;
        let min_lat = 49.871159;
        // above max lat
        check(&55.811742, (&min_lat, &max_lat)).unwrap();
    }
}
<|endoftext|>


<commit_before>use diesel;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use conduit::{Request, Response};
use time::Timespec;
use conduit_router::RequestParams;
use rustc_serialize::json;

use db::RequestTransaction;
use user::{RequestUser, AuthenticationSource};
use util::{RequestUtils, CargoResult, ChainError, bad_request, read_fill};
use schema::api_tokens;

/// The model representing a row in the `api_tokens` database table.
#[derive(Clone, Debug, PartialEq, Eq, Identifiable, Queryable)]
pub struct ApiToken {
    pub id: i32,
    pub user_id: i32,
    pub token: String,
    pub name: String,
    pub created_at: Timespec,
    pub last_used_at: Option<Timespec>,
}

/// The serialization format for the `ApiToken` model without its token value.
#[derive(RustcDecodable, RustcEncodable)]
pub struct EncodableApiToken {
    pub id: i32,
    pub name: String,
    pub created_at: String,
    pub last_used_at: Option<String>,
}

/// The serialization format for the `ApiToken` model with its token value.
/// This should only be used when initially creating a new token to minimize
/// the chance of token leaks.
#[derive(RustcDecodable, RustcEncodable)]
pub struct EncodableApiTokenWithToken {
    pub id: i32,
    pub name: String,
    pub token: String,
    pub created_at: String,
    pub last_used_at: Option<String>,
}

impl ApiToken {
    /// Generates a new named API token for a user
    pub fn insert(conn: &PgConnection, user_id: i32, name: &str) -> CargoResult<ApiToken> {
        #[table_name = "api_tokens"]
        #[derive(Insertable, AsChangeset)]
        struct NewApiToken<'a> {
            user_id: i32,
            name: &'a str,
        }

        diesel::insert(&NewApiToken {
            user_id: user_id,
            name: name,
        }).into(api_tokens::table)
            .get_result::<ApiToken>(conn)
            .map_err(From::from)
    }

    /// Deletes the provided API token if it belongs to the provided user
    pub fn delete(conn: &PgConnection, user_id: i32, id: i32) -> CargoResult<()> {
        diesel::delete(api_tokens::table.find(id).filter(
            api_tokens::user_id.eq(user_id),
        )).execute(conn)?;
        Ok(())
    }

    pub fn find_for_user(conn: &PgConnection, user_id: i32) -> CargoResult<Vec<ApiToken>> {
        api_tokens::table
            .filter(api_tokens::user_id.eq(user_id))
            .order(api_tokens::created_at.desc())
            .load::<ApiToken>(conn)
            .map_err(From::from)
    }

    pub fn count_for_user(conn: &PgConnection, user_id: i32) -> CargoResult<u64> {
        api_tokens::table
            .filter(api_tokens::user_id.eq(user_id))
            .count()
            .get_result::<i64>(conn)
            .map(|count| count as u64)
            .map_err(From::from)
    }

    /// Converts this `ApiToken` model into an `EncodableApiToken` for JSON
    /// serialization.
    pub fn encodable(self) -> EncodableApiToken {
        EncodableApiToken {
            id: self.id,
            name: self.name,
            created_at: ::encode_time(self.created_at),
            last_used_at: self.last_used_at.map(::encode_time),
        }
    }

    /// Converts this `ApiToken` model into an `EncodableApiToken` including
    /// the actual token value for JSON serialization.  This should only be
    /// used when initially creating a new token to minimize the chance of
    /// token leaks.
    pub fn encodable_with_token(self) -> EncodableApiTokenWithToken {
        EncodableApiTokenWithToken {
            id: self.id,
            name: self.name,
            token: self.token,
            created_at: ::encode_time(self.created_at),
            last_used_at: self.last_used_at.map(::encode_time),
        }
    }
}

/// Handles the `GET /me/tokens` route.
pub fn list(req: &mut Request) -> CargoResult<Response> {
    let tokens = ApiToken::find_for_user(&*req.db_conn()?, req.user()?.id)?
        .into_iter()
        .map(ApiToken::encodable)
        .collect();
    #[derive(RustcEncodable)]
    struct R {
        api_tokens: Vec<EncodableApiToken>,
    }
    Ok(req.json(&R { api_tokens: tokens }))
}

/// Handles the `POST /me/tokens` route.
pub fn new(req: &mut Request) -> CargoResult<Response> {
    /// The incoming serialization format for the `ApiToken` model.
    #[derive(RustcDecodable, RustcEncodable)]
    struct NewApiToken {
        pub name: String,
    }

    /// The incoming serialization format for the `ApiToken` model.
    #[derive(RustcDecodable, RustcEncodable)]
    struct NewApiTokenRequest {
        pub api_token: NewApiToken,
    }

    if req.authentication_source()? != AuthenticationSource::SessionCookie {
        return Err(bad_request("cannot use an API token to create a new API token"));
    }

    let max_post_size = 2000;
    let length = req.content_length().chain_error(|| {
        bad_request("missing header: Content-Length")
    })?;

    if length > max_post_size {
        return Err(bad_request(format!("max post size is: {}", max_post_size)));
    }

    let mut json = vec![0; length as usize];
    read_fill(req.body(), &mut json)?;

    let json = String::from_utf8(json).map_err(|_| {
        bad_request("json body was not valid utf-8")
    })?;

    let new: NewApiTokenRequest = json::decode(&json).map_err(|e| {
        bad_request(format!("invalid new token request: {:?}", e))
    })?;

    let name = &new.api_token.name;
    if name.len() < 1 {
        return Err(bad_request("name must have a value"));
    }

    let user = req.user()?;

    let max_token_per_user = 500;
    let count = ApiToken::count_for_user(&*req.db_conn()?, user.id)?;
    if count >= max_token_per_user {
        return Err(bad_request(format!(
            "maximum tokens per user is: {}",
            max_token_per_user
        )));
    }

    let api_token = ApiToken::insert(&*req.db_conn()?, user.id, name)?;

    #[derive(RustcEncodable)]
    struct R {
        api_token: EncodableApiTokenWithToken,
    }
    Ok(req.json(&R { api_token: api_token.encodable_with_token() }))
}

/// Handles the `DELETE /me/tokens/:id` route.
pub fn revoke(req: &mut Request) -> CargoResult<Response> {
    let user = req.user()?;
    let id = req.params()["id"].parse().map_err(|e| {
        bad_request(format!("invalid token id: {:?}", e))
    })?;

    ApiToken::delete(&*req.db_conn()?, user.id, id)?;

    #[derive(RustcEncodable)]
    struct R {}
    Ok(req.json(&R {}))
}
<commit_msg>Minor review changes<commit_after>use diesel;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use conduit::{Request, Response};
use time::Timespec;
use conduit_router::RequestParams;
use rustc_serialize::json;

use db::RequestTransaction;
use user::{RequestUser, AuthenticationSource};
use util::{RequestUtils, CargoResult, ChainError, bad_request, read_fill};
use schema::api_tokens;

/// The model representing a row in the `api_tokens` database table.
#[derive(Clone, Debug, PartialEq, Eq, Identifiable, Queryable)]
pub struct ApiToken {
    pub id: i32,
    pub user_id: i32,
    pub token: String,
    pub name: String,
    pub created_at: Timespec,
    pub last_used_at: Option<Timespec>,
}

/// The serialization format for the `ApiToken` model without its token value.
#[derive(RustcDecodable, RustcEncodable)]
pub struct EncodableApiToken {
    pub id: i32,
    pub name: String,
    pub created_at: String,
    pub last_used_at: Option<String>,
}

/// The serialization format for the `ApiToken` model with its token value.
/// This should only be used when initially creating a new token to minimize
/// the chance of token leaks.
#[derive(RustcDecodable, RustcEncodable)]
pub struct EncodableApiTokenWithToken {
    pub id: i32,
    pub name: String,
    pub token: String,
    pub created_at: String,
    pub last_used_at: Option<String>,
}

impl ApiToken {
    /// Generates a new named API token for a user
    pub fn insert(conn: &PgConnection, user_id: i32, name: &str) -> CargoResult<ApiToken> {
        #[table_name = "api_tokens"]
        #[derive(Insertable, AsChangeset)]
        struct NewApiToken<'a> {
            user_id: i32,
            name: &'a str,
        }

        diesel::insert(&NewApiToken {
            user_id: user_id,
            name: name,
        }).into(api_tokens::table)
            .get_result::<ApiToken>(conn)
            .map_err(From::from)
    }

    /// Deletes the provided API token if it belongs to the provided user
    pub fn delete(conn: &PgConnection, user_id: i32, id: i32) -> CargoResult<()> {
        diesel::delete(api_tokens::table.find(id).filter(
            api_tokens::user_id.eq(user_id),
        )).execute(conn)?;
        Ok(())
    }

    pub fn find_for_user(conn: &PgConnection, user_id: i32) -> CargoResult<Vec<ApiToken>> {
        api_tokens::table
            .filter(api_tokens::user_id.eq(user_id))
            .order(api_tokens::created_at.desc())
            .load::<ApiToken>(conn)
            .map_err(From::from)
    }

    pub fn count_for_user(conn: &PgConnection, user_id: i32) -> CargoResult<u64> {
        api_tokens::table
            .filter(api_tokens::user_id.eq(user_id))
            .count()
            .get_result::<i64>(conn)
            .map(|count| count as u64)
            .map_err(From::from)
    }

    /// Converts this `ApiToken` model into an `EncodableApiToken` for JSON
    /// serialization.
    pub fn encodable(self) -> EncodableApiToken {
        EncodableApiToken {
            id: self.id,
            name: self.name,
            created_at: ::encode_time(self.created_at),
            last_used_at: self.last_used_at.map(::encode_time),
        }
    }

    /// Converts this `ApiToken` model into an `EncodableApiToken` including
    /// the actual token value for JSON serialization.  This should only be
    /// used when initially creating a new token to minimize the chance of
    /// token leaks.
    pub fn encodable_with_token(self) -> EncodableApiTokenWithToken {
        EncodableApiTokenWithToken {
            id: self.id,
            name: self.name,
            token: self.token,
            created_at: ::encode_time(self.created_at),
            last_used_at: self.last_used_at.map(::encode_time),
        }
    }
}

/// Handles the `GET /me/tokens` route.
pub fn list(req: &mut Request) -> CargoResult<Response> {
    let db_conn = &*req.db_conn()?;
    let user_id = req.user()?.id;
    let tokens = ApiToken::find_for_user(db_conn, user_id)?
        .into_iter()
        .map(ApiToken::encodable)
        .collect();
    #[derive(RustcEncodable)]
    struct R {
        api_tokens: Vec<EncodableApiToken>,
    }
    Ok(req.json(&R { api_tokens: tokens }))
}

/// Handles the `POST /me/tokens` route.
pub fn new(req: &mut Request) -> CargoResult<Response> {
    /// The incoming serialization format for the `ApiToken` model.
    #[derive(RustcDecodable, RustcEncodable)]
    struct NewApiToken {
        name: String,
    }

    /// The incoming serialization format for the `ApiToken` model.
    #[derive(RustcDecodable, RustcEncodable)]
    struct NewApiTokenRequest {
        api_token: NewApiToken,
    }

    if req.authentication_source()? != AuthenticationSource::SessionCookie {
        return Err(bad_request("cannot use an API token to create a new API token"));
    }

    let max_post_size = 2000;
    let length = req.content_length().chain_error(|| {
        bad_request("missing header: Content-Length")
    })?;

    if length > max_post_size {
        return Err(bad_request(format!("max post size is: {}", max_post_size)));
    }

    let mut json = vec![0; length as usize];
    read_fill(req.body(), &mut json)?;

    let json = String::from_utf8(json).map_err(|_| {
        bad_request("json body was not valid utf-8")
    })?;

    let new: NewApiTokenRequest = json::decode(&json).map_err(|e| {
        bad_request(format!("invalid new token request: {:?}", e))
    })?;

    let name = &new.api_token.name;
    if name.len() < 1 {
        return Err(bad_request("name must have a value"));
    }

    let user = req.user()?;

    let max_token_per_user = 500;
    let count = ApiToken::count_for_user(&*req.db_conn()?, user.id)?;
    if count >= max_token_per_user {
        return Err(bad_request(format!(
            "maximum tokens per user is: {}",
            max_token_per_user
        )));
    }

    let api_token = ApiToken::insert(&*req.db_conn()?, user.id, name)?;

    #[derive(RustcEncodable)]
    struct R {
        api_token: EncodableApiTokenWithToken,
    }
    Ok(req.json(&R { api_token: api_token.encodable_with_token() }))
}

/// Handles the `DELETE /me/tokens/:id` route.
pub fn revoke(req: &mut Request) -> CargoResult<Response> {
    let user = req.user()?;
    let id = req.params()["id"].parse().map_err(|e| {
        bad_request(format!("invalid token id: {:?}", e))
    })?;

    ApiToken::delete(&*req.db_conn()?, user.id, id)?;

    #[derive(RustcEncodable)]
    struct R {}
    Ok(req.json(&R {}))
}
<|endoftext|>


<commit_before>
use std::collections::HashMap;

use ::{ParseError, ErrorKind};

/// A list of `DesignSet`. It list all the possible properties and their
/// format.
pub type Design = HashMap<Type, DesignElem>;

pub struct DesignElem {
    pub parse_str: fn(&str) -> Result<Value, ParseError>,
}

#[derive(Debug, PartialEq, Eq)]
pub enum Value {
    // Generics values
    Text(String),
    TextMulti(Vec<String>),
    Uri(String),
    Adr(String),
    Date(String),
    Integer(i32),
    // Boolean,
    // Float,
    // UtcOffset,
    LanguageTag(String),

    // Customs values
    N(String),
}

#[cfg(feature = "rustc-serialize")]
mod rustc_serialize {
    use rustc_serialize::json::{ToJson, Json};
    use super::Value;

    impl ToJson for Value {
        fn to_json(&self) -> Json {
            match *self {
                Value::Text(ref val) => Json::String(val.clone()),
                Value::Uri(ref val) => Json::String(val.clone()),
                Value::Adr(ref val) => Json::String(val.clone()),
                Value::Date(ref val) => Json::String(val.clone()),
                Value::Integer(ref val) => Json::I64(val.clone() as i64),
                Value::LanguageTag(ref val) => Json::String(val.clone()),
                Value::N(ref val) => Json::String(val.clone()),
                Value::TextMulti(ref list) => {
                    let mut res = Vec::new();

                    for elem in list {
                        res.push(Json::String(elem.clone()));
                    }

                    Json::Array(res)
                }
            }
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Type {
    Text,
    TextMulti,
    TextMultiQuote,
    Uri,
    Date,
    // Time,
    DateTime,
    DateAndOrTime,
    Timestamp,
    // Boolean,
    Integer,
    // Float,
    UtcOffset,
    LanguageTag,

    // Custom types
    N,
    Adr,
}

impl Type {
    pub fn from_str(input: &str) -> Result<Type, ParseError> {
        match input.to_lowercase().as_str() {
            "text" => Ok(Type::Text),
            "uri" => Ok(Type::Uri),
            "date" => Ok(Type::Date),
            "datetime" => Ok(Type::DateTime),
            "dateandortime" => Ok(Type::DateAndOrTime),
            "timestamp" => Ok(Type::Timestamp),
            "integer" => Ok(Type::Integer),
            "utcoffset" => Ok(Type::UtcOffset),
            "utc-offset" => Ok(Type::UtcOffset),
            "languagetag" => Ok(Type::LanguageTag),
            _ => Err(ParseError::new(ErrorKind::InvalidValueType)),
        }
    }
}

pub fn get_vcard_design() -> Design {
    let mut v_design = HashMap::with_capacity(15);

    v_design.insert(Type::Text, DesignElem { parse_str: parse_text });
    v_design.insert(Type::Uri, DesignElem { parse_str: parse_uri });
    v_design.insert(Type::Adr, DesignElem { parse_str: parse_adr });
    v_design.insert(Type::Date, DesignElem { parse_str: parse_date });
    v_design.insert(Type::DateTime, DesignElem { parse_str: parse_date_time });
    v_design.insert(Type::DateAndOrTime,
                    DesignElem { parse_str: parse_date_and_or_time });
    v_design.insert(Type::N, DesignElem { parse_str: parse_n });
    v_design.insert(Type::Timestamp, DesignElem { parse_str: parse_timestamp });
    v_design.insert(Type::UtcOffset, DesignElem { parse_str: parse_utcoffset });
    v_design.insert(Type::TextMulti, DesignElem { parse_str: parse_text_multi });
    v_design.insert(Type::TextMultiQuote,
                    DesignElem { parse_str: parse_text_multi_quote });

    v_design
}

pub fn parse_text(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Text(input.to_string()))
}

pub fn parse_text_multi_quote(input: &str) -> Result<Value, ParseError> {
    parse_multi(input, ',')
}

pub fn parse_text_multi(input: &str) -> Result<Value, ParseError> {
    parse_multi(input, ';')
}

fn parse_multi(input: &str, separator: char) -> Result<Value, ParseError> {
    let mut res = Vec::new();

    let list = input.split(separator);

    for elem in list {
        res.push(elem.to_string());
    }

    if res.len() == 1 {
        Ok(Value::Text(res.pop().unwrap()))
    } else {
        Ok(Value::TextMulti(res))
    }
}

pub fn parse_uri(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Uri(input.to_string()))
}

pub fn parse_adr(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Adr(input.to_string()))
}

pub fn parse_date(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}

pub fn parse_date_time(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}

pub fn parse_date_and_or_time(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}

pub fn parse_timestamp(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}

pub fn parse_n(input: &str) -> Result<Value, ParseError> {
    Ok(Value::N(input.to_string()))
}

pub fn parse_utcoffset(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}
<commit_msg>Document value.rs<commit_after>
use std::collections::HashMap;

use ::{ParseError, ErrorKind};

/// Value Design for a specific protocol and version.
///
/// Each version and protocol have its own implementation. The `Design`
/// type make it generic by implementing all the rules for the valid
/// value types.
pub type Design = HashMap<Type, DesignElem>;

/// Design rules for a specific type of value.
pub struct DesignElem {
    /// Parse a raw value into a typed `Value`.
    pub parse_str: fn(&str) -> Result<Value, ParseError>,
}

/// Parsed values.
#[derive(Debug, PartialEq, Eq)]
pub enum Value {
    // Generics values
    Text(String),
    TextMulti(Vec<String>),
    Uri(String),
    Adr(String),
    Date(String),
    Integer(i32),
    // Boolean,
    // Float,
    // UtcOffset,
    LanguageTag(String),

    // Customs values
    N(String),
}

#[cfg(feature = "rustc-serialize")]
mod rustc_serialize {
    use rustc_serialize::json::{ToJson, Json};
    use super::Value;

    impl ToJson for Value {
        fn to_json(&self) -> Json {
            match *self {
                Value::Text(ref val) => Json::String(val.clone()),
                Value::Uri(ref val) => Json::String(val.clone()),
                Value::Adr(ref val) => Json::String(val.clone()),
                Value::Date(ref val) => Json::String(val.clone()),
                Value::Integer(ref val) => Json::I64(val.clone() as i64),
                Value::LanguageTag(ref val) => Json::String(val.clone()),
                Value::N(ref val) => Json::String(val.clone()),
                Value::TextMulti(ref list) => {
                    let mut res = Vec::new();

                    for elem in list {
                        res.push(Json::String(elem.clone()));
                    }

                    Json::Array(res)
                }
            }
        }
    }
}

/// Type of value.
///
/// It is used by the `property::DesignElem` to specify which type of value
/// is expected for the property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Type {
    Text,
    TextMulti,
    TextMultiQuote,
    Uri,
    Date,
    // Time,
    DateTime,
    DateAndOrTime,
    Timestamp,
    // Boolean,
    Integer,
    // Float,
    UtcOffset,
    LanguageTag,

    // Custom types
    N,
    Adr,
}

impl Type {
    pub fn from_str(input: &str) -> Result<Type, ParseError> {
        match input.to_lowercase().as_str() {
            "text" => Ok(Type::Text),
            "uri" => Ok(Type::Uri),
            "date" => Ok(Type::Date),
            "datetime" => Ok(Type::DateTime),
            "dateandortime" => Ok(Type::DateAndOrTime),
            "timestamp" => Ok(Type::Timestamp),
            "integer" => Ok(Type::Integer),
            "utcoffset" => Ok(Type::UtcOffset),
            "utc-offset" => Ok(Type::UtcOffset),
            "languagetag" => Ok(Type::LanguageTag),
            _ => Err(ParseError::new(ErrorKind::InvalidValueType)),
        }
    }
}

/// Return the design rules for a vcard file.
pub fn get_vcard_design() -> Design {
    let mut v_design = HashMap::with_capacity(12);

    v_design.insert(Type::Text, DesignElem { parse_str: parse_text });
    v_design.insert(Type::Uri, DesignElem { parse_str: parse_uri });
    v_design.insert(Type::Adr, DesignElem { parse_str: parse_adr });
    v_design.insert(Type::Date, DesignElem { parse_str: parse_date });
    v_design.insert(Type::DateTime, DesignElem { parse_str: parse_date_time });
    v_design.insert(Type::DateAndOrTime,
                    DesignElem { parse_str: parse_date_and_or_time });
    v_design.insert(Type::N, DesignElem { parse_str: parse_n });
    v_design.insert(Type::Timestamp, DesignElem { parse_str: parse_timestamp });
    v_design.insert(Type::UtcOffset, DesignElem { parse_str: parse_utcoffset });
    v_design.insert(Type::TextMulti, DesignElem { parse_str: parse_text_multi });
    v_design.insert(Type::TextMultiQuote,
                    DesignElem { parse_str: parse_text_multi_quote });

    v_design
}

/// Parse a text value.
pub fn parse_text(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Text(input.to_string()))
}

/// Parse a multi text value separated by quote.
pub fn parse_text_multi_quote(input: &str) -> Result<Value, ParseError> {
    parse_multi(input, ',')
}

/// Parse a multi text value.
pub fn parse_text_multi(input: &str) -> Result<Value, ParseError> {
    parse_multi(input, ';')
}

fn parse_multi(input: &str, separator: char) -> Result<Value, ParseError> {
    let mut res = Vec::new();

    let list = input.split(separator);

    for elem in list {
        res.push(elem.to_string());
    }

    if res.len() == 1 {
        Ok(Value::Text(res.pop().unwrap()))
    } else {
        Ok(Value::TextMulti(res))
    }
}

/// Parse an Uri value.
pub fn parse_uri(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Uri(input.to_string()))
}

/// Parse an address value.
pub fn parse_adr(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Adr(input.to_string()))
}

/// Parse a date value.
pub fn parse_date(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}

/// Parse a date_time value.
pub fn parse_date_time(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}

/// Parse a date_and_or_time value.
pub fn parse_date_and_or_time(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}

/// Parse a timestamp value.
pub fn parse_timestamp(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}

/// Parse a N value.
pub fn parse_n(input: &str) -> Result<Value, ParseError> {
    Ok(Value::N(input.to_string()))
}

/// Parse an utc_offset value.
pub fn parse_utcoffset(input: &str) -> Result<Value, ParseError> {
    Ok(Value::Date(input.to_string()))
}
<|endoftext|>