diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-06-21 23:35:49 -0700 | 
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2014-06-22 22:23:27 -0700 | 
| commit | f06fae1602e857975ba3b3157ef103d38175c8db (patch) | |
| tree | 0d29f7f43ea86f62c919a21ff5ea6763830bcb40 /src | |
| parent | 3bbf216491b01a72398c26689c9ea14a5810adfc (diff) | |
| download | milf-rs-f06fae1602e857975ba3b3157ef103d38175c8db.tar.gz milf-rs-f06fae1602e857975ba3b3157ef103d38175c8db.zip | |
Implement Encoder/Decoder for libserialize traits
Diffstat (limited to 'src')
| -rw-r--r-- | src/serialization.rs | 726 | ||||
| -rw-r--r-- | src/show.rs | 170 | ||||
| -rw-r--r-- | src/toml.rs | 35 | 
3 files changed, 928 insertions, 3 deletions
| diff --git a/src/serialization.rs b/src/serialization.rs new file mode 100644 index 0000000..515e8dd --- /dev/null +++ b/src/serialization.rs @@ -0,0 +1,726 @@ +#![allow(warnings)] + +use std::collections::HashMap; +use std::mem; + +use serialize; +use {Value, Table, Array, String, Integer, Float, Boolean}; + +/// A structure to transform Rust values into TOML values. +/// +/// This encoder implements the serialization `Encoder` interface, allowing +/// `Encodable` rust types to be fed into the encoder. The output of this +/// encoder is a TOML `Table` structure. The resulting TOML can be stringified +/// if necessary. +/// +/// # Example +/// +/// ``` +/// extern crate serialize; +/// extern crate toml; +/// +/// # fn main() { +/// use toml::{Encoder, Integer}; +/// use serialize::Encodable; +/// +/// #[deriving(Encodable)] +/// struct MyStruct { foo: int, bar: String } +/// let my_struct = MyStruct { foo: 4, bar: "hello!".to_string() }; +/// +/// let mut e = Encoder::new(); +/// my_struct.encode(&mut e).unwrap(); +/// +/// assert_eq!(e.toml.find_equiv(&"foo"), Some(&Integer(4))) +/// # } +/// ``` +pub struct Encoder { +    /// Output TOML that is emitted. The current version of this encoder forces +    /// the top-level representation of a structure to be a table. +    /// +    /// This field can be used to extract the return value after feeding a value +    /// into this `Encoder`. +    pub toml: Table, +    state: EncoderState, +} + +pub struct Decoder { +    toml: Option<Value>, +} + +/// Enumeration of errors which can occur while encoding a rust value into a +/// TOML value. +#[deriving(Show)] +pub enum Error { +    /// Indication that a key was needed when a value was emitted, but no key +    /// was previously emitted. +    NeedsKey, +    /// Indication that a key was emitted, but not value was emitted. +    NoValue, +    /// Indicates that a map key was attempted to be emitted at an invalid +    /// location. +    InvalidMapKeyLocation, +    /// Indicates that a type other than a string was attempted to be used as a +    /// map key type. +    InvalidMapKeyType, +    /// Indicates that a type was decoded against a TOML value of a different +    /// type. +    InvalidType, +    /// Indicates that a field was attempted to be read that does not exist. +    MissingField, +} + +#[deriving(PartialEq, Show)] +enum EncoderState { +    Start, +    NextKey(String), +    NextArray(Vec<Value>), +    NextMapKey, +} + +/// Encodes an encodable value into a TOML value. +/// +/// This function expects the type given to represent a TOML table in some form. +/// If encoding encounters an error, then this function will fail the task. +pub fn encode<T: serialize::Encodable<Encoder, Error>>(t: &T) -> Value { +    let mut e = Encoder::new(); +    t.encode(&mut e).unwrap(); +    Table(e.toml) +} + +/// Encodes an encodable value into a TOML string. +/// +/// This function expects the type given to represent a TOML table in some form. +/// If encoding encounters an error, then this function will fail the task. +pub fn encode_str<T: serialize::Encodable<Encoder, Error>>(t: &T) -> String { +    format!("{}", encode(t)) +} + +impl Encoder { +    /// Constructs a new encoder which will emit to the given output stream. +    pub fn new() -> Encoder { +        Encoder { state: Start, toml: HashMap::new() } +    } + +    fn emit_value(&mut self, v: Value) -> Result<(), Error> { +        match mem::replace(&mut self.state, Start) { +            NextKey(key) => { self.toml.insert(key, v); Ok(()) } +            NextArray(mut vec) => { +                // TODO: validate types +                vec.push(v); +                self.state = NextArray(vec); +                Ok(()) +            } +            NextMapKey => { +                match v { +                    String(s) => { self.state = NextKey(s); Ok(()) } +                    _ => Err(InvalidMapKeyType) +                } +            } +            _ => Err(NeedsKey) +        } +    } +} + +impl serialize::Encoder<Error> for Encoder { +    fn emit_nil(&mut self) -> Result<(), Error> { Ok(()) } +    fn emit_uint(&mut self, v: uint) -> Result<(), Error> { +        self.emit_i64(v as i64) +    } +    fn emit_u8(&mut self, v: u8) -> Result<(), Error> { +        self.emit_i64(v as i64) +    } +    fn emit_u16(&mut self, v: u16) -> Result<(), Error> { +        self.emit_i64(v as i64) +    } +    fn emit_u32(&mut self, v: u32) -> Result<(), Error> { +        self.emit_i64(v as i64) +    } +    fn emit_u64(&mut self, v: u64) -> Result<(), Error> { +        self.emit_i64(v as i64) +    } +    fn emit_int(&mut self, v: int) -> Result<(), Error> { +        self.emit_i64(v as i64) +    } +    fn emit_i8(&mut self, v: i8) -> Result<(), Error> { +        self.emit_i64(v as i64) +    } +    fn emit_i16(&mut self, v: i16) -> Result<(), Error> { +        self.emit_i64(v as i64) +    } +    fn emit_i32(&mut self, v: i32) -> Result<(), Error> { +        self.emit_i64(v as i64) +    } +    fn emit_i64(&mut self, v: i64) -> Result<(), Error> { +        self.emit_value(Integer(v)) +    } +    fn emit_bool(&mut self, v: bool) -> Result<(), Error> { +        self.emit_value(Boolean(v)) +    } +    fn emit_f32(&mut self, v: f32) -> Result<(), Error> { self.emit_f64(v as f64) } +    fn emit_f64(&mut self, v: f64) -> Result<(), Error> { +        self.emit_value(Float(v)) +    } +    fn emit_char(&mut self, v: char) -> Result<(), Error> { +        self.emit_str(v.to_str().as_slice()) +    } +    fn emit_str(&mut self, v: &str) -> Result<(), Error> { +        self.emit_value(String(v.to_str())) +    } +    fn emit_enum(&mut self, _name: &str, +                 _f: |&mut Encoder| -> Result<(), Error>) -> Result<(), Error> { +        fail!() +    } +    fn emit_enum_variant(&mut self, _v_name: &str, _v_id: uint, _len: uint, +                         _f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        fail!() +    } +    fn emit_enum_variant_arg(&mut self, _a_idx: uint, +                             _f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        fail!() +    } +    fn emit_enum_struct_variant(&mut self, _v_name: &str, _v_id: uint, +                                _len: uint, +                                _f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        fail!() +    } +    fn emit_enum_struct_variant_field(&mut self, _f_name: &str, _f_idx: uint, +                                      _f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        fail!() +    } +    fn emit_struct(&mut self, _name: &str, _len: uint, +                   f: |&mut Encoder| -> Result<(), Error>) -> Result<(), Error> { +        match mem::replace(&mut self.state, Start) { +            NextKey(key) => { +                let mut nested = Encoder::new(); +                try!(f(&mut nested)); +                self.toml.insert(key, Table(nested.toml)); +                Ok(()) +            } +            NextArray(mut arr) => { +                let mut nested = Encoder::new(); +                try!(f(&mut nested)); +                arr.push(Table(nested.toml)); +                self.state = NextArray(arr); +                Ok(()) +            } +            Start => f(self), +            NextMapKey => Err(InvalidMapKeyLocation), +        } +    } +    fn emit_struct_field(&mut self, f_name: &str, _f_idx: uint, +                         f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        let old = mem::replace(&mut self.state, NextKey(f_name.to_str())); +        try!(f(self)); +        if self.state != Start { +            println!("{}", self.state); +            return Err(NoValue) +        } +        self.state = old; +        Ok(()) +    } +    fn emit_tuple(&mut self, len: uint, +                  f: |&mut Encoder| -> Result<(), Error>) -> Result<(), Error> { +        self.emit_seq(len, f) +    } +    fn emit_tuple_arg(&mut self, idx: uint, +                      f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        self.emit_seq_elt(idx, f) +    } +    fn emit_tuple_struct(&mut self, _name: &str, _len: uint, +                         _f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        unimplemented!() +    } +    fn emit_tuple_struct_arg(&mut self, _f_idx: uint, +                             _f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        unimplemented!() +    } +    fn emit_option(&mut self, +                   f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        f(self) +    } +    fn emit_option_none(&mut self) -> Result<(), Error> { +        match mem::replace(&mut self.state, Start) { +            Start => unreachable!(), +            NextKey(_) => Ok(()), +            NextArray(..) => fail!("how to encode None in an array?"), +            NextMapKey => Err(InvalidMapKeyLocation), +        } +    } +    fn emit_option_some(&mut self, +                        f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        f(self) +    } +    fn emit_seq(&mut self, _len: uint, +                f: |this: &mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        let old = mem::replace(&mut self.state, NextArray(Vec::new())); +        try!(f(self)); +        match mem::replace(&mut self.state, old) { +            NextArray(v) => self.emit_value(Array(v)), +            _ => unreachable!(), +        } +    } +    fn emit_seq_elt(&mut self, _idx: uint, +                    f: |this: &mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        f(self) +    } +    fn emit_map(&mut self, len: uint, +                f: |&mut Encoder| -> Result<(), Error>) -> Result<(), Error> { +        self.emit_struct("foo", len, f) +    } +    fn emit_map_elt_key(&mut self, _idx: uint, +                        f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        match mem::replace(&mut self.state, NextMapKey) { +            Start => {} +            _ => return Err(InvalidMapKeyLocation), +        } +        try!(f(self)); +        match self.state { +            NextKey(_) => Ok(()), +            _ => Err(InvalidMapKeyLocation), +        } +    } +    fn emit_map_elt_val(&mut self, _idx: uint, +                        f: |&mut Encoder| -> Result<(), Error>) +        -> Result<(), Error> +    { +        f(self) +    } +} + +impl Decoder { +    pub fn new(toml: Value) -> Decoder { +        Decoder { toml: Some(toml) } +    } +} + +impl serialize::Decoder<Error> for Decoder { +    fn read_nil(&mut self) -> Result<(), Error> { +        match self.toml { +            Some(String(ref s)) if s.len() == 0 => Ok(()), +            _ => Err(InvalidType), +        } +    } +    fn read_uint(&mut self) -> Result<uint, Error> { +        self.read_i64().map(|i| i as uint) +    } +    fn read_u64(&mut self) -> Result<u64, Error> { +        self.read_i64().map(|i| i as u64) +    } +    fn read_u32(&mut self) -> Result<u32, Error> { +        self.read_i64().map(|i| i as u32) +    } +    fn read_u16(&mut self) -> Result<u16, Error> { +        self.read_i64().map(|i| i as u16) +    } +    fn read_u8(&mut self) -> Result<u8, Error> { +        self.read_i64().map(|i| i as u8) +    } +    fn read_int(&mut self) -> Result<int, Error> { +        self.read_i64().map(|i| i as int) +    } +    fn read_i64(&mut self) -> Result<i64, Error> { +        match self.toml { +            Some(Integer(i)) => Ok(i), +            _ => Err(InvalidType), +        } +    } +    fn read_i32(&mut self) -> Result<i32, Error> { +        self.read_i64().map(|i| i as i32) +    } +    fn read_i16(&mut self) -> Result<i16, Error> { +        self.read_i64().map(|i| i as i16) +    } +    fn read_i8(&mut self) -> Result<i8, Error> { +        self.read_i64().map(|i| i as i8) +    } +    fn read_bool(&mut self) -> Result<bool, Error> { +        match self.toml { +            Some(Boolean(b)) => Ok(b), +            _ => Err(InvalidType), +        } +    } +    fn read_f64(&mut self) -> Result<f64, Error> { +        match self.toml { +            Some(Float(f)) => Ok(f), +            _ => Err(InvalidType), +        } +    } +    fn read_f32(&mut self) -> Result<f32, Error> { +        self.read_f64().map(|f| f as f32) +    } +    fn read_char(&mut self) -> Result<char, Error> { +        match self.toml { +            Some(String(ref s)) if s.as_slice().char_len() == 1 => +                Ok(s.as_slice().char_at(0)), +            _ => Err(InvalidType), +        } +    } +    fn read_str(&mut self) -> Result<String, Error> { +        match self.toml.take() { +            Some(String(s)) => Ok(s), +            toml => { self.toml = toml; Err(InvalidType) } +        } +    } + +    // Compound types: +    fn read_enum<T>(&mut self, name: &str, +                    f: |&mut Decoder| -> Result<T, Error>) -> Result<T, Error> { +        fail!() +    } + +    fn read_enum_variant<T>(&mut self, +                            names: &[&str], +                            f: |&mut Decoder, uint| -> Result<T, Error>) +                            -> Result<T, Error> { +        fail!() +    } +    fn read_enum_variant_arg<T>(&mut self, +                                a_idx: uint, +                                f: |&mut Decoder| -> Result<T, Error>) +                                -> Result<T, Error> { +        fail!() +    } + +    fn read_enum_struct_variant<T>(&mut self, +                                   names: &[&str], +                                   f: |&mut Decoder, uint| -> Result<T, Error>) +                                   -> Result<T, Error> { +        fail!() +    } +    fn read_enum_struct_variant_field<T>(&mut self, +                                         f_name: &str, +                                         f_idx: uint, +                                         f: |&mut Decoder| -> Result<T, Error>) +                                         -> Result<T, Error> { +        fail!() +    } + +    fn read_struct<T>(&mut self, _s_name: &str, _len: uint, +                      f: |&mut Decoder| -> Result<T, Error>) +        -> Result<T, Error> +    { +        match self.toml { +            Some(Table(..)) => f(self), +            _ => Err(InvalidType), +        } +    } +    fn read_struct_field<T>(&mut self, +                            f_name: &str, +                            f_idx: uint, +                            f: |&mut Decoder| -> Result<T, Error>) +                            -> Result<T, Error> { +        match self.toml { +            Some(Table(ref mut table)) => { +                match table.pop(&f_name.to_string()) { +                    Some(field) => f(&mut Decoder::new(field)), +                    None => f(&mut Decoder { toml: None }), +                } +            } +            _ => Err(InvalidType) +        } +    } + +    fn read_tuple<T>(&mut self, +                     f: |&mut Decoder, uint| -> Result<T, Error>) +        -> Result<T, Error> +    { +        self.read_seq(f) +    } +    fn read_tuple_arg<T>(&mut self, a_idx: uint, +                         f: |&mut Decoder| -> Result<T, Error>) +        -> Result<T, Error> +    { +        self.read_seq_elt(a_idx, f) +    } + +    fn read_tuple_struct<T>(&mut self, +                            s_name: &str, +                            f: |&mut Decoder, uint| -> Result<T, Error>) +        -> Result<T, Error> +    { +        fail!() +    } +    fn read_tuple_struct_arg<T>(&mut self, +                                a_idx: uint, +                                f: |&mut Decoder| -> Result<T, Error>) +        -> Result<T, Error> +    { +        fail!() +    } + +    // Specialized types: +    fn read_option<T>(&mut self, +                      f: |&mut Decoder, bool| -> Result<T, Error>) +        -> Result<T, Error> +    { +        match self.toml { +            Some(..) => f(self, true), +            None => f(self, false), +        } +    } + +    fn read_seq<T>(&mut self, f: |&mut Decoder, uint| -> Result<T, Error>) +        -> Result<T, Error> +    { +        let len = match self.toml { +            Some(Array(ref arr)) => arr.len(), +            _ => return Err(InvalidType), +        }; +        f(self, len) +    } +    fn read_seq_elt<T>(&mut self, idx: uint, f: |&mut Decoder| -> Result<T, Error>) +        -> Result<T, Error> +    { +        match self.toml { +            Some(Array(ref mut arr)) => { +                f(&mut Decoder::new(mem::replace(arr.get_mut(idx), Integer(0)))) +            } +            _ => Err(InvalidType), +        } +    } + +    fn read_map<T>(&mut self, f: |&mut Decoder, uint| -> Result<T, Error>) +        -> Result<T, Error> +    { +        let len = match self.toml { +            Some(Table(ref table)) => table.len(), +            _ => return Err(InvalidType), +        }; +        f(self, len) +    } +    fn read_map_elt_key<T>(&mut self, idx: uint, +                           f: |&mut Decoder| -> Result<T, Error>) +        -> Result<T, Error> +    { +        match self.toml { +            Some(Table(ref table)) => { +                match table.keys().skip(idx).next() { +                    Some(key) => { +                        f(&mut Decoder::new(String(key.to_str()))) +                    } +                    None => Err(InvalidType), +                } +            } +            _ => Err(InvalidType), +        } +    } +    fn read_map_elt_val<T>(&mut self, idx: uint, +                           f: |&mut Decoder| -> Result<T, Error>) +        -> Result<T, Error> +    { +        match self.toml { +            Some(Table(ref table)) => { +                match table.values().skip(idx).next() { +                    Some(key) => { +                        // XXX: this shouldn't clone +                        f(&mut Decoder::new(key.clone())) +                    } +                    None => Err(InvalidType), +                } +            } +            _ => Err(InvalidType), +        } +    } +} + +#[cfg(test)] +mod tests { +    use std::collections::{HashMap, HashSet}; +    use serialize::{Encodable, Decodable}; + +    use super::{Encoder, Decoder}; +    use {Table, Integer, String, Array, Float}; + +    macro_rules! encode( ($t:expr) => ({ +        let mut e = Encoder::new(); +        $t.encode(&mut e).unwrap(); +        e.toml +    }) ) + +    macro_rules! decode( ($t:expr) => ({ +        let mut d = Decoder::new($t); +        Decodable::decode(&mut d).unwrap() +    }) ) + +    macro_rules! map( ($($k:ident: $v:expr),*) => ({ +        let mut _m = HashMap::new(); +        $(_m.insert(stringify!($k).to_str(), $v);)* +        _m +    }) ) + +    #[test] +    fn smoke() { +        #[deriving(Encodable, Decodable, PartialEq, Show)] +        struct Foo { a: int } + +        let v = Foo { a: 2 }; +        assert_eq!(encode!(v), map! { a: Integer(2) }); +        assert_eq!(v, decode!(Table(encode!(v)))); +    } + +    #[test] +    fn nested() { +        #[deriving(Encodable, Decodable, PartialEq, Show)] +        struct Foo { a: int, b: Bar } +        #[deriving(Encodable, Decodable, PartialEq, Show)] +        struct Bar { a: String } + +        let v = Foo { a: 2, b: Bar { a: "test".to_string() } }; +        assert_eq!(encode!(v), +                   map! { +                       a: Integer(2), +                       b: Table(map! { +                           a: String("test".to_string()) +                       }) +                   }); +        assert_eq!(v, decode!(Table(encode!(v)))); +    } + +    #[test] +    fn array() { +        #[deriving(Encodable, Decodable, PartialEq, Show)] +        struct Foo { a: Vec<int> } + +        let v = Foo { a: vec![1, 2, 3, 4] }; +        assert_eq!(encode!(v), +                   map! { +                       a: Array(vec![ +                            Integer(1), +                            Integer(2), +                            Integer(3), +                            Integer(4) +                       ]) +                   }); +        assert_eq!(v, decode!(Table(encode!(v)))); +    } + +    #[test] +    fn tuple() { +        #[deriving(Encodable, Decodable, PartialEq, Show)] +        struct Foo { a: (int, int, int, int) } + +        let v = Foo { a: (1, 2, 3, 4) }; +        assert_eq!(encode!(v), +                   map! { +                       a: Array(vec![ +                            Integer(1), +                            Integer(2), +                            Integer(3), +                            Integer(4) +                       ]) +                   }); +        assert_eq!(v, decode!(Table(encode!(v)))); +    } + +    #[test] +    fn inner_structs_with_options() { +        #[deriving(Encodable, Decodable, PartialEq, Show)] +        struct Foo { +            a: Option<Box<Foo>>, +            b: Bar, +        } +        #[deriving(Encodable, Decodable, PartialEq, Show)] +        struct Bar { +            a: String, +            b: f64, +        } + +        let v = Foo { +            a: Some(box Foo { +                a: None, +                b: Bar { a: "foo".to_string(), b: 4.5 }, +            }), +            b: Bar { a: "bar".to_string(), b: 1.0 }, +        }; +        assert_eq!(encode!(v), +                   map! { +                       a: Table(map! { +                           b: Table(map! { +                               a: String("foo".to_string()), +                               b: Float(4.5) +                           }) +                       }), +                       b: Table(map! { +                           a: String("bar".to_string()), +                           b: Float(1.0) +                       }) +                   }); +        assert_eq!(v, decode!(Table(encode!(v)))); +    } + +    #[test] +    fn hashmap() { +        #[deriving(Encodable, Decodable, PartialEq, Show)] +        struct Foo { +            map: HashMap<String, int>, +            set: HashSet<char>, +        } + +        let v = Foo { +            map: { +                let mut m = HashMap::new(); +                m.insert("foo".to_string(), 10); +                m.insert("bar".to_string(), 4); +                m +            }, +            set: { +                let mut s = HashSet::new(); +                s.insert('a'); +                s +            }, +        }; +        assert_eq!(encode!(v), +            map! { +                map: Table(map! { +                    foo: Integer(10), +                    bar: Integer(4) +                }), +                set: Array(vec![String("a".to_str())]) +            } +        ); +        assert_eq!(v, decode!(Table(encode!(v)))); +    } + +    #[test] +    fn tuple_struct() { +        #[deriving(Encodable, Decodable, PartialEq, Show)] +        struct Foo(int, String, f64); + +        let v = Foo(1, "foo".to_string(), 4.5); +        assert_eq!( +            encode!(v), +            map! { +                _field0: Integer(1), +                _field1: String("foo".to_string()), +                _field2: Float(4.5) +            } +        ); +        assert_eq!(v, decode!(Table(encode!(v)))); +    } +} diff --git a/src/show.rs b/src/show.rs new file mode 100644 index 0000000..cde4773 --- /dev/null +++ b/src/show.rs @@ -0,0 +1,170 @@ +use std::fmt; + +use {Value, String, Integer, Float, Boolean, Datetime, Array, Table}; + +struct Printer<'a, 'b> { +    output: &'a mut fmt::Formatter<'b>, +    stack: Vec<&'a str>, +} + +impl fmt::Show for Value { +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +        match *self { +            String(ref s) => { +                try!(write!(f, "\"")); +                for ch in s.as_slice().chars() { +                    match ch { +                        '\u0008' => try!(write!(f, "\\b")), +                        '\u0009' => try!(write!(f, "\\t")), +                        '\u000a' => try!(write!(f, "\\n")), +                        '\u000c' => try!(write!(f, "\\f")), +                        '\u000d' => try!(write!(f, "\\r")), +                        '\u0022' => try!(write!(f, "\\\"")), +                        '\u002f' => try!(write!(f, "\\/")), +                        '\u005c' => try!(write!(f, "\\\\")), +                        ch => try!(write!(f, "{}", ch)), +                    } +                } +                write!(f, "\"") +            } +            Integer(i) => write!(f, "{}", i), +            Float(fp) => { +                try!(write!(f, "{}", fp)); +                if fp % 1.0 == 0.0 { try!(write!(f, ".0")) } +                Ok(()) +            } +            Boolean(b) => write!(f, "{}", b), +            Datetime(ref s) => write!(f, "{}", s), +            Array(ref a) => { +                match a.as_slice().head() { +                    Some(&Table(..)) => fail!("invalid toml array of tables"), +                    _ => {} +                } +                write!(f, "{}", a) +            } +            Table(ref t) => { +                let mut p = Printer { output: f, stack: Vec::new() }; +                p.print(t) +            } +        } +    } +} + +impl<'a, 'b> Printer<'a, 'b> { +    fn print(&mut self, table: &'a Table) -> fmt::Result { +        for (k, v) in table.iter() { +            match *v { +                Table(..) => continue, +                Array(ref a) => { +                    match a.as_slice().head() { +                        Some(&Table(..)) => continue, +                        _ => {} +                    } +                } +                _ => {} +            } +            try!(writeln!(self.output, "{} = {}", k, v)); +        } +        for (k, v) in table.iter() { +            match *v { +                Table(ref inner) => { +                    self.stack.push(k.as_slice()); +                    try!(writeln!(self.output, "\n[{}]", +                                  self.stack.connect("."))); +                    try!(self.print(inner)); +                    self.stack.pop(); +                } +                Array(ref inner) => { +                    match inner.as_slice().head() { +                        Some(&Table(..)) => {} +                        _ => continue +                    } +                    self.stack.push(k.as_slice()); +                    for inner in inner.iter() { +                        try!(writeln!(self.output, "\n[[{}]]", +                                      self.stack.connect("."))); +                        match *inner { +                            Table(ref inner) => try!(self.print(inner)), +                            _ => fail!("non-heterogeneous toml array"), +                        } +                    } +                    self.stack.pop(); +                } +                _ => {}, +            } +        } +        Ok(()) +    } +} + +#[cfg(test)] +#[allow(warnings)] +mod tests { +    use {Value, String, Integer, Float, Boolean, Datetime, Array, Table}; +    use std::collections::HashMap; + +    macro_rules! map( ($($k:expr: $v:expr),*) => ({ +        let mut _m = HashMap::new(); +        $(_m.insert($k.to_str(), $v);)* +        _m +    }) ) + +    #[test] +    fn simple_show() { +        assert_eq!(String("foo".to_str()).to_str().as_slice(), +                   "\"foo\"") +        assert_eq!(Integer(10).to_str().as_slice(), +                   "10") +        assert_eq!(Float(10.0).to_str().as_slice(), +                   "10.0") +        assert_eq!(Float(2.4).to_str().as_slice(), +                   "2.4") +        assert_eq!(Boolean(true).to_str().as_slice(), +                   "true") +        assert_eq!(Datetime("test".to_str()).to_str().as_slice(), +                   "test") +        assert_eq!(Array(vec![]).to_str().as_slice(), +                   "[]") +        assert_eq!(Array(vec![Integer(1), Integer(2)]).to_str().as_slice(), +                   "[1, 2]") +    } + +    #[test] +    fn table() { +        assert_eq!(Table(map! { }).to_str().as_slice(), +                   "") +        assert_eq!(Table(map! { "test": Integer(2) }).to_str().as_slice(), +                   "test = 2\n") +        assert_eq!(Table(map! { +                        "test": Integer(2), +                        "test2": Table(map! { +                            "test": String("wut".to_str()) +                        }) +                   }).to_str().as_slice(), +                   "test = 2\n\ +                    \n\ +                    [test2]\n\ +                    test = \"wut\"\n") +        assert_eq!(Table(map! { +                        "test": Integer(2), +                        "test2": Table(map! { +                            "test": String("wut".to_str()) +                        }) +                   }).to_str().as_slice(), +                   "test = 2\n\ +                    \n\ +                    [test2]\n\ +                    test = \"wut\"\n") +        assert_eq!(Table(map! { +                        "test": Integer(2), +                        "test2": Array(vec![Table(map! { +                            "test": String("wut".to_str()) +                        })]) +                   }).to_str().as_slice(), +                   "test = 2\n\ +                    \n\ +                    [[test2]]\n\ +                    test = \"wut\"\n") +    } +} + diff --git a/src/toml.rs b/src/toml.rs index 74a4d36..7c75085 100644 --- a/src/toml.rs +++ b/src/toml.rs @@ -18,23 +18,46 @@  //! println!("{}", value);  //! ```  //! +//! # Conversions +//! +//! This library also supports using the standard `Encodable` and `Decodable` +//! traits with TOML values. This library provides the following conversion +//! capabilities: +//! +//! * `String` => `toml::Value` - via `Parser` +//! * `toml::Value` => `String` - via `Show` +//! * `toml::Value` => rust object - via `Decoder` +//! * rust object => `toml::Value` - via `Encoder` +//! +//! Convenience functions for performing multiple conversions at a time are also +//! provided. +//!  //! [1]: https://github.com/mojombo/toml  //! [2]: https://github.com/BurntSushi/toml-test  #![crate_type = "lib"]  #![feature(macro_rules)]  #![deny(warnings, missing_doc)] +#![allow(visible_private_types)] + +extern crate serialize;  use std::collections::HashMap; +use std::from_str::FromStr;  pub use parser::{Parser, Error}; +pub use serialization::{Encoder, encode, encode_str}; +// pub use serialization::{Encoder, encode, encode_str}; +pub use serialization::{Error, NeedsKey, NoValue}; +pub use serialization::{InvalidMapKeyLocation, InvalidMapKeyType};  mod parser; -#[cfg(test)] -mod test; +mod show; +mod serialization; +#[cfg(test)] mod test;  /// Representation of a TOML value. -#[deriving(Show, PartialEq, Clone)] +#[deriving(PartialEq, Clone)]  #[allow(missing_doc)]  pub enum Value {      String(String), @@ -120,3 +143,9 @@ impl Value {          match *self { Table(ref s) => Some(s), _ => None }      }  } + +impl FromStr for Value { +    fn from_str(s: &str) -> Option<Value> { +        Parser::new(s).parse().map(Table) +    } +} |