| #![allow(renamed_and_removed_lints)] |
| #![allow(clippy::blacklisted_name)] |
| |
| use std::collections::HashMap; |
| use std::fmt::Debug; |
| |
| use serde::Deserialize; |
| use snapbox::assert_data_eq; |
| use snapbox::prelude::*; |
| use snapbox::str; |
| use toml::value::Datetime; |
| use toml::Spanned; |
| |
| /// A set of good datetimes. |
| pub(crate) fn good_datetimes() -> Vec<&'static str> { |
| vec![ |
| "1997-09-09T09:09:09Z", |
| "1997-09-09T09:09:09+09:09", |
| "1997-09-09T09:09:09-09:09", |
| "1997-09-09T09:09:09", |
| "1997-09-09", |
| "09:09:09", |
| "1997-09-09T09:09:09.09Z", |
| "1997-09-09T09:09:09.09+09:09", |
| "1997-09-09T09:09:09.09-09:09", |
| "1997-09-09T09:09:09.09", |
| "09:09:09.09", |
| ] |
| } |
| |
| #[test] |
| fn test_spanned_field() { |
| #[derive(Deserialize)] |
| struct Foo<T> { |
| foo: Spanned<T>, |
| } |
| |
| #[derive(Deserialize)] |
| struct BareFoo<T> { |
| foo: T, |
| } |
| |
| fn good<T>(s: &str, expected: &str, end: Option<usize>) |
| where |
| T: serde::de::DeserializeOwned + Debug + PartialEq, |
| { |
| let foo: Foo<T> = toml::from_str(s).unwrap(); |
| |
| assert_eq!(6, foo.foo.span().start); |
| if let Some(end) = end { |
| assert_eq!(end, foo.foo.span().end); |
| } else { |
| assert_eq!(s.len(), foo.foo.span().end); |
| } |
| assert_eq!(expected, &s[foo.foo.span()]); |
| |
| // Test for Spanned<> at the top level |
| let foo_outer: Spanned<BareFoo<T>> = toml::from_str(s).unwrap(); |
| |
| assert_eq!(0, foo_outer.span().start); |
| assert_eq!(s.len(), foo_outer.span().end); |
| assert_eq!(foo.foo.into_inner(), foo_outer.into_inner().foo); |
| } |
| |
| good::<String>("foo = \"foo\"", "\"foo\"", None); |
| good::<u32>("foo = 42", "42", None); |
| // leading plus |
| good::<u32>("foo = +42", "+42", None); |
| // table |
| good::<HashMap<String, u32>>( |
| "foo = {\"foo\" = 42, \"bar\" = 42}", |
| "{\"foo\" = 42, \"bar\" = 42}", |
| None, |
| ); |
| // array |
| good::<Vec<u32>>("foo = [0, 1, 2, 3, 4]", "[0, 1, 2, 3, 4]", None); |
| // datetime |
| good::<String>( |
| "foo = \"1997-09-09T09:09:09Z\"", |
| "\"1997-09-09T09:09:09Z\"", |
| None, |
| ); |
| |
| for expected in good_datetimes() { |
| let s = format!("foo = {}", expected); |
| good::<Datetime>(&s, expected, None); |
| } |
| // ending at something other than the absolute end |
| good::<u32>("foo = 42\nnoise = true", "42", Some(8)); |
| } |
| |
| #[test] |
| fn test_inner_spanned_table() { |
| #[derive(Deserialize)] |
| struct Foo { |
| foo: Spanned<HashMap<Spanned<String>, Spanned<String>>>, |
| } |
| |
| fn good(s: &str, zero: bool) { |
| let foo: Foo = toml::from_str(s).unwrap(); |
| |
| if zero { |
| assert_eq!(foo.foo.span().start, 0); |
| assert_eq!(foo.foo.span().end, 73); |
| } else { |
| assert_eq!(foo.foo.span().start, s.find('{').unwrap()); |
| assert_eq!(foo.foo.span().end, s.find('}').unwrap() + 1); |
| } |
| for (k, v) in foo.foo.as_ref().iter() { |
| assert_eq!(&s[k.span().start..k.span().end], k.as_ref()); |
| assert_eq!(&s[(v.span().start + 1)..(v.span().end - 1)], v.as_ref()); |
| } |
| } |
| |
| good( |
| "\ |
| [foo] |
| a = 'b' |
| bar = 'baz' |
| c = 'd' |
| e = \"f\" |
| ", |
| true, |
| ); |
| |
| good( |
| " |
| foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" }", |
| false, |
| ); |
| } |
| |
| #[test] |
| fn test_outer_spanned_table() { |
| #[derive(Deserialize)] |
| struct Foo { |
| foo: HashMap<Spanned<String>, Spanned<String>>, |
| } |
| |
| fn good(s: &str) { |
| let foo: Foo = toml::from_str(s).unwrap(); |
| |
| for (k, v) in foo.foo.iter() { |
| assert_eq!(&s[k.span().start..k.span().end], k.as_ref()); |
| assert_eq!(&s[(v.span().start + 1)..(v.span().end - 1)], v.as_ref()); |
| } |
| } |
| |
| good( |
| " |
| [foo] |
| a = 'b' |
| bar = 'baz' |
| c = 'd' |
| e = \"f\" |
| ", |
| ); |
| |
| good( |
| " |
| foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" } |
| ", |
| ); |
| } |
| |
| #[test] |
| fn test_spanned_nested() { |
| #[derive(Deserialize)] |
| struct Foo { |
| foo: HashMap<Spanned<String>, HashMap<Spanned<String>, Spanned<String>>>, |
| } |
| |
| fn good(s: &str) { |
| let foo: Foo = toml::from_str(s).unwrap(); |
| |
| for (k, v) in foo.foo.iter() { |
| assert_eq!(&s[k.span().start..k.span().end], k.as_ref()); |
| for (n_k, n_v) in v.iter() { |
| assert_eq!(&s[n_k.span().start..n_k.span().end], n_k.as_ref()); |
| assert_eq!( |
| &s[(n_v.span().start + 1)..(n_v.span().end - 1)], |
| n_v.as_ref() |
| ); |
| } |
| } |
| } |
| |
| good( |
| " |
| [foo.a] |
| a = 'b' |
| c = 'd' |
| e = \"f\" |
| [foo.bar] |
| baz = 'true' |
| ", |
| ); |
| |
| good( |
| " |
| [foo] |
| foo = { a = 'b', bar = 'baz', c = 'd', e = \"f\" } |
| bazz = {} |
| g = { h = 'i' } |
| ", |
| ); |
| } |
| |
| #[test] |
| fn test_spanned_array() { |
| #[derive(Deserialize)] |
| struct Foo { |
| foo: Vec<Spanned<HashMap<Spanned<String>, Spanned<String>>>>, |
| } |
| |
| let toml = "\ |
| [[foo]] |
| a = 'b' |
| bar = 'baz' |
| c = 'd' |
| e = \"f\" |
| [[foo]] |
| a = 'c' |
| bar = 'baz' |
| c = 'g' |
| e = \"h\" |
| "; |
| let foo_list: Foo = toml::from_str(toml).unwrap(); |
| |
| for (foo, expected) in foo_list.foo.iter().zip([0..75, 84..159]) { |
| assert_eq!(foo.span(), expected); |
| for (k, v) in foo.as_ref().iter() { |
| assert_eq!(&toml[k.span().start..k.span().end], k.as_ref()); |
| assert_eq!(&toml[(v.span().start + 1)..(v.span().end - 1)], v.as_ref()); |
| } |
| } |
| } |
| |
| #[test] |
| fn deny_unknown_fields() { |
| #[derive(Debug, serde::Deserialize)] |
| #[serde(deny_unknown_fields)] |
| struct Example { |
| #[allow(dead_code)] |
| real: u32, |
| } |
| |
| let error = toml::from_str::<Example>( |
| r#"# my comment |
| # bla bla bla |
| fake = 1"#, |
| ) |
| .unwrap_err(); |
| assert_data_eq!( |
| error.to_string(), |
| str![[r#" |
| TOML parse error at line 3, column 1 |
| | |
| 3 | fake = 1 |
| | ^^^^ |
| unknown field `fake`, expected `real` |
| |
| "#]] |
| .raw() |
| ); |
| } |