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

#![doc(html_root_url = "http://alexcrichton.com/toml-rs")]
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]

#[cfg(feature = "rustc-serialize")] extern crate rustc_serialize;
#[cfg(feature = "serde")] extern crate serde;

use std::collections::BTreeMap;
use std::str::FromStr;

pub use parser::{Parser, ParserError};

#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
pub use self::encoder::{Encoder, Error, encode, encode_str};
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
pub use self::decoder::{Decoder, DecodeError, DecodeErrorKind, decode, decode_str};

mod parser;
mod display;
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
mod encoder;
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
mod decoder;

/// Representation of a TOML value.
#[derive(PartialEq, Clone, Debug)]
#[allow(missing_docs)]
pub enum Value {
    String(String),
    Integer(i64),
    Float(f64),
    Boolean(bool),
    Datetime(String),
    Array(Array),
    Table(Table),
}

/// Type representing a TOML array, payload of the Value::Array variant
pub type Array = Vec<Value>;

/// Type representing a TOML table, payload of the Value::Table variant
pub type Table = BTreeMap<String, Value>;

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

            _ => false,
        }
    }

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

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

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

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

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

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

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

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

    /// Lookups for value at specified path.
    ///
    /// Uses '.' as a path separator.
    ///
    /// Note: arrays have zero-based indexes.
    ///
    /// Note: empty path returns self.
    ///
    /// ```
    /// # #![allow(unstable)]
    /// let toml = r#"
    ///      [test]
    ///      foo = "bar"
    ///
    ///      [[values]]
    ///      foo = "baz"
    ///
    ///      [[values]]
    ///      foo = "qux"
    /// "#;
    /// let value: toml::Value = toml.parse().unwrap();
    ///
    /// let foo = value.lookup("test.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "bar");
    ///
    /// let foo = value.lookup("values.1.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "qux");
    ///
    /// let no_bar = value.lookup("test.bar");
    /// assert_eq!(no_bar.is_none(), true);
    /// ```
    pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> {
        let ref path = match Parser::new(path).lookup() {
            Some(path) => path,
            None => return None,
        };
        let mut cur_value = self;
        if path.len() == 0 {
            return Some(cur_value)
        }

        for key in path {
            match *cur_value {
                Value::Table(ref hm) => {
                    match hm.get(key) {
                        Some(v) => cur_value = v,
                        None => return None
                    }
                },
                Value::Array(ref v) => {
                    match key.parse::<usize>().ok() {
                        Some(idx) if idx < v.len() => cur_value = &v[idx],
                        _ => return None
                    }
                },
                _ => return None
            }
        };

        Some(cur_value)

    }
    /// Lookups for mutable value at specified path.
    ///
    /// Uses '.' as a path separator.
    ///
    /// Note: arrays have zero-based indexes.
    ///
    /// Note: empty path returns self.
    ///
    /// ```
    /// # #![allow(unstable)]
    /// let toml = r#"
    ///      [test]
    ///      foo = "bar"
    ///
    ///      [[values]]
    ///      foo = "baz"
    ///
    ///      [[values]]
    ///      foo = "qux"
    /// "#;
    /// let mut value: toml::Value = toml.parse().unwrap();
    /// {
    ///    let string = value.lookup_mut("test.foo").unwrap();
    ///    assert_eq!(string, &mut toml::Value::String(String::from("bar")));
    ///    *string = toml::Value::String(String::from("foo"));
    /// }
    /// let result = value.lookup_mut("test.foo").unwrap();
    /// assert_eq!(result.as_str().unwrap(), "foo");
    /// ```
    pub fn lookup_mut(&mut self, path: &str) -> Option<&mut Value> {
       let ref path = match Parser::new(path).lookup() {
            Some(path) => path,
            None => return None,
        };

        let mut cur = self;
        if path.len() == 0 {
            return Some(cur)
        }

        for key in path {
            let tmp = cur;
            match *tmp {
                Value::Table(ref mut hm) => {
                    match hm.get_mut(key) {
                        Some(v) => cur = v,
                        None => return None
                    }
                }
                Value::Array(ref mut v) => {
                    match key.parse::<usize>().ok() {
                        Some(idx) if idx < v.len() => cur = &mut v[idx],
                        _ => return None
                    }
                }
                _ => return None
           }
        }
        Some(cur)
    }
}

impl FromStr for Value {
    type Err = Vec<ParserError>;
    fn from_str(s: &str) -> Result<Value, Vec<ParserError>> {
        let mut p = Parser::new(s);
        match p.parse().map(Value::Table) {
            Some(n) => Ok(n),
            None => Err(p.errors),
        }
    }
}

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

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

              [[values]]
              foo = "baz"

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

        let mut value: Value = toml.parse().unwrap();
        {
          let foo = value.lookup_mut("values.0.foo").unwrap();
          *foo = Value::String(String::from("bar"));
        }
        let foo = value.lookup("values.0.foo").unwrap();
        assert_eq!(foo.as_str().unwrap(), "bar");
    }

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

              [[values]]
              foo = "baz"

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

        let mut value: Value = toml.parse().unwrap();

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

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

        assert!(value.lookup_mut("test.bar").is_none());
        assert!(value.lookup_mut("test.foo.bar").is_none());
    }

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

        let mut value: Value = toml.parse().unwrap();

        {
            let foo = value.lookup_mut("test.foo");
            assert!(foo.is_none());
        }

        {
            let foo = value.lookup_mut("values.100.foo");
            assert!(foo.is_none());
        }

        {
            let foo = value.lookup_mut("values.str.foo");
            assert!(foo.is_none());
        }
    }

    #[test]
    fn lookup_mut_self() {
        let mut value: Value = r#"foo = "bar""#.parse().unwrap();

        {
            let foo = value.lookup_mut("foo").unwrap();
            assert_eq!(foo.as_str().unwrap(), "bar");
        }

        let foo = value.lookup_mut("").unwrap();
        assert!(foo.as_table().is_some());

        let baz = foo.lookup_mut("foo").unwrap();
        assert_eq!(baz.as_str().unwrap(), "bar");
    }

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

              [[values]]
              foo = "baz"

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

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

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

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

        assert!(value.lookup("test.bar").is_none());
        assert!(value.lookup("test.foo.bar").is_none());
    }

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

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

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

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

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

    #[test]
    fn lookup_self() {
        let value: Value = r#"foo = "bar""#.parse().unwrap();

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

        let foo = value.lookup("").unwrap();
        assert!(foo.as_table().is_some());

        let baz = foo.lookup("foo").unwrap();
        assert_eq!(baz.as_str().unwrap(), "bar");
    }

    #[test]
    fn lookup_advanced() {
        let value: Value = "[table]\n\"value\" = 0".parse().unwrap();
        let looked = value.lookup("table.\"value\"").unwrap();
        assert_eq!(*looked, Value::Integer(0));
    }

    #[test]
    fn lookup_mut_advanced() {
        let mut value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
        let looked = value.lookup_mut("table.\"value\".1").unwrap();
        assert_eq!(*looked, Value::Integer(1));
    }

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

#![doc(html_root_url = "http://alexcrichton.com/toml-rs")]
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]

#[cfg(feature = "rustc-serialize")] extern crate rustc_serialize;
#[cfg(feature = "serde")] extern crate serde;

use std::collections::BTreeMap;
use std::str::FromStr;

pub use parser::{Parser, ParserError};

#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
pub use self::encoder::{Encoder, Error, encode, encode_str};
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
pub use self::decoder::{Decoder, DecodeError, DecodeErrorKind, decode, decode_str};

mod parser;
mod display;
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
mod encoder;
#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
mod decoder;

/// Representation of a TOML value.
#[derive(PartialEq, Clone, Debug)]
#[allow(missing_docs)]
pub enum Value {
    String(String),
    Integer(i64),
    Float(f64),
    Boolean(bool),
    Datetime(String),
    Array(Array),
    Table(Table),
}

/// Type representing a TOML array, payload of the Value::Array variant
pub type Array = Vec<Value>;

/// Type representing a TOML table, payload of the Value::Table variant
pub type Table = BTreeMap<String, Value>;

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

            _ => false,
        }
    }

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

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

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

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

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

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

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

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

    /// Lookups for value at specified path.
    ///
    /// Uses '.' as a path separator.
    ///
    /// Note: arrays have zero-based indexes.
    ///
    /// Note: empty path returns self.
    ///
    /// ```
    /// # #![allow(unstable)]
    /// let toml = r#"
    ///      [test]
    ///      foo = "bar"
    ///
    ///      [[values]]
    ///      foo = "baz"
    ///
    ///      [[values]]
    ///      foo = "qux"
    /// "#;
    /// let value: toml::Value = toml.parse().unwrap();
    ///
    /// let foo = value.lookup("test.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "bar");
    ///
    /// let foo = value.lookup("values.1.foo").unwrap();
    /// assert_eq!(foo.as_str().unwrap(), "qux");
    ///
    /// let no_bar = value.lookup("test.bar");
    /// assert_eq!(no_bar.is_none(), true);
    /// ```
    pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> {
        let ref path = match Parser::new(path).lookup() {
            Some(path) => path,
            None => return None,
        };
        let mut cur_value = self;
        if path.len() == 0 {
            return Some(cur_value)
        }

        for key in path {
            match *cur_value {
                Value::Table(ref hm) => {
                    match hm.get(key) {
                        Some(v) => cur_value = v,
                        None => return None
                    }
                },
                Value::Array(ref v) => {
                    match key.parse::<usize>().ok() {
                        Some(idx) if idx < v.len() => cur_value = &v[idx],
                        _ => return None
                    }
                },
                _ => return None
            }
        };

        Some(cur_value)

    }
    /// Lookups for mutable value at specified path.
    ///
    /// Uses '.' as a path separator.
    ///
    /// Note: arrays have zero-based indexes.
    ///
    /// Note: empty path returns self.
    ///
    /// ```
    /// # #![allow(unstable)]
    /// let toml = r#"
    ///      [test]
    ///      foo = "bar"
    ///
    ///      [[values]]
    ///      foo = "baz"
    ///
    ///      [[values]]
    ///      foo = "qux"
    /// "#;
    /// let mut value: toml::Value = toml.parse().unwrap();
    /// {
    ///    let string = value.lookup_mut("test.foo").unwrap();
    ///    assert_eq!(string, &mut toml::Value::String(String::from("bar")));
    ///    *string = toml::Value::String(String::from("foo"));
    /// }
    /// let result = value.lookup_mut("test.foo").unwrap();
    /// assert_eq!(result.as_str().unwrap(), "foo");
    /// ```
    pub fn lookup_mut(&mut self, path: &str) -> Option<&mut Value> {
       let ref path = match Parser::new(path).lookup() {
            Some(path) => path,
            None => return None,
        };

        let mut cur = self;
        if path.len() == 0 {
            return Some(cur)
        }

        for key in path {
            let tmp = cur;
            match *tmp {
                Value::Table(ref mut hm) => {
                    match hm.get_mut(key) {
                        Some(v) => cur = v,
                        None => return None
                    }
                }
                Value::Array(ref mut v) => {
                    match key.parse::<usize>().ok() {
                        Some(idx) if idx < v.len() => cur = &mut v[idx],
                        _ => return None
                    }
                }
                _ => return None
           }
        }
        Some(cur)
    }
}

impl FromStr for Value {
    type Err = Vec<ParserError>;
    fn from_str(s: &str) -> Result<Value, Vec<ParserError>> {
        let mut p = Parser::new(s);
        match p.parse().map(Value::Table) {
            Some(n) => Ok(n),
            None => Err(p.errors),
        }
    }
}

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

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

              [[values]]
              foo = "baz"

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

        let mut value: Value = toml.parse().unwrap();
        {
          let foo = value.lookup_mut("values.0.foo").unwrap();
          *foo = Value::String(String::from("bar"));
        }
        let foo = value.lookup("values.0.foo").unwrap();
        assert_eq!(foo.as_str().unwrap(), "bar");
    }

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

              [[values]]
              foo = "baz"

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

        let mut value: Value = toml.parse().unwrap();

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

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

        assert!(value.lookup_mut("test.bar").is_none());
        assert!(value.lookup_mut("test.foo.bar").is_none());
    }

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

        let mut value: Value = toml.parse().unwrap();

        {
            let foo = value.lookup_mut("test.foo");
            assert!(foo.is_none());
        }

        {
            let foo = value.lookup_mut("values.100.foo");
            assert!(foo.is_none());
        }

        {
            let foo = value.lookup_mut("values.str.foo");
            assert!(foo.is_none());
        }
    }

    #[test]
    fn lookup_mut_self() {
        let mut value: Value = r#"foo = "bar""#.parse().unwrap();

        {
            let foo = value.lookup_mut("foo").unwrap();
            assert_eq!(foo.as_str().unwrap(), "bar");
        }

        let foo = value.lookup_mut("").unwrap();
        assert!(foo.as_table().is_some());

        let baz = foo.lookup_mut("foo").unwrap();
        assert_eq!(baz.as_str().unwrap(), "bar");
    }

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

              [[values]]
              foo = "baz"

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

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

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

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

        assert!(value.lookup("test.bar").is_none());
        assert!(value.lookup("test.foo.bar").is_none());
    }

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

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

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

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

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

    #[test]
    fn lookup_self() {
        let value: Value = r#"foo = "bar""#.parse().unwrap();

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

        let foo = value.lookup("").unwrap();
        assert!(foo.as_table().is_some());

        let baz = foo.lookup("foo").unwrap();
        assert_eq!(baz.as_str().unwrap(), "bar");
    }

    #[test]
    fn lookup_advanced() {
        let value: Value = "[table]\n\"value\" = 0".parse().unwrap();
        let looked = value.lookup("table.\"value\"").unwrap();
        assert_eq!(*looked, Value::Integer(0));
    }

		#[test]
		fn lookup_advanced_table() {
        let value: Value = r#"[table."name.other"] value = "my value""#.parse().unwrap();
        let looked = value.lookup(r#"table."name.other".value"#).unwrap();
        assert_eq!(*looked, Value::String(String::from("my value")));
		}

    #[test]
    fn lookup_mut_advanced() {
        let mut value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
        let looked = value.lookup_mut("table.\"value\".1").unwrap();
        assert_eq!(*looked, Value::Integer(1));
    }

}
<|endoftext|>


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

pub struct Keypad {
    layout: HashMap<u8, Key>,
    current_value: u8,
}

impl Keypad {
    fn new() -> Keypad {
        let mut layout = HashMap::new();

        layout.insert(1, Key {
           value: 1,
           left: None,
           up: None,
           right: Some(2),
           down: Some(4),
        });

        layout.insert(2, Key {
           value: 2,
           left: Some(1),
           up: None,
           right: Some(3),
           down: Some(5),
        });

        layout.insert(3, Key {
           value: 3,
           left: Some(2),
           up: None,
           right: None,
           down: Some(6),
        });

        layout.insert(4, Key {
           value: 4,
           left: None,
           up: Some(1),
           right: Some(5),
           down: Some(7),
        });

        layout.insert(5, Key {
           value: 5,
           left: Some(4),
           up: Some(2),
           right: Some(6),
           down: Some(8),
        });

        layout.insert(6, Key {
           value: 6,
           left: Some(5),
           up: Some(3),
           right: None,
           down: Some(9),
        });

        layout.insert(7, Key {
           value: 7,
           left: None,
           up: Some(4),
           right: Some(8),
           down: None,
        });

        layout.insert(8, Key {
           value: 1,
           left: Some(7),
           up: Some(5),
           right: Some(9),
           down: None,
        });

        layout.insert(9, Key {
           value: 9,
           left: Some(8),
           up: Some(6),
           right: None,
           down: None,
        });

        Keypad {
            layout: layout,
            current_value: 5,
        }
    }

    fn move_a_key(&mut self, instruction: char) {
        let current_key = self.layout.get(&self.current_value)
                                     .expect("impossible missing key");

        let next_key_value = match instruction {
            'U' => current_key.up.unwrap_or(current_key.value),
            'D' => current_key.down.unwrap_or(current_key.value),
            'L' => current_key.left.unwrap_or(current_key.value),
            'R' => current_key.right.unwrap_or(current_key.value),
            other => panic!("Don't know how to move ({})!", other),
        };

        self.current_value = next_key_value;
    }
}

struct Key {
    value: u8,
    left: Option<u8>,
    right: Option<u8>,
    up: Option<u8>,
    down: Option<u8>,
}

pub fn puzzle(input: &str) -> String {
    let mut keypad = Keypad::new();
    let mut answer = String::new();

    for line in input.lines() {
        for c in line.chars() {
            keypad.move_a_key(c);
        }
        answer.push_str(&format!("{}", keypad.current_value));
    }
    answer
}

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

    #[test]
    fn regular_move() {
        let mut keypad = Keypad::new();

        keypad.move_a_key('U');
        assert_eq!(keypad.current_value, 2);
    }

    #[test]
    fn ignored_move() {
        let mut keypad = Keypad::new();

        keypad.move_a_key('U');
        keypad.move_a_key('U');
        assert_eq!(keypad.current_value, 2);
    }

    #[test]
    fn overall_puzzle() {
        let input = "ULL\nRRDDD\nLURDL\nUUUUD\n";
        let answer = puzzle(input);
        assert_eq!(String::from("1985"), answer);
    }
}
<commit_msg>OMG data entry error. that will teach me (it wont)<commit_after>use std::collections::HashMap;

pub struct Keypad {
    layout: HashMap<u8, Key>,
    current_value: u8,
}

impl Keypad {
    fn new() -> Keypad {
        let mut layout = HashMap::new();

        layout.insert(1, Key {
           value: 1,
           left: None,
           up: None,
           right: Some(2),
           down: Some(4),
        });

        layout.insert(2, Key {
           value: 2,
           left: Some(1),
           up: None,
           right: Some(3),
           down: Some(5),
        });

        layout.insert(3, Key {
           value: 3,
           left: Some(2),
           up: None,
           right: None,
           down: Some(6),
        });

        layout.insert(4, Key {
           value: 4,
           left: None,
           up: Some(1),
           right: Some(5),
           down: Some(7),
        });

        layout.insert(5, Key {
           value: 5,
           left: Some(4),
           up: Some(2),
           right: Some(6),
           down: Some(8),
        });

        layout.insert(6, Key {
           value: 6,
           left: Some(5),
           up: Some(3),
           right: None,
           down: Some(9),
        });

        layout.insert(7, Key {
           value: 7,
           left: None,
           up: Some(4),
           right: Some(8),
           down: None,
        });

        layout.insert(8, Key {
           value: 8,
           left: Some(7),
           up: Some(5),
           right: Some(9),
           down: None,
        });

        layout.insert(9, Key {
           value: 9,
           left: Some(8),
           up: Some(6),
           right: None,
           down: None,
        });

        Keypad {
            layout: layout,
            current_value: 5,
        }
    }

    fn move_a_key(&mut self, instruction: char) {
        let current_key = self.layout.get(&self.current_value)
                                     .expect("impossible missing key");

        let next_key_value = match instruction {
            'U' => current_key.up.unwrap_or(current_key.value),
            'D' => current_key.down.unwrap_or(current_key.value),
            'L' => current_key.left.unwrap_or(current_key.value),
            'R' => current_key.right.unwrap_or(current_key.value),
            other => panic!("Don't know how to move ({})!", other),
        };

        self.current_value = next_key_value;
    }
}

struct Key {
    value: u8,
    left: Option<u8>,
    right: Option<u8>,
    up: Option<u8>,
    down: Option<u8>,
}

pub fn puzzle(input: &str) -> String {
    let mut keypad = Keypad::new();
    let mut answer = String::new();

    for line in input.lines() {
        for c in line.chars() {
            keypad.move_a_key(c);
        }
        answer.push_str(&format!("{}", keypad.current_value));
    }
    answer
}

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

    #[test]
    fn regular_move() {
        let mut keypad = Keypad::new();

        keypad.move_a_key('U');
        assert_eq!(keypad.current_value, 2);
    }

    #[test]
    fn ignored_move() {
        let mut keypad = Keypad::new();

        keypad.move_a_key('U');
        keypad.move_a_key('U');
        assert_eq!(keypad.current_value, 2);
    }

    #[test]
    fn overall_puzzle() {
        let input = "ULL\nRRDDD\nLURDL\nUUUUD\n";
        let answer = puzzle(input);
        assert_eq!(String::from("1985"), answer);
    }
}
<|endoftext|>


<commit_before>#![feature(proc_macro)]

extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

#[macro_use]
extern crate quote;

pub mod schema;

use std::borrow::Cow;
use std::error::Error;

use serde_json::Value;

use schema::{Schema, simpleTypes};

use quote::{Tokens, ToTokens};

struct Ident<S>(S);

impl<S: AsRef<str>> ToTokens for Ident<S> {
    fn to_tokens(&self, tokens: &mut Tokens) {
        tokens.append(self.0.as_ref())
    }
}

const ONE_OR_MANY: &'static str = r#"
use std::ops::{Deref, DerefMut};

#[derive(Clone, PartialEq, Debug)]
pub enum OneOrMany<T> {
    One(Box<T>),
    Many(Vec<T>),
}

impl<T> Deref for OneOrMany<T> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        match *self {
            OneOrMany::One(ref v) => unsafe { ::std::slice::from_raw_parts(&**v, 1) },
            OneOrMany::Many(ref v) => v,
        }
    }
}

impl<T> DerefMut for OneOrMany<T> {
    fn deref_mut(&mut self) -> &mut [T] {
        match *self {
            OneOrMany::One(ref mut v) => unsafe { ::std::slice::from_raw_parts_mut(&mut **v, 1) },
            OneOrMany::Many(ref mut v) => v,
        }
    }
}

impl<T> Default for OneOrMany<T> {
    fn default() -> OneOrMany<T> {
        OneOrMany::Many(Vec::new())
    }
}

impl<T> serde::Deserialize for OneOrMany<T>
    where T: serde::Deserialize
{
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: serde::Deserializer
    {
        T::deserialize(deserializer)
            .map(|one| OneOrMany::One(Box::new(one)))
            .or_else(|_| Vec::<T>::deserialize(deserializer).map(OneOrMany::Many))
    }
}

impl<T> serde::Serialize for OneOrMany<T>
    where T: serde::Serialize
{
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer
    {
        match *self {
            OneOrMany::One(ref one) => one.serialize(serializer),
            OneOrMany::Many(ref many) => many.serialize(serializer),
        }
    }
}
"#;

fn rename_keyword(prefix: &str, s: &str) -> Option<Tokens> {
    if ["type", "struct", "enum"].iter().any(|&keyword| keyword == s) {
        let n = Ident(format!("{}_", s));
        let prefix = Ident(prefix);
        Some(quote!{
            #[serde(rename = #s)]
            #prefix #n
        })
    } else {
        None
    }
}

fn field(s: &str) -> Tokens {
    if let Some(t) = rename_keyword("pub", s) {
        t
    } else {
        let mut snake = String::new();
        let mut chars = s.chars();
        let mut prev_was_upper = false;
        while let Some(c) = chars.next() {
            if c.is_uppercase() {
                if !prev_was_upper {
                    snake.push('_');
                }
                snake.extend(c.to_lowercase());
                prev_was_upper = true;
            } else {
                snake.push(c);
                prev_was_upper = false;
            }
        }
        if snake != s || snake.contains(|c: char| c == '$' || c == '#') {
            let field = if snake == "$ref" {
                Ident("ref_".into())
            } else {
                Ident(snake.replace('$', "").replace('#', ""))
            };

            quote!{
                #[serde(rename = #s)]
                pub #field
            }
        } else {
            let field = Ident(s);
            quote!( pub #field )
        }
    }
}

fn merge(result: &mut Schema, r: &Schema) {
    use std::collections::hash_map::Entry;

    for (k, v) in &r.properties {
        match result.properties.entry(k.clone()) {
            Entry::Vacant(entry) => {
                entry.insert(v.clone());
            }
            Entry::Occupied(mut entry) => merge(entry.get_mut(), v),
        }
    }
}

struct FieldExpander<'a, 'r: 'a> {
    default: bool,
    expander: &'a mut Expander<'r>,
}

impl<'a, 'r> FieldExpander<'a, 'r> {
    fn expand_fields(&mut self, type_name: &str, schema: &Schema) -> Vec<Tokens> {
        let schema = self.expander.schema(schema);
        schema.properties
            .iter()
            .map(|(field_name, value)| {
                let key = field(field_name);
                let required =
                    schema.required.iter().flat_map(|a| a.iter()).any(|req| req == field_name);
                let field_type = self.expander.expand_type(type_name, required, value);
                if !field_type.typ.starts_with("Option<") {
                    self.default = false;
                }
                let typ = Ident(field_type.typ);
                if field_type.default {
                    quote!( #[serde(default)] #key : #typ )
                } else {
                    quote!( #key : #typ )
                }
            })
            .collect()
    }
}

struct Expander<'r> {
    root_name: Option<&'r str>,
    root: &'r Schema,
    needs_one_or_many: bool,
}

struct FieldType {
    typ: String,
    default: bool,
}

impl<S> From<S> for FieldType
    where S: Into<String>
{
    fn from(s: S) -> FieldType {
        FieldType {
            typ: s.into(),
            default: false,
        }
    }
}

impl<'r> Expander<'r> {
    fn new(root_name: Option<&'r str>, root: &'r Schema) -> Expander<'r> {
        Expander {
            root_name: root_name,
            root: root,
            needs_one_or_many: false,
        }
    }

    fn type_ref(&self, s: &str) -> String {
        if s == "#" {
            self.root_name.expect("Root name").into()
        } else {
            s.split('/').last().expect("Component").into()
        }
    }

    fn schema(&self, schema: &'r Schema) -> Cow<'r, Schema> {
        let result = match schema.all_of.as_ref().and_then(|array| array.first()) {
            Some(result) => {
                schema.all_of
                    .iter()
                    .skip(1)
                    .fold(Cow::Borrowed(result), |mut result, def| {
                        merge(result.to_mut(), &self.schema(&def[0]));
                        result
                    })
            }
            None => Cow::Borrowed(schema),
        };
        if let Some(ref ref_) = result.ref_ {
            return Cow::Borrowed(self.schema_ref(ref_));
        }
        result
    }

    fn schema_ref(&self, s: &str) -> &'r Schema {
        s.split('/').fold(self.root, |schema, comp| {
            if comp == "#" {
                self.root
            } else if comp == "definitions" {
                schema
            } else {
                schema.definitions
                    .get(comp)
                    .unwrap_or_else(|| panic!("Expected definition: `{}` {}", s, comp))
            }
        })
    }

    fn expand_type(&mut self, type_name: &str, required: bool, typ: &Schema) -> FieldType {
        let mut result = self.expand_type_(typ);
        if type_name == result.typ {
            result.typ = format!("Box<{}>", result.typ)
        }
        if !required && !result.default {
            result.typ = format!("Option<{}>", result.typ)
        }
        result
    }

    fn expand_type_(&mut self, typ: &Schema) -> FieldType {
        if let Some(ref ref_) = typ.ref_ {
            self.type_ref(ref_).into()
        } else if typ.any_of.as_ref().map_or(false, |a| a.len() == 2) {
            let any_of = typ.any_of.as_ref().unwrap();
            let simple = self.schema(&any_of[0]);
            let array = self.schema(&any_of[1]);
            if let simpleTypes::array = array.type_[0] {
                if simple == self.schema(&array.items[0]) {
                    self.needs_one_or_many = true;
                    return FieldType {
                        typ: format!("OneOrMany<{}>", self.expand_type_(&any_of[0]).typ),
                        default: true,
                    };
                }
            }
            return "serde_json::Value".into();
        } else if typ.type_.len() == 1 {
            match typ.type_[0] {
                simpleTypes::string => {
                    if typ.enum_.as_ref().map_or(false, |e| e.is_empty()) {
                        "serde_json::Value".into()
                    } else {
                        "String".into()
                    }
                }
                simpleTypes::integer => "i64".into(),
                simpleTypes::boolean => "bool".into(),
                simpleTypes::number => "f64".into(),
                simpleTypes::object if typ.additional_properties.is_some() => {
                    let prop = serde_json::from_value(typ.additional_properties.clone().unwrap())
                        .unwrap();
                    let result =
                        format!("::std::collections::HashMap<String, {}>", self.expand_type_(&prop).typ);
                    FieldType {
                        typ: result,
                        default: typ.default == Some(Value::Object(Default::default())),
                    }
                }
                simpleTypes::array => {
                    let item_type = typ.items.get(0).map_or("serde_json::Value".into(),
                                                            |item| self.expand_type_(item).typ);
                    format!("Vec<{}>", item_type).into()
                }
                _ => "serde_json::Value".into(),
            }
        } else {
            "serde_json::Value".into()
        }
    }

    pub fn expand_definitions(&mut self, schema: &Schema) -> Vec<Tokens> {
        let mut types = Vec::new();
        for (name, def) in &schema.definitions {
            types.push(self.expand_schema(name, def));
        }
        types
    }

    pub fn expand_schema(&mut self, name: &str, schema: &Schema) -> Tokens {
        let (fields, default) = {
            let mut field_expander = FieldExpander {
                default: true,
                expander: self,
            };
            let fields = field_expander.expand_fields(name, schema);
            (fields, field_expander.default)
        };

        let name = Ident(name);
        if !fields.is_empty() {
            if default {
                quote! {
                    #[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
                    pub struct #name {
                        #(#fields),*
                    }
                }
            } else {
                quote! {
                    #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
                    pub struct #name {
                        #(#fields),*
                    }
                }
            }
        } else if schema.enum_.as_ref().map_or(false, |e| !e.is_empty()) {
            let variants = schema.enum_.as_ref().map_or(&[][..], |v| v).iter().map(|v| {
                match *v {
                    Value::String(ref v) => {
                        rename_keyword("", v).unwrap_or_else(|| {
                            let v = Ident(v);
                            quote!(#v)
                        })
                    }
                    _ => panic!("Expected string"),
                }
            });
            quote! {
                #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
                pub enum #name {
                    #(#variants),*
                }
            }
        } else {
            let typ = Ident(self.expand_type("", true, schema).typ);
            quote! {
                pub type #name = #typ;
            }
        }
    }

    pub fn expand(&mut self, schema: &Schema) -> Tokens {
        let mut types = self.expand_definitions(schema);
        if let Some(name) = self.root_name {
            types.push(self.expand_schema(name, schema));
        }

        let one_or_many = Ident(if self.needs_one_or_many {
            ONE_OR_MANY
        } else {
            ""
        });

        quote! {
            #one_or_many

            #( #types )*
        }
    }
}

pub fn generate(root_name: Option<&str>, s: &str) -> Result<String, Box<Error>> {
    use std::process::{Command, Stdio};
    use std::io::Write;

    let schema = serde_json::from_str(s).unwrap();
    let mut expander = Expander::new(root_name, &schema);
    let output = expander.expand(&schema).to_string();
    let mut child =
        try!(Command::new("rustfmt").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn());
    try!(child.stdin.as_mut().expect("stdin").write_all(output.as_bytes()));
    let output = try!(child.wait_with_output());
    assert!(output.status.success());
    Ok(try!(String::from_utf8(output.stdout)))
}

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

    use std::fs::File;
    use std::io::Write;
    use std::path::PathBuf;
    use std::process::{Command, Stdio};

    #[test]
    fn generate_schema() {
        let s = include_str!("schema.json");

        let s = generate(Some("Schema"), s).unwrap().to_string();

        verify_compile("schema", &s);

        assert!(s.contains("pub struct Schema"), "{}", s);
        assert!(s.contains("pub type positiveInteger = i64"));
        assert!(s.contains("pub type_: OneOrMany<simpleTypes>"));

        let result = Command::new("rustc")
            .args(&["-L",
                    "target/debug/deps/",
                    "-o",
                    "target/debug/schema-test",
                    "tests/support/schema-test.rs"])
            .status()
            .unwrap();

        assert!(result.success());
        let result = Command::new("./target/debug/schema-test")
            .status()
            .unwrap();

        assert!(result.success());
    }

    fn verify_compile(name: &str, s: &str) {

        let mut filename = PathBuf::from("target/debug");
        filename.push(&format!("{}.rs", name));
        {
            let mut file = File::create(&filename).unwrap();
            let header = r#"
            #![feature(proc_macro)]

            extern crate serde;
            #[macro_use]
            extern crate serde_derive;
            extern crate serde_json;
            "#;
            file.write_all(header.as_bytes()).unwrap();
            file.write_all(s.as_bytes()).unwrap();
        }
        println!("{}", filename.display());
        let child = Command::new("rustc")
            .args(&["-L",
                    "target/debug/deps/",
                    "--crate-type=rlib",
                    "-o",
                    &format!("target/debug/deps/lib{}.rlib", name),
                    filename.to_str().unwrap()])
            .stderr(Stdio::piped())
            .spawn()
            .unwrap();

        let output = child.wait_with_output().unwrap();
        let error = String::from_utf8(output.stderr).unwrap();
        assert!(output.status.success(), "{}", error);
    }

    #[test]
    fn builds_with_rustc() {
        let s = include_str!("../tests/debugserver-schema.json");

        let s = generate(None, s).unwrap().to_string();

        verify_compile("debug-server", &s)
    }
}
<commit_msg>fix: Use BTreeMap instead of HashMap for a consitent iteration order<commit_after>#![feature(proc_macro)]

extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

#[macro_use]
extern crate quote;

pub mod schema;

use std::borrow::Cow;
use std::error::Error;

use serde_json::Value;

use schema::{Schema, simpleTypes};

use quote::{Tokens, ToTokens};

struct Ident<S>(S);

impl<S: AsRef<str>> ToTokens for Ident<S> {
    fn to_tokens(&self, tokens: &mut Tokens) {
        tokens.append(self.0.as_ref())
    }
}

const ONE_OR_MANY: &'static str = r#"
use std::ops::{Deref, DerefMut};

#[derive(Clone, PartialEq, Debug)]
pub enum OneOrMany<T> {
    One(Box<T>),
    Many(Vec<T>),
}

impl<T> Deref for OneOrMany<T> {
    type Target = [T];
    fn deref(&self) -> &[T] {
        match *self {
            OneOrMany::One(ref v) => unsafe { ::std::slice::from_raw_parts(&**v, 1) },
            OneOrMany::Many(ref v) => v,
        }
    }
}

impl<T> DerefMut for OneOrMany<T> {
    fn deref_mut(&mut self) -> &mut [T] {
        match *self {
            OneOrMany::One(ref mut v) => unsafe { ::std::slice::from_raw_parts_mut(&mut **v, 1) },
            OneOrMany::Many(ref mut v) => v,
        }
    }
}

impl<T> Default for OneOrMany<T> {
    fn default() -> OneOrMany<T> {
        OneOrMany::Many(Vec::new())
    }
}

impl<T> serde::Deserialize for OneOrMany<T>
    where T: serde::Deserialize
{
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: serde::Deserializer
    {
        T::deserialize(deserializer)
            .map(|one| OneOrMany::One(Box::new(one)))
            .or_else(|_| Vec::<T>::deserialize(deserializer).map(OneOrMany::Many))
    }
}

impl<T> serde::Serialize for OneOrMany<T>
    where T: serde::Serialize
{
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer
    {
        match *self {
            OneOrMany::One(ref one) => one.serialize(serializer),
            OneOrMany::Many(ref many) => many.serialize(serializer),
        }
    }
}
"#;

fn rename_keyword(prefix: &str, s: &str) -> Option<Tokens> {
    if ["type", "struct", "enum"].iter().any(|&keyword| keyword == s) {
        let n = Ident(format!("{}_", s));
        let prefix = Ident(prefix);
        Some(quote!{
            #[serde(rename = #s)]
            #prefix #n
        })
    } else {
        None
    }
}

fn field(s: &str) -> Tokens {
    if let Some(t) = rename_keyword("pub", s) {
        t
    } else {
        let mut snake = String::new();
        let mut chars = s.chars();
        let mut prev_was_upper = false;
        while let Some(c) = chars.next() {
            if c.is_uppercase() {
                if !prev_was_upper {
                    snake.push('_');
                }
                snake.extend(c.to_lowercase());
                prev_was_upper = true;
            } else {
                snake.push(c);
                prev_was_upper = false;
            }
        }
        if snake != s || snake.contains(|c: char| c == '$' || c == '#') {
            let field = if snake == "$ref" {
                Ident("ref_".into())
            } else {
                Ident(snake.replace('$', "").replace('#', ""))
            };

            quote!{
                #[serde(rename = #s)]
                pub #field
            }
        } else {
            let field = Ident(s);
            quote!( pub #field )
        }
    }
}

fn merge(result: &mut Schema, r: &Schema) {
    use std::collections::hash_map::Entry;

    for (k, v) in &r.properties {
        match result.properties.entry(k.clone()) {
            Entry::Vacant(entry) => {
                entry.insert(v.clone());
            }
            Entry::Occupied(mut entry) => merge(entry.get_mut(), v),
        }
    }
}

struct FieldExpander<'a, 'r: 'a> {
    default: bool,
    expander: &'a mut Expander<'r>,
}

impl<'a, 'r> FieldExpander<'a, 'r> {
    fn expand_fields(&mut self, type_name: &str, schema: &Schema) -> Vec<Tokens> {
        let schema = self.expander.schema(schema);
        schema.properties
            .iter()
            .map(|(field_name, value)| {
                let key = field(field_name);
                let required =
                    schema.required.iter().flat_map(|a| a.iter()).any(|req| req == field_name);
                let field_type = self.expander.expand_type(type_name, required, value);
                if !field_type.typ.starts_with("Option<") {
                    self.default = false;
                }
                let typ = Ident(field_type.typ);
                if field_type.default {
                    quote!( #[serde(default)] #key : #typ )
                } else {
                    quote!( #key : #typ )
                }
            })
            .collect()
    }
}

struct Expander<'r> {
    root_name: Option<&'r str>,
    root: &'r Schema,
    needs_one_or_many: bool,
}

struct FieldType {
    typ: String,
    default: bool,
}

impl<S> From<S> for FieldType
    where S: Into<String>
{
    fn from(s: S) -> FieldType {
        FieldType {
            typ: s.into(),
            default: false,
        }
    }
}

impl<'r> Expander<'r> {
    fn new(root_name: Option<&'r str>, root: &'r Schema) -> Expander<'r> {
        Expander {
            root_name: root_name,
            root: root,
            needs_one_or_many: false,
        }
    }

    fn type_ref(&self, s: &str) -> String {
        if s == "#" {
            self.root_name.expect("Root name").into()
        } else {
            s.split('/').last().expect("Component").into()
        }
    }

    fn schema(&self, schema: &'r Schema) -> Cow<'r, Schema> {
        let result = match schema.all_of.as_ref().and_then(|array| array.first()) {
            Some(result) => {
                schema.all_of
                    .iter()
                    .skip(1)
                    .fold(Cow::Borrowed(result), |mut result, def| {
                        merge(result.to_mut(), &self.schema(&def[0]));
                        result
                    })
            }
            None => Cow::Borrowed(schema),
        };
        if let Some(ref ref_) = result.ref_ {
            return Cow::Borrowed(self.schema_ref(ref_));
        }
        result
    }

    fn schema_ref(&self, s: &str) -> &'r Schema {
        s.split('/').fold(self.root, |schema, comp| {
            if comp == "#" {
                self.root
            } else if comp == "definitions" {
                schema
            } else {
                schema.definitions
                    .get(comp)
                    .unwrap_or_else(|| panic!("Expected definition: `{}` {}", s, comp))
            }
        })
    }

    fn expand_type(&mut self, type_name: &str, required: bool, typ: &Schema) -> FieldType {
        let mut result = self.expand_type_(typ);
        if type_name == result.typ {
            result.typ = format!("Box<{}>", result.typ)
        }
        if !required && !result.default {
            result.typ = format!("Option<{}>", result.typ)
        }
        result
    }

    fn expand_type_(&mut self, typ: &Schema) -> FieldType {
        if let Some(ref ref_) = typ.ref_ {
            self.type_ref(ref_).into()
        } else if typ.any_of.as_ref().map_or(false, |a| a.len() == 2) {
            let any_of = typ.any_of.as_ref().unwrap();
            let simple = self.schema(&any_of[0]);
            let array = self.schema(&any_of[1]);
            if let simpleTypes::array = array.type_[0] {
                if simple == self.schema(&array.items[0]) {
                    self.needs_one_or_many = true;
                    return FieldType {
                        typ: format!("OneOrMany<{}>", self.expand_type_(&any_of[0]).typ),
                        default: true,
                    };
                }
            }
            return "serde_json::Value".into();
        } else if typ.type_.len() == 1 {
            match typ.type_[0] {
                simpleTypes::string => {
                    if typ.enum_.as_ref().map_or(false, |e| e.is_empty()) {
                        "serde_json::Value".into()
                    } else {
                        "String".into()
                    }
                }
                simpleTypes::integer => "i64".into(),
                simpleTypes::boolean => "bool".into(),
                simpleTypes::number => "f64".into(),
                simpleTypes::object if typ.additional_properties.is_some() => {
                    let prop = serde_json::from_value(typ.additional_properties.clone().unwrap())
                        .unwrap();
                    let result =
                        format!("::std::collections::BTreeMap<String, {}>", self.expand_type_(&prop).typ);
                    FieldType {
                        typ: result,
                        default: typ.default == Some(Value::Object(Default::default())),
                    }
                }
                simpleTypes::array => {
                    let item_type = typ.items.get(0).map_or("serde_json::Value".into(),
                                                            |item| self.expand_type_(item).typ);
                    format!("Vec<{}>", item_type).into()
                }
                _ => "serde_json::Value".into(),
            }
        } else {
            "serde_json::Value".into()
        }
    }

    pub fn expand_definitions(&mut self, schema: &Schema) -> Vec<Tokens> {
        let mut types = Vec::new();
        for (name, def) in &schema.definitions {
            types.push(self.expand_schema(name, def));
        }
        types
    }

    pub fn expand_schema(&mut self, name: &str, schema: &Schema) -> Tokens {
        let (fields, default) = {
            let mut field_expander = FieldExpander {
                default: true,
                expander: self,
            };
            let fields = field_expander.expand_fields(name, schema);
            (fields, field_expander.default)
        };

        let name = Ident(name);
        if !fields.is_empty() {
            if default {
                quote! {
                    #[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
                    pub struct #name {
                        #(#fields),*
                    }
                }
            } else {
                quote! {
                    #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
                    pub struct #name {
                        #(#fields),*
                    }
                }
            }
        } else if schema.enum_.as_ref().map_or(false, |e| !e.is_empty()) {
            let variants = schema.enum_.as_ref().map_or(&[][..], |v| v).iter().map(|v| {
                match *v {
                    Value::String(ref v) => {
                        rename_keyword("", v).unwrap_or_else(|| {
                            let v = Ident(v);
                            quote!(#v)
                        })
                    }
                    _ => panic!("Expected string"),
                }
            });
            quote! {
                #[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
                pub enum #name {
                    #(#variants),*
                }
            }
        } else {
            let typ = Ident(self.expand_type("", true, schema).typ);
            quote! {
                pub type #name = #typ;
            }
        }
    }

    pub fn expand(&mut self, schema: &Schema) -> Tokens {
        let mut types = self.expand_definitions(schema);
        if let Some(name) = self.root_name {
            types.push(self.expand_schema(name, schema));
        }

        let one_or_many = Ident(if self.needs_one_or_many {
            ONE_OR_MANY
        } else {
            ""
        });

        quote! {
            #one_or_many

            #( #types )*
        }
    }
}

pub fn generate(root_name: Option<&str>, s: &str) -> Result<String, Box<Error>> {
    use std::process::{Command, Stdio};
    use std::io::Write;

    let schema = serde_json::from_str(s).unwrap();
    let mut expander = Expander::new(root_name, &schema);
    let output = expander.expand(&schema).to_string();
    let mut child =
        try!(Command::new("rustfmt").stdin(Stdio::piped()).stdout(Stdio::piped()).spawn());
    try!(child.stdin.as_mut().expect("stdin").write_all(output.as_bytes()));
    let output = try!(child.wait_with_output());
    assert!(output.status.success());
    Ok(try!(String::from_utf8(output.stdout)))
}

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

    use std::fs::File;
    use std::io::Write;
    use std::path::PathBuf;
    use std::process::{Command, Stdio};

    #[test]
    fn generate_schema() {
        let s = include_str!("schema.json");

        let s = generate(Some("Schema"), s).unwrap().to_string();

        verify_compile("schema", &s);

        assert!(s.contains("pub struct Schema"), "{}", s);
        assert!(s.contains("pub type positiveInteger = i64"));
        assert!(s.contains("pub type_: OneOrMany<simpleTypes>"));

        let result = Command::new("rustc")
            .args(&["-L",
                    "target/debug/deps/",
                    "-o",
                    "target/debug/schema-test",
                    "tests/support/schema-test.rs"])
            .status()
            .unwrap();

        assert!(result.success());
        let result = Command::new("./target/debug/schema-test")
            .status()
            .unwrap();

        assert!(result.success());
    }

    fn verify_compile(name: &str, s: &str) {

        let mut filename = PathBuf::from("target/debug");
        filename.push(&format!("{}.rs", name));
        {
            let mut file = File::create(&filename).unwrap();
            let header = r#"
            #![feature(proc_macro)]

            extern crate serde;
            #[macro_use]
            extern crate serde_derive;
            extern crate serde_json;
            "#;
            file.write_all(header.as_bytes()).unwrap();
            file.write_all(s.as_bytes()).unwrap();
        }
        println!("{}", filename.display());
        let child = Command::new("rustc")
            .args(&["-L",
                    "target/debug/deps/",
                    "--crate-type=rlib",
                    "-o",
                    &format!("target/debug/deps/lib{}.rlib", name),
                    filename.to_str().unwrap()])
            .stderr(Stdio::piped())
            .spawn()
            .unwrap();

        let output = child.wait_with_output().unwrap();
        let error = String::from_utf8(output.stderr).unwrap();
        assert!(output.status.success(), "{}", error);
    }

    #[test]
    fn builds_with_rustc() {
        let s = include_str!("../tests/debugserver-schema.json");

        let s = generate(None, s).unwrap().to_string();

        verify_compile("debug-server", &s)
    }
}
<|endoftext|>


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

#[plugin]
extern crate peg_syntax_ext;

pub use parser::{PLY,Format,Version,ElementSpec,PropertySpec,Type,parse};
pub use property::Property;

pub mod parser;
pub mod property;

pub trait Model {
	fn new(&PLY) -> Result<Self,String>;
}

pub trait Element: Sized {
	// dummy parameter until UFCS
	fn check(Option<Self>, &ElementSpec) -> Result<(),String>;
	fn parse_one(&Vec<String>) -> Result<Self,String>;
	fn parse(e: &ElementSpec) -> Result<Vec<Self>,String> {
		let mut res = Vec::with_capacity(e.data.len());
		for l in e.data.iter() {
			res.push(try!(Element::parse_one(l)));
		}
		Ok(res)
	}
}

// Used in the macro expansions only.
#[doc(hidden)]
pub trait ElementVec {
	fn check(Option<Self>, &ElementSpec) -> Result<(),String>;
}
impl<T:Element> ElementVec for Vec<T> {
	fn check(_dummy: Option<Self>, spec: &ElementSpec) -> Result<(),String> {
		Element::check(None::<T>, spec)
	}
}
<commit_msg>Made the function `Element::parse_one` optional (with a run-time error if neither `parse` nor `parse_one` is specified).<commit_after>#![feature(plugin,core,collections)]

#[plugin]
extern crate peg_syntax_ext;

pub use parser::{PLY,Format,Version,ElementSpec,PropertySpec,Type,parse};
pub use property::Property;

pub mod parser;
pub mod property;

pub trait Model {
	fn new(&PLY) -> Result<Self,String>;
}

pub trait Element: Sized {
	// dummy parameter until UFCS
	fn check(Option<Self>, &ElementSpec) -> Result<(),String>;

	fn parse_one(&Vec<String>) -> Result<Self,String> {
		Err("`parse_one` must be implemented when used by the `parse` function.".to_string())
	}

	fn parse(e: &ElementSpec) -> Result<Vec<Self>,String> {
		let mut res = Vec::with_capacity(e.data.len());
		for l in e.data.iter() {
			res.push(try!(Element::parse_one(l)));
		}
		Ok(res)
	}
}

// Used in the macro expansions only.
#[doc(hidden)]
pub trait ElementVec {
	fn check(Option<Self>, &ElementSpec) -> Result<(),String>;
}
impl<T:Element> ElementVec for Vec<T> {
	fn check(_dummy: Option<Self>, spec: &ElementSpec) -> Result<(),String> {
		Element::check(None::<T>, spec)
	}
}
<|endoftext|>


<commit_before>#![crate_type="lib"]
extern crate byteorder;
pub mod xdr;

#[test]
fn u16_writer_test() {
	let mut x = xdr::XdrWriter::new();
	let wtr = vec![0,0,2,5];

	x.pack(517u16);
	assert_eq!(x.get_buffer(),wtr);
}
<commit_msg>tests: add u16 reader test, add packing/unpacking sequence of primitive types test<commit_after>#![crate_type="lib"]
extern crate byteorder;
pub mod xdr;

#[test]
fn u16_writer_test() {
	let mut x = xdr::XdrWriter::new();
	let wtr = vec![0,0,2,5];

	x.pack(517u16);
	assert_eq!(x.get_buffer(),wtr);
}

#[test]
fn u16_reader_test() {
	let wtr = vec![0,0,2,5];
	let mut x = xdr::XdrReader::new(wtr);

	let v = x.unpack::<u16>().unwrap();
	assert_eq!(v,517u16);
}

#[test]
fn r_w_primitive_test() {
	let mut wr = xdr::XdrWriter::new();
	wr.pack(0xCCu8);
	wr.pack(0xAAAAu16);
	wr.pack(0xDEADBEEFu32);
	wr.pack(-1i8);
	wr.pack(-256i16);
	wr.pack(-20i32);
	wr.pack(100.500f32);
	wr.pack(-100.500e10f64);

	let mut rdr = xdr::XdrReader::new(wr.get_buffer());

	assert_eq!(0xCCu8,rdr.unpack::<u8>().unwrap());
	assert_eq!(0xAAAAu16,rdr.unpack::<u16>().unwrap());
	assert_eq!(0xDEADBEEFu32,rdr.unpack::<u32>().unwrap());
	assert_eq!(-1i8,rdr.unpack::<i8>().unwrap());
	assert_eq!(-256i16,rdr.unpack::<i16>().unwrap());
	assert_eq!(-20i32,rdr.unpack::<i32>().unwrap());
	assert_eq!(100.500f32,rdr.unpack::<f32>().unwrap());
	assert_eq!(-100.500e10f64,rdr.unpack::<f64>().unwrap());
}
<|endoftext|>


<commit_before>#![no_std]

#[doc(hidden)]
pub mod _core {
    pub use core::*;
}

pub struct ByRef;
pub struct ByMut;
pub struct Read;
pub struct Write;

#[macro_export]
macro_rules! unsafe_unions {
    // endpoint
    () => {};

    // pub union
    (
        $(#[$attr:meta])*
        pub union $union:ident: $repr:ty {
            $($variant:ident: $variant_ty:ty),+
        }
        $($ff:tt)*
    ) => {
        pub struct $union<_Mode = ()> {
            _repr: $repr,
            _phantom: $crate::_core::marker::PhantomData<fn(_Mode)>,
        }

        unsafe_unions!(@impl_pub $union; $repr; $($variant: $variant_ty),+);
        unsafe_unions!($($ff)*);
    };

    // pub union (trailing comma)
    (
        $(#[$attr:meta])*
        pub union $union:ident: $repr:ty {
            $($variant:ident: $variant_ty:ty),+,
        }
        $($ff:tt)*
    ) => {
        pub struct $union<_Mode = ()> {
            _repr: $repr,
            _phantom: $crate::_core::marker::PhantomData<fn(_Mode)>,
        }

        unsafe_unions!(@impl_pub $union; $repr; $($variant: $variant_ty),+);
        unsafe_unions!($($ff)*);
    };

    // non-pub union
    (
        $(#[$attr:meta])*
        union $union:ident: $repr:ty {
            $($variant:ident: $variant_ty:ty),+
        }
        $($ff:tt)*
    ) => {
        struct $union<_Mode = ()> {
            _repr: $repr,
            _phantom: $crate::_core::marker::PhantomData<fn(_Mode)>,
        }

        unsafe_unions!(@impl_non_pub $union; $repr; $($variant: $variant_ty),+);
        unsafe_unions!($($ff)*);
    };

    // non-pub union (trailing comma)
    (
        $(#[$attr:meta])*
        union $union:ident: $repr:ty {
            $($variant:ident: $variant_ty:ty),+,
        }
        $($ff:tt)*
    ) => {
        struct $union<_Mode = ()> {
            _repr: $repr,
            _phantom: $crate::_core::marker::PhantomData<fn(_Mode)>,
        }

        unsafe_unions!(@impl_non_pub $union; $repr; $($variant: $variant_ty),+);
        unsafe_unions!($($ff)*);
    };

    (@impl_common $union: ident; $repr:ty; $($variant:ident: $variant_ty:ty),+) => {
        impl Clone for $union {
            fn clone(&self) -> Self {
                use $crate::_core;
                Self::_check_repr_type();
                $union{
                    _repr: unsafe { _core::ptr::read(&self._repr) },
                    _phantom: _core::marker::PhantomData
                }
            }
        }

        impl Copy for $union {}

        impl<_T> $union<_T> {
            #[doc(hidden)]
            fn _check_repr_type() {
                // Check repr-type size
                // Currently, we can only ensure this at run-time (Panics are preferable
                // to segfault, anyways).

                // TODO: errmsg is a bit too wordy.
                use $crate::_core::mem;
                $(
                    assert!(
                        mem::size_of::<$variant_ty>() <= mem::size_of::<$repr>(),
                        "union '{}': repr-type needs to be bigger or equal in \
                        size to the biggest field. \
                        '{}' is '{}' bytes, field '{}' of type '{}' is '{}' bytes",
                        stringify!($union),
                        stringify!($repr),
                        mem::size_of::<$repr>(),
                        stringify!($variant),
                        stringify!($variant_ty),
                        mem::size_of::<$variant_ty>(),
                    );
                )+
            }
        }
    };

    (@impl_pub $union: ident; $repr:ty; $($variant:ident: $variant_ty:ty),+) => {
        unsafe_unions!(@impl_common $union; $repr; $($variant: $variant_ty),+);

        impl $union {
            pub fn new() -> Self {
                use $crate::_core;
                Self::_check_repr_type();
                $union{
                    _repr: unsafe { _core::mem::uninitialized() },
                    _phantom: _core::marker::PhantomData
                }
            }
            pub fn zeroed() -> Self {
                use $crate::_core;
                Self::_check_repr_type();
                $union{
                    _repr: unsafe { _core::mem::zeroed() },
                    _phantom: _core::marker::PhantomData
                }
            }

            $(pub unsafe fn $variant(v: $variant_ty) -> Self {
                let mut ret = Self::new();
                ret.write().$variant(v);
                ret
            })+

            pub fn repr(&self) -> &$repr { &self._repr }
            pub fn repr_mut(&mut self) -> &mut $repr { &mut self._repr }

            pub fn by_ref(&self) -> &$union<$crate::ByRef> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            pub fn by_mut(&mut self) -> &mut $union<$crate::ByMut> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            pub fn read(&self) -> &$union<$crate::Read> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            pub fn write(&mut self) -> &mut $union<$crate::Write> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
        }

        impl $union<$crate::ByRef> {
            $(pub unsafe fn $variant(&self) -> &$variant_ty {
                use $crate::_core;
                _core::mem::transmute(self)
            })+
        }

        impl $union<$crate::ByMut> {
            $(pub unsafe fn $variant(&mut self) -> &mut $variant_ty {
                use $crate::_core;
                _core::mem::transmute(self)
            })+
        }

        impl $union<$crate::Read> {
            $(pub unsafe fn $variant(&self) -> $variant_ty {
                use $crate::_core;
                _core::ptr::read(_core::mem::transmute(self))
            })+
        }

        impl $union<$crate::Write> {
            $(pub unsafe fn $variant(&mut self, v: $variant_ty){
                use $crate::_core;
                _core::ptr::write(_core::mem::transmute(self), v)
            })+
        }
    };

    (@impl_non_pub $union: ident; $repr:ty; $($variant:ident: $variant_ty:ty),+) => {
        unsafe_unions!(@impl_common $union; $repr; $($variant: $variant_ty),+);

        #[allow(unused, missing_docs)]
        impl $union {
            fn new() -> Self {
                use $crate::_core;
                $union{
                _repr: unsafe { _core::mem::uninitialized() },
                _phantom: _core::marker::PhantomData
                }
            }
            fn zeroed() -> Self {
                use $crate::_core;
                $union{
                    _repr: unsafe { _core::mem::zeroed() },
                    _phantom: _core::marker::PhantomData
                }
            }

            $(unsafe fn $variant(v: $variant_ty) -> Self {
                let mut ret = Self::new();
                ret.write().$variant(v);
                ret
            })+

            fn repr(&self) -> &$repr { &self._repr }
            fn repr_mut(&mut self) -> &mut $repr { &mut self._repr }

            fn by_ref(&self) -> &$union<$crate::ByRef> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            fn by_mut(&mut self) -> &mut $union<$crate::ByMut> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            fn read(&self) -> &$union<$crate::Read> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            fn write(&mut self) -> &mut $union<$crate::Write> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
        }

        #[allow(unused, missing_docs)]
        impl $union<$crate::ByRef> {
            $(unsafe fn $variant(&self) -> &$variant_ty {
                use $crate::_core;
                _core::mem::transmute(self)
            })+
        }

        #[allow(unused, missing_docs)]
        impl $union<$crate::ByMut> {
            $(unsafe fn $variant(&mut self) -> &mut $variant_ty {
                use $crate::_core;
                _core::mem::transmute(self)
            })+
        }

        #[allow(unused, missing_docs)]
        impl $union<$crate::Read> {
            $(unsafe fn $variant(&self) -> $variant_ty {
                use $crate::_core;
                _core::ptr::read(_core::mem::transmute(self))
            })+
        }

        #[allow(unused, missing_docs)]
        impl $union<$crate::Write> {
            $(unsafe fn $variant(&mut self, v: $variant_ty){
                use $crate::_core;
                _core::ptr::write(_core::mem::transmute(self), v)
            })+
        }
    };
}

<commit_msg>remove ability to specify meta-attributes (for now)<commit_after>#![no_std]

#[doc(hidden)]
pub mod _core {
    pub use core::*;
}

pub struct ByRef;
pub struct ByMut;
pub struct Read;
pub struct Write;

#[macro_export]
macro_rules! unsafe_unions {
    // endpoint
    () => {};

    // pub union
    (
        pub union $union:ident: $repr:ty {
            $($variant:ident: $variant_ty:ty),+
        }
        $($ff:tt)*
    ) => {
        pub struct $union<_Mode = ()> {
            _repr: $repr,
            _phantom: $crate::_core::marker::PhantomData<fn(_Mode)>,
        }

        unsafe_unions!(@impl_pub $union; $repr; $($variant: $variant_ty),+);
        unsafe_unions!($($ff)*);
    };

    // pub union (trailing comma)
    (
        pub union $union:ident: $repr:ty {
            $($variant:ident: $variant_ty:ty),+,
        }
        $($ff:tt)*
    ) => {
        pub struct $union<_Mode = ()> {
            _repr: $repr,
            _phantom: $crate::_core::marker::PhantomData<fn(_Mode)>,
        }

        unsafe_unions!(@impl_pub $union; $repr; $($variant: $variant_ty),+);
        unsafe_unions!($($ff)*);
    };

    // non-pub union
    (
        union $union:ident: $repr:ty {
            $($variant:ident: $variant_ty:ty),+
        }
        $($ff:tt)*
    ) => {
        struct $union<_Mode = ()> {
            _repr: $repr,
            _phantom: $crate::_core::marker::PhantomData<fn(_Mode)>,
        }

        unsafe_unions!(@impl_non_pub $union; $repr; $($variant: $variant_ty),+);
        unsafe_unions!($($ff)*);
    };

    // non-pub union (trailing comma)
    (
        union $union:ident: $repr:ty {
            $($variant:ident: $variant_ty:ty),+,
        }
        $($ff:tt)*
    ) => {
        struct $union<_Mode = ()> {
            _repr: $repr,
            _phantom: $crate::_core::marker::PhantomData<fn(_Mode)>,
        }

        unsafe_unions!(@impl_non_pub $union; $repr; $($variant: $variant_ty),+);
        unsafe_unions!($($ff)*);
    };

    (@impl_common $union: ident; $repr:ty; $($variant:ident: $variant_ty:ty),+) => {
        impl Clone for $union {
            fn clone(&self) -> Self {
                use $crate::_core;
                Self::_check_repr_type();
                $union{
                    _repr: unsafe { _core::ptr::read(&self._repr) },
                    _phantom: _core::marker::PhantomData
                }
            }
        }

        impl Copy for $union {}

        impl<_T> $union<_T> {
            #[doc(hidden)]
            fn _check_repr_type() {
                // Check repr-type size
                // Currently, we can only ensure this at run-time (Panics are preferable
                // to segfault, anyways).

                // TODO: errmsg is a bit too wordy.
                use $crate::_core::mem;
                $(
                    assert!(
                        mem::size_of::<$variant_ty>() <= mem::size_of::<$repr>(),
                        "union '{}': repr-type needs to be bigger or equal in \
                        size to the biggest field. \
                        '{}' is '{}' bytes, field '{}' of type '{}' is '{}' bytes",
                        stringify!($union),
                        stringify!($repr),
                        mem::size_of::<$repr>(),
                        stringify!($variant),
                        stringify!($variant_ty),
                        mem::size_of::<$variant_ty>(),
                    );
                )+
            }
        }
    };

    (@impl_pub $union: ident; $repr:ty; $($variant:ident: $variant_ty:ty),+) => {
        unsafe_unions!(@impl_common $union; $repr; $($variant: $variant_ty),+);

        impl $union {
            pub fn new() -> Self {
                use $crate::_core;
                Self::_check_repr_type();
                $union{
                    _repr: unsafe { _core::mem::uninitialized() },
                    _phantom: _core::marker::PhantomData
                }
            }
            pub fn zeroed() -> Self {
                use $crate::_core;
                Self::_check_repr_type();
                $union{
                    _repr: unsafe { _core::mem::zeroed() },
                    _phantom: _core::marker::PhantomData
                }
            }

            $(pub unsafe fn $variant(v: $variant_ty) -> Self {
                let mut ret = Self::new();
                ret.write().$variant(v);
                ret
            })+

            pub fn repr(&self) -> &$repr { &self._repr }
            pub fn repr_mut(&mut self) -> &mut $repr { &mut self._repr }

            pub fn by_ref(&self) -> &$union<$crate::ByRef> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            pub fn by_mut(&mut self) -> &mut $union<$crate::ByMut> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            pub fn read(&self) -> &$union<$crate::Read> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            pub fn write(&mut self) -> &mut $union<$crate::Write> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
        }

        impl $union<$crate::ByRef> {
            $(pub unsafe fn $variant(&self) -> &$variant_ty {
                use $crate::_core;
                _core::mem::transmute(self)
            })+
        }

        impl $union<$crate::ByMut> {
            $(pub unsafe fn $variant(&mut self) -> &mut $variant_ty {
                use $crate::_core;
                _core::mem::transmute(self)
            })+
        }

        impl $union<$crate::Read> {
            $(pub unsafe fn $variant(&self) -> $variant_ty {
                use $crate::_core;
                _core::ptr::read(_core::mem::transmute(self))
            })+
        }

        impl $union<$crate::Write> {
            $(pub unsafe fn $variant(&mut self, v: $variant_ty){
                use $crate::_core;
                _core::ptr::write(_core::mem::transmute(self), v)
            })+
        }
    };

    (@impl_non_pub $union: ident; $repr:ty; $($variant:ident: $variant_ty:ty),+) => {
        unsafe_unions!(@impl_common $union; $repr; $($variant: $variant_ty),+);

        #[allow(unused, missing_docs)]
        impl $union {
            fn new() -> Self {
                use $crate::_core;
                $union{
                _repr: unsafe { _core::mem::uninitialized() },
                _phantom: _core::marker::PhantomData
                }
            }
            fn zeroed() -> Self {
                use $crate::_core;
                $union{
                    _repr: unsafe { _core::mem::zeroed() },
                    _phantom: _core::marker::PhantomData
                }
            }

            $(unsafe fn $variant(v: $variant_ty) -> Self {
                let mut ret = Self::new();
                ret.write().$variant(v);
                ret
            })+

            fn repr(&self) -> &$repr { &self._repr }
            fn repr_mut(&mut self) -> &mut $repr { &mut self._repr }

            fn by_ref(&self) -> &$union<$crate::ByRef> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            fn by_mut(&mut self) -> &mut $union<$crate::ByMut> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            fn read(&self) -> &$union<$crate::Read> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
            fn write(&mut self) -> &mut $union<$crate::Write> {
                use $crate::_core;
                unsafe { _core::mem::transmute(self) }
            }
        }

        #[allow(unused, missing_docs)]
        impl $union<$crate::ByRef> {
            $(unsafe fn $variant(&self) -> &$variant_ty {
                use $crate::_core;
                _core::mem::transmute(self)
            })+
        }

        #[allow(unused, missing_docs)]
        impl $union<$crate::ByMut> {
            $(unsafe fn $variant(&mut self) -> &mut $variant_ty {
                use $crate::_core;
                _core::mem::transmute(self)
            })+
        }

        #[allow(unused, missing_docs)]
        impl $union<$crate::Read> {
            $(unsafe fn $variant(&self) -> $variant_ty {
                use $crate::_core;
                _core::ptr::read(_core::mem::transmute(self))
            })+
        }

        #[allow(unused, missing_docs)]
        impl $union<$crate::Write> {
            $(unsafe fn $variant(&mut self, v: $variant_ty){
                use $crate::_core;
                _core::ptr::write(_core::mem::transmute(self), v)
            })+
        }
    };
}

<|endoftext|>