| #![allow(clippy::let_underscore_untyped, clippy::uninlined_format_args)] |
| |
| use serde_json::{json, Value}; |
| |
| macro_rules! bad { |
| ($toml:expr, $msg:expr) => { |
| match basic_toml::from_str::<Value>($toml) { |
| Ok(s) => panic!("parsed to: {:#?}", s), |
| Err(e) => assert_eq!(e.to_string(), $msg), |
| } |
| }; |
| } |
| |
| #[test] |
| fn crlf() { |
| let toml = "\ |
| [project]\r\n\ |
| \r\n\ |
| name = \"splay\"\r\n\ |
| version = \"0.1.0\"\r\n\ |
| authors = [\"alex@crichton.co\"]\r\n\ |
| \r\n\ |
| [[lib]]\r\n\ |
| \r\n\ |
| path = \"lib.rs\"\r\n\ |
| name = \"splay\"\r\n\ |
| description = \"\"\"\ |
| A Rust implementation of a TAR file reader and writer. This library does not\r\n\ |
| currently handle compression, but it is abstract over all I/O readers and\r\n\ |
| writers. Additionally, great lengths are taken to ensure that the entire\r\n\ |
| contents are never required to be entirely resident in memory all at once.\r\n\ |
| \"\"\"\ |
| "; |
| basic_toml::from_str::<Value>(toml).unwrap(); |
| } |
| |
| #[test] |
| fn fun_with_strings() { |
| let toml = r#" |
| bar = "\U00000000" |
| key1 = "One\nTwo" |
| key2 = """One\nTwo""" |
| key3 = """ |
| One |
| Two""" |
| |
| key4 = "The quick brown fox jumps over the lazy dog." |
| key5 = """ |
| The quick brown \ |
| |
| |
| fox jumps over \ |
| the lazy dog.""" |
| key6 = """\ |
| The quick brown \ |
| fox jumps over \ |
| the lazy dog.\ |
| """ |
| # What you see is what you get. |
| winpath = 'C:\Users\nodejs\templates' |
| winpath2 = '\\ServerX\admin$\system32\' |
| quoted = 'Tom "Dubs" Preston-Werner' |
| regex = '<\i\c*\s*>' |
| |
| regex2 = '''I [dw]on't need \d{2} apples''' |
| lines = ''' |
| The first newline is |
| trimmed in raw strings. |
| All other whitespace |
| is preserved. |
| ''' |
| "#; |
| let table: Value = basic_toml::from_str(toml).unwrap(); |
| assert_eq!(table["bar"], json!("\0")); |
| assert_eq!(table["key1"], json!("One\nTwo")); |
| assert_eq!(table["key2"], json!("One\nTwo")); |
| assert_eq!(table["key3"], json!("One\nTwo")); |
| |
| let msg = "The quick brown fox jumps over the lazy dog."; |
| assert_eq!(table["key4"], json!(msg)); |
| assert_eq!(table["key5"], json!(msg)); |
| assert_eq!(table["key6"], json!(msg)); |
| |
| assert_eq!(table["winpath"], json!(r"C:\Users\nodejs\templates")); |
| assert_eq!(table["winpath2"], json!(r"\\ServerX\admin$\system32\")); |
| assert_eq!(table["quoted"], json!(r#"Tom "Dubs" Preston-Werner"#)); |
| assert_eq!(table["regex"], json!(r"<\i\c*\s*>")); |
| assert_eq!(table["regex2"], json!(r"I [dw]on't need \d{2} apples")); |
| assert_eq!( |
| table["lines"], |
| json!( |
| "The first newline is\n\ |
| trimmed in raw strings.\n\ |
| All other whitespace\n\ |
| is preserved.\n" |
| ) |
| ); |
| } |
| |
| #[test] |
| fn tables_in_arrays() { |
| let toml = " |
| [[foo]] |
| #… |
| [foo.bar] |
| #… |
| |
| [[foo]] # ... |
| #… |
| [foo.bar] |
| #... |
| "; |
| let table: Value = basic_toml::from_str(toml).unwrap(); |
| table["foo"][0]["bar"].as_object().unwrap(); |
| table["foo"][1]["bar"].as_object().unwrap(); |
| } |
| |
| #[test] |
| fn empty_table() { |
| let toml = " |
| [foo]"; |
| let table: Value = basic_toml::from_str(toml).unwrap(); |
| table["foo"].as_object().unwrap(); |
| } |
| |
| #[test] |
| fn fruit() { |
| let toml = r#" |
| [[fruit]] |
| name = "apple" |
| |
| [fruit.physical] |
| color = "red" |
| shape = "round" |
| |
| [[fruit.variety]] |
| name = "red delicious" |
| |
| [[fruit.variety]] |
| name = "granny smith" |
| |
| [[fruit]] |
| name = "banana" |
| |
| [[fruit.variety]] |
| name = "plantain" |
| "#; |
| let table: Value = basic_toml::from_str(toml).unwrap(); |
| assert_eq!(table["fruit"][0]["name"], json!("apple")); |
| assert_eq!(table["fruit"][0]["physical"]["color"], json!("red")); |
| assert_eq!(table["fruit"][0]["physical"]["shape"], json!("round")); |
| assert_eq!( |
| table["fruit"][0]["variety"][0]["name"], |
| json!("red delicious") |
| ); |
| assert_eq!( |
| table["fruit"][0]["variety"][1]["name"], |
| json!("granny smith") |
| ); |
| assert_eq!(table["fruit"][1]["name"], json!("banana")); |
| assert_eq!(table["fruit"][1]["variety"][0]["name"], json!("plantain")); |
| } |
| |
| #[test] |
| fn stray_cr() { |
| bad!("\r", "unexpected character found: `\\r` at line 1 column 1"); |
| bad!( |
| "a = [ \r ]", |
| "unexpected character found: `\\r` at line 1 column 7" |
| ); |
| bad!( |
| "a = \"\"\"\r\"\"\"", |
| "invalid character in string: `\\r` at line 1 column 8" |
| ); |
| bad!( |
| "a = \"\"\"\\ \r \"\"\"", |
| "invalid escape character in string: ` ` at line 1 column 9" |
| ); |
| bad!( |
| "a = '''\r'''", |
| "invalid character in string: `\\r` at line 1 column 8" |
| ); |
| bad!( |
| "a = '\r'", |
| "invalid character in string: `\\r` at line 1 column 6" |
| ); |
| bad!( |
| "a = \"\r\"", |
| "invalid character in string: `\\r` at line 1 column 6" |
| ); |
| } |
| |
| #[test] |
| fn blank_literal_string() { |
| let table: Value = basic_toml::from_str("foo = ''").unwrap(); |
| assert_eq!(table["foo"], json!("")); |
| } |
| |
| #[test] |
| fn many_blank() { |
| let table: Value = basic_toml::from_str("foo = \"\"\"\n\n\n\"\"\"").unwrap(); |
| assert_eq!(table["foo"], json!("\n\n")); |
| } |
| |
| #[test] |
| fn literal_eats_crlf() { |
| let toml = " |
| foo = \"\"\"\\\r\n\"\"\" |
| bar = \"\"\"\\\r\n \r\n \r\n a\"\"\" |
| "; |
| let table: Value = basic_toml::from_str(toml).unwrap(); |
| assert_eq!(table["foo"], json!("")); |
| assert_eq!(table["bar"], json!("a")); |
| } |
| |
| #[test] |
| fn string_no_newline() { |
| bad!("a = \"\n\"", "newline in string found at line 1 column 6"); |
| bad!("a = '\n'", "newline in string found at line 1 column 6"); |
| } |
| |
| #[test] |
| fn bad_leading_zeros() { |
| bad!("a = 00", "invalid number at line 1 column 6"); |
| bad!("a = -00", "invalid number at line 1 column 7"); |
| bad!("a = +00", "invalid number at line 1 column 7"); |
| bad!("a = 00.0", "invalid number at line 1 column 6"); |
| bad!("a = -00.0", "invalid number at line 1 column 7"); |
| bad!("a = +00.0", "invalid number at line 1 column 7"); |
| bad!( |
| "a = 9223372036854775808", |
| "invalid number at line 1 column 5" |
| ); |
| bad!( |
| "a = -9223372036854775809", |
| "invalid number at line 1 column 5" |
| ); |
| } |
| |
| #[test] |
| fn bad_floats() { |
| bad!("a = 0.", "invalid number at line 1 column 7"); |
| bad!("a = 0.e", "invalid number at line 1 column 7"); |
| bad!("a = 0.E", "invalid number at line 1 column 7"); |
| bad!("a = 0.0E", "invalid number at line 1 column 5"); |
| bad!("a = 0.0e", "invalid number at line 1 column 5"); |
| bad!("a = 0.0e-", "invalid number at line 1 column 9"); |
| bad!("a = 0.0e+", "invalid number at line 1 column 5"); |
| } |
| |
| #[test] |
| fn floats() { |
| macro_rules! t { |
| ($actual:expr, $expected:expr) => {{ |
| let f = format!("foo = {}", $actual); |
| println!("{}", f); |
| let a: Value = basic_toml::from_str(&f).unwrap(); |
| assert_eq!(a["foo"], json!($expected)); |
| }}; |
| } |
| |
| t!("1.0", 1.0); |
| t!("1.0e0", 1.0); |
| t!("1.0e+0", 1.0); |
| t!("1.0e-0", 1.0); |
| t!("1E-0", 1.0); |
| t!("1.001e-0", 1.001); |
| t!("2e10", 2e10); |
| t!("2e+10", 2e10); |
| t!("2e-10", 2e-10); |
| t!("2_0.0", 20.0); |
| t!("2_0.0_0e1_0", 20.0e10); |
| t!("2_0.1_0e1_0", 20.1e10); |
| } |
| |
| #[test] |
| fn bare_key_names() { |
| let toml = " |
| foo = 3 |
| foo_3 = 3 |
| foo_-2--3--r23f--4-f2-4 = 3 |
| _ = 3 |
| - = 3 |
| 8 = 8 |
| \"a\" = 3 |
| \"!\" = 3 |
| \"a^b\" = 3 |
| \"\\\"\" = 3 |
| \"character encoding\" = \"value\" |
| 'ʎǝʞ' = \"value\" |
| "; |
| let a: Value = basic_toml::from_str(toml).unwrap(); |
| let _ = &a["foo"]; |
| let _ = &a["-"]; |
| let _ = &a["_"]; |
| let _ = &a["8"]; |
| let _ = &a["foo_3"]; |
| let _ = &a["foo_-2--3--r23f--4-f2-4"]; |
| let _ = &a["a"]; |
| let _ = &a["!"]; |
| let _ = &a["\""]; |
| let _ = &a["character encoding"]; |
| let _ = &a["ʎǝʞ"]; |
| } |
| |
| #[test] |
| fn bad_keys() { |
| bad!( |
| "key\n=3", |
| "expected an equals, found a newline at line 1 column 4" |
| ); |
| bad!( |
| "key=\n3", |
| "expected a value, found a newline at line 1 column 5" |
| ); |
| bad!( |
| "key|=3", |
| "unexpected character found: `|` at line 1 column 4" |
| ); |
| bad!( |
| "=3", |
| "expected a table key, found an equals at line 1 column 1" |
| ); |
| bad!( |
| "\"\"|=3", |
| "unexpected character found: `|` at line 1 column 3" |
| ); |
| bad!("\"\n\"|=3", "newline in string found at line 1 column 2"); |
| bad!( |
| "\"\r\"|=3", |
| "invalid character in string: `\\r` at line 1 column 2" |
| ); |
| bad!( |
| "''''''=3", |
| "multiline strings are not allowed for key at line 1 column 1" |
| ); |
| bad!( |
| "\"\"\"\"\"\"=3", |
| "multiline strings are not allowed for key at line 1 column 1" |
| ); |
| bad!( |
| "'''key'''=3", |
| "multiline strings are not allowed for key at line 1 column 1" |
| ); |
| bad!( |
| "\"\"\"key\"\"\"=3", |
| "multiline strings are not allowed for key at line 1 column 1" |
| ); |
| } |
| |
| #[test] |
| fn bad_table_names() { |
| bad!( |
| "[]", |
| "expected a table key, found a right bracket at line 1 column 2" |
| ); |
| bad!( |
| "[.]", |
| "expected a table key, found a period at line 1 column 2" |
| ); |
| bad!( |
| "[a.]", |
| "expected a table key, found a right bracket at line 1 column 4" |
| ); |
| bad!("[!]", "unexpected character found: `!` at line 1 column 2"); |
| bad!("[\"\n\"]", "newline in string found at line 1 column 3"); |
| bad!( |
| "[a.b]\n[a.\"b\"]", |
| "redefinition of table `a.b` for key `a.b` at line 2 column 1" |
| ); |
| bad!("[']", "unterminated string at line 1 column 2"); |
| bad!("[''']", "unterminated string at line 1 column 2"); |
| bad!( |
| "['''''']", |
| "multiline strings are not allowed for key at line 1 column 2" |
| ); |
| bad!( |
| "['''foo''']", |
| "multiline strings are not allowed for key at line 1 column 2" |
| ); |
| bad!( |
| "[\"\"\"bar\"\"\"]", |
| "multiline strings are not allowed for key at line 1 column 2" |
| ); |
| bad!("['\n']", "newline in string found at line 1 column 3"); |
| bad!("['\r\n']", "newline in string found at line 1 column 3"); |
| } |
| |
| #[test] |
| fn table_names() { |
| let toml = " |
| [a.\"b\"] |
| [\"f f\"] |
| [\"f.f\"] |
| [\"\\\"\"] |
| ['a.a'] |
| ['\"\"'] |
| "; |
| let a: Value = basic_toml::from_str(toml).unwrap(); |
| println!("{:?}", a); |
| let _ = &a["a"]["b"]; |
| let _ = &a["f f"]; |
| let _ = &a["f.f"]; |
| let _ = &a["\""]; |
| let _ = &a["\"\""]; |
| } |
| |
| #[test] |
| fn invalid_bare_numeral() { |
| bad!("4", "expected an equals, found eof at line 1 column 2"); |
| } |
| |
| #[test] |
| fn inline_tables() { |
| basic_toml::from_str::<Value>("a = {}").unwrap(); |
| basic_toml::from_str::<Value>("a = {b=1}").unwrap(); |
| basic_toml::from_str::<Value>("a = { b = 1 }").unwrap(); |
| basic_toml::from_str::<Value>("a = {a=1,b=2}").unwrap(); |
| basic_toml::from_str::<Value>("a = {a=1,b=2,c={}}").unwrap(); |
| |
| bad!( |
| "a = {a=1,}", |
| "expected a table key, found a right brace at line 1 column 10" |
| ); |
| bad!( |
| "a = {,}", |
| "expected a table key, found a comma at line 1 column 6" |
| ); |
| bad!( |
| "a = {a=1,a=1}", |
| "duplicate key: `a` for key `a` at line 1 column 10" |
| ); |
| bad!( |
| "a = {\n}", |
| "expected a table key, found a newline at line 1 column 6" |
| ); |
| bad!( |
| "a = {", |
| "expected a table key, found eof at line 1 column 6" |
| ); |
| |
| basic_toml::from_str::<Value>("a = {a=[\n]}").unwrap(); |
| basic_toml::from_str::<Value>("a = {\"a\"=[\n]}").unwrap(); |
| basic_toml::from_str::<Value>("a = [\n{},\n{},\n]").unwrap(); |
| } |
| |
| #[test] |
| fn number_underscores() { |
| macro_rules! t { |
| ($actual:expr, $expected:expr) => {{ |
| let f = format!("foo = {}", $actual); |
| let table: Value = basic_toml::from_str(&f).unwrap(); |
| assert_eq!(table["foo"], json!($expected)); |
| }}; |
| } |
| |
| t!("1_0", 10); |
| t!("1_0_0", 100); |
| t!("1_000", 1000); |
| t!("+1_000", 1000); |
| t!("-1_000", -1000); |
| } |
| |
| #[test] |
| fn bad_underscores() { |
| bad!("foo = 0_", "invalid number at line 1 column 7"); |
| bad!("foo = 0__0", "invalid number at line 1 column 7"); |
| bad!( |
| "foo = __0", |
| "invalid TOML value, did you mean to use a quoted string? at line 1 column 7" |
| ); |
| bad!("foo = 1_0_", "invalid number at line 1 column 7"); |
| } |
| |
| #[test] |
| fn bad_unicode_codepoint() { |
| bad!( |
| "foo = \"\\uD800\"", |
| "invalid escape value: `55296` at line 1 column 9" |
| ); |
| } |
| |
| #[test] |
| fn bad_strings() { |
| bad!( |
| "foo = \"\\uxx\"", |
| "invalid hex escape character in string: `x` at line 1 column 10" |
| ); |
| bad!( |
| "foo = \"\\u\"", |
| "invalid hex escape character in string: `\\\"` at line 1 column 10" |
| ); |
| bad!("foo = \"\\", "unterminated string at line 1 column 7"); |
| bad!("foo = '", "unterminated string at line 1 column 7"); |
| } |
| |
| #[test] |
| fn empty_string() { |
| let table: Value = basic_toml::from_str::<Value>("foo = \"\"").unwrap(); |
| assert_eq!(table["foo"], json!("")); |
| } |
| |
| #[test] |
| fn booleans() { |
| let table: Value = basic_toml::from_str("foo = true").unwrap(); |
| assert_eq!(table["foo"], json!(true)); |
| |
| let table: Value = basic_toml::from_str("foo = false").unwrap(); |
| assert_eq!(table["foo"], json!(false)); |
| |
| bad!( |
| "foo = true2", |
| "invalid TOML value, did you mean to use a quoted string? at line 1 column 7" |
| ); |
| bad!( |
| "foo = false2", |
| "invalid TOML value, did you mean to use a quoted string? at line 1 column 7" |
| ); |
| bad!( |
| "foo = t1", |
| "invalid TOML value, did you mean to use a quoted string? at line 1 column 7" |
| ); |
| bad!( |
| "foo = f2", |
| "invalid TOML value, did you mean to use a quoted string? at line 1 column 7" |
| ); |
| } |
| |
| #[test] |
| fn bad_nesting() { |
| bad!( |
| " |
| a = [2] |
| [[a]] |
| b = 5 |
| ", |
| "duplicate key: `a` at line 3 column 11" |
| ); |
| bad!( |
| " |
| a = 1 |
| [a.b] |
| ", |
| "duplicate key: `a` at line 3 column 10" |
| ); |
| bad!( |
| " |
| a = [] |
| [a.b] |
| ", |
| "duplicate key: `a` at line 3 column 10" |
| ); |
| bad!( |
| " |
| a = [] |
| [[a.b]] |
| ", |
| "duplicate key: `a` at line 3 column 11" |
| ); |
| bad!( |
| " |
| [a] |
| b = { c = 2, d = {} } |
| [a.b] |
| c = 2 |
| ", |
| "duplicate key: `b` for key `a` at line 4 column 12" |
| ); |
| } |
| |
| #[test] |
| fn bad_table_redefine() { |
| bad!( |
| " |
| [a] |
| foo=\"bar\" |
| [a.b] |
| foo=\"bar\" |
| [a] |
| ", |
| "redefinition of table `a` for key `a` at line 6 column 9" |
| ); |
| bad!( |
| " |
| [a] |
| foo=\"bar\" |
| b = { foo = \"bar\" } |
| [a] |
| ", |
| "redefinition of table `a` for key `a` at line 5 column 9" |
| ); |
| bad!( |
| " |
| [a] |
| b = {} |
| [a.b] |
| ", |
| "duplicate key: `b` for key `a` at line 4 column 12" |
| ); |
| |
| bad!( |
| " |
| [a] |
| b = {} |
| [a] |
| ", |
| "redefinition of table `a` for key `a` at line 4 column 9" |
| ); |
| } |
| |
| #[test] |
| fn datetimes() { |
| bad!( |
| "foo = 2016-09-09T09:09:09Z", |
| "invalid number at line 1 column 7" |
| ); |
| bad!( |
| "foo = 2016-09-09T09:09:09.1Z", |
| "invalid number at line 1 column 7" |
| ); |
| bad!( |
| "foo = 2016-09-09T09:09:09.2+10:00", |
| "invalid number at line 1 column 7" |
| ); |
| bad!( |
| "foo = 2016-09-09T09:09:09.123456789-02:00", |
| "invalid number at line 1 column 7" |
| ); |
| bad!( |
| "foo = 2016-09-09T09:09:09.Z", |
| "invalid number at line 1 column 7" |
| ); |
| bad!( |
| "foo = 2016-9-09T09:09:09Z", |
| "invalid number at line 1 column 7" |
| ); |
| bad!( |
| "foo = 2016-09-09T09:09:09+2:00", |
| "invalid number at line 1 column 7" |
| ); |
| bad!( |
| "foo = 2016-09-09T09:09:09-2:00", |
| "invalid number at line 1 column 7" |
| ); |
| bad!( |
| "foo = 2016-09-09T09:09:09Z-2:00", |
| "invalid number at line 1 column 7" |
| ); |
| } |
| |
| #[test] |
| fn require_newline_after_value() { |
| bad!("0=0r=false", "invalid number at line 1 column 3"); |
| bad!( |
| r#" |
| 0=""o=""m=""r=""00="0"q="""0"""e="""0""" |
| "#, |
| "expected newline, found an identifier at line 2 column 5" |
| ); |
| bad!( |
| r#" |
| [[0000l0]] |
| 0="0"[[0000l0]] |
| 0="0"[[0000l0]] |
| 0="0"l="0" |
| "#, |
| "expected newline, found a left bracket at line 3 column 6" |
| ); |
| bad!( |
| r#" |
| 0=[0]00=[0,0,0]t=["0","0","0"]s=[1000-00-00T00:00:00Z,2000-00-00T00:00:00Z] |
| "#, |
| "expected newline, found an identifier at line 2 column 6" |
| ); |
| bad!( |
| " |
| 0=0r0=0r=false |
| ", |
| "invalid number at line 2 column 3" |
| ); |
| bad!( |
| " |
| 0=0r0=0r=falsefal=false |
| ", |
| "invalid number at line 2 column 3" |
| ); |
| } |