From ff8572352dc3dbd403a65212673fd85606eeaa7d Mon Sep 17 00:00:00 2001 From: Charlie Ozinga Date: Thu, 11 May 2017 12:36:38 -0600 Subject: [PATCH 1/5] Rebased and resolved conflicts with the following: https://github.com/chyh1990/yaml-rust/pull/66 https://github.com/chyh1990/yaml-rust/pull/62 (closed in favor of 66) --- saphyr/src/emitter.rs | 68 +++++++++++---------------------------- saphyr/tests/spec_test.rs | 66 +++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 49 deletions(-) diff --git a/saphyr/src/emitter.rs b/saphyr/src/emitter.rs index 45f2024..ba0bf7e 100644 --- a/saphyr/src/emitter.rs +++ b/saphyr/src/emitter.rs @@ -132,48 +132,6 @@ impl<'a> YamlEmitter<'a> { Ok(()) } - 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, "+ ")); - } - 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(()) - }, - 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(()) - }, - _ => self.emit_node(node), - } - } - fn emit_node(&mut self, node: &Yaml) -> EmitResult { match *node { Yaml::Array(ref v) => self.emit_array(v), @@ -236,28 +194,37 @@ impl<'a> YamlEmitter<'a> { } else { self.level += 1; for (cnt, (k, v)) in h.iter().enumerate() { + let complex_key = match *k { + Yaml::Hash(_) | Yaml::Array(_) => true, + _ => false, + }; if cnt > 0 { try!(write!(self.writer, "\n")); try!(self.write_indent()); } - match *k { - Yaml::Array(_) | Yaml::Hash(_) => { - try!(self.emit_node_compact(k)); - } - _ => { - try!(self.emit_node(k)); - } + if complex_key { + try!(write!(self.writer, "? ")); + self.level += 1; + try!(self.emit_node(k)); + self.level -= 1; + try!(write!(self.writer, "\n")); + try!(self.write_indent()); + } else { + try!(self.emit_node(k)); } match *v { Yaml::Array(ref v) => { + if complex_key { self.level += 1; } if v.is_empty() { try!(write!(self.writer, ": ")); } else { try!(write!(self.writer, ":\n")); } try!(self.emit_array(v)); + if complex_key { self.level -= 1; } } Yaml::Hash(ref h) => { + if complex_key { self.level += 1; } if h.is_empty() { try!(write!(self.writer, ": ")); } else { @@ -267,10 +234,13 @@ impl<'a> YamlEmitter<'a> { self.level -= 1; } try!(self.emit_hash(h)); + if complex_key { self.level -= 1; } } _ => { + if complex_key { self.level += 1; } try!(write!(self.writer, ": ")); try!(self.emit_node(v)); + if complex_key { self.level -= 1; } } } } diff --git a/saphyr/tests/spec_test.rs b/saphyr/tests/spec_test.rs index b7316fc..3e5d043 100644 --- a/saphyr/tests/spec_test.rs +++ b/saphyr/tests/spec_test.rs @@ -73,3 +73,69 @@ include!("spec_test.rs.inc"); //#[test] //fn test_hc_alias() { //} + +#[test] +fn test_mapvec_legal() { + use yaml_rust::yaml::{Array, Hash, Yaml}; + use yaml_rust::{YamlLoader, YamlEmitter}; + + // Emitting a `map>, _>` should result in legal yaml that + // we can parse. + + let mut key = Array::new(); + key.push(Yaml::Integer(1)); + key.push(Yaml::Integer(2)); + key.push(Yaml::Integer(3)); + + let mut keyhash = Hash::new(); + keyhash.insert(Yaml::String("key".into()), Yaml::Array(key)); + + let mut val = Array::new(); + val.push(Yaml::Integer(4)); + val.push(Yaml::Integer(5)); + val.push(Yaml::Integer(6)); + + let mut hash = Hash::new(); + hash.insert(Yaml::Hash(keyhash), Yaml::Array(val)); + + let mut out_str = String::new(); + { + let mut emitter = YamlEmitter::new(&mut out_str); + emitter.dump(&Yaml::Hash(hash)).unwrap(); + } + + // At this point, we are tempted to naively render like this: + // + // ```yaml + // --- + // {key: + // - 1 + // - 2 + // - 3}: + // - 4 + // - 5 + // - 6 + // ``` + // + // However, this doesn't work, because the key sequence [1, 2, 3] is + // rendered in block mode, which is not legal (as far as I can tell) + // inside the flow mode of the key. We need to either fully render + // everything that's in a key in flow mode (which may make for some + // long lines), or use the explicit map identifier '?': + // + // ```yaml + // --- + // ? + // key: + // - 1 + // - 2 + // - 3 + // : + // - 4 + // - 5 + // - 6 + // ``` + + YamlLoader::load_from_str(&out_str).unwrap(); +} + From 06c9b22357002425c6cbb077b1a815c21ae1cea2 Mon Sep 17 00:00:00 2001 From: Charlie Ozinga Date: Thu, 11 May 2017 23:29:41 -0600 Subject: [PATCH 2/5] Fix nested arrays, emit compact in-line --- saphyr/src/emitter.rs | 112 +++++++++++++++++++++++--------------- saphyr/tests/spec_test.rs | 2 + 2 files changed, 70 insertions(+), 44 deletions(-) diff --git a/saphyr/src/emitter.rs b/saphyr/src/emitter.rs index ba0bf7e..065d46b 100644 --- a/saphyr/src/emitter.rs +++ b/saphyr/src/emitter.rs @@ -3,6 +3,15 @@ use std::convert::From; use std::error::Error; use yaml::{Hash, Yaml}; +/// If the emitter should output in 'compact inline notation' form, as +/// described for block +/// [sequences](http://www.yaml.org/spec/1.2/spec.html#id2797382) and +/// [mappings](http://www.yaml.org/spec/1.2/spec.html#id2798057). In +/// this form, blocks cannot have any properties (such as anchors or +/// tags), which should be OK, because this emitter doesn't (currently) +/// emit those anyways. +pub const COMPACT: bool = true; + #[derive(Copy, Clone, Debug)] pub enum EmitError { FmtError(fmt::Error), @@ -174,16 +183,16 @@ impl<'a> YamlEmitter<'a> { if v.is_empty() { try!(write!(self.writer, "[]")); } else { + self.level += 1; for (cnt, x) in v.iter().enumerate() { if cnt > 0 { try!(write!(self.writer, "\n")); + try!(self.write_indent()); } - try!(self.write_indent()); - self.level += 1; - try!(write!(self.writer, "- ")); - try!(self.emit_node(x)); - self.level -= 1; + try!(write!(self.writer, "-")); + try!(self.emit_val(true, x)); } + self.level -= 1; } Ok(()) } @@ -203,51 +212,57 @@ impl<'a> YamlEmitter<'a> { try!(self.write_indent()); } if complex_key { - try!(write!(self.writer, "? ")); - self.level += 1; - try!(self.emit_node(k)); - self.level -= 1; + try!(write!(self.writer, "?")); + try!(self.emit_val(true, k)); try!(write!(self.writer, "\n")); try!(self.write_indent()); + try!(write!(self.writer, ":")); + try!(self.emit_val(true, v)); } else { try!(self.emit_node(k)); - } - match *v { - Yaml::Array(ref v) => { - if complex_key { self.level += 1; } - if v.is_empty() { - try!(write!(self.writer, ": ")); - } else { - try!(write!(self.writer, ":\n")); - } - try!(self.emit_array(v)); - if complex_key { self.level -= 1; } - } - Yaml::Hash(ref h) => { - if complex_key { self.level += 1; } - if h.is_empty() { - try!(write!(self.writer, ": ")); - } else { - try!(write!(self.writer, ":\n")); - self.level += 1; - try!(self.write_indent()); - self.level -= 1; - } - try!(self.emit_hash(h)); - if complex_key { self.level -= 1; } - } - _ => { - if complex_key { self.level += 1; } - try!(write!(self.writer, ": ")); - try!(self.emit_node(v)); - if complex_key { self.level -= 1; } - } + try!(write!(self.writer, ":")); + try!(self.emit_val(false, v)); } } self.level -= 1; } Ok(()) } + + /// Emit a yaml as a hash or array value: i.e., which should appear + /// following a ":" or "-", either after a space, or on a new line. + /// If `inline` is true, then the preceeding characters are distinct + /// and short enough to respects the COMPACT constant. + fn emit_val(&mut self, inline: bool, val: &Yaml) -> EmitResult { + match *val { + Yaml::Array(ref v) => { + if (inline && COMPACT) || v.is_empty() { + try!(write!(self.writer, " ")); + } else { + try!(write!(self.writer, "\n")); + self.level += 1; + try!(self.write_indent()); + self.level -= 1; + } + self.emit_array(v) + }, + Yaml::Hash(ref h) => { + if (inline && COMPACT) || h.is_empty() { + try!(write!(self.writer, " ")); + } else { + try!(write!(self.writer, "\n")); + self.level += 1; + try!(self.write_indent()); + self.level -= 1; + } + self.emit_hash(h) + }, + _ => { + try!(write!(self.writer, " ")); + self.emit_node(val) + } + } + } } /// Check if the string requires quoting. @@ -406,15 +421,24 @@ y: string with spaces"#; #[test] fn test_empty_and_nested() { - let s = r#"--- + let s = if COMPACT { r#"--- a: b: c: hello d: {} e: -- f -- g -- h: []"#; + - 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]; diff --git a/saphyr/tests/spec_test.rs b/saphyr/tests/spec_test.rs index 3e5d043..1896d17 100644 --- a/saphyr/tests/spec_test.rs +++ b/saphyr/tests/spec_test.rs @@ -136,6 +136,8 @@ fn test_mapvec_legal() { // - 6 // ``` + println!("{}", out_str); + YamlLoader::load_from_str(&out_str).unwrap(); } From 80f967bc09bf2396af7770d4bf02d6d7ad2b51a2 Mon Sep 17 00:00:00 2001 From: Charlie Ozinga Date: Thu, 11 May 2017 23:36:51 -0600 Subject: [PATCH 3/5] Remove extraneous debug output from test --- saphyr/tests/spec_test.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/saphyr/tests/spec_test.rs b/saphyr/tests/spec_test.rs index 1896d17..3e5d043 100644 --- a/saphyr/tests/spec_test.rs +++ b/saphyr/tests/spec_test.rs @@ -136,8 +136,6 @@ fn test_mapvec_legal() { // - 6 // ``` - println!("{}", out_str); - YamlLoader::load_from_str(&out_str).unwrap(); } From 1cfd356df8a1511bc50362e543d3f951385be15f Mon Sep 17 00:00:00 2001 From: Charlie Ozinga Date: Tue, 23 May 2017 12:17:50 -0600 Subject: [PATCH 4/5] Move the compact flag into the emitter itself --- saphyr/src/emitter.rs | 45 +++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/saphyr/src/emitter.rs b/saphyr/src/emitter.rs index 065d46b..2ed20c2 100644 --- a/saphyr/src/emitter.rs +++ b/saphyr/src/emitter.rs @@ -3,14 +3,6 @@ use std::convert::From; use std::error::Error; use yaml::{Hash, Yaml}; -/// If the emitter should output in 'compact inline notation' form, as -/// described for block -/// [sequences](http://www.yaml.org/spec/1.2/spec.html#id2797382) and -/// [mappings](http://www.yaml.org/spec/1.2/spec.html#id2798057). In -/// this form, blocks cannot have any properties (such as anchors or -/// tags), which should be OK, because this emitter doesn't (currently) -/// emit those anyways. -pub const COMPACT: bool = true; #[derive(Copy, Clone, Debug)] pub enum EmitError { @@ -45,6 +37,7 @@ impl From for EmitError { pub struct YamlEmitter<'a> { writer: &'a mut fmt::Write, best_indent: usize, + compact: bool, level: isize, } @@ -119,11 +112,29 @@ impl<'a> YamlEmitter<'a> { YamlEmitter { writer: writer, best_indent: 2, + compact: true, level: -1 } } + /// Set 'compact inline notation' on or off, as described for block + /// [sequences](http://www.yaml.org/spec/1.2/spec.html#id2797382) + /// and + /// [mappings](http://www.yaml.org/spec/1.2/spec.html#id2798057). + /// + /// In this form, blocks cannot have any properties (such as anchors + /// or tags), which should be OK, because this emitter doesn't + /// (currently) emit those anyways. + pub fn compact(&mut self, compact: bool) { + self.compact = compact; + } + + /// Determine if this emitter is using 'compact inline notation'. + pub fn is_compact(&self) -> bool { + self.compact + } + pub fn dump(&mut self, doc: &Yaml) -> EmitResult { // write DocumentStart try!(write!(self.writer, "---\n")); @@ -232,11 +243,11 @@ impl<'a> YamlEmitter<'a> { /// Emit a yaml as a hash or array value: i.e., which should appear /// following a ":" or "-", either after a space, or on a new line. /// If `inline` is true, then the preceeding characters are distinct - /// and short enough to respects the COMPACT constant. + /// and short enough to respect the compact flag. fn emit_val(&mut self, inline: bool, val: &Yaml) -> EmitResult { match *val { Yaml::Array(ref v) => { - if (inline && COMPACT) || v.is_empty() { + if (inline && self.compact) || v.is_empty() { try!(write!(self.writer, " ")); } else { try!(write!(self.writer, "\n")); @@ -247,7 +258,7 @@ impl<'a> YamlEmitter<'a> { self.emit_array(v) }, Yaml::Hash(ref h) => { - if (inline && COMPACT) || h.is_empty() { + if (inline && self.compact) || h.is_empty() { try!(write!(self.writer, " ")); } else { try!(write!(self.writer, "\n")); @@ -421,7 +432,16 @@ y: string with spaces"#; #[test] fn test_empty_and_nested() { - let s = if COMPACT { r#"--- + 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 @@ -445,6 +465,7 @@ e: let mut writer = String::new(); { let mut emitter = YamlEmitter::new(&mut writer); + emitter.compact(compact); emitter.dump(doc).unwrap(); } From 078e1e882d6fe6252439c809da8eb19411028f15 Mon Sep 17 00:00:00 2001 From: Charlie Ozinga Date: Mon, 10 Jul 2017 10:08:20 -0600 Subject: [PATCH 5/5] Version bump --- saphyr/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/saphyr/Cargo.toml b/saphyr/Cargo.toml index a4e2fda..91dac70 100644 --- a/saphyr/Cargo.toml +++ b/saphyr/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yaml-rust" -version = "0.3.6" +version = "0.3.7" authors = ["Yuheng Chen "] homepage = "http://chyh1990.github.io/yaml-rust/" documentation = "http://chyh1990.github.io/yaml-rust/doc/yaml_rust/"