Emit multi-line string values as block scalars
This commit is contained in:
parent
a33d0ffe68
commit
5369a002ba
3 changed files with 92 additions and 2 deletions
|
@ -109,3 +109,16 @@ pub(crate) fn is_uri_char(c: char) -> bool {
|
||||||
pub(crate) fn is_tag_char(c: char) -> bool {
|
pub(crate) fn is_tag_char(c: char) -> bool {
|
||||||
is_uri_char(c) && !is_flow(c) && c != '!'
|
is_uri_char(c) && !is_flow(c) && c != '!'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the string can be expressed a valid literal block scalar.
|
||||||
|
/// The YAML spec supports all of the following in block literals except #xFEFF:
|
||||||
|
/// ```ignore
|
||||||
|
/// #x9 | #xA | [#x20-#x7E] /* 8 bit */
|
||||||
|
/// | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] /* 16 bit */
|
||||||
|
/// | [#x10000-#x10FFFF] /* 32 bit */
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn is_valid_literal_block_scalar(string: &str) -> bool {
|
||||||
|
string.chars().all(|character: char|
|
||||||
|
matches!(character, '\t' | '\n' | '\x20'..='\x7e' | '\u{0085}' | '\u{00a0}'..='\u{d7fff}'))
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::char_traits;
|
||||||
use crate::yaml::{Hash, Yaml};
|
use crate::yaml::{Hash, Yaml};
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
@ -35,8 +36,8 @@ pub struct YamlEmitter<'a> {
|
||||||
writer: &'a mut dyn fmt::Write,
|
writer: &'a mut dyn fmt::Write,
|
||||||
best_indent: usize,
|
best_indent: usize,
|
||||||
compact: bool,
|
compact: bool,
|
||||||
|
|
||||||
level: isize,
|
level: isize,
|
||||||
|
multiline_strings: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type EmitResult = Result<(), EmitError>;
|
pub type EmitResult = Result<(), EmitError>;
|
||||||
|
@ -111,6 +112,7 @@ impl<'a> YamlEmitter<'a> {
|
||||||
best_indent: 2,
|
best_indent: 2,
|
||||||
compact: true,
|
compact: true,
|
||||||
level: -1,
|
level: -1,
|
||||||
|
multiline_strings: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +134,40 @@ impl<'a> YamlEmitter<'a> {
|
||||||
self.compact
|
self.compact
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Render strings containing multiple lines in [literal style].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use yaml_rust2::{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 emit multiline strings when appropriate.
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_multiline_strings(&self) -> bool {
|
||||||
|
self.multiline_strings
|
||||||
|
}
|
||||||
|
|
||||||
pub fn dump(&mut self, doc: &Yaml) -> EmitResult {
|
pub fn dump(&mut self, doc: &Yaml) -> EmitResult {
|
||||||
// write DocumentStart
|
// write DocumentStart
|
||||||
writeln!(self.writer, "---")?;
|
writeln!(self.writer, "---")?;
|
||||||
|
@ -156,7 +192,20 @@ impl<'a> YamlEmitter<'a> {
|
||||||
Yaml::Array(ref v) => self.emit_array(v),
|
Yaml::Array(ref v) => self.emit_array(v),
|
||||||
Yaml::Hash(ref h) => self.emit_hash(h),
|
Yaml::Hash(ref h) => self.emit_hash(h),
|
||||||
Yaml::String(ref v) => {
|
Yaml::String(ref v) => {
|
||||||
if need_quotes(v) {
|
if self.multiline_strings
|
||||||
|
&& v.contains('\n')
|
||||||
|
&& char_traits::is_valid_literal_block_scalar(v)
|
||||||
|
{
|
||||||
|
write!(self.writer, "|")?;
|
||||||
|
self.level += 1;
|
||||||
|
for line in v.lines() {
|
||||||
|
writeln!(self.writer)?;
|
||||||
|
self.write_indent()?;
|
||||||
|
// It's literal text, so don't escape special chars.
|
||||||
|
write!(self.writer, "{line}")?;
|
||||||
|
}
|
||||||
|
self.level -= 1;
|
||||||
|
} else if need_quotes(v) {
|
||||||
escape_str(self.writer, v)?;
|
escape_str(self.writer, v)?;
|
||||||
} else {
|
} else {
|
||||||
write!(self.writer, "{v}")?;
|
write!(self.writer, "{v}")?;
|
||||||
|
@ -334,3 +383,19 @@ fn need_quotes(string: &str) -> bool {
|
||||||
|| string.parse::<i64>().is_ok()
|
|| string.parse::<i64>().is_ok()
|
||||||
|| string.parse::<f64>().is_ok()
|
|| string.parse::<f64>().is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::YamlEmitter;
|
||||||
|
use crate::YamlLoader;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_multiline_string() {
|
||||||
|
let input = r#"{foo: "bar!\nbar!", baz: 42}"#;
|
||||||
|
let parsed = YamlLoader::load_from_str(input).unwrap();
|
||||||
|
let mut output = String::new();
|
||||||
|
let mut emitter = YamlEmitter::new(&mut output);
|
||||||
|
emitter.multiline_strings(true);
|
||||||
|
emitter.dump(&parsed[0]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -68,3 +68,15 @@ fn test_issue133() {
|
||||||
let doc2 = YamlLoader::load_from_str(&out_str).unwrap().pop().unwrap();
|
let doc2 = YamlLoader::load_from_str(&out_str).unwrap().pop().unwrap();
|
||||||
assert_eq!(doc, doc2); // This failed because the type has changed to a number now
|
assert_eq!(doc, doc2); // This failed because the type has changed to a number now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_newline() {
|
||||||
|
let y = Yaml::Array(vec![Yaml::String("\n".to_owned())]);
|
||||||
|
roundtrip(&y);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_crlf() {
|
||||||
|
let y = Yaml::Array(vec![Yaml::String("\r\n".to_owned())]);
|
||||||
|
roundtrip(&y);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue