From 3fa9a24739876c327a875a6a701136be32dd45e5 Mon Sep 17 00:00:00 2001 From: Hendrik Sollich Date: Mon, 22 May 2017 19:30:01 +0200 Subject: [PATCH 1/3] quoting possible booleans fixes #53 --- parser/Cargo.toml | 2 +- parser/src/emitter.rs | 58 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 0c79598..a4e2fda 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yaml-rust" -version = "0.3.5" +version = "0.3.6" authors = ["Yuheng Chen "] homepage = "http://chyh1990.github.io/yaml-rust/" documentation = "http://chyh1990.github.io/yaml-rust/doc/yaml_rust/" diff --git a/parser/src/emitter.rs b/parser/src/emitter.rs index 45f2024..91c94b2 100644 --- a/parser/src/emitter.rs +++ b/parser/src/emitter.rs @@ -137,15 +137,12 @@ impl<'a> YamlEmitter<'a> { Yaml::Array(ref v) => { try!(write!(self.writer, "[")); if self.level >= 0 { - try!(write!(self.writer, "+ ")); + try!(write!(self.writer, "")); } - self.level += 1; for (cnt, x) in v.iter().enumerate() { - try!(self.write_indent()); if cnt > 0 { try!(write!(self.writer, ", ")); } try!(self.emit_node(x)); } - self.level -= 1; try!(write!(self.writer, "]")); Ok(()) }, @@ -181,8 +178,7 @@ impl<'a> YamlEmitter<'a> { Yaml::String(ref v) => { if need_quotes(v) { try!(escape_str(self.writer, v)); - } - else { + } else { try!(write!(self.writer, "{}", v)); } Ok(()) @@ -306,10 +302,13 @@ fn need_quotes(string: &str) -> bool { _ => false, } }) - || string == "true" - || string == "false" - || string == "null" - || string == "~" + || [// http://yaml.org/type/bool.html + "y","Y","yes","Yes","YES","n","N","no","No","NO", + "True", "TRUE", "true", "False", "FALSE", "false", + "on","On","ON","off","Off","OFF", + // http://yaml.org/type/null.html + "null","Null","NULL", "~" + ].contains(&string) || string.starts_with('.') || string.parse::().is_ok() || string.parse::().is_ok() @@ -421,7 +420,8 @@ products: "true": bool key "{}": empty hash key x: test -y: string with spaces"#; +"y": "can't avoid quoting here" +z: string with spaces"#; let docs = YamlLoader::load_from_str(&s).unwrap(); let doc = &docs[0]; @@ -434,6 +434,42 @@ y: string with spaces"#; assert_eq!(s, writer, "actual:\n\n{}\n", writer); } + #[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, "actual:\n\n{}\n", writer); + } + #[test] fn test_empty_and_nested() { let s = r#"--- From 083df3ce4c8f23eddfe58b809bac0da8fe60ba0f Mon Sep 17 00:00:00 2001 From: Hendrik Sollich Date: Sat, 10 Jun 2017 23:39:07 +0200 Subject: [PATCH 2/3] correctly emitting nested arrays fixed #70 too --- parser/src/emitter.rs | 111 +++++++++++++++++++++++------------------- parser/src/yaml.rs | 7 +++ 2 files changed, 69 insertions(+), 49 deletions(-) diff --git a/parser/src/emitter.rs b/parser/src/emitter.rs index 91c94b2..c7bc154 100644 --- a/parser/src/emitter.rs +++ b/parser/src/emitter.rs @@ -134,39 +134,8 @@ impl<'a> YamlEmitter<'a> { fn emit_node_compact(&mut self, node: &Yaml) -> EmitResult { match *node { - Yaml::Array(ref v) => { - try!(write!(self.writer, "[")); - if self.level >= 0 { - try!(write!(self.writer, "")); - } - for (cnt, x) in v.iter().enumerate() { - if cnt > 0 { try!(write!(self.writer, ", ")); } - try!(self.emit_node(x)); - } - try!(write!(self.writer, "]")); - Ok(()) - }, - Yaml::Hash(ref h) => { - try!(self.writer.write_str("{")); - self.level += 1; - for (cnt, (k, v)) in h.iter().enumerate() { - if cnt > 0 { - try!(write!(self.writer, ", ")); - } - match *k { - // complex key is not supported - Yaml::Array(_) | Yaml::Hash(_) => { - return Err(EmitError::BadHashmapKey); - }, - _ => { try!(self.emit_node(k)); } - } - try!(write!(self.writer, ": ")); - try!(self.emit_node(v)); - } - try!(self.writer.write_str("}")); - self.level -= 1; - Ok(()) - }, + Yaml::Array(ref v) => self.emit_array_compact(v), + Yaml::Hash(ref h) => self.emit_hash_compact(h), _ => self.emit_node(node), } } @@ -213,19 +182,36 @@ impl<'a> YamlEmitter<'a> { try!(write!(self.writer, "[]")); } else { for (cnt, x) in v.iter().enumerate() { + self.level += 1; if cnt > 0 { try!(write!(self.writer, "\n")); } try!(self.write_indent()); - self.level += 1; try!(write!(self.writer, "- ")); - try!(self.emit_node(x)); + if self.level >= 1 && x.is_array() { + try!(self.emit_node_compact(x)); + } else { + try!(self.emit_node(x)); + } self.level -= 1; } } Ok(()) } + fn emit_array_compact(&mut self, v: &[Yaml]) -> EmitResult { + try!(write!(self.writer, "[")); + if self.level >= 0 { + try!(write!(self.writer, "")); + } + for (cnt, x) in v.iter().enumerate() { + if cnt > 0 { try!(write!(self.writer, ", ")); } + try!(self.emit_node(x)); + } + try!(write!(self.writer, "]")); + Ok(()) + } + fn emit_hash(&mut self, h: &Hash) -> EmitResult { if h.is_empty() { try!(self.writer.write_str("{}")); @@ -274,6 +260,29 @@ impl<'a> YamlEmitter<'a> { } Ok(()) } + + fn emit_hash_compact(&mut self, h: &Hash) -> EmitResult { + try!(self.writer.write_str("{")); + self.level += 1; + for (cnt, (k, v)) in h.iter().enumerate() { + if cnt > 0 { + try!(write!(self.writer, ", ")); + } + match *k { + // complex key is not supported + Yaml::Array(_) | Yaml::Hash(_) => { + return Err(EmitError::BadHashmapKey); + }, + _ => { try!(self.emit_node(k)); } + } + try!(write!(self.writer, ": ")); + try!(self.emit_node(v)); + } + try!(self.writer.write_str("}")); + self.level -= 1; + Ok(()) + } + } /// Check if the string requires quoting. @@ -315,7 +324,7 @@ fn need_quotes(string: &str) -> bool { } #[cfg(test)] -mod tests { +mod test { use super::*; use YamlLoader; @@ -330,15 +339,8 @@ a1: a2: 4 # i'm comment a3: [1, 2, 3] a4: - - - a1 - - a2 + - [a1, a2] - 2 - - [] - - {} -a5: 'single_quoted' -a6: \"double_quoted\" -a7: 你好 -'key 1': \"ddd\\\tbbb\" "; @@ -349,7 +351,12 @@ a7: 你好 let mut emitter = YamlEmitter::new(&mut writer); emitter.dump(doc).unwrap(); } - let docs_new = YamlLoader::load_from_str(&s).unwrap(); + println!("original:\n{}", s); + println!("emitted:\n{}", writer); + let docs_new = match YamlLoader::load_from_str(&writer) { + Ok(y) => y, + Err(e) => panic!(format!("{}", e)) + }; let doc_new = &docs_new[0]; assert_eq!(doc, doc_new); @@ -383,7 +390,10 @@ products: let mut emitter = YamlEmitter::new(&mut writer); emitter.dump(doc).unwrap(); } - let docs_new = YamlLoader::load_from_str(&s).unwrap(); + let docs_new = match YamlLoader::load_from_str(&writer) { + Ok(y) => y, + Err(e) => panic!(format!("{}", e)) + }; let doc_new = &docs_new[0]; assert_eq!(doc, doc_new); } @@ -470,6 +480,9 @@ bool1: false"#; assert_eq!(expected, writer, "actual:\n\n{}\n", writer); } +//(left: `"---\na:\n b:\n c: hello\n d: {}\ne:\n- f\n- g\n- h: []"`, +//right: `"---\na:\n b:\n c: hello\n d: {}\ne:\n - f\n - g\n - h: []"`) + #[test] fn test_empty_and_nested() { let s = r#"--- @@ -478,9 +491,9 @@ a: c: hello d: {} e: -- f -- g -- h: []"#; + - f + - g + - h: []"#; let docs = YamlLoader::load_from_str(&s).unwrap(); let doc = &docs[0]; diff --git a/parser/src/yaml.rs b/parser/src/yaml.rs index d745dc6..b8ccdbd 100644 --- a/parser/src/yaml.rs +++ b/parser/src/yaml.rs @@ -266,6 +266,13 @@ impl Yaml { } } + pub fn is_array(&self) -> bool { + match *self { + Yaml::Array(_) => true, + _ => false + } + } + pub fn as_f64(&self) -> Option { match *self { Yaml::Real(ref v) => parse_f64(v), From aaf06fbce9926ba189e20e55c6cc51e8230f6e30 Mon Sep 17 00:00:00 2001 From: Hendrik Sollich Date: Sun, 11 Jun 2017 00:19:20 +0200 Subject: [PATCH 3/3] correctly emitting deeply nested arrays --- parser/src/emitter.rs | 86 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/parser/src/emitter.rs b/parser/src/emitter.rs index c7bc154..7a5662e 100644 --- a/parser/src/emitter.rs +++ b/parser/src/emitter.rs @@ -142,7 +142,7 @@ impl<'a> YamlEmitter<'a> { fn emit_node(&mut self, node: &Yaml) -> EmitResult { match *node { - Yaml::Array(ref v) => self.emit_array(v), + Yaml::Array(ref v) => self.emit_array(v, !node.is_array()), Yaml::Hash(ref h) => self.emit_hash(h), Yaml::String(ref v) => { if need_quotes(v) { @@ -177,7 +177,7 @@ impl<'a> YamlEmitter<'a> { } } - fn emit_array(&mut self, v: &[Yaml]) -> EmitResult { + fn emit_array(&mut self, v: &[Yaml], indent_first: bool) -> EmitResult { if v.is_empty() { try!(write!(self.writer, "[]")); } else { @@ -186,12 +186,14 @@ impl<'a> YamlEmitter<'a> { if cnt > 0 { try!(write!(self.writer, "\n")); } - try!(self.write_indent()); + if cnt > 0 || indent_first { + try!(self.write_indent()); + } try!(write!(self.writer, "- ")); - if self.level >= 1 && x.is_array() { + if self.level > 2 { try!(self.emit_node_compact(x)); } else { - try!(self.emit_node(x)); + try!(self.emit_node(x)); } self.level -= 1; } @@ -237,7 +239,7 @@ impl<'a> YamlEmitter<'a> { } else { try!(write!(self.writer, ":\n")); } - try!(self.emit_array(v)); + try!(self.emit_array(v, true)); } Yaml::Hash(ref h) => { if h.is_empty() { @@ -480,9 +482,6 @@ bool1: false"#; assert_eq!(expected, writer, "actual:\n\n{}\n", writer); } -//(left: `"---\na:\n b:\n c: hello\n d: {}\ne:\n- f\n- g\n- h: []"`, -//right: `"---\na:\n b:\n c: hello\n d: {}\ne:\n - f\n - g\n - h: []"`) - #[test] fn test_empty_and_nested() { let s = r#"--- @@ -505,4 +504,73 @@ e: 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); + } + }