toml-rs/tests/serde.rs
Alex Crichton e256931e9b Rewrite crate with serde support from ground up
This commit completely rewrites this crate from the ground up,
supporting serde at the lowest levels as I believe serde support was
intended to do. This is a major change from the previous versions of
this crate, with a summary of changes being:

* Serialization directly to TOML is now supported without going through
  a `Value` first.

* Deserialization directly from TOML is now supported without going
  through a `Value`. Note that due to the TOML format some values still
  are buffered in intermediate memory, but overall this should be at a
  minimum now.

* The API of `Value` was overhauled to match the API of
  `serde_json::Value`. The changes here were to:

  * Add `is_*` accessors
  * Add `get` and `get_mut` for one-field lookups.
  * Implement panicking lookups through `Index`

  The old `index` methods are now gone in favor of `get` and `Index`
  implementations.

* A `Datetime` type has been added to represent a TOML datetime in a
  first-class fashion. Currently this type provides no accessors other
  than a `Display` implementation, but the idea is that this will grow
  support over time for decomposing the date.

* Support for the `rustc-serialize` crate has been dropped, that'll stay
  on the 0.2 and 0.1 release trains.

* This crate no longer supports the detection of unused fields, for that though
  you can use the `serde_ignored` crate on crates.io
2017-02-08 21:21:18 -08:00

497 lines
12 KiB
Rust

extern crate serde;
extern crate toml;
#[macro_use]
extern crate serde_derive;
use std::collections::{BTreeMap, HashSet};
use serde::{Deserialize, Deserializer};
use toml::Value;
use toml::Value::{Table, Integer, Array, Float};
macro_rules! t {
($e:expr) => (match $e {
Ok(t) => t,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
})
}
macro_rules! equivalent {
($literal:expr, $toml:expr,) => ({
let toml = $toml;
let literal = $literal;
// In/out of Value is equivalent
println!("try_from");
assert_eq!(t!(Value::try_from(literal.clone())), toml);
println!("try_into");
assert_eq!(literal, t!(toml.clone().try_into()));
// Through a string equivalent
println!("to_string(literal)");
assert_eq!(t!(toml::to_string(&literal)), toml.to_string());
println!("to_string(toml)");
assert_eq!(t!(toml::to_string(&toml)), toml.to_string());
println!("literal, from_str(toml)");
assert_eq!(literal, t!(toml::from_str(&toml.to_string())));
println!("toml, from_str(toml)");
assert_eq!(toml, t!(toml::from_str(&toml.to_string())));
})
}
macro_rules! error {
($ty:ty, $toml:expr, $error:expr) => ({
println!("attempting parsing");
match toml::from_str::<$ty>(&$toml.to_string()) {
Ok(_) => panic!("successful"),
Err(e) => {
assert!(e.to_string().contains($error),
"bad error: {}", e);
}
}
println!("attempting toml decoding");
match $toml.try_into::<$ty>() {
Ok(_) => panic!("successful"),
Err(e) => {
assert!(e.to_string().contains($error),
"bad error: {}", e);
}
}
})
}
macro_rules! decode( ($t:expr) => ({
t!($t.try_into())
}) );
macro_rules! map( ($($k:ident: $v:expr),*) => ({
let mut _m = BTreeMap::new();
$(_m.insert(stringify!($k).to_string(), $v);)*
_m
}) );
#[test]
fn smoke() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: isize }
equivalent!(
Foo { a: 2 },
Table(map! { a: Integer(2) }),
);
}
#[test]
fn smoke_hyphen() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo {
a_b: isize,
}
equivalent! {
Foo { a_b: 2 },
Table(map! { a_b: Integer(2) }),
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo2 {
#[serde(rename = "a-b")]
a_b: isize,
}
let mut m = BTreeMap::new();
m.insert("a-b".to_string(), Integer(2));
equivalent! {
Foo2 { a_b: 2 },
Table(m),
}
}
#[test]
fn nested() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: isize, b: Bar }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar { a: String }
equivalent! {
Foo { a: 2, b: Bar { a: "test".to_string() } },
Table(map! {
a: Integer(2),
b: Table(map! {
a: Value::String("test".to_string())
})
}),
}
}
#[test]
fn application_decode_error() {
#[derive(PartialEq, Debug)]
struct Range10(usize);
impl Deserialize for Range10 {
fn deserialize<D: Deserializer>(d: D) -> Result<Range10, D::Error> {
let x: usize = try!(Deserialize::deserialize(d));
if x > 10 {
Err(serde::de::Error::custom("more than 10"))
} else {
Ok(Range10(x))
}
}
}
let d_good = Integer(5);
let d_bad1 = Value::String("not an isize".to_string());
let d_bad2 = Integer(11);
assert_eq!(Range10(5), d_good.try_into().unwrap());
let err1: Result<Range10, _> = d_bad1.try_into();
assert!(err1.is_err());
let err2: Result<Range10, _> = d_bad2.try_into();
assert!(err2.is_err());
}
#[test]
fn array() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: Vec<isize> }
equivalent! {
Foo { a: vec![1, 2, 3, 4] },
Table(map! {
a: Array(vec![
Integer(1),
Integer(2),
Integer(3),
Integer(4)
])
}),
};
}
#[test]
fn inner_structs_with_options() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo {
a: Option<Box<Foo>>,
b: Bar,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar {
a: String,
b: f64,
}
equivalent! {
Foo {
a: Some(Box::new(Foo {
a: None,
b: Bar { a: "foo".to_string(), b: 4.5 },
})),
b: Bar { a: "bar".to_string(), b: 1.0 },
},
Table(map! {
a: Table(map! {
b: Table(map! {
a: Value::String("foo".to_string()),
b: Float(4.5)
})
}),
b: Table(map! {
a: Value::String("bar".to_string()),
b: Float(1.0)
})
}),
}
}
#[test]
fn hashmap() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo {
set: HashSet<char>,
map: BTreeMap<String, isize>,
}
equivalent! {
Foo {
map: {
let mut m = BTreeMap::new();
m.insert("foo".to_string(), 10);
m.insert("bar".to_string(), 4);
m
},
set: {
let mut s = HashSet::new();
s.insert('a');
s
},
},
Table(map! {
map: Table(map! {
foo: Integer(10),
bar: Integer(4)
}),
set: Array(vec![Value::String("a".to_string())])
}),
}
}
#[test]
fn table_array() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: Vec<Bar>, }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar { a: isize }
equivalent! {
Foo { a: vec![Bar { a: 1 }, Bar { a: 2 }] },
Table(map! {
a: Array(vec![
Table(map!{ a: Integer(1) }),
Table(map!{ a: Integer(2) }),
])
}),
}
}
#[test]
fn type_errors() {
#[derive(Deserialize)]
#[allow(dead_code)]
struct Foo { bar: isize }
error! {
Foo,
Table(map! {
bar: Value::String("a".to_string())
}),
"invalid type: string \"a\", expected isize for key `bar`"
}
#[derive(Deserialize)]
#[allow(dead_code)]
struct Bar { foo: Foo }
error! {
Bar,
Table(map! {
foo: Table(map! {
bar: Value::String("a".to_string())
})
}),
"invalid type: string \"a\", expected isize for key `foo.bar`"
}
}
#[test]
fn missing_errors() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { bar: isize }
error! {
Foo,
Table(map! { }),
"missing field `bar`"
}
}
#[test]
fn parse_enum() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: E }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(untagged)]
enum E {
Bar(isize),
Baz(String),
Last(Foo2),
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo2 {
test: String,
}
equivalent! {
Foo { a: E::Bar(10) },
Table(map! { a: Integer(10) }),
}
equivalent! {
Foo { a: E::Baz("foo".to_string()) },
Table(map! { a: Value::String("foo".to_string()) }),
}
equivalent! {
Foo { a: E::Last(Foo2 { test: "test".to_string() }) },
Table(map! { a: Table(map! { test: Value::String("test".to_string()) }) }),
}
}
// #[test]
// fn unused_fields() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: isize }
//
// let v = Foo { a: 2 };
// let mut d = Decoder::new(Table(map! {
// a, Integer(2),
// b, Integer(5)
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, Some(Table(map! {
// b, Integer(5)
// })));
// }
//
// #[test]
// fn unused_fields2() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: Bar }
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Bar { a: isize }
//
// let v = Foo { a: Bar { a: 2 } };
// let mut d = Decoder::new(Table(map! {
// a, Table(map! {
// a, Integer(2),
// b, Integer(5)
// })
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, Some(Table(map! {
// a, Table(map! {
// b, Integer(5)
// })
// })));
// }
//
// #[test]
// fn unused_fields3() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: Bar }
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Bar { a: isize }
//
// let v = Foo { a: Bar { a: 2 } };
// let mut d = Decoder::new(Table(map! {
// a, Table(map! {
// a, Integer(2)
// })
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields4() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: BTreeMap<String, String> }
//
// let v = Foo { a: map! { a, "foo".to_string() } };
// let mut d = Decoder::new(Table(map! {
// a, Table(map! {
// a, Value::String("foo".to_string())
// })
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields5() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: Vec<String> }
//
// let v = Foo { a: vec!["a".to_string()] };
// let mut d = Decoder::new(Table(map! {
// a, Array(vec![Value::String("a".to_string())])
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields6() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: Option<Vec<String>> }
//
// let v = Foo { a: Some(vec![]) };
// let mut d = Decoder::new(Table(map! {
// a, Array(vec![])
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields7() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: Vec<Bar> }
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Bar { a: isize }
//
// let v = Foo { a: vec![Bar { a: 1 }] };
// let mut d = Decoder::new(Table(map! {
// a, Array(vec![Table(map! {
// a, Integer(1),
// b, Integer(2)
// })])
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, Some(Table(map! {
// a, Array(vec![Table(map! {
// b, Integer(2)
// })])
// })));
// }
#[test]
fn empty_arrays() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: Vec<Bar> }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar;
equivalent! {
Foo { a: vec![] },
Table(map! {a: Array(Vec::new())}),
}
}
#[test]
fn empty_arrays2() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: Option<Vec<Bar>> }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar;
equivalent! {
Foo { a: None },
Table(map! {}),
}
equivalent!{
Foo { a: Some(vec![]) },
Table(map! { a: Array(vec![]) }),
}
}
#[test]
fn extra_keys() {
#[derive(Serialize, Deserialize)]
struct Foo { a: isize }
let toml = Table(map! { a: Integer(2), b: Integer(2) });
assert!(toml.clone().try_into::<Foo>().is_ok());
assert!(toml::from_str::<Foo>(&toml.to_string()).is_ok());
}