diff --git a/src/emitter.rs b/src/emitter.rs index 09e9f87..06cb45b 100644 --- a/src/emitter.rs +++ b/src/emitter.rs @@ -41,6 +41,8 @@ pub struct YamlEmitter<'a> { writer: &'a mut fmt::Write, best_indent: usize, compact: bool, + escape_all_strings: bool, + multiline_strings: bool, level: isize, } @@ -117,6 +119,8 @@ impl<'a> YamlEmitter<'a> { best_indent: 2, compact: true, level: -1, + escape_all_strings: false, + multiline_strings: false, } } @@ -137,6 +141,55 @@ impl<'a> YamlEmitter<'a> { self.compact } + /// Wrap all `YAML::String` nodes in double-quotes and escape special characters (if any), + /// regardless of whether or not they contain special characters. + /// + /// This maintains typing, ensuring that `example: "0x00"` is not emitted as `example: 0x00`. + pub fn escape_all_strings(&mut self, escape_all_strings: bool) { + self.escape_all_strings = escape_all_strings + } + + /// Determine if this emitter will wrap all strings in double-quotes. + pub fn is_escape_all_strings(&self) -> bool { + self.escape_all_strings + } + + /// Render strings containing multiple lines in [literal style]. + /// + /// # Examples + /// + /// ```rust + /// use yaml_rust::{Yaml, YamlEmitter, YamlLoader}; + /// + /// let input = r#"{foo: "bar!\nbar!", baz: 42}"#; + /// let parsed = YamlLoader::load_from_str(input).unwrap(); + /// eprintln!("{:?}", parsed); + /// + /// let mut output = String::new(); + /// # { + /// let mut emitter = YamlEmitter::new(&mut output); + /// emitter.multiline_strings(true); + /// emitter.dump(&parsed[0]).unwrap(); + /// # } + /// + /// assert_eq!(output.as_str(), "\ + /// --- + /// foo: | + /// bar! + /// bar! + /// baz: 42"); + /// ``` + /// + /// [literal style]: https://yaml.org/spec/1.2/spec.html#id2795688 + pub fn multiline_strings(&mut self, multiline_strings: bool) { + self.multiline_strings = multiline_strings + } + + /// Determine if this emitter will wrap all strings in double-quotes. + pub fn is_multiline_strings(&self) -> bool { + self.multiline_strings + } + pub fn dump(&mut self, doc: &Yaml) -> EmitResult { // write DocumentStart writeln!(self.writer, "---")?; @@ -161,7 +214,16 @@ impl<'a> YamlEmitter<'a> { Yaml::Array(ref v) => self.emit_array(v), Yaml::Hash(ref h) => self.emit_hash(h), Yaml::String(ref v) => { - if need_quotes(v) { + if self.multiline_strings && v.contains('\n') { + write!(self.writer, "|")?; + self.level += 1; + for line in v.lines() { + writeln!(self.writer)?; + self.write_indent()?; + write!(self.writer, "{}", line)?; + } + self.level -= 1; + } else if self.escape_all_strings || need_quotes(v) { escape_str(self.writer, v)?; } else { write!(self.writer, "{}", v)?; @@ -638,4 +700,40 @@ a: assert_eq!(s, writer); } + #[test] + fn test_escape_all_strings() { + let s = r#"--- +example: "0x00""#; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + + let mut wr = String::new(); + { + let mut emitter = YamlEmitter::new(&mut wr); + assert_eq!(emitter.is_escape_all_strings(), false); + emitter.dump(doc).unwrap(); + } + + assert_eq!( + wr, + r#"--- +example: 0x00"# + ); + + let mut wr = String::new(); + { + let mut emitter = YamlEmitter::new(&mut wr); + emitter.escape_all_strings(true); + assert_eq!(emitter.is_escape_all_strings(), true); + emitter.dump(doc).unwrap(); + } + + assert_eq!( + wr, + r#"--- +"example": "0x00""# + ); + } + }