use std::collections::HashMap; use std::str::FromStr; use toml_spanned_value::spanned_value::ValueKind as K; use toml::value::Datetime; use toml_spanned_value::SpannedValue; /// A set of good datetimes. pub fn good_datetimes() -> Vec<&'static str> { let mut v = Vec::new(); v.push("1997-09-09T09:09:09Z"); v.push("1997-09-09T09:09:09+09:09"); v.push("1997-09-09T09:09:09-09:09"); v.push("1997-09-09T09:09:09"); v.push("1997-09-09"); v.push("09:09:09"); v.push("1997-09-09T09:09:09.09Z"); v.push("1997-09-09T09:09:09.09+09:09"); v.push("1997-09-09T09:09:09.09-09:09"); v.push("1997-09-09T09:09:09.09"); v.push("09:09:09.09"); v } #[test] fn test_spanned_field() { fn good<'de, T>(s: &'de str, expected: &str) where T: serde::Deserialize<'de>, { let foo: SpannedValue = toml::from_str(s).unwrap(); let foo = &foo.get_ref()["foo"]; assert_eq!(6, foo.start()); assert_eq!(s.len(), foo.end()); assert_eq!(expected, &s[foo.start()..foo.end()]); } good::("foo = \"foo\"", "\"foo\""); good::("foo = 42", "42"); // leading plus good::("foo = +42", "+42"); // table good::>( "foo = {\"foo\" = 42, \"bar\" = 42}", "{\"foo\" = 42, \"bar\" = 42}", ); // array good::>("foo = [0, 1, 2, 3, 4]", "[0, 1, 2, 3, 4]"); // datetime good::("foo = \"1997-09-09T09:09:09Z\"", "\"1997-09-09T09:09:09Z\""); for expected in good_datetimes() { let s = format!("foo = {}", expected); good::(&s, expected); } } #[test] fn test_spanned_vals() { fn assert_span_subspan(outer: (usize, usize), inner: (usize, usize)) { if outer == (0, 0) || inner == (0, 0) { // One of the spans is not available. // In the general case, the toml format doesn't allow valid spans // to be created for dotted tables as well as arrays // that use [[]] syntax return; } // Allow the inner span to start xor end at the same place // as the outer one assert!((inner.1 - inner.0) < (outer.1 - outer.0)); assert!(outer.0 <= inner.0); assert!(inner.1 <= outer.1); } fn visit(val: &SpannedValue, s: &str) { let substr = &s[val.start()..val.end()]; match val.get_ref() { K::Array(_) | K::Table(_) => {} K::String(c) => assert_eq!(format!("\"{}\"", c), substr), K::Integer(c) => assert_eq!(Ok(c.clone()), i64::from_str_radix(substr, 10)), K::Float(c) => assert_eq!(Ok(c.clone()), f64::from_str(substr)), K::Boolean(c) => assert_eq!(format!("{}", c), substr.to_lowercase()), K::Datetime(c) => { let dt = Datetime::from_str(substr).unwrap(); assert_eq!(c.clone(), dt); } } match val.get_ref() { K::Array(arr) => { for v in arr.iter() { assert_span_subspan(val.span(), v.span()); visit(v, s); } } K::Table(tbl) => { for (key, v) in tbl.iter() { assert_eq!(&s[key.start()..key.end()], key.get_ref()); assert_span_subspan(val.span(), key.span()); assert_span_subspan(val.span(), v.span()); visit(v, s); } } K::String(_) | K::Integer(_) | K::Float(_) | K::Boolean(_) | K::Datetime(_) => {} } } const TEST_TOML: &str = r#" # comments are supported key_foo = "bazz" key_baz = false empty_arr = [] filled_arr_ints = [1, 2, 3, 4, 5] key_nested_baz = { extremely_nested = "yess", other_val = true } even_more_nesting = { u = 22, f = { s = { v = 32 }, w = 77 }, v = 33 } [tbl] hello = "world" d = "hi" [[arr]] this_is = "something in the array" [[arr]] this_is = "something else in the array" "#; let foo: SpannedValue = toml::from_str(TEST_TOML).unwrap(); // Ensure that indexing still works assert_eq!(foo.get_ref()["key_baz"].get_ref(), &K::Boolean(false)); visit(&foo, TEST_TOML); }