From f06fae1602e857975ba3b3157ef103d38175c8db Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 21 Jun 2014 23:35:49 -0700 Subject: Implement Encoder/Decoder for libserialize traits --- Makefile | 2 +- src/serialization.rs | 726 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/show.rs | 170 ++++++++++++ src/toml.rs | 35 ++- 4 files changed, 929 insertions(+), 4 deletions(-) create mode 100644 src/serialization.rs create mode 100644 src/show.rs diff --git a/Makefile b/Makefile index c06948b..4500730 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ all: $(LIB) $(LIB): src/toml.rs @mkdir -p $(@D) - $(RUSTC) -O $< --out-dir $(@D) --dep-info + $(RUSTC) $< --out-dir $(@D) --dep-info check: $(TEST) doctest $(TEST) 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, +} + +/// 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) + } +} + +impl Decoder { + 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)))); + } +} 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 { + Parser::new(s).parse().map(Table) + } +} -- cgit v1.2.3