diff --git a/parser/src/emitter.rs b/parser/src/emitter.rs index bf1bbaa..081654a 100644 --- a/parser/src/emitter.rs +++ b/parser/src/emitter.rs @@ -334,302 +334,3 @@ fn need_quotes(string: &str) -> bool { || string.parse::().is_ok() || string.parse::().is_ok() } - -#[cfg(test)] -#[allow(clippy::similar_names)] -mod test { - use super::*; - use crate::YamlLoader; - - #[test] - fn test_emit_simple() { - let s = " -# comment -a0 bb: val -a1: - b1: 4 - b2: d -a2: 4 # i'm comment -a3: [1, 2, 3] -a4: - - [a1, a2] - - 2 -"; - - let docs = YamlLoader::load_from_str(s).unwrap(); - let doc = &docs[0]; - let mut writer = String::new(); - { - let mut emitter = YamlEmitter::new(&mut writer); - emitter.dump(doc).unwrap(); - } - println!("original:\n{s}"); - println!("emitted:\n{writer}"); - let docs_new = match YamlLoader::load_from_str(&writer) { - Ok(y) => y, - Err(e) => panic!("{}", e), - }; - let doc_new = &docs_new[0]; - - assert_eq!(doc, doc_new); - } - - #[test] - fn test_emit_complex() { - let s = r#" -cataloge: - product: &coffee { name: Coffee, price: 2.5 , unit: 1l } - product: &cookies { name: Cookies!, price: 3.40 , unit: 400g} - -products: - *coffee: - amount: 4 - *cookies: - amount: 4 - [1,2,3,4]: - array key - 2.4: - real key - true: - bool key - {}: - empty hash key - "#; - let docs = YamlLoader::load_from_str(s).unwrap(); - let doc = &docs[0]; - let mut writer = String::new(); - { - let mut emitter = YamlEmitter::new(&mut writer); - emitter.dump(doc).unwrap(); - } - let docs_new = match YamlLoader::load_from_str(&writer) { - Ok(y) => y, - Err(e) => panic!("{}", e), - }; - let doc_new = &docs_new[0]; - assert_eq!(doc, doc_new); - } - - #[test] - fn test_emit_avoid_quotes() { - let s = r#"--- -a7: 你好 -boolean: "true" -boolean2: "false" -date: 2014-12-31 -empty_string: "" -empty_string1: " " -empty_string2: " a" -empty_string3: " a " -exp: "12e7" -field: ":" -field2: "{" -field3: "\\" -field4: "\n" -field5: "can't avoid quote" -float: "2.6" -int: "4" -nullable: "null" -nullable2: "~" -products: - "*coffee": - amount: 4 - "*cookies": - amount: 4 - ".milk": - amount: 1 - "2.4": real key - "[1,2,3,4]": array key - "true": bool key - "{}": empty hash key -x: test -y: avoid quoting here -z: string with spaces"#; - - let docs = YamlLoader::load_from_str(s).unwrap(); - let doc = &docs[0]; - let mut writer = String::new(); - { - let mut emitter = YamlEmitter::new(&mut writer); - emitter.dump(doc).unwrap(); - } - - assert_eq!(s, writer, "actual:\n\n{writer}\n"); - } - - #[test] - fn emit_quoted_bools() { - let input = r#"--- -string0: yes -string1: no -string2: "true" -string3: "false" -string4: "~" -null0: ~ -[true, false]: real_bools -[True, TRUE, False, FALSE, y,Y,yes,Yes,YES,n,N,no,No,NO,on,On,ON,off,Off,OFF]: false_bools -bool0: true -bool1: false"#; - let expected = r#"--- -string0: "yes" -string1: "no" -string2: "true" -string3: "false" -string4: "~" -null0: ~ -? - true - - false -: real_bools -? - "True" - - "TRUE" - - "False" - - "FALSE" - - y - - Y - - "yes" - - "Yes" - - "YES" - - n - - N - - "no" - - "No" - - "NO" - - "on" - - "On" - - "ON" - - "off" - - "Off" - - "OFF" -: false_bools -bool0: true -bool1: false"#; - - let docs = YamlLoader::load_from_str(input).unwrap(); - let doc = &docs[0]; - let mut writer = String::new(); - { - let mut emitter = YamlEmitter::new(&mut writer); - emitter.dump(doc).unwrap(); - } - - assert_eq!( - expected, writer, - "expected:\n{expected}\nactual:\n{writer}\n", - ); - } - - #[test] - fn test_empty_and_nested() { - test_empty_and_nested_flag(false); - } - - #[test] - fn test_empty_and_nested_compact() { - test_empty_and_nested_flag(true); - } - - fn test_empty_and_nested_flag(compact: bool) { - let s = if compact { - r#"--- -a: - b: - c: hello - d: {} -e: - - f - - g - - h: []"# - } else { - r#"--- -a: - b: - c: hello - d: {} -e: - - f - - g - - - h: []"# - }; - - let docs = YamlLoader::load_from_str(s).unwrap(); - let doc = &docs[0]; - let mut writer = String::new(); - { - let mut emitter = YamlEmitter::new(&mut writer); - emitter.compact(compact); - emitter.dump(doc).unwrap(); - } - - assert_eq!(s, writer); - } - - #[test] - fn test_nested_arrays() { - let s = r#"--- -a: - - b - - - c - - d - - - e - - f"#; - - let docs = YamlLoader::load_from_str(s).unwrap(); - let doc = &docs[0]; - let mut writer = String::new(); - { - let mut emitter = YamlEmitter::new(&mut writer); - emitter.dump(doc).unwrap(); - } - println!("original:\n{s}"); - println!("emitted:\n{writer}"); - - assert_eq!(s, writer); - } - - #[test] - fn test_deeply_nested_arrays() { - let s = r#"--- -a: - - b - - - c - - d - - - e - - - f - - - e"#; - - let docs = YamlLoader::load_from_str(s).unwrap(); - let doc = &docs[0]; - let mut writer = String::new(); - { - let mut emitter = YamlEmitter::new(&mut writer); - emitter.dump(doc).unwrap(); - } - println!("original:\n{s}"); - println!("emitted:\n{writer}"); - - assert_eq!(s, writer); - } - - #[test] - fn test_nested_hashes() { - let s = r#"--- -a: - b: - c: - d: - e: f"#; - - let docs = YamlLoader::load_from_str(s).unwrap(); - let doc = &docs[0]; - let mut writer = String::new(); - { - let mut emitter = YamlEmitter::new(&mut writer); - emitter.dump(doc).unwrap(); - } - println!("original:\n{s}"); - println!("emitted:\n{writer}"); - - assert_eq!(s, writer); - } -} diff --git a/parser/src/scanner.rs b/parser/src/scanner.rs index d5ce224..ed8c592 100644 --- a/parser/src/scanner.rs +++ b/parser/src/scanner.rs @@ -951,7 +951,7 @@ impl> Scanner { self.skip(); self.lookahead(1); - while is_alpha(self.ch()) { + while is_alpha(self.ch()) || self.ch_is(':') { string.push(self.ch()); self.skip(); self.lookahead(1); @@ -960,7 +960,7 @@ impl> Scanner { if string.is_empty() || match self.ch() { c if is_blankz(c) => false, - '?' | ':' | ',' | ']' | '}' | '%' | '@' | '`' => false, + '?' | ',' | ']' | '}' | '%' | '@' | '`' => false, _ => true, } { @@ -1747,447 +1747,3 @@ impl> Scanner { Ok(()) } } - -#[cfg(test)] -#[allow(clippy::enum_glob_use)] -mod test { - use super::TokenType::*; - use super::*; - - macro_rules! next { - ($p:ident, $tk:pat) => {{ - let tok = $p.next().unwrap(); - match tok.1 { - $tk => {} - _ => panic!("unexpected token: {:?}", tok), - } - }}; - } - - macro_rules! next_scalar { - ($p:ident, $tk:expr, $v:expr) => {{ - let tok = $p.next().unwrap(); - match tok.1 { - Scalar(style, ref v) => { - assert_eq!(style, $tk); - assert_eq!(v, $v); - } - _ => panic!("unexpected token: {:?}", tok), - } - }}; - } - - macro_rules! end { - ($p:ident) => {{ - assert_eq!($p.next(), None); - }}; - } - /// test cases in libyaml scanner.c - #[test] - fn test_empty() { - let s = ""; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_scalar() { - let s = "a scalar"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, Scalar(TScalarStyle::Plain, _)); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_explicit_scalar() { - let s = "--- -'a scalar' -... -"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, DocumentStart); - next!(p, Scalar(TScalarStyle::SingleQuoted, _)); - next!(p, DocumentEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_multiple_documents() { - let s = " -'a scalar' ---- -'a scalar' ---- -'a scalar' -"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, Scalar(TScalarStyle::SingleQuoted, _)); - next!(p, DocumentStart); - next!(p, Scalar(TScalarStyle::SingleQuoted, _)); - next!(p, DocumentStart); - next!(p, Scalar(TScalarStyle::SingleQuoted, _)); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_a_flow_sequence() { - let s = "[item 1, item 2, item 3]"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, FlowSequenceStart); - next_scalar!(p, TScalarStyle::Plain, "item 1"); - next!(p, FlowEntry); - next!(p, Scalar(TScalarStyle::Plain, _)); - next!(p, FlowEntry); - next!(p, Scalar(TScalarStyle::Plain, _)); - next!(p, FlowSequenceEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_a_flow_mapping() { - let s = " -{ - a simple key: a value, # Note that the KEY token is produced. - ? a complex key: another value, -} -"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, FlowMappingStart); - next!(p, Key); - next!(p, Scalar(TScalarStyle::Plain, _)); - next!(p, Value); - next!(p, Scalar(TScalarStyle::Plain, _)); - next!(p, FlowEntry); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "a complex key"); - next!(p, Value); - next!(p, Scalar(TScalarStyle::Plain, _)); - next!(p, FlowEntry); - next!(p, FlowMappingEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_block_sequences() { - let s = " -- item 1 -- item 2 -- - - item 3.1 - - item 3.2 -- - key 1: value 1 - key 2: value 2 -"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, BlockSequenceStart); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 1"); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 2"); - next!(p, BlockEntry); - next!(p, BlockSequenceStart); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 3.1"); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 3.2"); - next!(p, BlockEnd); - next!(p, BlockEntry); - next!(p, BlockMappingStart); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "key 1"); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, "value 1"); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "key 2"); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, "value 2"); - next!(p, BlockEnd); - next!(p, BlockEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_block_mappings() { - let s = " -a simple key: a value # The KEY token is produced here. -? a complex key -: another value -a mapping: - key 1: value 1 - key 2: value 2 -a sequence: - - item 1 - - item 2 -"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, BlockMappingStart); - next!(p, Key); - next!(p, Scalar(_, _)); - next!(p, Value); - next!(p, Scalar(_, _)); - next!(p, Key); - next!(p, Scalar(_, _)); - next!(p, Value); - next!(p, Scalar(_, _)); - next!(p, Key); - next!(p, Scalar(_, _)); - next!(p, Value); // libyaml comment seems to be wrong - next!(p, BlockMappingStart); - next!(p, Key); - next!(p, Scalar(_, _)); - next!(p, Value); - next!(p, Scalar(_, _)); - next!(p, Key); - next!(p, Scalar(_, _)); - next!(p, Value); - next!(p, Scalar(_, _)); - next!(p, BlockEnd); - next!(p, Key); - next!(p, Scalar(_, _)); - next!(p, Value); - next!(p, BlockSequenceStart); - next!(p, BlockEntry); - next!(p, Scalar(_, _)); - next!(p, BlockEntry); - next!(p, Scalar(_, _)); - next!(p, BlockEnd); - next!(p, BlockEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_no_block_sequence_start() { - let s = " -key: -- item 1 -- item 2 -"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, BlockMappingStart); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "key"); - next!(p, Value); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 1"); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 2"); - next!(p, BlockEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_collections_in_sequence() { - let s = " -- - item 1 - - item 2 -- key 1: value 1 - key 2: value 2 -- ? complex key - : complex value -"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, BlockSequenceStart); - next!(p, BlockEntry); - next!(p, BlockSequenceStart); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 1"); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 2"); - next!(p, BlockEnd); - next!(p, BlockEntry); - next!(p, BlockMappingStart); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "key 1"); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, "value 1"); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "key 2"); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, "value 2"); - next!(p, BlockEnd); - next!(p, BlockEntry); - next!(p, BlockMappingStart); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "complex key"); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, "complex value"); - next!(p, BlockEnd); - next!(p, BlockEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_collections_in_mapping() { - let s = " -? a sequence -: - item 1 - - item 2 -? a mapping -: key 1: value 1 - key 2: value 2 -"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, BlockMappingStart); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "a sequence"); - next!(p, Value); - next!(p, BlockSequenceStart); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 1"); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "item 2"); - next!(p, BlockEnd); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "a mapping"); - next!(p, Value); - next!(p, BlockMappingStart); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "key 1"); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, "value 1"); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "key 2"); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, "value 2"); - next!(p, BlockEnd); - next!(p, BlockEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_spec_ex7_3() { - let s = " -{ - ? foo :, - : bar, -} -"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, FlowMappingStart); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "foo"); - next!(p, Value); - next!(p, FlowEntry); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, "bar"); - next!(p, FlowEntry); - next!(p, FlowMappingEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_plain_scalar_starting_with_indicators_in_flow() { - // "Plain scalars must not begin with most indicators, as this would cause ambiguity with - // other YAML constructs. However, the “:”, “?” and “-” indicators may be used as the first - // character if followed by a non-space “safe” character, as this causes no ambiguity." - - let s = "{a: :b}"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, FlowMappingStart); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "a"); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, ":b"); - next!(p, FlowMappingEnd); - next!(p, StreamEnd); - end!(p); - - let s = "{a: ?b}"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, FlowMappingStart); - next!(p, Key); - next_scalar!(p, TScalarStyle::Plain, "a"); - next!(p, Value); - next_scalar!(p, TScalarStyle::Plain, "?b"); - next!(p, FlowMappingEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_plain_scalar_starting_with_indicators_in_block() { - let s = ":a"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next_scalar!(p, TScalarStyle::Plain, ":a"); - next!(p, StreamEnd); - end!(p); - - let s = "?a"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next_scalar!(p, TScalarStyle::Plain, "?a"); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_plain_scalar_containing_indicators_in_block() { - let s = "a:,b"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next_scalar!(p, TScalarStyle::Plain, "a:,b"); - next!(p, StreamEnd); - end!(p); - - let s = ":,b"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next_scalar!(p, TScalarStyle::Plain, ":,b"); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_scanner_cr() { - let s = "---\r\n- tok1\r\n- tok2"; - let mut p = Scanner::new(s.chars()); - next!(p, StreamStart(..)); - next!(p, DocumentStart); - next!(p, BlockSequenceStart); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "tok1"); - next!(p, BlockEntry); - next_scalar!(p, TScalarStyle::Plain, "tok2"); - next!(p, BlockEnd); - next!(p, StreamEnd); - end!(p); - } - - #[test] - fn test_uri() { - // TODO - } - - #[test] - fn test_uri_escapes() { - // TODO - } -} diff --git a/parser/tests/emitter.rs b/parser/tests/emitter.rs new file mode 100644 index 0000000..d60e3a1 --- /dev/null +++ b/parser/tests/emitter.rs @@ -0,0 +1,294 @@ +use yaml_rust::{YamlEmitter, YamlLoader}; + +#[allow(clippy::similar_names)] +#[test] +fn test_emit_simple() { + let s = " +# comment +a0 bb: val +a1: + b1: 4 + b2: d +a2: 4 # i'm comment +a3: [1, 2, 3] +a4: + - [a1, a2] + - 2 +"; + + let docs = YamlLoader::load_from_str(s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{s}"); + println!("emitted:\n{writer}"); + let docs_new = match YamlLoader::load_from_str(&writer) { + Ok(y) => y, + Err(e) => panic!("{}", e), + }; + let doc_new = &docs_new[0]; + + assert_eq!(doc, doc_new); +} + +#[test] +fn test_emit_complex() { + let s = r#" +cataloge: + product: &coffee { name: Coffee, price: 2.5 , unit: 1l } + product: &cookies { name: Cookies!, price: 3.40 , unit: 400g} + +products: + *coffee : + amount: 4 + *cookies : + amount: 4 + [1,2,3,4]: + array key + 2.4: + real key + true: + bool key + {}: + empty hash key + "#; + let docs = YamlLoader::load_from_str(s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + let docs_new = match YamlLoader::load_from_str(&writer) { + Ok(y) => y, + Err(e) => panic!("{}", e), + }; + let doc_new = &docs_new[0]; + assert_eq!(doc, doc_new); +} + +#[test] +fn test_emit_avoid_quotes() { + let s = r#"--- +a7: 你好 +boolean: "true" +boolean2: "false" +date: 2014-12-31 +empty_string: "" +empty_string1: " " +empty_string2: " a" +empty_string3: " a " +exp: "12e7" +field: ":" +field2: "{" +field3: "\\" +field4: "\n" +field5: "can't avoid quote" +float: "2.6" +int: "4" +nullable: "null" +nullable2: "~" +products: + "*coffee": + amount: 4 + "*cookies": + amount: 4 + ".milk": + amount: 1 + "2.4": real key + "[1,2,3,4]": array key + "true": bool key + "{}": empty hash key +x: test +y: avoid quoting here +z: string with spaces"#; + + let docs = YamlLoader::load_from_str(s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + + assert_eq!(s, writer, "actual:\n\n{writer}\n"); +} + +#[test] +fn emit_quoted_bools() { + let input = r#"--- +string0: yes +string1: no +string2: "true" +string3: "false" +string4: "~" +null0: ~ +[true, false]: real_bools +[True, TRUE, False, FALSE, y,Y,yes,Yes,YES,n,N,no,No,NO,on,On,ON,off,Off,OFF]: false_bools +bool0: true +bool1: false"#; + let expected = r#"--- +string0: "yes" +string1: "no" +string2: "true" +string3: "false" +string4: "~" +null0: ~ +? - true + - false +: real_bools +? - "True" + - "TRUE" + - "False" + - "FALSE" + - y + - Y + - "yes" + - "Yes" + - "YES" + - n + - N + - "no" + - "No" + - "NO" + - "on" + - "On" + - "ON" + - "off" + - "Off" + - "OFF" +: false_bools +bool0: true +bool1: false"#; + + let docs = YamlLoader::load_from_str(input).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + + assert_eq!( + expected, writer, + "expected:\n{expected}\nactual:\n{writer}\n", + ); +} + +#[test] +fn test_empty_and_nested() { + test_empty_and_nested_flag(false); +} + +#[test] +fn test_empty_and_nested_compact() { + test_empty_and_nested_flag(true); +} + +fn test_empty_and_nested_flag(compact: bool) { + let s = if compact { + r#"--- +a: + b: + c: hello + d: {} +e: + - f + - g + - h: []"# + } else { + r#"--- +a: + b: + c: hello + d: {} +e: + - f + - g + - + h: []"# + }; + + let docs = YamlLoader::load_from_str(s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.compact(compact); + emitter.dump(doc).unwrap(); + } + + assert_eq!(s, writer); +} + +#[test] +fn test_nested_arrays() { + let s = r#"--- +a: + - b + - - c + - d + - - e + - f"#; + + let docs = YamlLoader::load_from_str(s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{s}"); + println!("emitted:\n{writer}"); + + assert_eq!(s, writer); +} + +#[test] +fn test_deeply_nested_arrays() { + let s = r#"--- +a: + - b + - - c + - d + - - e + - - f + - - e"#; + + let docs = YamlLoader::load_from_str(s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{s}"); + println!("emitted:\n{writer}"); + + assert_eq!(s, writer); +} + +#[test] +fn test_nested_hashes() { + let s = r#"--- +a: + b: + c: + d: + e: f"#; + + let docs = YamlLoader::load_from_str(s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{s}"); + println!("emitted:\n{writer}"); + + assert_eq!(s, writer); +} diff --git a/parser/tests/scanner.rs b/parser/tests/scanner.rs new file mode 100644 index 0000000..27d3760 --- /dev/null +++ b/parser/tests/scanner.rs @@ -0,0 +1,440 @@ +#![allow(clippy::enum_glob_use)] + +use yaml_rust::{scanner::TokenType::*, scanner::*}; + +macro_rules! next { + ($p:ident, $tk:pat) => {{ + let tok = $p.next().unwrap(); + match tok.1 { + $tk => {} + _ => panic!("unexpected token: {:?}", tok), + } + }}; +} + +macro_rules! next_scalar { + ($p:ident, $tk:expr, $v:expr) => {{ + let tok = $p.next().unwrap(); + match tok.1 { + Scalar(style, ref v) => { + assert_eq!(style, $tk); + assert_eq!(v, $v); + } + _ => panic!("unexpected token: {:?}", tok), + } + }}; +} + +macro_rules! end { + ($p:ident) => {{ + assert_eq!($p.next(), None); + }}; +} +/// test cases in libyaml scanner.c +#[test] +fn test_empty() { + let s = ""; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_scalar() { + let s = "a scalar"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_explicit_scalar() { + let s = "--- +'a scalar' +... +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, DocumentStart); + next!(p, Scalar(TScalarStyle::SingleQuoted, _)); + next!(p, DocumentEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_multiple_documents() { + let s = " +'a scalar' +--- +'a scalar' +--- +'a scalar' +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, Scalar(TScalarStyle::SingleQuoted, _)); + next!(p, DocumentStart); + next!(p, Scalar(TScalarStyle::SingleQuoted, _)); + next!(p, DocumentStart); + next!(p, Scalar(TScalarStyle::SingleQuoted, _)); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_a_flow_sequence() { + let s = "[item 1, item 2, item 3]"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowSequenceStart); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, FlowEntry); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, FlowEntry); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, FlowSequenceEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_a_flow_mapping() { + let s = " +{ + a simple key: a value, # Note that the KEY token is produced. + ? a complex key: another value, +} +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowMappingStart); + next!(p, Key); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, Value); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, FlowEntry); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a complex key"); + next!(p, Value); + next!(p, Scalar(TScalarStyle::Plain, _)); + next!(p, FlowEntry); + next!(p, FlowMappingEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_block_sequences() { + let s = " +- item 1 +- item 2 +- + - item 3.1 + - item 3.2 +- + key 1: value 1 + key 2: value 2 +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 2"); + next!(p, BlockEntry); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 3.1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 3.2"); + next!(p, BlockEnd); + next!(p, BlockEntry); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 1"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 1"); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 2"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 2"); + next!(p, BlockEnd); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_block_mappings() { + let s = " +a simple key: a value # The KEY token is produced here. +? a complex key +: another value +a mapping: + key 1: value 1 + key 2: value 2 +a sequence: + - item 1 + - item 2 +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockMappingStart); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, Scalar(_, _)); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, Scalar(_, _)); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); // libyaml comment seems to be wrong + next!(p, BlockMappingStart); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, Scalar(_, _)); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, Scalar(_, _)); + next!(p, BlockEnd); + next!(p, Key); + next!(p, Scalar(_, _)); + next!(p, Value); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next!(p, Scalar(_, _)); + next!(p, BlockEntry); + next!(p, Scalar(_, _)); + next!(p, BlockEnd); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_no_block_sequence_start() { + let s = " +key: +- item 1 +- item 2 +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key"); + next!(p, Value); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 2"); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_collections_in_sequence() { + let s = " +- - item 1 + - item 2 +- key 1: value 1 + key 2: value 2 +- ? complex key + : complex value +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 2"); + next!(p, BlockEnd); + next!(p, BlockEntry); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 1"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 1"); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 2"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 2"); + next!(p, BlockEnd); + next!(p, BlockEntry); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "complex key"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "complex value"); + next!(p, BlockEnd); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_collections_in_mapping() { + let s = " +? a sequence +: - item 1 + - item 2 +? a mapping +: key 1: value 1 + key 2: value 2 +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a sequence"); + next!(p, Value); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "item 2"); + next!(p, BlockEnd); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a mapping"); + next!(p, Value); + next!(p, BlockMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 1"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 1"); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "key 2"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "value 2"); + next!(p, BlockEnd); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_spec_ex7_3() { + let s = " +{ + ? foo :, + : bar, +} +"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "foo"); + next!(p, Value); + next!(p, FlowEntry); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "bar"); + next!(p, FlowEntry); + next!(p, FlowMappingEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_plain_scalar_starting_with_indicators_in_flow() { + // "Plain scalars must not begin with most indicators, as this would cause ambiguity with + // other YAML constructs. However, the “:”, “?” and “-” indicators may be used as the first + // character if followed by a non-space “safe” character, as this causes no ambiguity." + + let s = "{a: :b}"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, ":b"); + next!(p, FlowMappingEnd); + next!(p, StreamEnd); + end!(p); + + let s = "{a: ?b}"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, FlowMappingStart); + next!(p, Key); + next_scalar!(p, TScalarStyle::Plain, "a"); + next!(p, Value); + next_scalar!(p, TScalarStyle::Plain, "?b"); + next!(p, FlowMappingEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_plain_scalar_starting_with_indicators_in_block() { + let s = ":a"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next_scalar!(p, TScalarStyle::Plain, ":a"); + next!(p, StreamEnd); + end!(p); + + let s = "?a"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next_scalar!(p, TScalarStyle::Plain, "?a"); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_plain_scalar_containing_indicators_in_block() { + let s = "a:,b"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next_scalar!(p, TScalarStyle::Plain, "a:,b"); + next!(p, StreamEnd); + end!(p); + + let s = ":,b"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next_scalar!(p, TScalarStyle::Plain, ":,b"); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_scanner_cr() { + let s = "---\r\n- tok1\r\n- tok2"; + let mut p = Scanner::new(s.chars()); + next!(p, StreamStart(..)); + next!(p, DocumentStart); + next!(p, BlockSequenceStart); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "tok1"); + next!(p, BlockEntry); + next_scalar!(p, TScalarStyle::Plain, "tok2"); + next!(p, BlockEnd); + next!(p, StreamEnd); + end!(p); +} + +#[test] +fn test_uri() { + // TODO +} + +#[test] +fn test_uri_escapes() { + // TODO +} diff --git a/parser/tests/yaml-test-suite.rs b/parser/tests/yaml-test-suite.rs index 61f54b6..6b3bf29 100644 --- a/parser/tests/yaml-test-suite.rs +++ b/parser/tests/yaml-test-suite.rs @@ -282,6 +282,7 @@ fn expected_events(expected_tree: &str) -> Vec { .collect() } +#[rustfmt::skip] static EXPECTED_FAILURES: &[&str] = &[ // These seem to be API limited (not enough information on the event stream level) // No tag available for SEQ and MAP @@ -339,10 +340,8 @@ static EXPECTED_FAILURES: &[&str] = &[ "HWV9", "QT73", // Unusual characters in anchors/aliases - "2SXE", // : "8XYN", // emoji!! "W5VH", // :@*!$": - "Y2GN", // : in the middle // Flow mapping colon on next line / multiline key in flow mapping "4MUZ-00", "4MUZ-01",