<commit_before>//! Value module.

use serde::{
    Deserialize, Serialize,
    de::{
        DeserializeOwned, DeserializeSeed, Deserializer, Error as SerdeError, MapAccess, SeqAccess,
        Visitor,
    },
    forward_to_deserialize_any,
};
use std::{
    cmp::{Eq, Ordering},
    collections::BTreeMap,
    hash::{Hash, Hasher},
};

use crate::de::{Error as RonError, Result};
use std::iter::FromIterator;

/// A `Value` to `Value` map.
///
/// This structure either uses a [BTreeMap](std::collections::BTreeMap) or the
/// [IndexMap](indexmap::IndexMap) internally.
/// The latter can be used by enabling the `indexmap` feature. This can be used to preserve the
/// order of the parsed map.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Map(MapInner);

impl Map {
    pub fn new() -> Map {
        Default::default()
    }

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

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

    pub fn insert(&mut self, key: Value, value: Value) -> Option<Value> {
        self.0.insert(key, value)
    }

    pub fn remove(&mut self, key: &Value) -> Option<Value> {
        self.0.remove(key)
    }

    pub fn iter(&self) -> impl Iterator<Item = (&Value, &Value)> + DoubleEndedIterator {
        self.0.iter()
    }

    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Value, &mut Value)> + DoubleEndedIterator {
        self.0.iter_mut()
    }

    pub fn keys(&self) -> impl Iterator<Item = &Value> + DoubleEndedIterator {
        self.0.keys()
    }

    pub fn values(&self) -> impl Iterator<Item = &Value> + DoubleEndedIterator {
        self.0.values()
    }

    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Value> + DoubleEndedIterator {
        self.0.values_mut()
    }

    /// Will return `None` if `indexmap` is enabled.
    pub fn as_b_tree_map(&self) -> Option<&BTreeMap<Value, Value>> {
        #[cfg(not(feature = "indexmap"))]
        let x = Some(&self.0);
        #[cfg(feature = "indexmap")]
        let x = None;

        x
    }

    /// Will return `None` if `indexmap` is enabled.
    pub fn as_b_tree_map_mut(&mut self) -> Option<&mut BTreeMap<Value, Value>> {
        #[cfg(not(feature = "indexmap"))]
        let x = Some(&mut self.0);
        #[cfg(feature = "indexmap")]
        let x = None;

        x
    }

    /// Will return `None` if `indexmap` is disabled.
    pub fn as_index_map(&self) -> Option<&indexmap::IndexMap<Value, Value>> {
        #[cfg(feature = "indexmap")]
        let x = Some(&self.0);
        #[cfg(not(feature = "indexmap"))]
        let x = None;

        x
    }

    /// Will return `None` if `indexmap` is disabled.
    pub fn as_index_map_mut(&mut self) -> Option<&mut indexmap::IndexMap<Value, Value>> {
        #[cfg(feature = "indexmap")]
        let x = Some(&mut self.0);
        #[cfg(not(feature = "indexmap"))]
        let x = None;

        x
    }
}

impl FromIterator<(Value, Value)> for Map {
    fn from_iter<T: IntoIterator<Item=(Value, Value)>>(iter: T) -> Self {
        Map(MapInner::from_iter(iter))
    }
}

/// Note: equality is only given if both values and order of values match
impl Eq for Map {}

impl Hash for Map {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.iter().for_each(|x| x.hash(state));
    }
}

impl Ord for Map {
    fn cmp(&self, other: &Map) -> Ordering {
        self.iter().cmp(other.iter())
    }
}

/// Note: equality is only given if both values and order of values match
impl PartialEq for Map {
    fn eq(&self, other: &Map) -> bool {
        self.iter().zip(other.iter()).all(|(a, b)| a == b)
    }
}

impl PartialOrd for Map {
    fn partial_cmp(&self, other: &Map) -> Option<Ordering> {
        self.iter().partial_cmp(other.iter())
    }
}

#[cfg(not(feature = "indexmap"))]
type MapInner = BTreeMap<Value, Value>;
#[cfg(feature = "indexmap")]
type MapInner = indexmap::IndexMap<Value, Value>;

/// A wrapper for `f64` which guarantees that the inner value
/// is finite and thus implements `Eq`, `Hash` and `Ord`.
#[derive(Copy, Clone, Debug)]
pub struct Number(f64);

impl Number {
    pub fn new(v: f64) -> Self {
        Number(v)
    }

    /// Returns the wrapped float.
    pub fn get(self) -> f64 {
        self.0
    }
}

/// Partial equality comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other. It is not the case for underlying `f64` values itself.
impl PartialEq for Number {

    fn eq(&self, other: &Self) -> bool {
        if self.0.is_nan() && other.0.is_nan() {
            return true;
        }
        return self.0 == other.0;
    }
}

/// Equality comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other. It is not the case for underlying `f64` values itself.
impl Eq for Number {}

impl Hash for Number {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_u64(self.0 as u64);
    }
}

/// Partial ordering comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other and are less then any other floating value.
/// It is not the case for underlying `f64` values itself.
/// ```
/// use ron::value::Number;
/// assert!(Number::new(std::f64::NAN) < Number::new(std::f64::NEG_INFINITY));
/// assert_eq!(Number::new(std::f64::NAN), Number::new(std::f64::NAN));
/// ```
impl PartialOrd for Number {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match (self.0.is_nan(), other.0.is_nan()) {
            (true, true) => Some(Ordering::Equal),
            (true, false) => Some(Ordering::Less),
            (false, true) => Some(Ordering::Greater),
            _ => self.0.partial_cmp(&other.0)
        }
    }
}

/// Ordering comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other and are less then any other floating value.
/// It is not the case for underlying `f64` values itself. See the `PartialEq` implementation.
impl Ord for Number {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).expect("Bug: Contract violation")
    }
}

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Value {
    Bool(bool),
    Char(char),
    Map(Map),
    Number(Number),
    Option(Option<Box<Value>>),
    String(String),
    Seq(Vec<Value>),
    Unit,
}

impl Value {
    /// Tries to deserialize this `Value` into `T`.
    pub fn into_rust<T>(self) -> Result<T>
    where
        T: DeserializeOwned,
    {
        T::deserialize(self)
    }
}

/// Deserializer implementation for RON `Value`.
/// This does not support enums (because `Value` doesn't store them).
impl<'de> Deserializer<'de> for Value {
    type Error = RonError;

    forward_to_deserialize_any! {
        bool f32 f64 char str string bytes
        byte_buf option unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        match self {
            Value::Bool(b) => visitor.visit_bool(b),
            Value::Char(c) => visitor.visit_char(c),
            Value::Map(m) => visitor.visit_map(MapAccessor {
                keys: m.keys().cloned().rev().collect(),
                values: m.values().cloned().rev().collect(),
            }),
            Value::Number(n) => visitor.visit_f64(n.get()),
            Value::Option(Some(o)) => visitor.visit_some(*o),
            Value::Option(None) => visitor.visit_none(),
            Value::String(s) => visitor.visit_string(s),
            Value::Seq(mut seq) => {
                seq.reverse();
                visitor.visit_seq(Seq { seq })
            }
            Value::Unit => visitor.visit_unit(),
        }
    }

    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        match self {
            Value::Number(n) => visitor.visit_i64(n.get() as i64),
            v => Err(RonError::custom(format!("Expected a number, got {:?}", v))),
        }
    }

    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_u64(visitor)
    }

    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_u64(visitor)
    }

    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_u64(visitor)
    }

    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        match self {
            Value::Number(n) => visitor.visit_u64(n.get() as u64),
            v => Err(RonError::custom(format!("Expected a number, got {:?}", v))),
        }
    }
}

struct MapAccessor {
    keys: Vec<Value>,
    values: Vec<Value>,
}

impl<'de> MapAccess<'de> for MapAccessor {
    type Error = RonError;

    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
    where
        K: DeserializeSeed<'de>,
    {
        // The `Vec` is reversed, so we can pop to get the originally first element
        self.keys
            .pop()
            .map_or(Ok(None), |v| seed.deserialize(v).map(Some))
    }

    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
    where
        V: DeserializeSeed<'de>,
    {
        // The `Vec` is reversed, so we can pop to get the originally first element
        self.values
            .pop()
            .map(|v| seed.deserialize(v))
            .expect("Contract violation")
    }
}

struct Seq {
    seq: Vec<Value>,
}

impl<'de> SeqAccess<'de> for Seq {
    type Error = RonError;

    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
    where
        T: DeserializeSeed<'de>,
    {
        // The `Vec` is reversed, so we can pop to get the originally first element
        self.seq
            .pop()
            .map_or(Ok(None), |v| seed.deserialize(v).map(Some))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::Deserialize;
    use std::fmt::Debug;

    fn assert_same<'de, T>(s: &'de str)
    where
        T: Debug + Deserialize<'de> + PartialEq,
    {
        use crate::de::from_str;

        let direct: T = from_str(s).unwrap();
        let value: Value = from_str(s).unwrap();
        let value = T::deserialize(value).unwrap();

        assert_eq!(direct, value, "Deserialization for {:?} is not the same", s);
    }

    #[test]
    fn boolean() {
        assert_same::<bool>("true");
        assert_same::<bool>("false");
    }

    #[test]
    fn float() {
        assert_same::<f64>("0.123");
        assert_same::<f64>("-4.19");
    }

    #[test]
    fn int() {
        assert_same::<u32>("626");
        assert_same::<i32>("-50");
    }

    #[test]
    fn char() {
        assert_same::<char>("'4'");
        assert_same::<char>("'c'");
    }

    #[test]
    fn map() {
        assert_same::<BTreeMap<char, String>>(
            "{
'a': \"Hello\",
'b': \"Bye\",
        }",
        );
    }

    #[test]
    fn option() {
        assert_same::<Option<char>>("Some('a')");
        assert_same::<Option<char>>("None");
    }

    #[test]
    fn seq() {
        assert_same::<Vec<f64>>("[1.0, 2.0, 3.0, 4.0]");
    }

    #[test]
    fn unit() {
        assert_same::<()>("()");
    }
}
<commit_msg>Add rustdoc<commit_after>//! Value module.

use serde::{
    Deserialize, Serialize,
    de::{
        DeserializeOwned, DeserializeSeed, Deserializer, Error as SerdeError, MapAccess, SeqAccess,
        Visitor,
    },
    forward_to_deserialize_any,
};
use std::{
    cmp::{Eq, Ordering},
    collections::BTreeMap,
    hash::{Hash, Hasher},
};

use crate::de::{Error as RonError, Result};
use std::iter::FromIterator;

/// A `Value` to `Value` map.
///
/// This structure either uses a [BTreeMap](std::collections::BTreeMap) or the
/// [IndexMap](indexmap::IndexMap) internally.
/// The latter can be used by enabling the `indexmap` feature. This can be used to preserve the
/// order of the parsed map.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct Map(MapInner);

impl Map {
    /// Creates a new, empty `Map`.
    pub fn new() -> Map {
        Default::default()
    }

    /// Returns the number of elements in the map.
    pub fn len(&self) -> usize {
        self.0.len()
    }

    /// Returns `true` if `self.len() == 0`, `false` otherwise.
    pub fn is_empty(&self) -> usize {
        self.0.len()
    }

    /// Inserts a new element, returning the previous element with this `key` if there was any.
    pub fn insert(&mut self, key: Value, value: Value) -> Option<Value> {
        self.0.insert(key, value)
    }

    /// Removes an element by its `key`.
    pub fn remove(&mut self, key: &Value) -> Option<Value> {
        self.0.remove(key)
    }

    /// Iterate all key-value pairs.
    pub fn iter(&self) -> impl Iterator<Item = (&Value, &Value)> + DoubleEndedIterator {
        self.0.iter()
    }

    /// Iterate all key-value pairs mutably.
    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&Value, &mut Value)> + DoubleEndedIterator {
        self.0.iter_mut()
    }

    /// Iterate all keys.
    pub fn keys(&self) -> impl Iterator<Item = &Value> + DoubleEndedIterator {
        self.0.keys()
    }

    /// Iterate all values.
    pub fn values(&self) -> impl Iterator<Item = &Value> + DoubleEndedIterator {
        self.0.values()
    }

    /// Iterate all values mutably.
    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut Value> + DoubleEndedIterator {
        self.0.values_mut()
    }

    /// Can be used to retrieve the internal map representation.
    /// Will return `None` if `indexmap` is enabled.
    pub fn as_b_tree_map(&self) -> Option<&BTreeMap<Value, Value>> {
        #[cfg(not(feature = "indexmap"))]
        let x = Some(&self.0);
        #[cfg(feature = "indexmap")]
        let x = None;

        x
    }

    /// Can be used to retrieve the internal map representation.
    /// Will return `None` if `indexmap` is enabled.
    pub fn as_b_tree_map_mut(&mut self) -> Option<&mut BTreeMap<Value, Value>> {
        #[cfg(not(feature = "indexmap"))]
        let x = Some(&mut self.0);
        #[cfg(feature = "indexmap")]
        let x = None;

        x
    }

    /// Can be used to retrieve the internal map representation.
    /// Will return `None` if `indexmap` is disabled.
    pub fn as_index_map(&self) -> Option<&indexmap::IndexMap<Value, Value>> {
        #[cfg(feature = "indexmap")]
        let x = Some(&self.0);
        #[cfg(not(feature = "indexmap"))]
        let x = None;

        x
    }

    /// Can be used to retrieve the internal map representation.
    /// Will return `None` if `indexmap` is disabled.
    pub fn as_index_map_mut(&mut self) -> Option<&mut indexmap::IndexMap<Value, Value>> {
        #[cfg(feature = "indexmap")]
        let x = Some(&mut self.0);
        #[cfg(not(feature = "indexmap"))]
        let x = None;

        x
    }
}

impl FromIterator<(Value, Value)> for Map {
    fn from_iter<T: IntoIterator<Item=(Value, Value)>>(iter: T) -> Self {
        Map(MapInner::from_iter(iter))
    }
}

/// Note: equality is only given if both values and order of values match
impl Eq for Map {}

impl Hash for Map {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.iter().for_each(|x| x.hash(state));
    }
}

impl Ord for Map {
    fn cmp(&self, other: &Map) -> Ordering {
        self.iter().cmp(other.iter())
    }
}

/// Note: equality is only given if both values and order of values match
impl PartialEq for Map {
    fn eq(&self, other: &Map) -> bool {
        self.iter().zip(other.iter()).all(|(a, b)| a == b)
    }
}

impl PartialOrd for Map {
    fn partial_cmp(&self, other: &Map) -> Option<Ordering> {
        self.iter().partial_cmp(other.iter())
    }
}

#[cfg(not(feature = "indexmap"))]
type MapInner = BTreeMap<Value, Value>;
#[cfg(feature = "indexmap")]
type MapInner = indexmap::IndexMap<Value, Value>;

/// A wrapper for `f64` which guarantees that the inner value
/// is finite and thus implements `Eq`, `Hash` and `Ord`.
#[derive(Copy, Clone, Debug)]
pub struct Number(f64);

impl Number {
    pub fn new(v: f64) -> Self {
        Number(v)
    }

    /// Returns the wrapped float.
    pub fn get(self) -> f64 {
        self.0
    }
}

/// Partial equality comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other. It is not the case for underlying `f64` values itself.
impl PartialEq for Number {

    fn eq(&self, other: &Self) -> bool {
        if self.0.is_nan() && other.0.is_nan() {
            return true;
        }
        return self.0 == other.0;
    }
}

/// Equality comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other. It is not the case for underlying `f64` values itself.
impl Eq for Number {}

impl Hash for Number {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_u64(self.0 as u64);
    }
}

/// Partial ordering comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other and are less then any other floating value.
/// It is not the case for underlying `f64` values itself.
/// ```
/// use ron::value::Number;
/// assert!(Number::new(std::f64::NAN) < Number::new(std::f64::NEG_INFINITY));
/// assert_eq!(Number::new(std::f64::NAN), Number::new(std::f64::NAN));
/// ```
impl PartialOrd for Number {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match (self.0.is_nan(), other.0.is_nan()) {
            (true, true) => Some(Ordering::Equal),
            (true, false) => Some(Ordering::Less),
            (false, true) => Some(Ordering::Greater),
            _ => self.0.partial_cmp(&other.0)
        }
    }
}

/// Ordering comparison
/// In order to be able to use `Number` as a mapping key, NaN floating values wrapped in `Number`
/// are equals to each other and are less then any other floating value.
/// It is not the case for underlying `f64` values itself. See the `PartialEq` implementation.
impl Ord for Number {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).expect("Bug: Contract violation")
    }
}

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Value {
    Bool(bool),
    Char(char),
    Map(Map),
    Number(Number),
    Option(Option<Box<Value>>),
    String(String),
    Seq(Vec<Value>),
    Unit,
}

impl Value {
    /// Tries to deserialize this `Value` into `T`.
    pub fn into_rust<T>(self) -> Result<T>
    where
        T: DeserializeOwned,
    {
        T::deserialize(self)
    }
}

/// Deserializer implementation for RON `Value`.
/// This does not support enums (because `Value` doesn't store them).
impl<'de> Deserializer<'de> for Value {
    type Error = RonError;

    forward_to_deserialize_any! {
        bool f32 f64 char str string bytes
        byte_buf option unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        match self {
            Value::Bool(b) => visitor.visit_bool(b),
            Value::Char(c) => visitor.visit_char(c),
            Value::Map(m) => visitor.visit_map(MapAccessor {
                keys: m.keys().cloned().rev().collect(),
                values: m.values().cloned().rev().collect(),
            }),
            Value::Number(n) => visitor.visit_f64(n.get()),
            Value::Option(Some(o)) => visitor.visit_some(*o),
            Value::Option(None) => visitor.visit_none(),
            Value::String(s) => visitor.visit_string(s),
            Value::Seq(mut seq) => {
                seq.reverse();
                visitor.visit_seq(Seq { seq })
            }
            Value::Unit => visitor.visit_unit(),
        }
    }

    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_i64(visitor)
    }

    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        match self {
            Value::Number(n) => visitor.visit_i64(n.get() as i64),
            v => Err(RonError::custom(format!("Expected a number, got {:?}", v))),
        }
    }

    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_u64(visitor)
    }

    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_u64(visitor)
    }

    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        self.deserialize_u64(visitor)
    }

    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
    where
        V: Visitor<'de>,
    {
        match self {
            Value::Number(n) => visitor.visit_u64(n.get() as u64),
            v => Err(RonError::custom(format!("Expected a number, got {:?}", v))),
        }
    }
}

struct MapAccessor {
    keys: Vec<Value>,
    values: Vec<Value>,
}

impl<'de> MapAccess<'de> for MapAccessor {
    type Error = RonError;

    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
    where
        K: DeserializeSeed<'de>,
    {
        // The `Vec` is reversed, so we can pop to get the originally first element
        self.keys
            .pop()
            .map_or(Ok(None), |v| seed.deserialize(v).map(Some))
    }

    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
    where
        V: DeserializeSeed<'de>,
    {
        // The `Vec` is reversed, so we can pop to get the originally first element
        self.values
            .pop()
            .map(|v| seed.deserialize(v))
            .expect("Contract violation")
    }
}

struct Seq {
    seq: Vec<Value>,
}

impl<'de> SeqAccess<'de> for Seq {
    type Error = RonError;

    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
    where
        T: DeserializeSeed<'de>,
    {
        // The `Vec` is reversed, so we can pop to get the originally first element
        self.seq
            .pop()
            .map_or(Ok(None), |v| seed.deserialize(v).map(Some))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::Deserialize;
    use std::fmt::Debug;

    fn assert_same<'de, T>(s: &'de str)
    where
        T: Debug + Deserialize<'de> + PartialEq,
    {
        use crate::de::from_str;

        let direct: T = from_str(s).unwrap();
        let value: Value = from_str(s).unwrap();
        let value = T::deserialize(value).unwrap();

        assert_eq!(direct, value, "Deserialization for {:?} is not the same", s);
    }

    #[test]
    fn boolean() {
        assert_same::<bool>("true");
        assert_same::<bool>("false");
    }

    #[test]
    fn float() {
        assert_same::<f64>("0.123");
        assert_same::<f64>("-4.19");
    }

    #[test]
    fn int() {
        assert_same::<u32>("626");
        assert_same::<i32>("-50");
    }

    #[test]
    fn char() {
        assert_same::<char>("'4'");
        assert_same::<char>("'c'");
    }

    #[test]
    fn map() {
        assert_same::<BTreeMap<char, String>>(
            "{
'a': \"Hello\",
'b': \"Bye\",
        }",
        );
    }

    #[test]
    fn option() {
        assert_same::<Option<char>>("Some('a')");
        assert_same::<Option<char>>("None");
    }

    #[test]
    fn seq() {
        assert_same::<Vec<f64>>("[1.0, 2.0, 3.0, 4.0]");
    }

    #[test]
    fn unit() {
        assert_same::<()>("()");
    }
}
<|endoftext|>


<commit_before>//! Structs for creating a new zip archive

use compression::CompressionMethod;
use types::ZipFileData;
use spec;
use crc32;
use result::{ZipResult, ZipError};
use std::default::Default;
use std::io;
use std::io::prelude::*;
use std::mem;
use std::error::Error;
use std::ascii::AsciiExt;
use time;
use flate2;
use flate2::FlateWriteExt;
use flate2::write::DeflateEncoder;
use bzip2;
use bzip2::writer::BzCompressor;
use util;
use podio::{WritePodExt, LittleEndian};

enum GenericZipWriter<W: Write + io::Seek>
{
    Closed,
    Storer(W),
    Deflater(DeflateEncoder<W>),
    Bzip2(BzCompressor<W>),
}

/// Generator for ZIP files.
///
/// ```
/// fn doit() -> zip::result::ZipResult<()>
/// {
///     use std::io::Write;
///
///     // For this example we write to a buffer, but normally you should use a File
///     let mut buf: &mut [u8] = &mut [0u8; 65536];
///     let mut w = std::io::Cursor::new(buf);
///     let mut zip = zip::ZipWriter::new(w);
///
///     try!(zip.start_file("hello_world.txt", zip::CompressionMethod::Stored));
///     try!(zip.write(b"Hello, World!"));
///
///     // Optionally finish the zip. (this is also done on drop)
///     try!(zip.finish());
///
///     Ok(())
/// }
///
/// println!("Result: {:?}", doit());
/// ```
pub struct ZipWriter<W: Write + io::Seek>
{
    inner: GenericZipWriter<W>,
    files: Vec<ZipFileData>,
    stats: ZipWriterStats,
}

#[derive(Default)]
struct ZipWriterStats
{
    crc32: u32,
    start: u64,
    bytes_written: u64,
}

impl<W: Write+io::Seek> Write for ZipWriter<W>
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize>
    {
        if self.files.len() == 0 { return Err(io::Error::new(io::ErrorKind::Other, "No file has been started", None)) }
        self.stats.update(buf);
        match self.inner
        {
            GenericZipWriter::Storer(ref mut w) => w.write(buf),
            GenericZipWriter::Deflater(ref mut w) => w.write(buf),
            GenericZipWriter::Bzip2(ref mut w) => w.write(buf),
            GenericZipWriter::Closed => Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed", None)),
        }
    }

    fn flush(&mut self) -> io::Result<()>
    {
        let result = self.finalize();
        self.inner = GenericZipWriter::Closed;
        match result {
            Ok(..) => Ok(()),
            Err(ZipError::Io(io_err)) => Err(io_err),
            Err(zip_err) => Err(io::Error::new(io::ErrorKind::Other, "A zip error occured", Some(zip_err.description().to_string()))),
        }
    }
}

impl ZipWriterStats
{
    fn update(&mut self, buf: &[u8])
    {
        self.crc32 = crc32::update(self.crc32, buf);
        self.bytes_written += buf.len() as u64;
    }
}

impl<W: Write+io::Seek> ZipWriter<W>
{
    /// Initializes the ZipWriter.
    ///
    /// Before writing to this object, the start_file command should be called.
    pub fn new(inner: W) -> ZipWriter<W>
    {
        ZipWriter
        {
            inner: GenericZipWriter::Storer(inner),
            files: Vec::new(),
            stats: Default::default(),
        }
    }

    /// Start a new file for with the requested compression method.
    pub fn start_file(&mut self, name: &str, compression: CompressionMethod) -> ZipResult<()>
    {
        try!(self.finish_file());

        {
            let writer = self.inner.get_plain();
            let header_start = try!(writer.seek(io::SeekFrom::Current(0)));

            let mut file = ZipFileData
            {
                encrypted: false,
                compression_method: compression,
                last_modified_time: time::now(),
                crc32: 0,
                compressed_size: 0,
                uncompressed_size: 0,
                file_name: name.to_string(),
                file_comment: String::new(),
                header_start: header_start,
                data_start: 0,
            };
            try!(write_local_file_header(writer, &file));

            let header_end = try!(writer.seek(io::SeekFrom::Current(0)));
            self.stats.start = header_end;
            file.data_start = header_end;

            self.stats.bytes_written = 0;
            self.stats.crc32 = 0;

            self.files.push(file);
        }

        try!(self.inner.switch_to(compression));

        Ok(())
    }

    fn finish_file(&mut self) -> ZipResult<()>
    {
        try!(self.inner.switch_to(CompressionMethod::Stored));
        let writer = self.inner.get_plain();

        let file = match self.files.last_mut()
        {
            None => return Ok(()),
            Some(f) => f,
        };
        file.crc32 = self.stats.crc32;
        file.uncompressed_size = self.stats.bytes_written;
        file.compressed_size = try!(writer.seek(io::SeekFrom::Current(0))) - self.stats.start;

        try!(update_local_file_header(writer, file));
        try!(writer.seek(io::SeekFrom::End(0)));
        Ok(())
    }

    /// Finish the last file and write all other zip-structures
    ///
    /// This will return the writer, but one should normally not append any data to the end of the file.
    /// Note that the zipfile will also be finished on drop.
    pub fn finish(mut self) -> ZipResult<W>
    {
        try!(self.finalize());
        let inner = mem::replace(&mut self.inner, GenericZipWriter::Closed);
        Ok(inner.unwrap())
    }

    fn finalize(&mut self) -> ZipResult<()>
    {
        try!(self.finish_file());

        {
            let writer = self.inner.get_plain();

            let central_start = try!(writer.seek(io::SeekFrom::Current(0)));
            for file in self.files.iter()
            {
                try!(write_central_directory_header(writer, file));
            }
            let central_size = try!(writer.seek(io::SeekFrom::Current(0))) - central_start;

            let footer = spec::CentralDirectoryEnd
            {
                disk_number: 0,
                disk_with_central_directory: 0,
                number_of_files_on_this_disk: self.files.len() as u16,
                number_of_files: self.files.len() as u16,
                central_directory_size: central_size as u32,
                central_directory_offset: central_start as u32,
                zip_file_comment: b"zip-rs".to_vec(),
            };

            try!(footer.write(writer));
        }

        Ok(())
    }
}

#[unsafe_destructor]
impl<W: Write+io::Seek> Drop for ZipWriter<W>
{
    fn drop(&mut self)
    {
        if !self.inner.is_closed()
        {
            if let Err(e) = self.finalize() {
                let _ = write!(&mut io::stderr(), "ZipWriter drop failed: {:?}", e);
            }
        }
    }
}

impl<W: Write+io::Seek> GenericZipWriter<W>
{
    fn switch_to(&mut self, compression: CompressionMethod) -> ZipResult<()>
    {
        let bare = match mem::replace(self, GenericZipWriter::Closed)
        {
            GenericZipWriter::Storer(w) => w,
            GenericZipWriter::Deflater(w) => try!(w.finish()),
            GenericZipWriter::Bzip2(w) => match w.into_inner() { Ok(r) => r, Err((_, err)) => try!(Err(err)) },
            GenericZipWriter::Closed => try!(Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed", None))),
        };

        *self = match compression
        {
            CompressionMethod::Stored => GenericZipWriter::Storer(bare),
            CompressionMethod::Deflated => GenericZipWriter::Deflater(bare.deflate_encode(flate2::Compression::Default)),
            CompressionMethod::Bzip2 => GenericZipWriter::Bzip2(BzCompressor::new(bare, bzip2::Compress::Default)),
            _ => return Err(ZipError::UnsupportedArchive("Unsupported compression")),
        };

        Ok(())
    }

    fn is_closed(&self) -> bool
    {
        match *self
        {
            GenericZipWriter::Closed => true,
            _ => false,
        }
    }

    fn get_plain(&mut self) -> &mut W
    {
        match *self
        {
            GenericZipWriter::Storer(ref mut w) => w,
            _ => panic!("Should have switched to stored beforehand"),
        }
    }

    fn unwrap(self) -> W
    {
        match self
        {
            GenericZipWriter::Storer(w) => w,
            _ => panic!("Should have switched to stored beforehand"),
        }
    }
}

fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
{
    try!(writer.write_u32::<LittleEndian>(spec::LOCAL_FILE_HEADER_SIGNATURE));
    try!(writer.write_u16::<LittleEndian>(20));
    let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
    try!(writer.write_u16::<LittleEndian>(flag));
    try!(writer.write_u16::<LittleEndian>(file.compression_method.to_u16()));
    try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_time(file.last_modified_time)));
    try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_date(file.last_modified_time)));
    try!(writer.write_u32::<LittleEndian>(file.crc32));
    try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
    try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
    try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
    let extra_field = try!(build_extra_field(file));
    try!(writer.write_u16::<LittleEndian>(extra_field.len() as u16));
    try!(writer.write_all(file.file_name.as_bytes()));
    try!(writer.write_all(&extra_field));

    Ok(())
}

fn update_local_file_header<T: Write+io::Seek>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
{
    static CRC32_OFFSET : u64 = 14;
    try!(writer.seek(io::SeekFrom::Start(file.header_start + CRC32_OFFSET)));
    try!(writer.write_u32::<LittleEndian>(file.crc32));
    try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
    try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
    Ok(())
}

fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
{
    try!(writer.write_u32::<LittleEndian>(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE));
    try!(writer.write_u16::<LittleEndian>(0x14FF));
    try!(writer.write_u16::<LittleEndian>(20));
    let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
    try!(writer.write_u16::<LittleEndian>(flag));
    try!(writer.write_u16::<LittleEndian>(file.compression_method.to_u16()));
    try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_time(file.last_modified_time)));
    try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_date(file.last_modified_time)));
    try!(writer.write_u32::<LittleEndian>(file.crc32));
    try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
    try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
    try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
    let extra_field = try!(build_extra_field(file));
    try!(writer.write_u16::<LittleEndian>(extra_field.len() as u16));
    try!(writer.write_u16::<LittleEndian>(0));
    try!(writer.write_u16::<LittleEndian>(0));
    try!(writer.write_u16::<LittleEndian>(0));
    try!(writer.write_u32::<LittleEndian>(0));
    try!(writer.write_u32::<LittleEndian>(file.header_start as u32));
    try!(writer.write_all(file.file_name.as_bytes()));
    try!(writer.write_all(&extra_field));

    Ok(())
}

fn build_extra_field(_file: &ZipFileData) -> ZipResult<Vec<u8>>
{
    let writer = Vec::new();
    // Future work
    Ok(writer)
}
<commit_msg>Do not close the zip on flush<commit_after>//! Structs for creating a new zip archive

use compression::CompressionMethod;
use types::ZipFileData;
use spec;
use crc32;
use result::{ZipResult, ZipError};
use std::default::Default;
use std::io;
use std::io::prelude::*;
use std::mem;
use std::ascii::AsciiExt;
use time;
use flate2;
use flate2::FlateWriteExt;
use flate2::write::DeflateEncoder;
use bzip2;
use bzip2::writer::BzCompressor;
use util;
use podio::{WritePodExt, LittleEndian};

enum GenericZipWriter<W: Write + io::Seek>
{
    Closed,
    Storer(W),
    Deflater(DeflateEncoder<W>),
    Bzip2(BzCompressor<W>),
}

/// Generator for ZIP files.
///
/// ```
/// fn doit() -> zip::result::ZipResult<()>
/// {
///     use std::io::Write;
///
///     // For this example we write to a buffer, but normally you should use a File
///     let mut buf: &mut [u8] = &mut [0u8; 65536];
///     let mut w = std::io::Cursor::new(buf);
///     let mut zip = zip::ZipWriter::new(w);
///
///     try!(zip.start_file("hello_world.txt", zip::CompressionMethod::Stored));
///     try!(zip.write(b"Hello, World!"));
///
///     // Optionally finish the zip. (this is also done on drop)
///     try!(zip.finish());
///
///     Ok(())
/// }
///
/// println!("Result: {:?}", doit());
/// ```
pub struct ZipWriter<W: Write + io::Seek>
{
    inner: GenericZipWriter<W>,
    files: Vec<ZipFileData>,
    stats: ZipWriterStats,
}

#[derive(Default)]
struct ZipWriterStats
{
    crc32: u32,
    start: u64,
    bytes_written: u64,
}

impl<W: Write+io::Seek> Write for ZipWriter<W>
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize>
    {
        if self.files.len() == 0 { return Err(io::Error::new(io::ErrorKind::Other, "No file has been started", None)) }
        self.stats.update(buf);
        match self.inner
        {
            GenericZipWriter::Storer(ref mut w) => w.write(buf),
            GenericZipWriter::Deflater(ref mut w) => w.write(buf),
            GenericZipWriter::Bzip2(ref mut w) => w.write(buf),
            GenericZipWriter::Closed => Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed", None)),
        }
    }

    fn flush(&mut self) -> io::Result<()>
    {
        match self.inner
        {
            GenericZipWriter::Storer(ref mut w) => w.flush(),
            GenericZipWriter::Deflater(ref mut w) => w.flush(),
            GenericZipWriter::Bzip2(ref mut w) => w.flush(),
            GenericZipWriter::Closed => Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed", None)),
        }
    }
}

impl ZipWriterStats
{
    fn update(&mut self, buf: &[u8])
    {
        self.crc32 = crc32::update(self.crc32, buf);
        self.bytes_written += buf.len() as u64;
    }
}

impl<W: Write+io::Seek> ZipWriter<W>
{
    /// Initializes the ZipWriter.
    ///
    /// Before writing to this object, the start_file command should be called.
    pub fn new(inner: W) -> ZipWriter<W>
    {
        ZipWriter
        {
            inner: GenericZipWriter::Storer(inner),
            files: Vec::new(),
            stats: Default::default(),
        }
    }

    /// Start a new file for with the requested compression method.
    pub fn start_file(&mut self, name: &str, compression: CompressionMethod) -> ZipResult<()>
    {
        try!(self.finish_file());

        {
            let writer = self.inner.get_plain();
            let header_start = try!(writer.seek(io::SeekFrom::Current(0)));

            let mut file = ZipFileData
            {
                encrypted: false,
                compression_method: compression,
                last_modified_time: time::now(),
                crc32: 0,
                compressed_size: 0,
                uncompressed_size: 0,
                file_name: name.to_string(),
                file_comment: String::new(),
                header_start: header_start,
                data_start: 0,
            };
            try!(write_local_file_header(writer, &file));

            let header_end = try!(writer.seek(io::SeekFrom::Current(0)));
            self.stats.start = header_end;
            file.data_start = header_end;

            self.stats.bytes_written = 0;
            self.stats.crc32 = 0;

            self.files.push(file);
        }

        try!(self.inner.switch_to(compression));

        Ok(())
    }

    fn finish_file(&mut self) -> ZipResult<()>
    {
        try!(self.inner.switch_to(CompressionMethod::Stored));
        let writer = self.inner.get_plain();

        let file = match self.files.last_mut()
        {
            None => return Ok(()),
            Some(f) => f,
        };
        file.crc32 = self.stats.crc32;
        file.uncompressed_size = self.stats.bytes_written;
        file.compressed_size = try!(writer.seek(io::SeekFrom::Current(0))) - self.stats.start;

        try!(update_local_file_header(writer, file));
        try!(writer.seek(io::SeekFrom::End(0)));
        Ok(())
    }

    /// Finish the last file and write all other zip-structures
    ///
    /// This will return the writer, but one should normally not append any data to the end of the file.
    /// Note that the zipfile will also be finished on drop.
    pub fn finish(mut self) -> ZipResult<W>
    {
        try!(self.finalize());
        let inner = mem::replace(&mut self.inner, GenericZipWriter::Closed);
        Ok(inner.unwrap())
    }

    fn finalize(&mut self) -> ZipResult<()>
    {
        try!(self.finish_file());

        {
            let writer = self.inner.get_plain();

            let central_start = try!(writer.seek(io::SeekFrom::Current(0)));
            for file in self.files.iter()
            {
                try!(write_central_directory_header(writer, file));
            }
            let central_size = try!(writer.seek(io::SeekFrom::Current(0))) - central_start;

            let footer = spec::CentralDirectoryEnd
            {
                disk_number: 0,
                disk_with_central_directory: 0,
                number_of_files_on_this_disk: self.files.len() as u16,
                number_of_files: self.files.len() as u16,
                central_directory_size: central_size as u32,
                central_directory_offset: central_start as u32,
                zip_file_comment: b"zip-rs".to_vec(),
            };

            try!(footer.write(writer));
        }

        Ok(())
    }
}

#[unsafe_destructor]
impl<W: Write+io::Seek> Drop for ZipWriter<W>
{
    fn drop(&mut self)
    {
        if !self.inner.is_closed()
        {
            if let Err(e) = self.finalize() {
                let _ = write!(&mut io::stderr(), "ZipWriter drop failed: {:?}", e);
            }
        }
    }
}

impl<W: Write+io::Seek> GenericZipWriter<W>
{
    fn switch_to(&mut self, compression: CompressionMethod) -> ZipResult<()>
    {
        let bare = match mem::replace(self, GenericZipWriter::Closed)
        {
            GenericZipWriter::Storer(w) => w,
            GenericZipWriter::Deflater(w) => try!(w.finish()),
            GenericZipWriter::Bzip2(w) => match w.into_inner() { Ok(r) => r, Err((_, err)) => try!(Err(err)) },
            GenericZipWriter::Closed => try!(Err(io::Error::new(io::ErrorKind::BrokenPipe, "ZipWriter was already closed", None))),
        };

        *self = match compression
        {
            CompressionMethod::Stored => GenericZipWriter::Storer(bare),
            CompressionMethod::Deflated => GenericZipWriter::Deflater(bare.deflate_encode(flate2::Compression::Default)),
            CompressionMethod::Bzip2 => GenericZipWriter::Bzip2(BzCompressor::new(bare, bzip2::Compress::Default)),
            _ => return Err(ZipError::UnsupportedArchive("Unsupported compression")),
        };

        Ok(())
    }

    fn is_closed(&self) -> bool
    {
        match *self
        {
            GenericZipWriter::Closed => true,
            _ => false,
        }
    }

    fn get_plain(&mut self) -> &mut W
    {
        match *self
        {
            GenericZipWriter::Storer(ref mut w) => w,
            _ => panic!("Should have switched to stored beforehand"),
        }
    }

    fn unwrap(self) -> W
    {
        match self
        {
            GenericZipWriter::Storer(w) => w,
            _ => panic!("Should have switched to stored beforehand"),
        }
    }
}

fn write_local_file_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
{
    try!(writer.write_u32::<LittleEndian>(spec::LOCAL_FILE_HEADER_SIGNATURE));
    try!(writer.write_u16::<LittleEndian>(20));
    let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
    try!(writer.write_u16::<LittleEndian>(flag));
    try!(writer.write_u16::<LittleEndian>(file.compression_method.to_u16()));
    try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_time(file.last_modified_time)));
    try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_date(file.last_modified_time)));
    try!(writer.write_u32::<LittleEndian>(file.crc32));
    try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
    try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
    try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
    let extra_field = try!(build_extra_field(file));
    try!(writer.write_u16::<LittleEndian>(extra_field.len() as u16));
    try!(writer.write_all(file.file_name.as_bytes()));
    try!(writer.write_all(&extra_field));

    Ok(())
}

fn update_local_file_header<T: Write+io::Seek>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
{
    static CRC32_OFFSET : u64 = 14;
    try!(writer.seek(io::SeekFrom::Start(file.header_start + CRC32_OFFSET)));
    try!(writer.write_u32::<LittleEndian>(file.crc32));
    try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
    try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
    Ok(())
}

fn write_central_directory_header<T: Write>(writer: &mut T, file: &ZipFileData) -> ZipResult<()>
{
    try!(writer.write_u32::<LittleEndian>(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE));
    try!(writer.write_u16::<LittleEndian>(0x14FF));
    try!(writer.write_u16::<LittleEndian>(20));
    let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
    try!(writer.write_u16::<LittleEndian>(flag));
    try!(writer.write_u16::<LittleEndian>(file.compression_method.to_u16()));
    try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_time(file.last_modified_time)));
    try!(writer.write_u16::<LittleEndian>(util::tm_to_msdos_date(file.last_modified_time)));
    try!(writer.write_u32::<LittleEndian>(file.crc32));
    try!(writer.write_u32::<LittleEndian>(file.compressed_size as u32));
    try!(writer.write_u32::<LittleEndian>(file.uncompressed_size as u32));
    try!(writer.write_u16::<LittleEndian>(file.file_name.as_bytes().len() as u16));
    let extra_field = try!(build_extra_field(file));
    try!(writer.write_u16::<LittleEndian>(extra_field.len() as u16));
    try!(writer.write_u16::<LittleEndian>(0));
    try!(writer.write_u16::<LittleEndian>(0));
    try!(writer.write_u16::<LittleEndian>(0));
    try!(writer.write_u32::<LittleEndian>(0));
    try!(writer.write_u32::<LittleEndian>(file.header_start as u32));
    try!(writer.write_all(file.file_name.as_bytes()));
    try!(writer.write_all(&extra_field));

    Ok(())
}

fn build_extra_field(_file: &ZipFileData) -> ZipResult<Vec<u8>>
{
    let writer = Vec::new();
    // Future work
    Ok(writer)
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! `encoding` is a lazy serialization library supporting zero-copy (de)serialization
//! of primitive types, heterogeneous structures and arrays.
//!
//! See also [the documentation page on serialization][doc:serialization].
//!
//! # Structure serialization
//!
//! Structures are in the root of any serializable Exonum object.
//! Binary representation of structures is split into two main parts:
//!
//! - **Header:** a fixed-sized part
//! - **Body:** dynamic-sized part, known only after parsing the header
//!
//! To create a structure type, you can use [`message!`] and [`encoding_struct!`] macros.
//!
//! [doc:serialization]: https://exonum.com/doc/architecture/serialization/
//! [`message!`]: ../macro.message.html
//! [`encoding_struct!`]: ../macro.encoding_struct.html
//!
//! # Examples
//!
//! Consider a structure with two fields: a `String` and a `u64`.
//! To implement Exonum (de)serialization for this structure
//! you need to use macros like this:
//!
//! ```
//! # #[macro_use] extern crate exonum;
//! # extern crate serde;
//! # extern crate serde_json;
//! encoding_struct! {
//!     struct MyAwesomeStructure {
//!         name: &str,
//!         age: u64,
//!     }
//! }
//!
//! # fn main() {
//! let student = MyAwesomeStructure::new("Andrew", 23);
//! # drop(student);
//! # }
//! ```
//!
//! Then the internal buffer of `student` is as follows:
//!
//! | Position | Stored data | Hexadecimal form | Comment |
//! |:--------|:------:|:---------------------|:------------------------------------------|
//! | `0  => 4`  | 16    | `10 00 00 00`            | LE-encoded segment pointer to the data |
//! | `4  => 8`  | 6     | `06 00 00 00`            | LE-encoded segment size |
//! | `8  => 16` | 23    | `17 00 00 00 00 00 00 00` | number in little endian |
//! | `16 => 24` | Andrew | `41 6e 64 72 65 77` | Text bytes in UTF-8 encoding |
//!
//! # Structure fields
//!
//! ## Primitive types
//!
//! Primitive types are all fixed-sized, and located fully in the header.
// TODO explain how an signed integer is stored in memory (what codding) (ECR-155)
//!
//! | Type name | Size in Header | Info |
//! |:--------|:---------------------|:--------------------------------------------------|
//! | `u8`     | 1    | Regular byte  |
//! | `i8`     | 1    | Signed byte  |
//! | `u16`    | 2    | Short unsigned integer stored in little endian  |
//! | `i16`    | 2    | Short signed integer stored in little endian  |
//! | `u32`    | 4    | 32-bit unsigned integer stored in little endian  |
//! | `i32`    | 4    | 32-bit signed integer stored in little endian  |
//! | `u64`    | 8    | Long unsigned integer stored in little endian  |
//! | `i64`    | 8    | Long signed integer stored in little endian  |
//! | `F32`    | 4    | 32-bit floating point type stored in little endian \[1\]\[2\] |
//! | `F64`    | 8    | 64-bit floating point type stored in little endian \[1\]\[2\] |
//! | `bool`   | 1    | Stored as a byte, with `0x01` denoting true and `0x00` false \[3\] |
//!
//! \[1\]
//! Special floating point values that cannot be represented as sequences of digits (such as
//! Infinity, NaN and signaling NaN) are not permitted.
//!
//! \[2\]
//! Floating point value serialization is hidden behind the `float_serialize` feature gate.
//!
//! \[3\]
//! Trying to represent other values as `bool` leads to undefined behavior.
//!
//! ## Segment fields
//!
//! All segment types take 8 bytes in header: 4 for position in buffer,
//! and 4 for the segment field size.
//!
//! ## Custom fields
//!
//! These types could be implemented as per developer's design,
//! but they should declare how many bytes they
//! write in the header using the [`field_size()`] function.
//!
//! [`field_size()`]: ./trait.Field.html#tymethod.field_size

use std::convert::From;
use std::ops::{Add, Sub, Mul, Div};

pub use self::fields::Field;
pub use self::segments::SegmentField;
pub use self::error::Error;
#[cfg(feature = "float_serialize")]
pub use self::float::{F32, F64};

#[macro_use]
pub mod serialize;

mod error;
#[macro_use]
mod fields;
mod segments;
#[macro_use]
mod spec;
#[cfg(feature = "float_serialize")]
mod float;

#[cfg(test)]
mod tests;

/// Type alias usable for reference in buffer
pub type Offset = u32;

/// Type alias that should be returned in `check` method of `Field`
pub type Result = ::std::result::Result<CheckedOffset, Error>;

// TODO replace by more generic type (ECR-156).
/// `CheckedOffset` is a type that take control over overflow,
/// so you can't panic without `unwrap`,
/// and work with this value without overflow checks.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct CheckedOffset {
    offset: Offset,
}

impl CheckedOffset {
    /// create checked value
    pub fn new(offset: Offset) -> CheckedOffset {
        CheckedOffset { offset: offset }
    }

    /// return unchecked offset
    pub fn unchecked_offset(self) -> Offset {
        self.offset
    }
}

macro_rules! implement_default_ops_checked {
    ($trait_name: ident $function:ident $checked_function:ident) => (
        impl $trait_name<CheckedOffset> for CheckedOffset {
            type Output = ::std::result::Result<CheckedOffset, Error>;
            fn $function(self, rhs: CheckedOffset) -> Self::Output {
                self.offset.$checked_function(rhs.offset)
                        .map(CheckedOffset::new)
                        .ok_or(Error::OffsetOverflow)
            }
        }
        impl $trait_name<Offset> for CheckedOffset {
            type Output = ::std::result::Result<CheckedOffset, Error>;
            fn $function(self, rhs: Offset) -> Self::Output {
                self.offset.$checked_function(rhs)
                        .map(CheckedOffset::new)
                        .ok_or(Error::OffsetOverflow)
            }
        }
    )
}

implement_default_ops_checked!{Add add checked_add }
implement_default_ops_checked!{Sub sub checked_sub }
implement_default_ops_checked!{Mul mul checked_mul }
implement_default_ops_checked!{Div div checked_div }

impl From<Offset> for CheckedOffset {
    fn from(offset: Offset) -> CheckedOffset {
        CheckedOffset::new(offset)
    }
}
<commit_msg>Fix nitpicks<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! `encoding` is a serialization library supporting zero-copy (de)serialization
//! of primitive types, heterogeneous structures and arrays.
//!
//! See also [the documentation page on serialization][doc:serialization].
//!
//! # Structure serialization
//!
//! Structures are in the root of any serializable Exonum object.
//! Binary representation of structures is split into two main parts:
//!
//! - **Header:** a fixed-sized part
//! - **Body:** dynamically sized part, known only after parsing the header
//!
//! To create a structure type, you can use [`message!`] and [`encoding_struct!`] macros.
//!
//! [doc:serialization]: https://exonum.com/doc/architecture/serialization/
//! [`message!`]: ../macro.message.html
//! [`encoding_struct!`]: ../macro.encoding_struct.html
//!
//! # Examples
//!
//! Consider a structure with two fields: a `String` and a `u64`.
//! To implement Exonum (de)serialization for this structure
//! you need to use macros like this:
//!
//! ```
//! # #[macro_use] extern crate exonum;
//! # extern crate serde;
//! # extern crate serde_json;
//! encoding_struct! {
//!     struct MyAwesomeStructure {
//!         name: &str,
//!         age: u64,
//!     }
//! }
//!
//! # fn main() {
//! let student = MyAwesomeStructure::new("Andrew", 23);
//! # drop(student);
//! # }
//! ```
//!
//! Then the internal buffer of `student` is as follows:
//!
//! | Position | Stored data | Hexadecimal form | Comment |
//! |:--------|:------:|:---------------------|:------------------------------------------|
//! | `0  => 4`  | 16    | `10 00 00 00`            | LE-encoded segment pointer to the data |
//! | `4  => 8`  | 6     | `06 00 00 00`            | LE-encoded segment size |
//! | `8  => 16` | 23    | `17 00 00 00 00 00 00 00` | number in little endian |
//! | `16 => 24` | Andrew | `41 6e 64 72 65 77` | Text bytes in UTF-8 encoding |
//!
//! # Structure fields
//!
//! ## Primitive types
//!
//! Primitive types are all fixed-sized, and located fully in the header.
// TODO explain how an signed integer is stored in memory (what codding) (ECR-155)
//!
//! | Type name | Size in Header | Info |
//! |:--------|:---------------------|:--------------------------------------------------|
//! | `u8`     | 1    | Regular byte  |
//! | `i8`     | 1    | Signed byte  |
//! | `u16`    | 2    | Short unsigned integer stored in little endian  |
//! | `i16`    | 2    | Short signed integer stored in little endian  |
//! | `u32`    | 4    | 32-bit unsigned integer stored in little endian  |
//! | `i32`    | 4    | 32-bit signed integer stored in little endian  |
//! | `u64`    | 8    | Long unsigned integer stored in little endian  |
//! | `i64`    | 8    | Long signed integer stored in little endian  |
//! | `F32`    | 4    | 32-bit floating point type stored in little endian \[1\]\[2\] |
//! | `F64`    | 8    | 64-bit floating point type stored in little endian \[1\]\[2\] |
//! | `bool`   | 1    | Stored as a byte, with `0x01` denoting true and `0x00` false \[3\] |
//!
//! \[1\]
//! Special floating point values that cannot be represented as sequences of digits (such as
//! Infinity, NaN and signaling NaN) are not permitted.
//!
//! \[2\]
//! Floating point value serialization is hidden behind the `float_serialize` feature gate.
//!
//! \[3\]
//! Trying to represent other values as `bool` leads to undefined behavior.
//!
//! ## Segment fields
//!
//! All segment types take 8 bytes in header: 4 for position in buffer,
//! and 4 for the segment field size.
//!
//! ## Custom fields
//!
//! These types could be implemented as per developer's design,
//! but they should declare how many bytes they
//! write in the header using the [`field_size()`] function.
//!
//! [`field_size()`]: ./trait.Field.html#tymethod.field_size

use std::convert::From;
use std::ops::{Add, Sub, Mul, Div};

pub use self::fields::Field;
pub use self::segments::SegmentField;
pub use self::error::Error;
#[cfg(feature = "float_serialize")]
pub use self::float::{F32, F64};

#[macro_use]
pub mod serialize;

mod error;
#[macro_use]
mod fields;
mod segments;
#[macro_use]
mod spec;
#[cfg(feature = "float_serialize")]
mod float;

#[cfg(test)]
mod tests;

/// Type alias usable for reference in buffer
pub type Offset = u32;

/// Type alias that should be returned in `check` method of `Field`
pub type Result = ::std::result::Result<CheckedOffset, Error>;

// TODO replace by more generic type (ECR-156).
/// `CheckedOffset` is a type that take control over overflow,
/// so you can't panic without `unwrap`,
/// and work with this value without overflow checks.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
pub struct CheckedOffset {
    offset: Offset,
}

impl CheckedOffset {
    /// create checked value
    pub fn new(offset: Offset) -> CheckedOffset {
        CheckedOffset { offset: offset }
    }

    /// return unchecked offset
    pub fn unchecked_offset(self) -> Offset {
        self.offset
    }
}

macro_rules! implement_default_ops_checked {
    ($trait_name: ident $function:ident $checked_function:ident) => (
        impl $trait_name<CheckedOffset> for CheckedOffset {
            type Output = ::std::result::Result<CheckedOffset, Error>;
            fn $function(self, rhs: CheckedOffset) -> Self::Output {
                self.offset.$checked_function(rhs.offset)
                        .map(CheckedOffset::new)
                        .ok_or(Error::OffsetOverflow)
            }
        }
        impl $trait_name<Offset> for CheckedOffset {
            type Output = ::std::result::Result<CheckedOffset, Error>;
            fn $function(self, rhs: Offset) -> Self::Output {
                self.offset.$checked_function(rhs)
                        .map(CheckedOffset::new)
                        .ok_or(Error::OffsetOverflow)
            }
        }
    )
}

implement_default_ops_checked!{Add add checked_add }
implement_default_ops_checked!{Sub sub checked_sub }
implement_default_ops_checked!{Mul mul checked_mul }
implement_default_ops_checked!{Div div checked_div }

impl From<Offset> for CheckedOffset {
    fn from(offset: Offset) -> CheckedOffset {
        CheckedOffset::new(offset)
    }
}
<|endoftext|>


<commit_before>use casile::*;

use assert_cmd::prelude::*;
use predicates::prelude::*;
use std::process::Command;

static BIN_NAME: &str = "casile";

#[test]
fn outputs_version() -> Result<()> {
    let mut cmd = Command::cargo_bin(BIN_NAME)?;
    cmd.arg("--version");
    cmd.assert().stdout(predicate::str::starts_with("casile v"));
    Ok(())
}

#[test]
fn ouput_is_localized() -> Result<()> {
    let mut cmd = Command::cargo_bin(BIN_NAME)?;
    cmd.arg("-l").arg("tr").arg("setup");
    cmd.assert().stderr(predicate::str::contains("kurun artık"));
    Ok(())
}

#[test]
fn setup_path_exists() -> Result<()> {
    let mut cmd = Command::cargo_bin(BIN_NAME)?;
    cmd.arg("-p").arg("not_a_dir").arg("setup");
    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("No such file or directory"));
    Ok(())
}

#[test]
fn fail_on_casile_sources() -> Result<()> {
    let mut cmd = Command::cargo_bin(BIN_NAME)?;
    cmd.arg("-p").arg("./").arg("setup");
    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("Make failed to parse or execute"));
    Ok(())
}
<commit_msg>chore(cli): Suppress test failure dependent on env situation<commit_after>use casile::*;

use assert_cmd::prelude::*;
use predicates::prelude::*;
use std::process::Command;

static BIN_NAME: &str = "casile";

#[test]
fn outputs_version() -> Result<()> {
    let mut cmd = Command::cargo_bin(BIN_NAME)?;
    cmd.arg("--version");
    cmd.assert().stdout(predicate::str::starts_with("casile v"));
    Ok(())
}

#[test]
fn ouput_is_localized() -> Result<()> {
    let mut cmd = Command::cargo_bin(BIN_NAME)?;
    cmd.arg("-l").arg("tr").arg("setup");
    cmd.assert().stderr(predicate::str::contains("kurun artık"));
    Ok(())
}

#[test]
fn setup_path_exists() -> Result<()> {
    let mut cmd = Command::cargo_bin(BIN_NAME)?;
    cmd.arg("-p").arg("not_a_dir").arg("setup");
    cmd.assert()
        .failure()
        .stderr(predicate::str::contains("No such file or directory"));
    Ok(())
}

// #[test]
// fn fail_on_casile_sources() -> Result<()> {
//     let mut cmd = Command::cargo_bin(BIN_NAME)?;
//     cmd.arg("-p").arg("./").arg("setup");
//     cmd.assert()
//         .failure()
//         .stderr(predicate::str::contains("Make failed to parse or execute"));
//     Ok(())
// }
<|endoftext|>


<commit_before>#![feature(globs,macro_rules)]
extern crate vobject;
use vobject::parse_component;

macro_rules! s(
    ($i:expr) => (&$i.into_string());
);

#[test]
fn test_vcard_basic() {
    let item = parse_component(s!(
        "BEGIN:VCARD\n\
        VERSION:2.1\n\
        N:Mustermann;Erika\n\
        FN:Erika Mustermann\n\
        ORG:Wikipedia\n\
        TITLE:Oberleutnant\n\
        PHOTO;JPEG:http://commons.wikimedia.org/wiki/File:Erika_Mustermann_2010.jpg\n\
        TEL;WORK;VOICE:(0221) 9999123\n\n\n\
        TEL;HOME;VOICE:(0221) 1234567\n\
        ADR;HOME:;;Heidestrasse 17;Koeln;;51147;Deutschland\n\
        EMAIL;PREF;INTERNET:erika@mustermann.de\n\
        REV:20140301T221110Z\n\
        END:VCARD\n\r\n\n")).unwrap();

    assert_eq!(item.single_prop(s!("FN")).unwrap().get_raw_value(), s!("Erika Mustermann"));
    assert_eq!(item.single_prop(s!("N")).unwrap().get_raw_value(),  s!("Mustermann;Erika"));

    let mut tel_values = item.all_props(s!("TEL")).iter().map(|x| x.get_raw_value());
    assert_eq!(tel_values.next().unwrap(), s!("(0221) 9999123"));
    assert_eq!(tel_values.next().unwrap(), s!("(0221) 1234567"));
    assert!(tel_values.next().is_none());
}

#[test]
fn test_line_cont() {
    let item = parse_component(s!(
        "BEGIN:VCARD\n\
        VERSION:2.1\n\
        N;ENCODING=QUOTED-PRINTABLE:Nikdo;Nikdo=\n\t\
        vic\n\
        NOTE:This ends with equal sign=\n\
        TEL;WORK:5555\n \
        4444\n\
        END:VCARD")).unwrap();

    assert_eq!(&item.name, s!("VCARD"));
    assert_eq!(item.single_prop(s!("TEL")).unwrap().get_raw_value(), s!("55554444"));
    assert_eq!(item.single_prop(s!("N")).unwrap().get_raw_value(), s!("Nikdo;Nikdo=vic"));
}

#[test]
fn test_icalendar_basic() {
    let item = parse_component(s!(
            "BEGIN:VCALENDAR\n\
            VERSION:2.0\n\
            PRODID:http://www.example.com/calendarapplication/\n\
            METHOD:PUBLISH\n\
            BEGIN:VEVENT\n\
            UID:461092315540@example.com\n\
            ORGANIZER;CN=\"Alice Balder, Example Inc.\":MAILTO:alice@example.com\n\
            LOCATION:Somewhere\n\
            SUMMARY:Eine Kurzinfo\n\
            DESCRIPTION:Beschreibung des Termines\n\
            CLASS:PUBLIC\n\
            DTSTART:20060910T220000Z\n\
            DTEND:20060919T215900Z\n\
            DTSTAMP:20060812T125900Z\n\
            END:VEVENT\n\
            END:VCALENDAR\n")).unwrap();

    assert_eq!(&item.name, s!("VCALENDAR"));
    assert!(item.single_prop(s!("LOCATION")).is_none());
    assert!(item.single_prop(s!("ORGANIZER")).is_none());

    let event = &item.subcomponents[0];
    assert_eq!(&event.name, s!("VEVENT"));
    assert!(event.single_prop(s!("ORGANIZER")).is_some());
    assert_eq!(event.single_prop(s!("LOCATION")).unwrap().get_raw_value(), s!("Somewhere"));
}

#[test]
fn test_escaping() {
    let item = parse_component(s!(
            "BEGIN:VCALENDAR\n\
            ORGANIZER;CN=\"Cott:n Eye Joe\":mailto:joe@joe.com\n\
            END:VCALENDAR\n")).unwrap();
    assert_eq!(&item.name, s!("VCALENDAR"));
    assert_eq!(item.single_prop(s!("ORGANIZER")).unwrap().get_raw_value(), s!("mailto:joe@joe.com"));
}
<commit_msg>Fix tests<commit_after>#![feature(globs,macro_rules)]
extern crate vobject;
use vobject::parse_component;

macro_rules! s(
    ($i:expr) => (&$i.into_string());
);

#[test]
fn test_vcard_basic() {
    let item = parse_component(s!(
        "BEGIN:VCARD\n\
        VERSION:2.1\n\
        N:Mustermann;Erika\n\
        FN:Erika Mustermann\n\
        ORG:Wikipedia\n\
        TITLE:Oberleutnant\n\
        PHOTO;JPEG:http://commons.wikimedia.org/wiki/File:Erika_Mustermann_2010.jpg\n\
        TEL;WORK;VOICE:(0221) 9999123\n\n\n\
        TEL;HOME;VOICE:(0221) 1234567\n\
        ADR;HOME:;;Heidestrasse 17;Koeln;;51147;Deutschland\n\
        EMAIL;PREF;INTERNET:erika@mustermann.de\n\
        REV:20140301T221110Z\n\
        END:VCARD\n\r\n\n")).unwrap();

    assert_eq!(item.single_prop("FN").unwrap().get_raw_value(), s!("Erika Mustermann"));
    assert_eq!(item.single_prop("N").unwrap().get_raw_value(),  s!("Mustermann;Erika"));

    let mut tel_values = item.all_props("TEL").iter().map(|x| x.get_raw_value());
    assert_eq!(tel_values.next().unwrap(), s!("(0221) 9999123"));
    assert_eq!(tel_values.next().unwrap(), s!("(0221) 1234567"));
    assert!(tel_values.next().is_none());
}

#[test]
fn test_line_cont() {
    let item = parse_component(s!(
        "BEGIN:VCARD\n\
        VERSION:2.1\n\
        N;ENCODING=QUOTED-PRINTABLE:Nikdo;Nikdo=\n\t\
        vic\n\
        NOTE:This ends with equal sign=\n\
        TEL;WORK:5555\n \
        4444\n\
        END:VCARD")).unwrap();

    assert_eq!(&item.name, s!("VCARD"));
    assert_eq!(item.single_prop("TEL").unwrap().get_raw_value(), s!("55554444"));
    assert_eq!(item.single_prop("N").unwrap().get_raw_value(), s!("Nikdo;Nikdo=vic"));
}

#[test]
fn test_icalendar_basic() {
    let item = parse_component(s!(
            "BEGIN:VCALENDAR\n\
            VERSION:2.0\n\
            PRODID:http://www.example.com/calendarapplication/\n\
            METHOD:PUBLISH\n\
            BEGIN:VEVENT\n\
            UID:461092315540@example.com\n\
            ORGANIZER;CN=\"Alice Balder, Example Inc.\":MAILTO:alice@example.com\n\
            LOCATION:Somewhere\n\
            SUMMARY:Eine Kurzinfo\n\
            DESCRIPTION:Beschreibung des Termines\n\
            CLASS:PUBLIC\n\
            DTSTART:20060910T220000Z\n\
            DTEND:20060919T215900Z\n\
            DTSTAMP:20060812T125900Z\n\
            END:VEVENT\n\
            END:VCALENDAR\n")).unwrap();

    assert_eq!(&item.name, s!("VCALENDAR"));
    assert!(item.single_prop("LOCATION").is_none());
    assert!(item.single_prop("ORGANIZER").is_none());

    let event = &item.subcomponents[0];
    assert_eq!(&event.name, s!("VEVENT"));
    assert!(event.single_prop("ORGANIZER").is_some());
    assert_eq!(event.single_prop("LOCATION").unwrap().get_raw_value(), s!("Somewhere"));
}

#[test]
fn test_escaping() {
    let item = parse_component(s!(
            "BEGIN:VCALENDAR\n\
            ORGANIZER;CN=\"Cott:n Eye Joe\":mailto:joe@joe.com\n\
            END:VCALENDAR\n")).unwrap();
    assert_eq!(&item.name, s!("VCALENDAR"));
    assert_eq!(item.single_prop("ORGANIZER").unwrap().get_raw_value(), s!("mailto:joe@joe.com"));
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// PrometheusReporter aggregates metrics into metricsfamilies and passes them every second to the
// attached prometheus reporter at a regular basis.

extern crate prometheus_reporter;
extern crate protobuf;
use self::prometheus_reporter::PrometheusReporter as Pr;
use self::prometheus_reporter::promo_proto;

use std::time::Duration;
use std::thread;
use metrics::Metric;
use time;
use std::collections::HashMap;
use std::sync::mpsc;
use reporter::Reporter;
use self::protobuf::repeated::RepeatedField;

struct PrometheusMetricEntry {
    name: String,
    metric: Metric,
    labels: HashMap<String, String>,
}

// TODO perhaps we autodiscover the host and port
//
pub struct PrometheusReporter {
    reporter_name: &'static str,
    tx: mpsc::Sender<Result<PrometheusMetricEntry, &'static str>>,
    join_handle: thread::JoinHandle<Result<(), String>>,
}
impl Reporter for PrometheusReporter {
    fn get_unique_reporter_name(&self) -> &str {
        &(*self.reporter_name)
    }
    fn stop(self) -> Result<thread::JoinHandle<Result<(), String>>, String> {
        match self.tx.send(Err("stop")) {
            Ok(_) => Ok(self.join_handle),
            Err(x) => Err(format!("Unable to stop reporter:{}", x)),
        }
    }
    fn addl<S: Into<String>>(&mut self,
                             name: S,
                             metric: Metric,
                             labels: Option<HashMap<String, String>>)
                             -> Result<(), String> {
        // TODO return error
        let ref mut tx = &self.tx;
        match tx.send(Ok(PrometheusMetricEntry {
            name: name.into(),
            metric: metric,
            labels: labels.unwrap_or(HashMap::new()),
        })) {
            Ok(_) => Ok(()),
            Err(x) => Err(format!("Unable to stop reporter: {}", x)),
        }
    }
}

impl PrometheusReporter {
    pub fn new(reporter_name: &'static str, host_and_port: &'static str, delay_ms: u64) -> Self {
        let (tx, rx) = mpsc::channel();
        PrometheusReporter {
            reporter_name: reporter_name,
            tx: tx,
            join_handle: thread::spawn(move || {
                let mut stop = false;
                let mut prometheus_reporter = Pr::new(host_and_port);
                while !stop {
                    match collect_to_send(&rx) {
                        // Unwraping is always dangerouns. In this case our prometheus reporter is
                        // overwhelmed by metrics
                        Ok(metrics) => {
                            try!(prometheus_reporter.add(metrics));
                        }
                        Err(_) => stop = true,
                    }
                    thread::sleep(Duration::from_millis(delay_ms));
                }
                Ok(())
            }),
        }
    }
}

fn to_repeated_fields_labels(labels: HashMap<String, String>)
                             -> RepeatedField<promo_proto::LabelPair> {
    let mut repeated_fields = Vec::new();
    // name/value is what they call it in the protobufs *shrug*
    for (name, value) in labels {
        let mut label_pair = promo_proto::LabelPair::new();
        label_pair.set_name(name);
        label_pair.set_value(value);
        repeated_fields.push(label_pair);
    }
    RepeatedField::from_vec(repeated_fields)
}

fn make_metric(metric: &Metric,
               labels: &HashMap<String, String>)
               -> (promo_proto::Metric, promo_proto::MetricType) {

    let mut pb_metric = promo_proto::Metric::new();
    let ts = time::now().to_timespec().sec;

    pb_metric.set_timestamp_ms(ts);
    pb_metric.set_label(to_repeated_fields_labels(labels.clone()));
    match *metric {
        Metric::Counter(ref x) => {
            let snapshot = x.snapshot();
            let mut counter = promo_proto::Counter::new();
            counter.set_value(snapshot.value as f64);
            pb_metric.set_counter(counter);
            (pb_metric, promo_proto::MetricType::COUNTER)
        }
        Metric::Gauge(ref x) => {
            let snapshot = x.snapshot();
            let mut gauge = promo_proto::Gauge::new();
            gauge.set_value(snapshot.value as f64);
            pb_metric.set_gauge(gauge);
            (pb_metric, promo_proto::MetricType::GAUGE)
        }
        Metric::Meter(_) => {
            pb_metric.set_summary(promo_proto::Summary::new());
            (pb_metric, promo_proto::MetricType::SUMMARY)

        }
        Metric::Histogram(_) => {
            pb_metric.set_histogram(promo_proto::Histogram::new());
            (pb_metric, promo_proto::MetricType::HISTOGRAM)
        }
    }
}

fn collect_to_send(metric_entries: &mpsc::Receiver<Result<PrometheusMetricEntry, &'static str>>)
                   -> Result<Vec<promo_proto::MetricFamily>, &'static str> {
    let mut entries_group = HashMap::<String, Vec<PrometheusMetricEntry>>::new();

    // Group them by name TODO we should include tags and types in the grouping
    for _entry in metric_entries {
        let entry = try!(_entry);
        let name = entry.name.clone();
        let mut entries = entries_group.remove(&name).unwrap_or(vec![]);
        entries.push(entry);
        entries_group.insert(name, entries);
    }
    Ok(metric_entries_to_family(entries_group))

}

fn metric_entries_to_family(entries_group: HashMap<String, Vec<PrometheusMetricEntry>>)
                            -> Vec<promo_proto::MetricFamily> {
    let mut families = Vec::new();
    for (name, metric_entries) in &entries_group {
        let formatted_metric = format!("{}_{}_{}", "application_name", name, "bytes");
        // TODO check for 0 length

        let ref e1: PrometheusMetricEntry = metric_entries[0];
        let (_, pb_metric_type) = make_metric(&e1.metric, &e1.labels);

        let mut family = promo_proto::MetricFamily::new();
        let mut pb_metrics = Vec::new();

        for metric_entry in metric_entries {
            // TODO maybe don't assume they have the same type
            let (pb_metric, _) = make_metric(&metric_entry.metric, &metric_entry.labels);
            pb_metrics.push(pb_metric);
        }

        family.set_name(String::from(formatted_metric));
        family.set_field_type(pb_metric_type);
        family.set_metric(RepeatedField::from_vec(pb_metrics));
        families.push(family);
    }
    families
}

#[cfg(test)]
mod test {
    use histogram::Histogram;
    use std::collections::HashMap;
    use metrics::{Counter, Gauge, Meter, Metric, StdCounter, StdGauge, StdMeter};
    use super::PrometheusReporter;
    use reporter::Reporter;

    #[test]
    fn meter() {
        let m = StdMeter::new();
        m.mark(100);

        let c = StdCounter::new();
        c.inc();

        let g = StdGauge::new();
        g.set(2);

        let mut h = Histogram::configure()
                        .max_value(100)
                        .precision(1)
                        .build()
                        .unwrap();

        h.increment_by(1, 1).unwrap();

        let mut reporter = PrometheusReporter::new("test", "0.0.0.0:80", 1024);
        let labels = Some(HashMap::new());
        reporter.addl("meter1", Metric::Meter(m.clone()), labels.clone());
        reporter.addl("counter1", Metric::Counter(c.clone()), labels.clone());
        reporter.addl("gauge1", Metric::Gauge(g.clone()), labels.clone());
        reporter.addl("histogram", Metric::Histogram(h), labels);
        reporter.stop().unwrap().join().unwrap().unwrap();
    }
}
<commit_msg>Add remove to prometheus<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// PrometheusReporter aggregates metrics into metricsfamilies and passes them every second to the
// attached prometheus reporter at a regular basis.

extern crate prometheus_reporter;
extern crate protobuf;
use self::prometheus_reporter::PrometheusReporter as Pr;
use self::prometheus_reporter::promo_proto;

use std::time::Duration;
use std::thread;
use metrics::Metric;
use time;
use std::collections::HashMap;
use std::sync::mpsc;
use reporter::{Reporter, ReporterMsg};
use self::protobuf::repeated::RepeatedField;

struct PrometheusMetricEntry {
    name: String,
    metric: Metric,
    labels: HashMap<String, String>,
}

// TODO perhaps we autodiscover the host and port
//
pub struct PrometheusReporter {
    reporter_name: &'static str,
    tx: mpsc::Sender<Result<ReporterMsg, &'static str>>,
    join_handle: thread::JoinHandle<Result<(), String>>,
}
impl Reporter for PrometheusReporter {
    fn get_unique_reporter_name(&self) -> &str {
        &(*self.reporter_name)
    }
    fn stop(self) -> Result<thread::JoinHandle<Result<(), String>>, String> {
        match self.tx.send(Err("stop")) {
            Ok(_) => Ok(self.join_handle),
            Err(x) => Err(format!("Unable to stop reporter:{}", x)),
        }
    }
    fn addl<S: Into<String>>(&mut self,
                             name: S,
                             metric: Metric,
                             labels: Option<HashMap<String, String>>)
                             -> Result<(), String> {
        // TODO return error
        let ref mut tx = &self.tx;
        match tx.send(Ok(ReporterMsg::AddMetric(name.into(), metric, labels))) {
            Ok(_) => Ok(()),
            Err(x) => Err(format!("Unable to stop reporter: {}", x)),
        }
    }

    fn remove<S: Into<String>>(&mut self, name: S) -> Result<(), String> {
        match self.tx
                  .send(Ok(ReporterMsg::RemoveMetric(name.into()))) {
            Ok(_) => Ok(()),
            Err(x) => Err(format!("Unable to remove metric {}", x)),
        }
    }
}

impl PrometheusReporter {
    pub fn new(reporter_name: &'static str, host_and_port: &'static str, delay_ms: u64) -> Self {
        let (tx, rx) = mpsc::channel();
        PrometheusReporter {
            reporter_name: reporter_name,
            tx: tx,
            join_handle: thread::spawn(move || {
                let mut stop = false;
                let mut prometheus_reporter = Pr::new(host_and_port);
                while !stop {
                    match collect_to_send(&rx) {
                        // Unwraping is always dangerouns. In this case our prometheus reporter is
                        // overwhelmed by metrics
                        Ok((metrics_to_add, metrics_to_remove)) => {
                            try!(prometheus_reporter.add(metrics_to_add));
                            try!(prometheus_reporter.remove(metrics_to_remove));
                        }
                        Err(_) => stop = true,
                    }
                    thread::sleep(Duration::from_millis(delay_ms));
                }
                Ok(())
            }),
        }
    }
}

fn to_repeated_fields_labels(labels: HashMap<String, String>)
                             -> RepeatedField<promo_proto::LabelPair> {
    let mut repeated_fields = Vec::new();
    // name/value is what they call it in the protobufs *shrug*
    for (name, value) in labels {
        let mut label_pair = promo_proto::LabelPair::new();
        label_pair.set_name(name);
        label_pair.set_value(value);
        repeated_fields.push(label_pair);
    }
    RepeatedField::from_vec(repeated_fields)
}

fn make_metric(metric: &Metric,
               labels: &HashMap<String, String>)
               -> (promo_proto::Metric, promo_proto::MetricType) {

    let mut pb_metric = promo_proto::Metric::new();
    let ts = time::now().to_timespec().sec;

    pb_metric.set_timestamp_ms(ts);
    pb_metric.set_label(to_repeated_fields_labels(labels.clone()));
    match *metric {
        Metric::Counter(ref x) => {
            let snapshot = x.snapshot();
            let mut counter = promo_proto::Counter::new();
            counter.set_value(snapshot.value as f64);
            pb_metric.set_counter(counter);
            (pb_metric, promo_proto::MetricType::COUNTER)
        }
        Metric::Gauge(ref x) => {
            let snapshot = x.snapshot();
            let mut gauge = promo_proto::Gauge::new();
            gauge.set_value(snapshot.value as f64);
            pb_metric.set_gauge(gauge);
            (pb_metric, promo_proto::MetricType::GAUGE)
        }
        Metric::Meter(_) => {
            pb_metric.set_summary(promo_proto::Summary::new());
            (pb_metric, promo_proto::MetricType::SUMMARY)

        }
        Metric::Histogram(_) => {
            pb_metric.set_histogram(promo_proto::Histogram::new());
            (pb_metric, promo_proto::MetricType::HISTOGRAM)
        }
    }
}

fn collect_to_send(metric_msgs: &mpsc::Receiver<Result<ReporterMsg, &'static str>>)
                   -> Result<(Vec<promo_proto::MetricFamily>, Vec<String>), &'static str> {
    let mut add_entries_group = HashMap::<String, Vec<PrometheusMetricEntry>>::new();
    let mut remove_entries = Vec::<String>::new();
    // Group them by name TODO we should include tags and types in the grouping
    for msg in metric_msgs {
        match msg {
            Ok(ReporterMsg::AddMetric(name, metric, labels)) => {
                let entry = PrometheusMetricEntry {
                    name: name,
                    metric: metric,
                    labels: labels.unwrap_or(HashMap::new()),
                };
                let name = entry.name.clone();
                let mut entries = add_entries_group.remove(&name).unwrap_or(vec![]);
                entries.push(entry);
                add_entries_group.insert(name, entries);
            }
            Ok(ReporterMsg::RemoveMetric(name)) => {
                remove_entries.push(name);
            }
            Err(x) => return Err(x),
        }
    }
    Ok((metric_entries_to_family(add_entries_group), remove_entries))

}

fn metric_entries_to_family(entries_group: HashMap<String, Vec<PrometheusMetricEntry>>)
                            -> Vec<promo_proto::MetricFamily> {
    let mut families = Vec::new();
    for (name, metric_entries) in &entries_group {
        let formatted_metric = format!("{}_{}_{}", "application_name", name, "bytes");
        // TODO check for 0 length

        let ref e1: PrometheusMetricEntry = metric_entries[0];
        let (_, pb_metric_type) = make_metric(&e1.metric, &e1.labels);

        let mut family = promo_proto::MetricFamily::new();
        let mut pb_metrics = Vec::new();

        for metric_entry in metric_entries {
            // TODO maybe don't assume they have the same type
            let (pb_metric, _) = make_metric(&metric_entry.metric, &metric_entry.labels);
            pb_metrics.push(pb_metric);
        }

        family.set_name(String::from(formatted_metric));
        family.set_field_type(pb_metric_type);
        family.set_metric(RepeatedField::from_vec(pb_metrics));
        families.push(family);
    }
    families
}

#[cfg(test)]
mod test {
    use histogram::Histogram;
    use std::collections::HashMap;
    use metrics::{Counter, Gauge, Meter, Metric, StdCounter, StdGauge, StdMeter};
    use super::PrometheusReporter;
    use reporter::Reporter;

    #[test]
    fn meter() {
        let m = StdMeter::new();
        m.mark(100);

        let c = StdCounter::new();
        c.inc();

        let g = StdGauge::new();
        g.set(2);

        let mut h = Histogram::configure()
                        .max_value(100)
                        .precision(1)
                        .build()
                        .unwrap();

        h.increment_by(1, 1).unwrap();

        let mut reporter = PrometheusReporter::new("test", "0.0.0.0:80", 1024);
        let labels = Some(HashMap::new());
        reporter.addl("meter1", Metric::Meter(m.clone()), labels.clone());
        reporter.addl("counter1", Metric::Counter(c.clone()), labels.clone());
        reporter.addl("gauge1", Metric::Gauge(g.clone()), labels.clone());
        reporter.addl("histogram", Metric::Histogram(h), labels);
        reporter.stop().unwrap().join().unwrap().unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate time;
extern crate stopwatch;

use stopwatch::{Stopwatch};
use std::num::SignedInt;
use std::io::timer;
use std::time::Duration;

static SLEEP_MS: i64 = 50;
static TOLERANCE_PERCENTAGE: f64 = 0.3;

#[test]
fn elapsed_ms() {
	let sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn stop() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);

	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn resume_once() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);
	sw.start();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, 2 * SLEEP_MS);
}

#[test]
fn resume_twice() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);
	sw.start();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, 2 * SLEEP_MS);
	sw.start();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, 3 * SLEEP_MS);
}

#[test]
fn is_running() {
	let mut sw = Stopwatch::new();
	assert!(!sw.is_running());
	sw.start();
	assert!(sw.is_running());
	sw.stop();
	assert!(!sw.is_running());
}

#[test]
fn restart() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.restart();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn reset() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.reset();
	assert!(!sw.is_running());
	assert_eq!(sw.elapsed_ms(), 0);
}

/////////////// helpers

fn assert_near(x: i64, y: i64, tolerance: i64) {
	let diff = (x - y).abs();
	if diff > tolerance {
		panic!("Expected {}, got {}", x, y);
	}
}

fn assert_sw_near(sw: Stopwatch, elapsed: i64) {
	let tolerance_value = (TOLERANCE_PERCENTAGE * elapsed as f64) as i64;
	assert_near(elapsed, sw.elapsed_ms(), tolerance_value);
}<commit_msg>added new test for blank stopwatch<commit_after>extern crate time;
extern crate stopwatch;

use stopwatch::{Stopwatch};
use std::num::SignedInt;
use std::io::timer;
use std::time::Duration;

static SLEEP_MS: i64 = 50;
static TOLERANCE_PERCENTAGE: f64 = 0.3;

#[test]
fn elapsed_none() {
	let sw = Stopwatch::new();
	assert_eq!(sw.elapsed_ms(), 0);
}

#[test]
fn elapsed_ms() {
	let sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn stop() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);

	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn resume_once() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);
	sw.start();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, 2 * SLEEP_MS);
}

#[test]
fn resume_twice() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, SLEEP_MS);
	sw.start();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.stop();
	assert_sw_near(sw, 2 * SLEEP_MS);
	sw.start();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, 3 * SLEEP_MS);
}

#[test]
fn is_running() {
	let mut sw = Stopwatch::new();
	assert!(!sw.is_running());
	sw.start();
	assert!(sw.is_running());
	sw.stop();
	assert!(!sw.is_running());
}

#[test]
fn restart() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.restart();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	assert_sw_near(sw, SLEEP_MS);
}

#[test]
fn reset() {
	let mut sw = Stopwatch::start_new();
	timer::sleep(Duration::milliseconds(SLEEP_MS));
	sw.reset();
	assert!(!sw.is_running());
	assert_eq!(sw.elapsed_ms(), 0);
}

/////////////// helpers

fn assert_near(x: i64, y: i64, tolerance: i64) {
	let diff = (x - y).abs();
	if diff > tolerance {
		panic!("Expected {}, got {}", x, y);
	}
}

fn assert_sw_near(sw: Stopwatch, elapsed: i64) {
	let tolerance_value = (TOLERANCE_PERCENTAGE * elapsed as f64) as i64;
	assert_near(elapsed, sw.elapsed_ms(), tolerance_value);
}<|endoftext|>


<commit_before>#![cfg_attr(feature = "nightly", feature(plugin, custom_derive))]
#![cfg_attr(feature = "nightly", plugin(json_str))]

#[cfg_attr(feature = "nightly", allow(plugin_as_library))]
#[macro_use]
extern crate json_str;

use json_str::parse::*;

#[test]
fn can_generate_json() {
	let j = json_str!({
		"a": 7,
		"b": { "c": "some stuff" },
		"data": [
			{ "id": 1, "name": "stuff" },
			{ "id": 2, "name": "stuff" }
		]
	});

	assert_eq!("{\"a\":7,\"b\":{\"c\":\"some stuff\"},\"data\":[{\"id\":1,\"name\":\"stuff\"},{\"id\":2,\"name\":\"stuff\"}]}", j);
}

#[test]
fn can_generate_quasi_json() {
	let j = json_str!({
		a: 7,
		b: { c: "some stuff" },
		data: [
			{ id: 1, name: "stuff" },
			{ id: 2, name: "stuff" }
		]
	});

	assert_eq!("{\"a\":7,\"b\":{\"c\":\"some stuff\"},\"data\":[{\"id\":1,\"name\":\"stuff\"},{\"id\":2,\"name\":\"stuff\"}]}", j);
}

#[test]
fn sanitisation_removes_whitespace() {
	let j = "\n{ \"a\" : \"stuff\", \"b\":{  \"c\":[ 0, \r\n1 ] }		,\"d\":14 }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff\",\"b\":{\"c\":[0,1]},\"d\":14}", &sanitised);
}

#[test]
fn sanitisation_does_not_affect_strings() {
	let j = "\n{ \"a\" : \"stuff and data.\n 	More.\"}";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff and data.\n 	More.\"}", &sanitised);
}

#[test]
fn sanitisation_standardises_quotes() {
	let j = "{ 'a' : \"stuff\", \"b\":{  \"c\":[ '0', 1 ] },\"d\":14 }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff\",\"b\":{\"c\":[\"0\",1]},\"d\":14}", &sanitised);
}

#[test]
fn sanitisation_quotes_unquoted_keys() {
	let j = "{ a : \"stuff\", \"b\":{  c:[ 0, 1 ] },d:14 }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff\",\"b\":{\"c\":[0,1]},\"d\":14}", &sanitised);
}

#[test]
fn sanitisation_does_not_quote_special_values() {
	let j = "{ \"a\": \"stuff\", \"b\": true, \"c\": false, \"d\": null }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff\",\"b\":true,\"c\":false,\"d\":null}", &sanitised);
}

#[test]
fn sanitisation_works_on_empty_string_values() {
	let j = "{ \"a\": \"\", \"b\": 1 }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"\",\"b\":1}", &sanitised);
}<commit_msg>add special chars to string test<commit_after>#![cfg_attr(feature = "nightly", feature(plugin, custom_derive))]
#![cfg_attr(feature = "nightly", plugin(json_str))]

#[cfg_attr(feature = "nightly", allow(plugin_as_library))]
#[macro_use]
extern crate json_str;

use json_str::parse::*;

#[test]
fn can_generate_json() {
	let j = json_str!({
		"a": 7,
		"b": { "c": "some stuff" },
		"data": [
			{ "id": 1, "name": "stuff" },
			{ "id": 2, "name": "stuff" }
		]
	});

	assert_eq!("{\"a\":7,\"b\":{\"c\":\"some stuff\"},\"data\":[{\"id\":1,\"name\":\"stuff\"},{\"id\":2,\"name\":\"stuff\"}]}", j);
}

#[test]
fn can_generate_quasi_json() {
	let j = json_str!({
		a: 7,
		b: { c: "some stuff" },
		data: [
			{ id: 1, name: "stuff" },
			{ id: 2, name: "stuff" }
		]
	});

	assert_eq!("{\"a\":7,\"b\":{\"c\":\"some stuff\"},\"data\":[{\"id\":1,\"name\":\"stuff\"},{\"id\":2,\"name\":\"stuff\"}]}", j);
}

#[test]
fn sanitisation_removes_whitespace() {
	let j = "\n{ \"a\" : \"stuff\", \"b\":{  \"c\":[ 0, \r\n1 ] }		,\"d\":14 }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff\",\"b\":{\"c\":[0,1]},\"d\":14}", &sanitised);
}

#[test]
fn sanitisation_does_not_affect_strings() {
	let j = "\n{ \"a\" : \"stuff and data.\n 	More.\", \"b\":\"色は匂へど 散りぬるを\"}";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff and data.\n 	More.\",\"b\":\"色は匂へど 散りぬるを\"}", &sanitised);
}

#[test]
fn sanitisation_standardises_quotes() {
	let j = "{ 'a' : \"stuff\", \"b\":{  \"c\":[ '0', 1 ] },\"d\":14 }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff\",\"b\":{\"c\":[\"0\",1]},\"d\":14}", &sanitised);
}

#[test]
fn sanitisation_quotes_unquoted_keys() {
	let j = "{ a : \"stuff\", \"b\":{  c:[ 0, 1 ] },d:14 }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff\",\"b\":{\"c\":[0,1]},\"d\":14}", &sanitised);
}

#[test]
fn sanitisation_does_not_quote_special_values() {
	let j = "{ \"a\": \"stuff\", \"b\": true, \"c\": false, \"d\": null }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"stuff\",\"b\":true,\"c\":false,\"d\":null}", &sanitised);
}

#[test]
fn sanitisation_works_on_empty_string_values() {
	let j = "{ \"a\": \"\", \"b\": 1 }";

	let mut sanitised = String::new();
	sanitise(j.as_bytes(), &mut sanitised);

	assert_eq!("{\"a\":\"\",\"b\":1}", &sanitised);
}<|endoftext|>