use std::collections::HashMap; use std::mem; use serialize; use {Value, Table, Array, String, Integer, Float, Boolean, Parser}; /// 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, } /// A structure to transform TOML values into Rust values. /// /// This decoder implements the serialization `Decoder` interface, allowing /// `Decodable` types to be generated by this decoder. The input is any /// arbitrary TOML value. pub struct Decoder { toml: Option, } /// 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), 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: &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: &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 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) } } /// Decodes a TOML value into a decodable type. /// /// This function will consume the given TOML value and attempt to decode it /// into the type specified. If decoding fails, `None` will be returned. If a /// finer-grained error is desired, then it is recommended to use `Decodable` /// directly. pub fn decode>(toml: Value) -> Option { serialize::Decodable::decode(&mut Decoder::new(toml)).ok() } /// Decodes a string into a toml-encoded value. /// /// This function will parse the given string into a TOML value, and then parse /// the TOML value into the desired type. If any error occurs `None` is return. /// If more fine-grained errors are desired, these steps should be driven /// manually. pub fn decode_str>(s: &str) -> Option { Parser::new(s).parse().and_then(|t| decode(Table(t))) } impl Decoder { /// Creates a new decoder, consuming the TOML value to decode. /// /// This decoder can be passed to the `Decodable` methods or driven /// manually. pub fn new(toml: Value) -> Decoder { Decoder { toml: Some(toml) } } } impl serialize::Decoder 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 { self.read_i64().map(|i| i as uint) } fn read_u64(&mut self) -> Result { self.read_i64().map(|i| i as u64) } fn read_u32(&mut self) -> Result { self.read_i64().map(|i| i as u32) } fn read_u16(&mut self) -> Result { self.read_i64().map(|i| i as u16) } fn read_u8(&mut self) -> Result { self.read_i64().map(|i| i as u8) } fn read_int(&mut self) -> Result { self.read_i64().map(|i| i as int) } fn read_i64(&mut self) -> Result { match self.toml { Some(Integer(i)) => Ok(i), _ => Err(InvalidType), } } fn read_i32(&mut self) -> Result { self.read_i64().map(|i| i as i32) } fn read_i16(&mut self) -> Result { self.read_i64().map(|i| i as i16) } fn read_i8(&mut self) -> Result { self.read_i64().map(|i| i as i8) } fn read_bool(&mut self) -> Result { match self.toml { Some(Boolean(b)) => Ok(b), _ => Err(InvalidType), } } fn read_f64(&mut self) -> Result { match self.toml { Some(Float(f)) => Ok(f), _ => Err(InvalidType), } } fn read_f32(&mut self) -> Result { self.read_f64().map(|f| f as f32) } fn read_char(&mut self) -> Result { 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 { match self.toml.take() { Some(String(s)) => Ok(s), toml => { self.toml = toml; Err(InvalidType) } } } // Compound types: fn read_enum(&mut self, _name: &str, _f: |&mut Decoder| -> Result) -> Result { fail!() } fn read_enum_variant(&mut self, _names: &[&str], _f: |&mut Decoder, uint| -> Result) -> Result { fail!() } fn read_enum_variant_arg(&mut self, _a_idx: uint, _f: |&mut Decoder| -> Result) -> Result { fail!() } fn read_enum_struct_variant(&mut self, _names: &[&str], _f: |&mut Decoder, uint| -> Result) -> Result { fail!() } fn read_enum_struct_variant_field(&mut self, _f_name: &str, _f_idx: uint, _f: |&mut Decoder| -> Result) -> Result { fail!() } fn read_struct(&mut self, _s_name: &str, _len: uint, f: |&mut Decoder| -> Result) -> Result { match self.toml { Some(Table(..)) => f(self), _ => Err(InvalidType), } } fn read_struct_field(&mut self, f_name: &str, _f_idx: uint, f: |&mut Decoder| -> Result) -> Result { 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(&mut self, f: |&mut Decoder, uint| -> Result) -> Result { self.read_seq(f) } fn read_tuple_arg(&mut self, a_idx: uint, f: |&mut Decoder| -> Result) -> Result { self.read_seq_elt(a_idx, f) } fn read_tuple_struct(&mut self, _s_name: &str, _f: |&mut Decoder, uint| -> Result) -> Result { fail!() } fn read_tuple_struct_arg(&mut self, _a_idx: uint, _f: |&mut Decoder| -> Result) -> Result { fail!() } // Specialized types: fn read_option(&mut self, f: |&mut Decoder, bool| -> Result) -> Result { match self.toml { Some(..) => f(self, true), None => f(self, false), } } fn read_seq(&mut self, f: |&mut Decoder, uint| -> Result) -> Result { let len = match self.toml { Some(Array(ref arr)) => arr.len(), _ => return Err(InvalidType), }; f(self, len) } fn read_seq_elt(&mut self, idx: uint, f: |&mut Decoder| -> Result) -> Result { 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(&mut self, f: |&mut Decoder, uint| -> Result) -> Result { let len = match self.toml { Some(Table(ref table)) => table.len(), _ => return Err(InvalidType), }; f(self, len) } fn read_map_elt_key(&mut self, idx: uint, f: |&mut Decoder| -> Result) -> Result { 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(&mut self, idx: uint, f: |&mut Decoder| -> Result) -> Result { 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 } 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>, 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, set: HashSet, } 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)))); } }