<commit_before>extern crate file;
extern crate ring;
extern crate rpassword;
extern crate rprompt;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate username;
extern crate xdg;

use ring::{aead, digest, pbkdf2, rand};
use ring::rand::SecureRandom;
use std::path::PathBuf;
use xdg::BaseDirectories;

#[derive(Debug,PartialEq,Deserialize,Serialize)]
#[serde(tag = "type")]
pub enum Entity {
    Credentials(Credentials),
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
pub struct Credentials {
    pub url:         String,
    pub login:       String,
    pub password:    String,
    pub totp_secret: Option<String>
}

#[derive(Debug)]
pub struct MnemesisUtils {
    base_dirs: BaseDirectories,
    config:    MnemesisConfig,
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
struct MnemesisConfig {
    secret: String,
}

impl MnemesisUtils {
    pub fn new() -> MnemesisUtils {
        let base_dirs = BaseDirectories::with_prefix("mnemesis").expect("Failed getting base directories");
        let config    = MnemesisConfig::load(&base_dirs);

        MnemesisUtils {
            base_dirs,
            config,
        }
    }

    pub fn prompt_for_input(msg: &str) -> String {
        rprompt::prompt_reply_stdout(msg).expect("Failed reading from stdin")
    }

    pub fn prompt_for_password(msg: &str) -> String {
        rpassword::prompt_password_stdout(msg).expect("Failed reading password")
    }

    fn credentials_directory(&self, path: &str) -> PathBuf {
        self.base_dirs.place_data_file(path).expect("Failed computing credentials directory")
    }

    pub fn read_entities(&self, path: &str) -> Vec<Entity> {
        let full_path = self.credentials_directory(path);

        if full_path.exists() {
            if full_path.is_file() {
                let data = file::get_text(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path))).expect(&format!("Failed to read {:?}", full_path));
                serde_json::from_str::<Vec<Entity>>(&data).expect(&format!("Found garbage in {:?}", full_path))
            } else {
                panic!("{:?} exists and is not a file", full_path);
            }
        } else {
            Vec::new()
        }
    }

    pub fn write_entities(&self, path: &str, entities: Vec<Entity>) {
        let full_path = self.credentials_directory(path);

        file::put_text(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), serde_json::to_string(&entities).expect("Failed to serialize entities")).expect(&format!("Failed to write {:?}", full_path));
    }
}

impl MnemesisConfig {
    pub fn load(base_dirs: &BaseDirectories) -> MnemesisConfig {
        base_dirs.find_config_file("mnemesis-config.json").and_then(|conf_file| {
            conf_file.to_str().and_then(|conf_file| file::get_text(conf_file).ok())
        }).and_then(|conf| {
            serde_json::from_str::<MnemesisConfig>(&conf).ok()
        }).unwrap_or(MnemesisConfig {
            // FIXME: generate default config or error out?
            secret: "".to_string(),
        })
    }
}

struct Crypt {
    key:  [u8; 32],
    rand: rand::SystemRandom,
}

impl Crypt {
    fn new(passphrase: &str) -> Crypt {
        let salt      = username::get_user_name().expect("Failed to query username");
        let mut key   = [0; 32];

        pbkdf2::derive(&digest::SHA512, 100, salt.as_bytes(), passphrase.as_bytes(), &mut key);

        Crypt {
            key,
            rand: rand::SystemRandom::new(),
        }
    }

    fn nonce(&self) -> Vec<u8> {
        let mut nonce = vec![0; 12];
        self.rand.fill(&mut nonce).expect("Failed generating nonce");
        nonce
    }

    fn encrypt(&self, data: &str, nonce: &Vec<u8>) -> Vec<u8> {
        let sealing_key              = aead::SealingKey::new(&aead::CHACHA20_POLY1305, &self.key).expect("Failed creating sealing key");
        let mut in_out               = data.to_string().into_bytes();
        let additional_data: [u8; 0] = [];

        for _ in 0..aead::CHACHA20_POLY1305.tag_len() {
            in_out.push(0);
        }

        aead::seal_in_place(&sealing_key, &nonce, &additional_data, &mut in_out, aead::CHACHA20_POLY1305.tag_len()).map(|len| in_out[..len].to_vec()).expect("Failed sealing data")
    }

    fn decrypt(&self, mut data: Vec<u8>, nonce: &Vec<u8>) -> String {
        let opening_key              = aead::OpeningKey::new(&aead::CHACHA20_POLY1305, &self.key).expect("Failed creating opening key");
        let additional_data: [u8; 0] = [];
        let decrypted_data           = aead::open_in_place(&opening_key, nonce, &additional_data, 0, &mut data).expect("Failed opening data");

        String::from_utf8(decrypted_data.to_vec()).expect("Failed decoding data")
    }
}

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

    #[test]
    fn test_encrypt_decrypt() {
        let pass  = "This is a nice passphrase.";
        let c     = Crypt::new(pass);
        let nonce = c.nonce();
        let data  = "Very secret data";
        let e     = c.encrypt(data, &nonce);
        let e2    = e.clone();

        assert_eq!(c.decrypt(e, &nonce), data);
        assert_eq!(Crypt::new(pass).decrypt(e2, &nonce), data);
    }
}
<commit_msg>encrypt data to disk<commit_after>extern crate file;
extern crate ring;
extern crate rpassword;
extern crate rprompt;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate username;
extern crate xdg;

use ring::{aead, digest, pbkdf2, rand};
use ring::rand::SecureRandom;
use std::path::PathBuf;
use xdg::BaseDirectories;

#[derive(Debug,PartialEq,Deserialize,Serialize)]
#[serde(tag = "type")]
pub enum Entity {
    Credentials(Credentials),
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
pub struct Credentials {
    pub url:         String,
    pub login:       String,
    pub password:    String,
    pub totp_secret: Option<String>
}

pub struct MnemesisUtils {
    base_dirs: BaseDirectories,
    config:    MnemesisConfig,
    crypt:     Crypt,
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
struct MnemesisConfig {
    secret: String,
}

impl MnemesisUtils {
    pub fn new() -> MnemesisUtils {
        let base_dirs = BaseDirectories::with_prefix("mnemesis").expect("Failed getting base directories");
        let config    = MnemesisConfig::load(&base_dirs);
        let crypt     = Crypt::new(&config.secret);

        MnemesisUtils {
            base_dirs,
            config,
            crypt,
        }
    }

    pub fn prompt_for_input(msg: &str) -> String {
        rprompt::prompt_reply_stdout(msg).expect("Failed reading from stdin")
    }

    pub fn prompt_for_password(msg: &str) -> String {
        rpassword::prompt_password_stdout(msg).expect("Failed reading password")
    }

    fn credentials_directory(&self, path: &str) -> PathBuf {
        self.base_dirs.place_data_file(path).expect("Failed computing credentials directory")
    }

    pub fn read_entities(&self, path: &str) -> Vec<Entity> {
        let full_path = self.credentials_directory(path);

        if full_path.exists() {
            if full_path.is_file() {
                let encrypted_data = file::get(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path))).expect(&format!("Failed to read {:?}", full_path));
                let nonce          = file::get(full_path.with_extension("nonce").to_str().expect(&format!("{:?} is not valid UTF-8", full_path))).expect(&format!("Failed to read {:?}", full_path));
                let decrypted_data = self.crypt.decrypt(encrypted_data, &nonce);
                serde_json::from_str::<Vec<Entity>>(&decrypted_data).expect(&format!("Found garbage in {:?}", full_path))
            } else {
                panic!("{:?} exists and is not a file", full_path);
            }
        } else {
            Vec::new()
        }
    }

    pub fn write_entities(&self, path: &str, entities: Vec<Entity>) {
        let full_path      = self.credentials_directory(path);
        let nonce          = self.crypt.nonce();
        let decrypted_data = serde_json::to_vec(&entities).expect("Failed to serialize entities");
        let encrypted_data = self.crypt.encrypt(decrypted_data, &nonce);

        file::put(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), &encrypted_data).expect(&format!("Failed to write {:?}", full_path));
        file::put(full_path.with_extension("nonce").to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), &nonce).expect(&format!("Failed to write {:?}", full_path));
    }
}

impl MnemesisConfig {
    pub fn load(base_dirs: &BaseDirectories) -> MnemesisConfig {
        base_dirs.find_config_file("mnemesis-config.json").and_then(|conf_file| {
            conf_file.to_str().and_then(|conf_file| file::get_text(conf_file).ok())
        }).and_then(|conf| {
            serde_json::from_str::<MnemesisConfig>(&conf).ok()
        }).unwrap_or(MnemesisConfig {
            // FIXME: generate default config or error out?
            secret: "".to_string(),
        })
    }
}

struct Crypt {
    key:  [u8; 32],
    rand: rand::SystemRandom,
}

impl Crypt {
    fn new(passphrase: &str) -> Crypt {
        let salt      = username::get_user_name().expect("Failed to query username");
        let mut key   = [0; 32];

        pbkdf2::derive(&digest::SHA512, 100, salt.as_bytes(), passphrase.as_bytes(), &mut key);

        Crypt {
            key,
            rand: rand::SystemRandom::new(),
        }
    }

    fn nonce(&self) -> Vec<u8> {
        let mut nonce = vec![0; 12];
        self.rand.fill(&mut nonce).expect("Failed generating nonce");
        nonce
    }

    fn encrypt(&self, mut data: Vec<u8>, nonce: &Vec<u8>) -> Vec<u8> {
        let sealing_key              = aead::SealingKey::new(&aead::CHACHA20_POLY1305, &self.key).expect("Failed creating sealing key");
        let additional_data: [u8; 0] = [];

        for _ in 0..aead::CHACHA20_POLY1305.tag_len() {
            data.push(0);
        }

        aead::seal_in_place(&sealing_key, &nonce, &additional_data, &mut data, aead::CHACHA20_POLY1305.tag_len()).map(|len| data[..len].to_vec()).expect("Failed sealing data")
    }

    fn decrypt(&self, mut data: Vec<u8>, nonce: &Vec<u8>) -> String {
        let opening_key              = aead::OpeningKey::new(&aead::CHACHA20_POLY1305, &self.key).expect("Failed creating opening key");
        let additional_data: [u8; 0] = [];
        let decrypted_data           = aead::open_in_place(&opening_key, nonce, &additional_data, 0, &mut data).expect("Failed opening data");

        String::from_utf8(decrypted_data.to_vec()).expect("Failed decoding data")
    }
}

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

    #[test]
    fn test_encrypt_decrypt() {
        let pass  = "This is a nice passphrase.";
        let c     = Crypt::new(pass);
        let nonce = c.nonce();
        let data  = "Very secret data";
        let e     = c.encrypt(data.as_bytes().to_vec(), &nonce);
        let e2    = e.clone();

        assert_eq!(c.decrypt(e, &nonce), data);
        assert_eq!(Crypt::new(pass).decrypt(e2, &nonce), data);
    }
}
<|endoftext|>


<commit_before>//! Cuckoo filter probabilistic data structure for membership testing and cardinality counting.
//!
//! # Usage
//!
//! This crate is [on crates.io](https://crates.io/crates/cuckoofilter) and can be
//! used by adding `cuckoofilter` to the dependencies in your project's `Cargo.toml`.
//!
//! ```toml
//! [dependencies]
//! cuckoofilter = "0.1"
//! ```
//!
//! And this in your crate root:
//!
//! ```rust
//! extern crate cuckoofilter;
//! ```

#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod bucket;
mod util;

extern crate rand;
extern crate byteorder;

use bucket::{Bucket, Fingerprint, BUCKET_SIZE};
use util::{get_fai, get_alt_index, FaI};
use rand::Rng;
use std::iter::repeat;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hasher, Hash};
use std::marker::PhantomData;
use std::mem;

/// If insertion fails, we will retry this many times.
pub const MAX_REBUCKET: u32 = 500;

/// The default number of buckets.
pub const DEFAULT_CAPACITY: u64 = (1 << 20) - 1;

/// A cuckoo filter class exposes a Bloomier filter interface,
/// providing methods of add, delete, contains.
///
/// # Examples
///
/// ```
/// extern crate cuckoofilter;
///
/// let words = vec!["foo", "bar", "xylophone", "milagro"];
/// let mut cf = cuckoofilter::CuckooFilter::new();
///
/// let mut insertions = 0;
/// for s in &words {
///     if cf.test_and_add(s) {
///         insertions += 1;
///     }
/// }
///
/// assert_eq!(insertions, words.len());
/// assert_eq!(cf.len(), words.len() as u64);
///
/// // Re-add the first element.
/// cf.add(words[0]);
///
/// assert_eq!(cf.len(), words.len() as u64 + 1);
///
/// for s in &words {
///     cf.delete(s);
/// }
///
/// assert_eq!(cf.len(), 1);
/// assert!(!cf.is_empty());
///
/// cf.delete(words[0]);
///
/// assert_eq!(cf.len(), 0);
/// assert!(cf.is_empty());
///
/// ```
pub struct CuckooFilter<H> {
    buckets: Box<[Bucket]>,
    len: u64,
    _hasher: std::marker::PhantomData<H>,
}

impl Default for CuckooFilter<DefaultHasher> {
    fn default() -> Self {
        CuckooFilter::new()
    }
}

impl CuckooFilter<DefaultHasher> {
    /// Construct a CuckooFilter with default capacity and hasher.
    pub fn new() -> CuckooFilter<DefaultHasher> {
        Self::with_capacity(DEFAULT_CAPACITY)
    }
}

impl<H> CuckooFilter<H>
    where H: Hasher + Default
{
    /// Constructs a Cuckoo Filter with a given max capacity
    pub fn with_capacity(cap: u64) -> CuckooFilter<H> {
        let capacity = match cap.next_power_of_two() / BUCKET_SIZE as u64 {
            0 => 1,
            cap => cap,
        };

        CuckooFilter {
            buckets: repeat(Bucket::new())
                .take(capacity as usize)
                .collect::<Vec<_>>()
                .into_boxed_slice(),
            len: 0,
            _hasher: PhantomData,
        }
    }

    /// Checks if `data` is in the filter.
    pub fn contains<T: ?Sized + Hash>(&self, data: &T) -> bool {
        let FaI { fp, i1, i2 } = get_fai::<T, H>(data);
        let len = self.buckets.len();
        self.buckets[i1 % len]
            .get_fingerprint_index(fp)
            .or(self.buckets[i2 % len].get_fingerprint_index(fp))
            .is_some()
    }

    /// Adds `data` to the filter. Returns true if the insertion was successful.
    pub fn add<T: ?Sized + Hash>(&mut self, data: &T) -> bool {
        let fai = get_fai::<T, H>(data);
        if self.put(fai.fp, fai.i1) || self.put(fai.fp, fai.i2) {
            return true;
        }
        let len = self.buckets.len();
        let mut rng = rand::thread_rng();
        let mut i = fai.random_index(&mut rng);
        let mut fp = fai.fp;
        for _ in 0..MAX_REBUCKET {
            let other_fp;
            {
                let loc = &mut self.buckets[i % len].buffer[rng.gen_range(0, BUCKET_SIZE)];
                other_fp = *loc;
                *loc = fp;
                i = get_alt_index::<H>(other_fp, i);
            }
            if self.put(other_fp, i) {
                return true;
            }
            fp = other_fp;
        }
        panic!("Map is full, could not insert item");
    }

    /// Adds `data` to the filter if it does not exist in the filter yet.
    /// Returns `true` if `data` was not yet present in the filter and added
    /// successfully.
    pub fn test_and_add<T: ?Sized + Hash>(&mut self, data: &T) -> bool {
        if self.contains(data) {
            false
        } else {
            self.add(data)
        }
    }

    /// Number of items in the filter.
    pub fn len(&self) -> u64 {
        self.len
    }

    /// Number of bytes the filter occupies in memory
    pub fn memory_usage(&self) -> usize {
        mem::size_of_val(self) + self.buckets.len() * mem::size_of::<Bucket>()
    }

    /// Check if filter is empty
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Deletes `data` from the filter. Returns true if `data` existed in the
    /// filter before.
    pub fn delete<T: ?Sized + Hash>(&mut self, data: &T) -> bool {
        let FaI { fp, i1, i2 } = get_fai::<T, H>(data);
        self.remove(fp, i1) || self.remove(fp, i2)
    }

    /// Removes the item with the given fingerprint from the bucket indexed by i.
    fn remove(&mut self, fp: Fingerprint, i: usize) -> bool {
        let len = self.buckets.len();
        if self.buckets[i % len].delete(fp) {
            self.len -= 1;
            true
        } else {
            false
        }
    }

    fn put(&mut self, fp: Fingerprint, i: usize) -> bool {
        let len = self.buckets.len();
        if self.buckets[i % len].insert(fp) {
            self.len += 1;
            true
        } else {
            false
        }
    }
}
<commit_msg>Document the filter's untypedness pointing out a pitfall when using different types for the same value that got me when writing the false positives integration test.<commit_after>//! Cuckoo filter probabilistic data structure for membership testing and cardinality counting.
//!
//! # Usage
//!
//! This crate is [on crates.io](https://crates.io/crates/cuckoofilter) and can be
//! used by adding `cuckoofilter` to the dependencies in your project's `Cargo.toml`.
//!
//! ```toml
//! [dependencies]
//! cuckoofilter = "0.1"
//! ```
//!
//! And this in your crate root:
//!
//! ```rust
//! extern crate cuckoofilter;
//! ```

#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod bucket;
mod util;

extern crate rand;
extern crate byteorder;

use bucket::{Bucket, Fingerprint, BUCKET_SIZE};
use util::{get_fai, get_alt_index, FaI};
use rand::Rng;
use std::iter::repeat;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hasher, Hash};
use std::marker::PhantomData;
use std::mem;

/// If insertion fails, we will retry this many times.
pub const MAX_REBUCKET: u32 = 500;

/// The default number of buckets.
pub const DEFAULT_CAPACITY: u64 = (1 << 20) - 1;

/// A cuckoo filter class exposes a Bloomier filter interface,
/// providing methods of add, delete, contains.
///
/// # Examples
///
/// ```
/// extern crate cuckoofilter;
///
/// let words = vec!["foo", "bar", "xylophone", "milagro"];
/// let mut cf = cuckoofilter::CuckooFilter::new();
///
/// let mut insertions = 0;
/// for s in &words {
///     if cf.test_and_add(s) {
///         insertions += 1;
///     }
/// }
///
/// assert_eq!(insertions, words.len());
/// assert_eq!(cf.len(), words.len() as u64);
///
/// // Re-add the first element.
/// cf.add(words[0]);
///
/// assert_eq!(cf.len(), words.len() as u64 + 1);
///
/// for s in &words {
///     cf.delete(s);
/// }
///
/// assert_eq!(cf.len(), 1);
/// assert!(!cf.is_empty());
///
/// cf.delete(words[0]);
///
/// assert_eq!(cf.len(), 0);
/// assert!(cf.is_empty());
///
/// ```
pub struct CuckooFilter<H> {
    buckets: Box<[Bucket]>,
    len: u64,
    _hasher: std::marker::PhantomData<H>,
}

impl Default for CuckooFilter<DefaultHasher> {
    fn default() -> Self {
        CuckooFilter::new()
    }
}

impl CuckooFilter<DefaultHasher> {
    /// Construct a CuckooFilter with default capacity and hasher.
    pub fn new() -> CuckooFilter<DefaultHasher> {
        Self::with_capacity(DEFAULT_CAPACITY)
    }
}

impl<H> CuckooFilter<H>
    where H: Hasher + Default
{
    /// Constructs a Cuckoo Filter with a given max capacity
    pub fn with_capacity(cap: u64) -> CuckooFilter<H> {
        let capacity = match cap.next_power_of_two() / BUCKET_SIZE as u64 {
            0 => 1,
            cap => cap,
        };

        CuckooFilter {
            buckets: repeat(Bucket::new())
                .take(capacity as usize)
                .collect::<Vec<_>>()
                .into_boxed_slice(),
            len: 0,
            _hasher: PhantomData,
        }
    }

    /// Checks if `data` is in the filter.
    pub fn contains<T: ?Sized + Hash>(&self, data: &T) -> bool {
        let FaI { fp, i1, i2 } = get_fai::<T, H>(data);
        let len = self.buckets.len();
        self.buckets[i1 % len]
            .get_fingerprint_index(fp)
            .or(self.buckets[i2 % len].get_fingerprint_index(fp))
            .is_some()
    }

    /// Adds `data` to the filter. Returns true if the insertion was successful.
    /// Note that while you can put any hashable type in the same filter, beware
    /// for side effects like that the same number can have diferent hashes
    /// depending on the type.
    /// So for the filter, 4711i64 isn't the same as 4711u64.
    pub fn add<T: ?Sized + Hash>(&mut self, data: &T) -> bool {
        let fai = get_fai::<T, H>(data);
        if self.put(fai.fp, fai.i1) || self.put(fai.fp, fai.i2) {
            return true;
        }
        let len = self.buckets.len();
        let mut rng = rand::thread_rng();
        let mut i = fai.random_index(&mut rng);
        let mut fp = fai.fp;
        for _ in 0..MAX_REBUCKET {
            let other_fp;
            {
                let loc = &mut self.buckets[i % len].buffer[rng.gen_range(0, BUCKET_SIZE)];
                other_fp = *loc;
                *loc = fp;
                i = get_alt_index::<H>(other_fp, i);
            }
            if self.put(other_fp, i) {
                return true;
            }
            fp = other_fp;
        }
        panic!("Map is full, could not insert item");
    }

    /// Adds `data` to the filter if it does not exist in the filter yet.
    /// Returns `true` if `data` was not yet present in the filter and added
    /// successfully.
    pub fn test_and_add<T: ?Sized + Hash>(&mut self, data: &T) -> bool {
        if self.contains(data) {
            false
        } else {
            self.add(data)
        }
    }

    /// Number of items in the filter.
    pub fn len(&self) -> u64 {
        self.len
    }

    /// Number of bytes the filter occupies in memory
    pub fn memory_usage(&self) -> usize {
        mem::size_of_val(self) + self.buckets.len() * mem::size_of::<Bucket>()
    }

    /// Check if filter is empty
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Deletes `data` from the filter. Returns true if `data` existed in the
    /// filter before.
    pub fn delete<T: ?Sized + Hash>(&mut self, data: &T) -> bool {
        let FaI { fp, i1, i2 } = get_fai::<T, H>(data);
        self.remove(fp, i1) || self.remove(fp, i2)
    }

    /// Removes the item with the given fingerprint from the bucket indexed by i.
    fn remove(&mut self, fp: Fingerprint, i: usize) -> bool {
        let len = self.buckets.len();
        if self.buckets[i % len].delete(fp) {
            self.len -= 1;
            true
        } else {
            false
        }
    }

    fn put(&mut self, fp: Fingerprint, i: usize) -> bool {
        let len = self.buckets.len();
        if self.buckets[i % len].insert(fp) {
            self.len += 1;
            true
        } else {
            false
        }
    }
}
<|endoftext|>


<commit_before>extern crate fastup;
use fastup::parse;

#[macro_use]
pub mod utils;
pub mod output;
pub mod renderers;
pub mod widgets;

#[derive(Debug)]
pub enum Error {
    ParseFailed(String),
    RendererNotFound,
}

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

/// Parse `source` as fastup markup language, expand all widgets, then
/// renders it into a `String` by the specific renderer named as `renderer_name`.
pub fn render_status(source: &str, renderer_name: &str) -> Result<String> {
    let doc = parse(&source).map_err(Error::ParseFailed)?;
    let doc = widgets::expand(doc);
    let renderer = renderers::make(renderer_name).ok_or(Error::RendererNotFound)?;
    Ok(renderer.render(&doc))
}

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

    #[test]
    fn dump() {
        println!("{:?}", render_status("<hello>[", "dump"));
    }
}

<commit_msg>+ tests for dump and html renderers<commit_after>extern crate fastup;
use fastup::parse;

#[macro_use]
pub mod utils;
pub mod output;
pub mod renderers;
pub mod widgets;

#[derive(Debug)]
pub enum Error {
    ParseFailed(String),
    RendererNotFound,
}

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

/// Parse `source` as fastup markup language, expand all widgets, then
/// renders it into a `String` by the specific renderer named as `renderer_name`.
pub fn render_status(source: &str, renderer_name: &str) -> Result<String> {
    let doc = parse(&source).map_err(Error::ParseFailed)?;
    let doc = widgets::expand(doc);
    let renderer = renderers::make(renderer_name).ok_or(Error::RendererNotFound)?;
    Ok(renderer.render(&doc))
}

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

    const TEST_SOURCE: &'static str = r#"hello <hello> {world}"#;

    #[test]
    fn dump() {
        println!("{}", render_status(TEST_SOURCE, "dump").unwrap());
    }

    #[test]
    fn html() {
        println!("{}", render_status(TEST_SOURCE, "html").unwrap());
    }

    #[test]
    #[should_panic]
    fn renderer_not_found() {
        println!("{}", render_status(TEST_SOURCE, "not exists").unwrap());
    }
}

<|endoftext|>


<commit_before>extern crate base64;
extern crate file;
extern crate ring;
extern crate rpassword;
extern crate rprompt;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate username;
extern crate xdg;

use ring::{aead, digest, pbkdf2, rand};
use ring::rand::SecureRandom;
use std::str::FromStr;
use std::path::PathBuf;
use xdg::BaseDirectories;

const SERIALIZATION_API:    usize = 1;
const SERIALIZATION_FIELDS: usize = 4;

#[derive(Debug,PartialEq,Deserialize,Serialize)]
#[serde(tag = "type")]
pub enum Entity {
    Credentials(Credentials),
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
pub struct Credentials {
    pub url:         String,
    pub login:       String,
    pub password:    String,
    pub totp_secret: Option<String>
}

pub struct MnemesisUtils {
    base_dirs: BaseDirectories,
    config:    MnemesisConfig,
    crypt:     Crypt,
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
struct MnemesisConfig {
    secret: String,
}

impl MnemesisUtils {
    pub fn new() -> MnemesisUtils {
        let base_dirs = BaseDirectories::with_prefix("mnemesis").expect("Failed getting base directories");
        let config    = MnemesisConfig::load(&base_dirs, &Crypt::new(&MnemesisUtils::prompt_for_password("Passphrase: "), Algorithm::ChaCha20Poly1305));
        let crypt     = Crypt::new(&config.secret, Algorithm::ChaCha20Poly1305);

        MnemesisUtils {
            base_dirs,
            config,
            crypt,
        }
    }

    pub fn prompt_for_input(msg: &str) -> String {
        rprompt::prompt_reply_stdout(msg).expect("Failed reading from stdin")
    }

    pub fn prompt_for_password(msg: &str) -> String {
        rpassword::prompt_password_stdout(msg).expect("Failed reading password")
    }

    fn credentials_directory(&self, path: &str) -> PathBuf {
        self.base_dirs.place_data_file(path).expect("Failed computing credentials directory")
    }

    pub fn read_entities(&self, path: &str) -> Vec<Entity> {
        let full_path = self.credentials_directory(path);

        if full_path.exists() {
            if full_path.is_file() {
                let encrypted_data = file::get_text(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path))).expect(&format!("Failed to read {:?}", full_path));
                let decrypted_data = self.crypt.decrypt(encrypted_data);
                serde_json::from_str::<Vec<Entity>>(&decrypted_data).expect(&format!("Found garbage in {:?}", full_path))
            } else {
                panic!("{:?} exists and is not a file", full_path);
            }
        } else {
            Vec::new()
        }
    }

    pub fn write_entities(&self, path: &str, entities: Vec<Entity>) {
        let full_path      = self.credentials_directory(path);
        let decrypted_data = serde_json::to_string(&entities).expect("Failed to serialize entities");
        let encrypted_data = self.crypt.encrypt(decrypted_data);

        file::put(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), &encrypted_data).expect(&format!("Failed to write {:?}", full_path));
    }
}

impl MnemesisConfig {
    pub fn load(base_dirs: &BaseDirectories, crypt: &Crypt) -> MnemesisConfig {
        base_dirs.find_config_file("mnemesis-config.json").and_then(|conf_file| {
            conf_file.to_str().and_then(|conf_file| file::get_text(conf_file).ok()).map(|encrypted_conf| crypt.decrypt(encrypted_conf))
        }).and_then(|conf| {
            serde_json::from_str::<MnemesisConfig>(&conf).ok()
        }).unwrap_or(MnemesisConfig {
            // FIXME: generate default config or error out?
            secret: "".to_string(),
        })
    }
}

struct Crypt {
    algorithm: Algorithm,
    key:       [u8; 32],
    rand:      rand::SystemRandom,
}

#[derive(Clone, Debug, PartialEq)]
enum Algorithm {
    ChaCha20Poly1305,
}

impl Algorithm {
    fn aead_algorithm(&self) -> &'static aead::Algorithm {
        match *self {
            Algorithm::ChaCha20Poly1305 => &aead::CHACHA20_POLY1305
        }
    }
}

impl ToString for Algorithm {
    fn to_string(&self) -> String {
        format!("{:?}", self)
    }
}

impl FromStr for Algorithm {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ChaCha20Poly1305" => Ok(Algorithm::ChaCha20Poly1305),
            _                   => Err(format!("Unknown algorithm: {}", s)),
        }
    }
}

impl Crypt {
    fn new(passphrase: &str, algorithm: Algorithm) -> Crypt {
        let salt      = username::get_user_name().expect("Failed to query username");
        let mut key   = [0; 32];

        pbkdf2::derive(&digest::SHA512, 100, salt.as_bytes(), passphrase.as_bytes(), &mut key);

        Crypt {
            algorithm,
            key,
            rand: rand::SystemRandom::new(),
        }
    }

    fn nonce(&self) -> Vec<u8> {
        let mut nonce = vec![0; 12];
        self.rand.fill(&mut nonce).expect("Failed generating nonce");
        nonce
    }

    fn encrypt(&self, data: String) -> String {
        let algorithm                = self.algorithm.aead_algorithm();
        let sealing_key              = aead::SealingKey::new(&algorithm, &self.key).expect("Failed creating sealing key");
        let nonce                    = self.nonce();
        let additional_data: [u8; 0] = [];
        let mut data                 = data.into_bytes();

        for _ in 0..algorithm.tag_len() {
            data.push(0);
        }

        let encrypted_data = aead::seal_in_place(&sealing_key, &nonce, &additional_data, &mut data, algorithm.tag_len()).map(|len| data[..len].to_vec()).expect("Failed sealing data");

        format!("{}:{}:{}:{}", SERIALIZATION_API, self.algorithm.to_string(), base64::encode(&nonce), base64::encode(&encrypted_data))
    }

    fn decrypt(&self, data: String) -> String {
        let components: Vec<_> = data.splitn(SERIALIZATION_FIELDS, ':').collect();

        assert_eq!(components.len(),      SERIALIZATION_FIELDS);
        assert_eq!(components[0].parse(), Ok(SERIALIZATION_API));
        assert_eq!(components[1].parse(), Ok(self.algorithm.clone())); // TODO: move me to param and use the read one

        let algorithm                = self.algorithm.aead_algorithm();
        let opening_key              = aead::OpeningKey::new(&algorithm, &self.key).expect("Failed creating opening key");
        let nonce                    = base64::decode(components[2]).expect("Failed to decode nonce");
        let mut data                 = base64::decode(components[3]).expect("Failed to decode data");
        let additional_data: [u8; 0] = [];
        let decrypted_data           = aead::open_in_place(&opening_key, &nonce, &additional_data, 0, &mut data).expect("Failed opening data");

        String::from_utf8(decrypted_data.to_vec()).expect("Failed decoding data")
    }
}

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

    #[test]
    fn test_encrypt_decrypt() {
        let pass  = "This is a nice passphrase.";
        let c     = Crypt::new(pass, Algorithm::ChaCha20Poly1305);
        let data  = "Very secret data";
        let e     = c.encrypt(data.to_string());
        let e2    = e.clone();

        assert_eq!(c.decrypt(e), data);
        assert_eq!(Crypt::new(pass, Algorithm::ChaCha20Poly1305).decrypt(e2), data);
    }
}
<commit_msg>generate default config as needed<commit_after>extern crate base64;
extern crate file;
extern crate ring;
extern crate rpassword;
extern crate rprompt;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
extern crate username;
extern crate xdg;

use ring::{aead, digest, pbkdf2, rand};
use ring::rand::SecureRandom;
use std::str::FromStr;
use std::path::PathBuf;
use xdg::BaseDirectories;

const SERIALIZATION_API:    usize = 1;
const SERIALIZATION_FIELDS: usize = 4;

#[derive(Debug,PartialEq,Deserialize,Serialize)]
#[serde(tag = "type")]
pub enum Entity {
    Credentials(Credentials),
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
pub struct Credentials {
    pub url:         String,
    pub login:       String,
    pub password:    String,
    pub totp_secret: Option<String>
}

pub struct MnemesisUtils {
    base_dirs: BaseDirectories,
    config:    MnemesisConfig,
    crypt:     Crypt,
}

#[derive(Debug,PartialEq,Deserialize,Serialize)]
struct MnemesisConfig {
    secret: String,
}

impl MnemesisUtils {
    pub fn new() -> MnemesisUtils {
        let base_dirs  = BaseDirectories::with_prefix("mnemesis").expect("Failed getting base directories");
        let passphrase = MnemesisUtils::prompt_for_password("Passphrase: ");
        let config     = MnemesisConfig::load(&base_dirs, &Crypt::new(&passphrase, Algorithm::ChaCha20Poly1305), &passphrase);
        let crypt      = Crypt::new(&config.secret, Algorithm::ChaCha20Poly1305);

        MnemesisUtils {
            base_dirs,
            config,
            crypt,
        }
    }

    pub fn prompt_for_input(msg: &str) -> String {
        rprompt::prompt_reply_stdout(msg).expect("Failed reading from stdin")
    }

    pub fn prompt_for_password(msg: &str) -> String {
        rpassword::prompt_password_stdout(msg).expect("Failed reading password")
    }

    fn credentials_directory(&self, path: &str) -> PathBuf {
        self.base_dirs.place_data_file(path).expect("Failed computing credentials directory")
    }

    pub fn read_entities(&self, path: &str) -> Vec<Entity> {
        let full_path = self.credentials_directory(path);

        if full_path.exists() {
            if full_path.is_file() {
                let encrypted_data = file::get_text(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path))).expect(&format!("Failed to read {:?}", full_path));
                let decrypted_data = self.crypt.decrypt(encrypted_data);
                serde_json::from_str::<Vec<Entity>>(&decrypted_data).expect(&format!("Found garbage in {:?}", full_path))
            } else {
                panic!("{:?} exists and is not a file", full_path);
            }
        } else {
            Vec::new()
        }
    }

    pub fn write_entities(&self, path: &str, entities: Vec<Entity>) {
        let full_path      = self.credentials_directory(path);
        let decrypted_data = serde_json::to_string(&entities).expect("Failed to serialize entities");
        let encrypted_data = self.crypt.encrypt(decrypted_data);

        file::put(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), &encrypted_data).expect(&format!("Failed to write {:?}", full_path));
    }
}

impl MnemesisConfig {
    pub fn load(base_dirs: &BaseDirectories, crypt: &Crypt, passphrase: &str) -> MnemesisConfig {
        base_dirs.find_config_file("mnemesis-config.json").and_then(|conf_file| {
            conf_file.to_str().and_then(|conf_file| file::get_text(conf_file).ok()).map(|encrypted_conf| crypt.decrypt(encrypted_conf))
        }).and_then(|conf| {
            serde_json::from_str::<MnemesisConfig>(&conf).ok()
        }).unwrap_or_else(|| {
            let config = MnemesisConfig {
                secret: passphrase.to_string(),
            };
            let decrypted_data = serde_json::to_string(&config).expect("Failed to serialize config");
            let encrypted_data = crypt.encrypt(decrypted_data);
            let full_path      = base_dirs.place_config_file("mnemesis-config.json").expect("Failed to create config file");

            file::put(full_path.to_str().expect(&format!("{:?} is not valid UTF-8", full_path)), &encrypted_data).expect(&format!("Failed to write {:?}", full_path));
            config
        })
    }
}

struct Crypt {
    algorithm: Algorithm,
    key:       [u8; 32],
    rand:      rand::SystemRandom,
}

#[derive(Clone, Debug, PartialEq)]
enum Algorithm {
    ChaCha20Poly1305,
}

impl Algorithm {
    fn aead_algorithm(&self) -> &'static aead::Algorithm {
        match *self {
            Algorithm::ChaCha20Poly1305 => &aead::CHACHA20_POLY1305
        }
    }
}

impl ToString for Algorithm {
    fn to_string(&self) -> String {
        format!("{:?}", self)
    }
}

impl FromStr for Algorithm {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "ChaCha20Poly1305" => Ok(Algorithm::ChaCha20Poly1305),
            _                   => Err(format!("Unknown algorithm: {}", s)),
        }
    }
}

impl Crypt {
    fn new(passphrase: &str, algorithm: Algorithm) -> Crypt {
        let salt      = username::get_user_name().expect("Failed to query username");
        let mut key   = [0; 32];

        pbkdf2::derive(&digest::SHA512, 100, salt.as_bytes(), passphrase.as_bytes(), &mut key);

        Crypt {
            algorithm,
            key,
            rand: rand::SystemRandom::new(),
        }
    }

    fn nonce(&self) -> Vec<u8> {
        let mut nonce = vec![0; 12];
        self.rand.fill(&mut nonce).expect("Failed generating nonce");
        nonce
    }

    fn encrypt(&self, data: String) -> String {
        let algorithm                = self.algorithm.aead_algorithm();
        let sealing_key              = aead::SealingKey::new(&algorithm, &self.key).expect("Failed creating sealing key");
        let nonce                    = self.nonce();
        let additional_data: [u8; 0] = [];
        let mut data                 = data.into_bytes();

        for _ in 0..algorithm.tag_len() {
            data.push(0);
        }

        let encrypted_data = aead::seal_in_place(&sealing_key, &nonce, &additional_data, &mut data, algorithm.tag_len()).map(|len| data[..len].to_vec()).expect("Failed sealing data");

        format!("{}:{}:{}:{}", SERIALIZATION_API, self.algorithm.to_string(), base64::encode(&nonce), base64::encode(&encrypted_data))
    }

    fn decrypt(&self, data: String) -> String {
        let components: Vec<_> = data.splitn(SERIALIZATION_FIELDS, ':').collect();

        assert_eq!(components.len(),      SERIALIZATION_FIELDS);
        assert_eq!(components[0].parse(), Ok(SERIALIZATION_API));
        assert_eq!(components[1].parse(), Ok(self.algorithm.clone())); // TODO: move me to param and use the read one

        let algorithm                = self.algorithm.aead_algorithm();
        let opening_key              = aead::OpeningKey::new(&algorithm, &self.key).expect("Failed creating opening key");
        let nonce                    = base64::decode(components[2]).expect("Failed to decode nonce");
        let mut data                 = base64::decode(components[3]).expect("Failed to decode data");
        let additional_data: [u8; 0] = [];
        let decrypted_data           = aead::open_in_place(&opening_key, &nonce, &additional_data, 0, &mut data).expect("Failed opening data");

        String::from_utf8(decrypted_data.to_vec()).expect("Failed decoding data")
    }
}

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

    #[test]
    fn test_encrypt_decrypt() {
        let pass  = "This is a nice passphrase.";
        let c     = Crypt::new(pass, Algorithm::ChaCha20Poly1305);
        let data  = "Very secret data";
        let e     = c.encrypt(data.to_string());
        let e2    = e.clone();

        assert_eq!(c.decrypt(e), data);
        assert_eq!(Crypt::new(pass, Algorithm::ChaCha20Poly1305).decrypt(e2), data);
    }
}
<|endoftext|>


<commit_before>#[macro_use(expect)]
extern crate expectest;
pub use expectest::prelude::*;

pub type TestResult = Result<(), ()>;

pub struct Context<'a> {
    pub tests: Vec<Box<FnMut() -> TestResult + 'a>>
}

impl<'a> Context<'a> {
    pub fn describe<F>(&mut self, _name: &'a str, mut body: F)
        where F : 'a + FnMut(&mut Context<'a>) -> () {
        body(self)
    }

    pub fn it<F>(&mut self, _name: &'a str, body: F)
        where F : 'a + FnMut() -> TestResult {

        self.tests.push(Box::new(body))
    }
}

pub fn describe<'a, 'b, F>(_block_name: &'b str, body: F) -> Runner<'a>
    where F : 'a + FnOnce(&mut Context<'a>) -> () {

    let mut c = Context { tests: vec!() };
    body(&mut c);
    Runner { describe: c, result: None }
}

pub struct Runner<'a> {
    describe: Context<'a>,
    result: Option<TestResult>
}

impl<'a> Runner<'a> {

    pub fn run(&mut self) -> Result<(), ()> {
        use std::panic::{catch_unwind, AssertUnwindSafe};

        for test_function in self.describe.tests.iter_mut() {
            let res = match catch_unwind(AssertUnwindSafe(|| test_function())) {
                Ok(res) => res,
                _ => Err(())
            };

            self.result = match self.result {
                None => Some(res),
                Some(Ok(())) => Some(res),
                old @ _ => old
            }
        }
        Ok(())
    }

    pub fn result(&self) -> Result<(), ()> {
        self.result.unwrap_or(Ok(()))
    }
}

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

    mod describe {
        pub use super::*;

        #[test]
        fn it_has_a_root_describe_function() {
            describe("A Test", |_ctx|{});
        }

        #[test]
        fn it_can_call_describe_inside_describe_body() {
            describe("A Root", |ctx| {
                ctx.describe("nested describe", |_ctx| {})
            });
        }

        #[test]
        fn it_can_call_it_inside_describe_body() {
            describe("A root", |ctx| {
                ctx.it("is a test", || { Ok(()) })
            });
        }
    }

    mod runner {
        pub use super::*;

        #[test]
        fn it_create_a_runner_that_can_be_runned() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("is expected to run", || {
                    assert_eq!(true, true);
                    Ok(())
                })
            });
            assert_eq!(Ok(()), runner.run())
        }

        #[test]
        fn runner_effectively_run_tests() {
            let ran = &mut false;

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.it("is expected to run", || {
                        *ran = true;
                        Ok(())
                    })
                });
                assert_eq!(Ok(()), runner.run());
            }

            assert_eq!(true, *ran)
        }

        #[test]
        fn runner_effectively_run_two_tests() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let ran_counter = &mut AtomicUsize::new(0);

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.it("first run",  || { ran_counter.fetch_add(1, Ordering::Relaxed); Ok(()) });
                    ctx.it("second run", || { ran_counter.fetch_add(1, Ordering::Relaxed); Ok(()) });
                });
                let _ = runner.run().unwrap();
            }

            assert_eq!(2, ran_counter.load(Ordering::Relaxed))
        }

        #[test]
        fn runner_effectively_run_two_tests_in_nested_describe() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let ran_counter = &mut AtomicUsize::new(0);

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.describe("first describe", |ctx| {
                        ctx.it("first run",  || { ran_counter.fetch_add(1, Ordering::Relaxed); Ok(() )});
                    });
                    ctx.describe("second describe", |ctx| {
                        ctx.it("second run",  || { ran_counter.fetch_add(1, Ordering::Relaxed); Ok(()) });
                    });
                    ctx.describe("third describe", |ctx| {
                        ctx.describe("fourth describe", |ctx| {
                            ctx.it("third run",  || { ran_counter.fetch_add(1, Ordering::Relaxed);  Ok(()) });
                        })
                    })
                });
                let _ = runner.run().unwrap();
            }

            assert_eq!(3, ran_counter.load(Ordering::Relaxed))
        }

        #[test]
        fn tests_can_fail_with_an_error_result() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("should fail", || {
                    Err(())
                })
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_err());
        }

        #[test]
        fn result_should_be_ok_if_tests_are_ok() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("should be ok", || { Ok(()) })
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_ok());
        }

        #[test]
        fn results_is_ok_if_no_tests_have_been_runned() {
            let mut runner = describe("A root", |_ctx| {});
            runner.run().unwrap();

            expect!(runner.result()).to(be_ok());
        }

        #[test]
        fn results_is_err_if_one_test_is_err() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("an err", || { Err(()) });
                ctx.it("an ok", || { Ok(()) });
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_err());
        }

        #[test]
        fn results_is_ok_if_all_tests_are_ok() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("ok 1", || { Ok(()) });
                ctx.it("ok 2", || { Ok(()) });
                ctx.it("ok 3", || { Ok(()) });
                ctx.it("ok 4", || { Ok(()) });
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_ok());
        }

        #[test]
        fn tests_can_contains_asserts_that_panic() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let counter = &mut AtomicUsize::new(0);

            let mut runner = describe("A root", |ctx| {
                ctx.it("assert_eq fail", || { assert_eq!(true, false); Ok(()) });
                ctx.it("this should also be runned", || { counter.fetch_add(1, Ordering::Relaxed); Ok(()) })
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_err());
            expect!(counter.load(Ordering::Relaxed)).to(be_equal_to(1));
        }
    }

    /*
     * Test list:
     * x check that tests can call `assert_eq!`
     * x check that tests can return Err or Ok
     * - runner can count the tests
     * - runner can count the success and failures
     * - check that runner displays the tests names and their results
     * - check that we can use before in a describe
     * - check that we can use after in a describe
     * - check that after/before are run in all child contextes
     */

}
<commit_msg> count tests<commit_after>#[macro_use(expect)]
extern crate expectest;
pub use expectest::prelude::*;

pub type TestResult = Result<(), ()>;

pub struct Context<'a> {
    pub tests: Vec<Box<FnMut() -> TestResult + 'a>>
}

impl<'a> Context<'a> {
    pub fn describe<F>(&mut self, _name: &'a str, mut body: F)
        where F : 'a + FnMut(&mut Context<'a>) -> () {
        body(self)
    }

    pub fn it<F>(&mut self, _name: &'a str, body: F)
        where F : 'a + FnMut() -> TestResult {

        self.tests.push(Box::new(body))
    }
}

pub fn describe<'a, 'b, F>(_block_name: &'b str, body: F) -> Runner<'a>
    where F : 'a + FnOnce(&mut Context<'a>) -> () {

    let mut c = Context { tests: vec!() };
    body(&mut c);
    Runner { describe: c, report: None }
}

pub struct Runner<'a> {
    describe: Context<'a>,
    report: Option<Result<TestReport, TestReport>>
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct TestReport {
    total_tests: u32
}

impl<'a> Runner<'a> {

    pub fn run(&mut self) -> Result<(), ()> {
        use std::panic::{catch_unwind, AssertUnwindSafe};

        let mut report = TestReport { total_tests: 0 };
        let mut result = Ok(());

        for test_function in self.describe.tests.iter_mut() {
            let res = match catch_unwind(AssertUnwindSafe(|| test_function())) {
                Ok(res) => res,
                _ => Err(())
            };

            result = match result {
                Ok(()) => res,
                old @ _ => old
            };

            report.total_tests += 1;
        }

        if let Ok(_) = result {
            self.report = Some(Ok(report))
        } else {
            self.report = Some(Err(report))
        }

        Ok(())
    }

    pub fn result(&self) -> Result<TestReport, TestReport> {
        self.report.unwrap_or(Ok(TestReport { total_tests: 0 }))
    }
}

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

    mod describe {
        pub use super::*;

        #[test]
        fn it_has_a_root_describe_function() {
            describe("A Test", |_ctx|{});
        }

        #[test]
        fn it_can_call_describe_inside_describe_body() {
            describe("A Root", |ctx| {
                ctx.describe("nested describe", |_ctx| {})
            });
        }

        #[test]
        fn it_can_call_it_inside_describe_body() {
            describe("A root", |ctx| {
                ctx.it("is a test", || { Ok(()) })
            });
        }
    }

    mod runner {
        pub use super::*;

        #[test]
        fn it_create_a_runner_that_can_be_runned() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("is expected to run", || {
                    assert_eq!(true, true);
                    Ok(())
                })
            });
            expect!(runner.run()).to(be_ok());
        }

        #[test]
        fn runner_effectively_run_tests() {
            let ran = &mut false;

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.it("is expected to run", || {
                        *ran = true;
                        Ok(())
                    })
                });
                runner.run().unwrap()
            }

            assert_eq!(true, *ran)
        }

        #[test]
        fn runner_effectively_run_two_tests() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let ran_counter = &mut AtomicUsize::new(0);

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.it("first run",  || { ran_counter.fetch_add(1, Ordering::Relaxed); Ok(()) });
                    ctx.it("second run", || { ran_counter.fetch_add(1, Ordering::Relaxed); Ok(()) });
                });
                let _ = runner.run().unwrap();
            }

            assert_eq!(2, ran_counter.load(Ordering::Relaxed))
        }

        #[test]
        fn runner_effectively_run_two_tests_in_nested_describe() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let ran_counter = &mut AtomicUsize::new(0);

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.describe("first describe", |ctx| {
                        ctx.it("first run",  || { ran_counter.fetch_add(1, Ordering::Relaxed); Ok(() )});
                    });
                    ctx.describe("second describe", |ctx| {
                        ctx.it("second run",  || { ran_counter.fetch_add(1, Ordering::Relaxed); Ok(()) });
                    });
                    ctx.describe("third describe", |ctx| {
                        ctx.describe("fourth describe", |ctx| {
                            ctx.it("third run",  || { ran_counter.fetch_add(1, Ordering::Relaxed);  Ok(()) });
                        })
                    })
                });
                let _ = runner.run().unwrap();
            }

            assert_eq!(3, ran_counter.load(Ordering::Relaxed))
        }

        #[test]
        fn tests_can_fail_with_an_error_result() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("should fail", || {
                    Err(())
                })
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_err());
        }

        #[test]
        fn result_should_be_ok_if_tests_are_ok() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("should be ok", || { Ok(()) })
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_ok());
        }

        #[test]
        fn results_is_ok_if_no_tests_have_been_runned() {
            let mut runner = describe("A root", |_ctx| {});
            runner.run().unwrap();

            expect!(runner.result()).to(be_ok());
        }

        #[test]
        fn results_is_err_if_one_test_is_err() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("an err", || { Err(()) });
                ctx.it("an ok", || { Ok(()) });
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_err());
        }

        #[test]
        fn results_is_ok_if_all_tests_are_ok() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("ok 1", || { Ok(()) });
                ctx.it("ok 2", || { Ok(()) });
                ctx.it("ok 3", || { Ok(()) });
                ctx.it("ok 4", || { Ok(()) });
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_ok());
        }

        #[test]
        fn tests_can_contains_asserts_that_panic() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let counter = &mut AtomicUsize::new(0);

            let mut runner = describe("A root", |ctx| {
                ctx.it("assert_eq fail", || { assert_eq!(true, false); Ok(()) });
                ctx.it("this should also be runned", || { counter.fetch_add(1, Ordering::Relaxed); Ok(()) })
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_err());
            expect!(counter.load(Ordering::Relaxed)).to(be_equal_to(1));
        }

        #[test]
        fn can_count_the_tests() {
            let mut runner = describe("a root", |ctx| {
                ctx.it("first", || { Ok(()) });
                ctx.it("second", || { Ok(()) });
                ctx.it("third", || { Ok(()) });
            });
            runner.run().unwrap();

            expect!(runner.result()).to(be_ok().value(TestReport { total_tests: 3 }));
        }
    }

    /*
     * Test list:
     * x check that tests can call `assert_eq!`
     * x check that tests can return Err or Ok
     * - runner can count the tests
     * - runner can count the success and failures
     * - check that runner displays the tests names and their results
     * - check that we can use before in a describe
     * - check that we can use after in a describe
     * - check that after/before are run in all child contextes
     */

}
<|endoftext|>


<commit_before>//! Functionality for working with SIMD registers and instructions.
//!
//! [Source](https://github.com/huonw/simd), [crates.io](https://crates.io/crates/simd)

#![feature(trace_macros, link_llvm_intrinsics, simd_ffi)]

extern crate simdty;
extern crate llvmint;

use std::mem;

pub use convert::bitcast::Bitcast;
pub use convert::semantic::{Convert, Tails, TailsMut};

mod convert;

mod generated;

pub mod maths;

/// A SIMD vector, storing precisely `count()` elements of (primitive)
/// type `Item` sequentially in memory.
///
/// This trait is `unsafe` because it is used inside `unsafe` code
/// relying on the correctness of `Item` and `count()`.
pub unsafe trait Vector: Sized + Copy {
    /// The type that this vector contains.
    type Item;

    /// Return the number of items in `self`.
    #[inline(always)]
    fn count(_: Option<Self>) -> usize {
        let vsize = mem::size_of::<Self>();
        let esize = mem::size_of::<Self::Item>();
        assert!(vsize % esize == 0);

        vsize / esize
    }
}

/// SIMD vectors which can be separated into two SIMD vectors of half
/// the size with the same elements.
pub unsafe trait HalfVector: Vector {
    type Half /*: Vector<Item = Self::Item>*/;

    /// Retrieve the upper and lower halves of the `self` vector.
    fn split(self) -> (Self::Half, Self::Half);

    /// Retrieve the lower half of the `self` vector.
    #[inline]
    fn lower(self) -> Self::Half { self.split().0 }
    /// Retrieve the upper half of the `self` vector.
    #[inline]
    fn upper(self) -> Self::Half { self.split().1 }
}
/// SIMD vectors which can be merged with another of the same type to
/// create one of double the length with the same elements.
pub unsafe trait DoubleVector: Vector {
    type Double /*: Vector<Item = Self::Item>*/;

    /// Concatenate the elements of `self` and `other` into a single
    /// SIMD vector.
    fn merge(self, other: Self) -> Self::Double;
}

/// Return a platform-specific approximation to the square root of
/// `x`.
#[inline]
pub fn sqrt<T: maths::sqrt::Sqrt>(x: T) -> T {
    x.sqrt()
}
/// Return a platform-specific approximation to the reciprocal-square
/// root of `x`.
#[inline]
pub fn rsqrt<T: maths::sqrt::RSqrt>(x: T) -> T {
    x.rsqrt()
}
<commit_msg>Update to c5961ad06 2015-01-28 21:49:38 +0000.<commit_after>//! Functionality for working with SIMD registers and instructions.
//!
//! [Source](https://github.com/huonw/simd), [crates.io](https://crates.io/crates/simd)

#![feature(trace_macros, link_llvm_intrinsics, simd_ffi,
           core)]

extern crate simdty;
extern crate llvmint;

use std::mem;

pub use convert::bitcast::Bitcast;
pub use convert::semantic::{Convert, Tails, TailsMut};

mod convert;

mod generated;

pub mod maths;

/// A SIMD vector, storing precisely `count()` elements of (primitive)
/// type `Item` sequentially in memory.
///
/// This trait is `unsafe` because it is used inside `unsafe` code
/// relying on the correctness of `Item` and `count()`.
pub unsafe trait Vector: Sized + Copy {
    /// The type that this vector contains.
    type Item;

    /// Return the number of items in `self`.
    #[inline(always)]
    fn count(_: Option<Self>) -> usize {
        let vsize = mem::size_of::<Self>();
        let esize = mem::size_of::<Self::Item>();
        assert!(vsize % esize == 0);

        vsize / esize
    }
}

/// SIMD vectors which can be separated into two SIMD vectors of half
/// the size with the same elements.
pub unsafe trait HalfVector: Vector {
    type Half /*: Vector<Item = Self::Item>*/;

    /// Retrieve the upper and lower halves of the `self` vector.
    fn split(self) -> (Self::Half, Self::Half);

    /// Retrieve the lower half of the `self` vector.
    #[inline]
    fn lower(self) -> Self::Half { self.split().0 }
    /// Retrieve the upper half of the `self` vector.
    #[inline]
    fn upper(self) -> Self::Half { self.split().1 }
}
/// SIMD vectors which can be merged with another of the same type to
/// create one of double the length with the same elements.
pub unsafe trait DoubleVector: Vector {
    type Double /*: Vector<Item = Self::Item>*/;

    /// Concatenate the elements of `self` and `other` into a single
    /// SIMD vector.
    fn merge(self, other: Self) -> Self::Double;
}

/// Return a platform-specific approximation to the square root of
/// `x`.
#[inline]
pub fn sqrt<T: maths::sqrt::Sqrt>(x: T) -> T {
    x.sqrt()
}
/// Return a platform-specific approximation to the reciprocal-square
/// root of `x`.
#[inline]
pub fn rsqrt<T: maths::sqrt::RSqrt>(x: T) -> T {
    x.rsqrt()
}
<|endoftext|>


<commit_before>#![feature(convert, test)]

//! sysly is a rust interface for [syslog](https://tools.ietf.org/html/rfc5424

extern crate test;
extern crate time;
extern crate unix_socket;

use std::convert::AsRef;
use std::io::{ Error, Write };
use std::net::{ Ipv4Addr, UdpSocket, SocketAddr, SocketAddrV4 };
use std::path::Path;
use std::result;
use time::Tm;
use unix_socket::UnixStream;

/// A type alias for `result::Result<(), std::io::Error>`, the result of writing a log message
pub type Result = result::Result<(), Error>;

static NIL: &'static str = "-";

/// Syslog [Facilities](https://tools.ietf.org/html/rfc5424#page-10)
#[derive(Copy,Clone)]
pub enum Facility {
  KERN     = 0,
  USER     = 1 << 3,
  MAIL     = 2 << 3,
  DAEMON   = 3 << 3,
  AUTH     = 4 << 3,
  SYSLOG   = 5 << 3,
  LINEPTR  = 6 << 3,
  NEWS     = 7 << 3,
  UUCP     = 8 << 3,
  CLOCK    = 9 << 3,
  AUTHPRIV = 10 << 3,
  FTP      = 11 << 3,
  LOCAL0   = 16 << 3,
  LOCAL1   = 17 << 3,
  LOCAL2   = 18 << 3,
  LOCAL3   = 19 << 3,
  LOCAL4   = 20 << 3,
  LOCAL5   = 21 << 3,
  LOCAL6   = 22 << 3,
  LOCAL7   = 23 << 3
}

/// Syslog [Severities](https://tools.ietf.org/html/rfc5424#page-11)
pub enum Severity {
  EMERGENCY,
  ALERT,
  CRITICAL,
  ERROR,
  WARNING,
  NOTICE,
  INFO,
  DEBUG
}

trait Transport {
  fn send(&mut self, line: &str) -> Result;
}

impl Transport for (UdpSocket, SocketAddr) {
  fn send(&mut self, line: &str) -> Result {
    self.0.send_to(line.as_bytes(), &self.1).map(|_| ())
  }
}

impl Transport for UnixStream {
  fn send(&mut self, line: &str) -> Result {
    self.write_all(line.as_bytes())
  }
}

/// A rust interface for Syslog, a standard unix system logging service
pub struct Syslog {
  /// A Syslog facility to target when logging
  facility: Facility,
  /// A Syslog host entry as defined by
  /// [rfc5424#section-6.2.4](https://tools.ietf.org/html/rfc5424#section-6.2.4)
  host: Option<String>,
  /// An optional app-name appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.5](https://tools.ietf.org/html/rfc5424#section-6.2.5)
  app: Option<String>,
  /// An optional proc-id appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.6](https://tools.ietf.org/html/rfc5424#section-6.2.6)
  pid: Option<String>,
  /// An optional msg-id appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.7](https://tools.ietf.org/html/rfc5424#section-6.2.7)
  msgid: Option<String>,
  transport: Box<Transport>
}

impl Syslog {
   /// Factory for a Syslog appender that writes to
   /// remote Syslog daemon listening a SocketAddr
   pub fn udp(host: SocketAddr) -> Syslog {
     let socket =
       match UdpSocket::bind("0.0.0.0:0") {
         Err(e) => panic!("error binding to local addr {}", e),
         Ok(s) => s
       };
      let tup = (socket, host);
      Syslog {
        facility: Facility::USER,
        host: None,
        app: None,
        pid: None,
        msgid: None,
        transport: Box::new(tup)
      }
  }

  /// Same as udp with providing local loopback address with the standard syslog port
  pub fn localudp() -> Syslog {
    Syslog::udp(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127,0,0,1), 514)))
  }

  /// Factory for a Syslog appender that writes
  /// to a host-local Syslog daemon listening on a unix socket domain
  /// hosted at the given Path
  pub fn unix<P: AsRef<Path>>(path: P) -> Syslog {
    let stream =
      match UnixStream::connect(path) {
        Err(_) => panic!("failed to connect to socket"),
        Ok(s)  => s
      };
    Syslog {
      facility: Facility::USER,
      host: None,
      app: None,
      pid: None,
      msgid: None,
      transport: Box::new(stream)
    }
  }
  /// Returns a new Syslog appender configured to append with
  /// the provided Facility
  pub fn facility(self, facility: Facility) -> Syslog {
    Syslog {
      facility: facility,
      host: self.host,
      app: self.app,
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender configured to append with
  /// the provided host addr
  pub fn host(self, local: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: Some(local.to_string()),
      app: self.app,
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender, configured to append with
  /// the provided app-name
  pub fn app(self, app: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: Some(app.to_string()),
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  pub fn pid(self, pid: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: self.app,
      pid: Some(pid.to_string()),
      msgid: self.msgid,
      transport: self.transport
    }
  }

  pub fn msgid(self, id: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: self.app,
      pid: self.pid,
      msgid: Some(id.to_string()),
      transport: self.transport
    }
  }

  pub fn debug(&mut self, msg: &str) -> Result {
    self.log(Severity::DEBUG, msg)
  }

  pub fn info(&mut self, msg: &str) -> Result {
    self.log(Severity::INFO, msg)
  }

  pub fn notice(&mut self, msg: &str) -> Result {
    self.log(Severity::NOTICE, msg)
  }

  pub fn warn(&mut self, msg: &str) -> Result {
    self.log(Severity::WARNING, msg)
  }

  pub fn err(&mut self, msg: &str) -> Result {
    self.log(Severity::ERROR, msg)
  }

  pub fn critical(&mut self, msg: &str) -> Result {
    self.log(Severity::CRITICAL, msg)
  }

  pub fn alert(&mut self, msg: &str) -> Result {
    self.log(Severity::ALERT, msg)
  }

  pub fn emergency(&mut self, msg: &str) -> Result {
    self.log(Severity::EMERGENCY, msg)
  }

  fn log(&mut self, severity: Severity,  msg: &str) -> Result {
    let formatted = Syslog::line(
        self.facility.clone(), severity, time::now(), self.host.clone(), self.app.clone(), self.pid.clone(), self.msgid.clone(), msg);
    self.transport.send(&formatted)
  }

  fn line(facility: Facility, severity: Severity, timestamp: Tm, host: Option<String>, app: Option<String>, pid: Option<String>, msgid: Option<String>, msg: &str) -> String {
    format!(
      "<{:?}>1 {} {} {} {} {} {}",
        Syslog::priority(facility, severity),
        timestamp.rfc3339(),
        host.unwrap_or(NIL.to_string()),
        app.unwrap_or(NIL.to_string()),
        pid.unwrap_or(NIL.to_string()),
        msgid.unwrap_or(NIL.to_string()),
        msg)
  }

  // computes the priority of a message based on a facility and severity
  fn priority(facility: Facility, severity: Severity) -> u8 {
    facility as u8 | severity as u8
  }
}

#[cfg(test)]
mod tests {
  use super::{Syslog, Facility, Severity};
  use time;
  use test::Bencher;

  #[test]
  fn test_syslog_line_defaults() {
    let ts = time::now();
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, None, None, "yo"),
      format!("<134>1 {} - - - - yo", ts.rfc3339()));
  }

  #[test]
  fn test_syslog_line_host() {
    let ts = time::now();
    let host = "foo.local";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, Some(host.to_string()), None, None, None, "yo"),
      format!("<134>1 {} {} - - - yo", ts.rfc3339(), host));
  }

  #[test]
  fn test_syslog_line_app() {
    let ts = time::now();
    let app = "sysly";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, Some(app.to_string()), None, None, "yo"),
      format!("<134>1 {} - {} - - yo", ts.rfc3339(), app));
  }

  #[test]
  fn test_syslog_line_pid() {
    let ts = time::now();
    let pid = "16";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, Some(pid.to_string()), None, "yo"),
      format!("<134>1 {} - - {} - yo", ts.rfc3339(), pid));
  }

  #[test]
  fn test_syslog_line_msgid() {
    let ts = time::now();
    let msgid = "TCPIN";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, None, Some(msgid.to_string()), "yo"),
      format!("<134>1 {} - - - {} yo", ts.rfc3339(), msgid));
  }

  #[bench]
  fn bench_assembly_line(b: &mut Bencher) {
    b.iter(|| Syslog::line(
      Facility::LOCAL0, Severity::INFO, time::now(), None, None, None, None, "yo"))
  }
}
<commit_msg>compiling<commit_after>#![cfg_attr(test, feature(test))]

//! sysly is a rust interface for [syslog](https://tools.ietf.org/html/rfc5424

#[cfg(test)]
extern crate test;

extern crate time;
extern crate unix_socket;

use std::convert::AsRef;
use std::io::{ Error, Write };
use std::net::{ Ipv4Addr, UdpSocket, SocketAddr, SocketAddrV4 };
use std::path::Path;
use std::result;
use time::Tm;
use unix_socket::UnixStream;

/// A type alias for `result::Result<(), std::io::Error>`, the result of writing a log message
pub type Result = result::Result<(), Error>;

static NIL: &'static str = "-";

/// Syslog [Facilities](https://tools.ietf.org/html/rfc5424#page-10)
#[derive(Copy,Clone)]
pub enum Facility {
  KERN     = 0,
  USER     = 1 << 3,
  MAIL     = 2 << 3,
  DAEMON   = 3 << 3,
  AUTH     = 4 << 3,
  SYSLOG   = 5 << 3,
  LINEPTR  = 6 << 3,
  NEWS     = 7 << 3,
  UUCP     = 8 << 3,
  CLOCK    = 9 << 3,
  AUTHPRIV = 10 << 3,
  FTP      = 11 << 3,
  LOCAL0   = 16 << 3,
  LOCAL1   = 17 << 3,
  LOCAL2   = 18 << 3,
  LOCAL3   = 19 << 3,
  LOCAL4   = 20 << 3,
  LOCAL5   = 21 << 3,
  LOCAL6   = 22 << 3,
  LOCAL7   = 23 << 3
}

/// Syslog [Severities](https://tools.ietf.org/html/rfc5424#page-11)
pub enum Severity {
  EMERGENCY,
  ALERT,
  CRITICAL,
  ERROR,
  WARNING,
  NOTICE,
  INFO,
  DEBUG
}

trait Transport {
  fn send(&mut self, line: &str) -> Result;
}

impl Transport for (UdpSocket, SocketAddr) {
  fn send(&mut self, line: &str) -> Result {
    self.0.send_to(line.as_bytes(), &self.1).map(|_| ())
  }
}

impl Transport for UnixStream {
  fn send(&mut self, line: &str) -> Result {
    self.write_all(line.as_bytes())
  }
}

/// A rust interface for Syslog, a standard unix system logging service
pub struct Syslog {
  /// A Syslog facility to target when logging
  facility: Facility,
  /// A Syslog host entry as defined by
  /// [rfc5424#section-6.2.4](https://tools.ietf.org/html/rfc5424#section-6.2.4)
  host: Option<String>,
  /// An optional app-name appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.5](https://tools.ietf.org/html/rfc5424#section-6.2.5)
  app: Option<String>,
  /// An optional proc-id appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.6](https://tools.ietf.org/html/rfc5424#section-6.2.6)
  pid: Option<String>,
  /// An optional msg-id appended to Syslog messages as defined by
  /// [rfc5424#section-6.2.7](https://tools.ietf.org/html/rfc5424#section-6.2.7)
  msgid: Option<String>,
  transport: Box<Transport>
}

impl Syslog {
   /// Factory for a Syslog appender that writes to
   /// remote Syslog daemon listening a SocketAddr
   pub fn udp(host: SocketAddr) -> Syslog {
     let socket =
       match UdpSocket::bind("0.0.0.0:0") {
         Err(e) => panic!("error binding to local addr {}", e),
         Ok(s) => s
       };
      let tup = (socket, host);
      Syslog {
        facility: Facility::USER,
        host: None,
        app: None,
        pid: None,
        msgid: None,
        transport: Box::new(tup)
      }
  }

  /// Same as udp with providing local loopback address with the standard syslog port
  pub fn localudp() -> Syslog {
    Syslog::udp(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127,0,0,1), 514)))
  }

  /// Factory for a Syslog appender that writes
  /// to a host-local Syslog daemon listening on a unix socket domain
  /// hosted at the given Path
  pub fn unix<P: AsRef<Path>>(path: P) -> Syslog {
    let stream =
      match UnixStream::connect(path) {
        Err(_) => panic!("failed to connect to socket"),
        Ok(s)  => s
      };
    Syslog {
      facility: Facility::USER,
      host: None,
      app: None,
      pid: None,
      msgid: None,
      transport: Box::new(stream)
    }
  }
  /// Returns a new Syslog appender configured to append with
  /// the provided Facility
  pub fn facility(self, facility: Facility) -> Syslog {
    Syslog {
      facility: facility,
      host: self.host,
      app: self.app,
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender configured to append with
  /// the provided host addr
  pub fn host(self, local: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: Some(local.to_string()),
      app: self.app,
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender, configured to append with
  /// the provided app-name
  pub fn app(self, app: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: Some(app.to_string()),
      pid: self.pid,
      msgid: self.msgid,
      transport: self.transport
    }
  }

  pub fn pid(self, pid: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: self.app,
      pid: Some(pid.to_string()),
      msgid: self.msgid,
      transport: self.transport
    }
  }

  pub fn msgid(self, id: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      app: self.app,
      pid: self.pid,
      msgid: Some(id.to_string()),
      transport: self.transport
    }
  }

  pub fn debug(&mut self, msg: &str) -> Result {
    self.log(Severity::DEBUG, msg)
  }

  pub fn info(&mut self, msg: &str) -> Result {
    self.log(Severity::INFO, msg)
  }

  pub fn notice(&mut self, msg: &str) -> Result {
    self.log(Severity::NOTICE, msg)
  }

  pub fn warn(&mut self, msg: &str) -> Result {
    self.log(Severity::WARNING, msg)
  }

  pub fn err(&mut self, msg: &str) -> Result {
    self.log(Severity::ERROR, msg)
  }

  pub fn critical(&mut self, msg: &str) -> Result {
    self.log(Severity::CRITICAL, msg)
  }

  pub fn alert(&mut self, msg: &str) -> Result {
    self.log(Severity::ALERT, msg)
  }

  pub fn emergency(&mut self, msg: &str) -> Result {
    self.log(Severity::EMERGENCY, msg)
  }

  fn log(&mut self, severity: Severity,  msg: &str) -> Result {
    let formatted = Syslog::line(
        self.facility.clone(), severity, time::now(), self.host.clone(), self.app.clone(), self.pid.clone(), self.msgid.clone(), msg);
    self.transport.send(&formatted)
  }

  fn line(facility: Facility, severity: Severity, timestamp: Tm, host: Option<String>, app: Option<String>, pid: Option<String>, msgid: Option<String>, msg: &str) -> String {
    format!(
      "<{:?}>1 {} {} {} {} {} {}",
        Syslog::priority(facility, severity),
        timestamp.rfc3339(),
        host.unwrap_or(NIL.to_string()),
        app.unwrap_or(NIL.to_string()),
        pid.unwrap_or(NIL.to_string()),
        msgid.unwrap_or(NIL.to_string()),
        msg)
  }

  // computes the priority of a message based on a facility and severity
  fn priority(facility: Facility, severity: Severity) -> u8 {
    facility as u8 | severity as u8
  }
}

#[cfg(test)]
mod tests {
  use super::{Syslog, Facility, Severity};
  use time;
  use test::Bencher;

  #[test]
  fn test_syslog_line_defaults() {
    let ts = time::now();
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, None, None, "yo"),
      format!("<134>1 {} - - - - yo", ts.rfc3339()));
  }

  #[test]
  fn test_syslog_line_host() {
    let ts = time::now();
    let host = "foo.local";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, Some(host.to_string()), None, None, None, "yo"),
      format!("<134>1 {} {} - - - yo", ts.rfc3339(), host));
  }

  #[test]
  fn test_syslog_line_app() {
    let ts = time::now();
    let app = "sysly";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, Some(app.to_string()), None, None, "yo"),
      format!("<134>1 {} - {} - - yo", ts.rfc3339(), app));
  }

  #[test]
  fn test_syslog_line_pid() {
    let ts = time::now();
    let pid = "16";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, Some(pid.to_string()), None, "yo"),
      format!("<134>1 {} - - {} - yo", ts.rfc3339(), pid));
  }

  #[test]
  fn test_syslog_line_msgid() {
    let ts = time::now();
    let msgid = "TCPIN";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, None, Some(msgid.to_string()), "yo"),
      format!("<134>1 {} - - - {} yo", ts.rfc3339(), msgid));
  }

  #[bench]
  fn bench_assembly_line(b: &mut Bencher) {
    b.iter(|| Syslog::line(
      Facility::LOCAL0, Severity::INFO, time::now(), None, None, None, None, "yo"))
  }
}
<|endoftext|>


<commit_before>//! A TOML-parsing library
//!
//! This library is an implementation in Rust of a parser for TOML configuration
//! files [1]. It is focused around high quality errors including specific spans
//! and detailed error messages when things go wrong.
//!
//! This implementation currently passes the language agnostic [test suite][2].
//!
//! # Example
//!
//! ```
//! let toml = r#"
//!     [test]
//!     foo = "bar"
//! "#;
//!
//! let value = toml::Parser::new(toml).parse().unwrap();
//! println!("{}", value);
//! ```
//!
//! # Conversions
//!
//! This library also supports using the standard `Encodable` and `Decodable`
//! traits with TOML values. This library provides the following conversion
//! capabilities:
//!
//! * `String` => `toml::Value` - via `Parser`
//! * `toml::Value` => `String` - via `Show`
//! * `toml::Value` => rust object - via `Decoder`
//! * rust object => `toml::Value` - via `Encoder`
//!
//! Convenience functions for performing multiple conversions at a time are also
//! provided.
//!
//! [1]: https://github.com/mojombo/toml
//! [2]: https://github.com/BurntSushi/toml-test
//!

#![crate_name = "toml"]
#![crate_type = "lib"]
#![feature(macro_rules)]
#![deny(missing_docs)]

extern crate serialize;

use std::collections::TreeMap;
use std::from_str::FromStr;
use std::string;

pub use parser::{Parser, ParserError};
pub use serialization::{Encoder, encode, encode_str};
pub use serialization::{Decoder, decode, decode_str};
pub use serialization::{Error, NeedsKey, NoValue};
pub use serialization::{InvalidMapKeyLocation, InvalidMapKeyType};
pub use serialization::{DecodeError, ApplicationError, ExpectedField};
pub use serialization::{ExpectedMapElement, ExpectedMapKey, NoEnumVariants};
pub use serialization::{ExpectedType, NilTooLong};

mod parser;
mod show;
mod serialization;
#[cfg(test)]mod test;
/// Representation of a TOML value.
#[deriving(PartialEq, Clone)]
#[allow(missing_docs)]
pub enum Value {
    String(string::String),
    Integer(i64),
    Float(f64),
    Boolean(bool),
    Datetime(string::String),
    Array(TomlArray),
    Table(TomlTable),
}

pub type TomlArray = Vec<Value>;
pub type TomlTable = TreeMap<string::String, Value>;

impl Value {
    /// Tests whether this and another value have the same type.
    pub fn same_type(&self, other: &Value) -> bool {
        match (self, other) {
            (&String(..), &String(..)) |
            (&Integer(..), &Integer(..)) |
            (&Float(..), &Float(..)) |
            (&Boolean(..), &Boolean(..)) |
            (&Datetime(..), &Datetime(..)) |
            (&Array(..), &Array(..)) |
            (&Table(..), &Table(..)) => true,

            _ => false,
        }
    }

    /// Returns a human-readable representation of the type of this value.
    pub fn type_str(&self) -> &'static str {
        match *self {
            String(..) => "string",
            Integer(..) => "integer",
            Float(..) => "float",
            Boolean(..) => "boolean",
            Datetime(..) => "datetime",
            Array(..) => "array",
            Table(..) => "table",
        }
    }

    /// Extracts the string of this value if it is a string.
    pub fn as_str<'a>(&'a self) -> Option<&'a str> {
        match *self { String(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the integer value if it is an integer.
    pub fn as_integer(&self) -> Option<i64> {
        match *self { Integer(i) => Some(i), _ => None }
    }

    /// Extracts the float value if it is a float.
    pub fn as_float(&self) -> Option<f64> {
        match *self { Float(f) => Some(f), _ => None }
    }

    /// Extracts the boolean value if it is a boolean.
    pub fn as_bool(&self) -> Option<bool> {
        match *self { Boolean(b) => Some(b), _ => None }
    }

    /// Extracts the datetime value if it is a datetime.
    ///
    /// Note that a parsed TOML value will only contain ISO 8601 dates. An
    /// example date is:
    ///
    /// ```notrust
    /// 1979-05-27T07:32:00Z
    /// ```
    pub fn as_datetime<'a>(&'a self) -> Option<&'a str> {
        match *self { Datetime(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the array value if it is an array.
    pub fn as_slice<'a>(&'a self) -> Option<&'a [Value]> {
        match *self { Array(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the table value if it is a table.
    pub fn as_table<'a>(&'a self) -> Option<&'a TomlTable> {
        match *self { Table(ref s) => Some(s), _ => None }
    }

    /// Lookups for value at specified path.
    ///
    /// Uses '.' as a path separator.
    ///
    /// Note: arrays have zero-based indexes.
    ///
    /// ```
    /// let toml = r#"
    ///      [test]
    ///      foo = "bar"
    ///
    ///      [[values]]
    ///      foo = "baz"
    ///
    ///      [[values]]
    ///      foo = "qux"
    /// "#;
    /// let value: toml::Value = from_str(toml).unwrap();
    ///
    /// let foo = value.lookup("test.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "bar");
    ///
    /// let foo = value.lookup("values.1.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "qux");
    ///
    /// let no_bar = value.lookup("test.bar");
    /// assert_eq!(no_bar.is_none(), true);
    /// ```
    pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> {
        let mut cur_value = self;
        for key in path.split('.') {
            match cur_value {
                &Table(ref hm) => {
                    match hm.find_with(|k| key.cmp(&k.as_slice())) {
                        Some(v) => cur_value = v,
                        _ => return None
                    }
                },
                &Array(ref v) => {
                    let idx: Option<uint> = FromStr::from_str(key);
                    match idx {
                        Some(idx) if idx < v.len() => cur_value = &v[idx],
                        _ => return None
                    }
                },
                _ => return None
            }
        };

        Some(cur_value)
    }
}

impl FromStr for Value {
    fn from_str(s: &str) -> Option<Value> {
        Parser::new(s).parse().map(Table)
    }
}

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

    #[test]
    fn lookup_valid() {
        let toml = r#"
              [test]
              foo = "bar"

              [[values]]
              foo = "baz"

              [[values]]
              foo = "qux"
        "#;

        let value: Value = from_str(toml).unwrap();

        let test_foo = value.lookup("test.foo").unwrap();
        assert_eq!(test_foo.as_str().unwrap(), "bar");

        let foo1 = value.lookup("values.1.foo").unwrap();
        assert_eq!(foo1.as_str().unwrap(), "qux");

        let no_bar = value.lookup("test.bar");
        assert!(no_bar.is_none());
    }

    #[test]
    fn lookup_invalid_index() {
        let toml = r#"
            [[values]]
            foo = "baz"
        "#;

        let value: Value = from_str(toml).unwrap();

        let foo = value.lookup("test.foo");
        assert!(foo.is_none());

        let foo = value.lookup("values.100.foo");
        assert!(foo.is_none());

        let foo = value.lookup("values.str.foo");
        assert!(foo.is_none());
    }
}
<commit_msg>fix error on master<commit_after>//! A TOML-parsing library
//!
//! This library is an implementation in Rust of a parser for TOML configuration
//! files [1]. It is focused around high quality errors including specific spans
//! and detailed error messages when things go wrong.
//!
//! This implementation currently passes the language agnostic [test suite][2].
//!
//! # Example
//!
//! ```
//! let toml = r#"
//!     [test]
//!     foo = "bar"
//! "#;
//!
//! let value = toml::Parser::new(toml).parse().unwrap();
//! println!("{}", value);
//! ```
//!
//! # Conversions
//!
//! This library also supports using the standard `Encodable` and `Decodable`
//! traits with TOML values. This library provides the following conversion
//! capabilities:
//!
//! * `String` => `toml::Value` - via `Parser`
//! * `toml::Value` => `String` - via `Show`
//! * `toml::Value` => rust object - via `Decoder`
//! * rust object => `toml::Value` - via `Encoder`
//!
//! Convenience functions for performing multiple conversions at a time are also
//! provided.
//!
//! [1]: https://github.com/mojombo/toml
//! [2]: https://github.com/BurntSushi/toml-test
//!

#![crate_name = "toml"]
#![crate_type = "lib"]
#![feature(macro_rules)]
#![deny(missing_docs)]

extern crate serialize;

use std::collections::TreeMap;
use std::from_str::FromStr;
use std::string;

pub use parser::{Parser, ParserError};
pub use serialization::{Encoder, encode, encode_str};
pub use serialization::{Decoder, decode, decode_str};
pub use serialization::{Error, NeedsKey, NoValue};
pub use serialization::{InvalidMapKeyLocation, InvalidMapKeyType};
pub use serialization::{DecodeError, ApplicationError, ExpectedField};
pub use serialization::{ExpectedMapElement, ExpectedMapKey, NoEnumVariants};
pub use serialization::{ExpectedType, NilTooLong};

mod parser;
mod show;
mod serialization;
#[cfg(test)]mod test;
/// Representation of a TOML value.
#[deriving(PartialEq, Clone)]
#[allow(missing_docs)]
pub enum Value {
    String(string::String),
    Integer(i64),
    Float(f64),
    Boolean(bool),
    Datetime(string::String),
    Array(TomlArray),
    Table(TomlTable),
}

pub type TomlArray = Vec<Value>;
pub type TomlTable = TreeMap<string::String, Value>;

impl Value {
    /// Tests whether this and another value have the same type.
    pub fn same_type(&self, other: &Value) -> bool {
        match (self, other) {
            (&String(..), &String(..)) |
            (&Integer(..), &Integer(..)) |
            (&Float(..), &Float(..)) |
            (&Boolean(..), &Boolean(..)) |
            (&Datetime(..), &Datetime(..)) |
            (&Array(..), &Array(..)) |
            (&Table(..), &Table(..)) => true,

            _ => false,
        }
    }

    /// Returns a human-readable representation of the type of this value.
    pub fn type_str(&self) -> &'static str {
        match *self {
            String(..) => "string",
            Integer(..) => "integer",
            Float(..) => "float",
            Boolean(..) => "boolean",
            Datetime(..) => "datetime",
            Array(..) => "array",
            Table(..) => "table",
        }
    }

    /// Extracts the string of this value if it is a string.
    pub fn as_str<'a>(&'a self) -> Option<&'a str> {
        match *self { String(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the integer value if it is an integer.
    pub fn as_integer(&self) -> Option<i64> {
        match *self { Integer(i) => Some(i), _ => None }
    }

    /// Extracts the float value if it is a float.
    pub fn as_float(&self) -> Option<f64> {
        match *self { Float(f) => Some(f), _ => None }
    }

    /// Extracts the boolean value if it is a boolean.
    pub fn as_bool(&self) -> Option<bool> {
        match *self { Boolean(b) => Some(b), _ => None }
    }

    /// Extracts the datetime value if it is a datetime.
    ///
    /// Note that a parsed TOML value will only contain ISO 8601 dates. An
    /// example date is:
    ///
    /// ```notrust
    /// 1979-05-27T07:32:00Z
    /// ```
    pub fn as_datetime<'a>(&'a self) -> Option<&'a str> {
        match *self { Datetime(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the array value if it is an array.
    pub fn as_slice<'a>(&'a self) -> Option<&'a [Value]> {
        match *self { Array(ref s) => Some(s.as_slice()), _ => None }
    }

    /// Extracts the table value if it is a table.
    pub fn as_table<'a>(&'a self) -> Option<&'a TomlTable> {
        match *self { Table(ref s) => Some(s), _ => None }
    }

    /// Lookups for value at specified path.
    ///
    /// Uses '.' as a path separator.
    ///
    /// Note: arrays have zero-based indexes.
    ///
    /// ```
    /// let toml = r#"
    ///      [test]
    ///      foo = "bar"
    ///
    ///      [[values]]
    ///      foo = "baz"
    ///
    ///      [[values]]
    ///      foo = "qux"
    /// "#;
    /// let value: toml::Value = from_str(toml).unwrap();
    ///
    /// let foo = value.lookup("test.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "bar");
    ///
    /// let foo = value.lookup("values.1.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "qux");
    ///
    /// let no_bar = value.lookup("test.bar");
    /// assert_eq!(no_bar.is_none(), true);
    /// ```
    pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> {
        let mut cur_value = self;
        for key in path.split('.') {
            match cur_value {
                &Table(ref hm) => {
                    match hm.find_with(|k| key.cmp(k.as_slice())) {
                        Some(v) => cur_value = v,
                        _ => return None
                    }
                },
                &Array(ref v) => {
                    let idx: Option<uint> = FromStr::from_str(key);
                    match idx {
                        Some(idx) if idx < v.len() => cur_value = &v[idx],
                        _ => return None
                    }
                },
                _ => return None
            }
        };

        Some(cur_value)
    }
}

impl FromStr for Value {
    fn from_str(s: &str) -> Option<Value> {
        Parser::new(s).parse().map(Table)
    }
}

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

    #[test]
    fn lookup_valid() {
        let toml = r#"
              [test]
              foo = "bar"

              [[values]]
              foo = "baz"

              [[values]]
              foo = "qux"
        "#;

        let value: Value = from_str(toml).unwrap();

        let test_foo = value.lookup("test.foo").unwrap();
        assert_eq!(test_foo.as_str().unwrap(), "bar");

        let foo1 = value.lookup("values.1.foo").unwrap();
        assert_eq!(foo1.as_str().unwrap(), "qux");

        let no_bar = value.lookup("test.bar");
        assert!(no_bar.is_none());
    }

    #[test]
    fn lookup_invalid_index() {
        let toml = r#"
            [[values]]
            foo = "baz"
        "#;

        let value: Value = from_str(toml).unwrap();

        let foo = value.lookup("test.foo");
        assert!(foo.is_none());

        let foo = value.lookup("values.100.foo");
        assert!(foo.is_none());

        let foo = value.lookup("values.str.foo");
        assert!(foo.is_none());
    }
}
<|endoftext|>