#![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 { foo: Spanned, } #[derive(Deserialize)] struct BareFoo { foo: T, } fn good(s: &str, expected: &str, end: Option) where T: serde::de::DeserializeOwned + Debug + PartialEq, { let foo: Foo = 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> = 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::("foo = \"foo\"", "\"foo\"", None); good::("foo = 42", "42", None); // leading plus good::("foo = +42", "+42", None); // table good::>( "foo = {\"foo\" = 42, \"bar\" = 42}", "{\"foo\" = 42, \"bar\" = 42}", None, ); // array good::>("foo = [0, 1, 2, 3, 4]", "[0, 1, 2, 3, 4]", None); // datetime good::( "foo = \"1997-09-09T09:09:09Z\"", "\"1997-09-09T09:09:09Z\"", None, ); for expected in good_datetimes() { let s = format!("foo = {}", expected); good::(&s, expected, None); } // ending at something other than the absolute end good::("foo = 42\nnoise = true", "42", Some(8)); } #[test] fn test_inner_spanned_table() { #[derive(Deserialize)] struct Foo { foo: Spanned, Spanned>>, } 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>, } 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, HashMap, Spanned>>, } 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>>>, } 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::( 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() ); }