make single lines also pretty
This commit is contained in:
parent
83be829f0b
commit
dabe477d64
150
src/ser.rs
150
src/ser.rs
|
@ -201,6 +201,7 @@ enum State<'a> {
|
||||||
parent: &'a State<'a>,
|
parent: &'a State<'a>,
|
||||||
first: &'a Cell<bool>,
|
first: &'a Cell<bool>,
|
||||||
type_: &'a Cell<Option<&'static str>>,
|
type_: &'a Cell<Option<&'static str>>,
|
||||||
|
len: Option<usize>,
|
||||||
},
|
},
|
||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
|
@ -210,6 +211,7 @@ pub struct SerializeSeq<'a: 'b, 'b> {
|
||||||
ser: &'b mut Serializer<'a>,
|
ser: &'b mut Serializer<'a>,
|
||||||
first: Cell<bool>,
|
first: Cell<bool>,
|
||||||
type_: Cell<Option<&'static str>>,
|
type_: Cell<Option<&'static str>>,
|
||||||
|
len: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -377,12 +379,12 @@ impl<'a> Serializer<'a> {
|
||||||
fn _emit_key(&mut self, state: &State) -> Result<(), Error> {
|
fn _emit_key(&mut self, state: &State) -> Result<(), Error> {
|
||||||
match *state {
|
match *state {
|
||||||
State::End => Ok(()),
|
State::End => Ok(()),
|
||||||
State::Array { parent, first, type_ } => {
|
State::Array { parent, first, type_, len } => {
|
||||||
assert!(type_.get().is_some());
|
assert!(type_.get().is_some());
|
||||||
if first.get() {
|
if first.get() {
|
||||||
self._emit_key(parent)?;
|
self._emit_key(parent)?;
|
||||||
}
|
}
|
||||||
self.emit_array(first)
|
self.emit_array(first, len)
|
||||||
}
|
}
|
||||||
State::Table { parent, first, table_emitted, key } => {
|
State::Table { parent, first, table_emitted, key } => {
|
||||||
if table_emitted.get() {
|
if table_emitted.get() {
|
||||||
|
@ -399,9 +401,16 @@ impl<'a> Serializer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_array(&mut self, first: &Cell<bool>) -> Result<(), Error> {
|
fn emit_array(&mut self, first: &Cell<bool>, len: Option<usize>) -> Result<(), Error> {
|
||||||
match self.settings.array {
|
match (len, &self.settings.array) {
|
||||||
Some(ref a) => {
|
(Some(0...1), _) | (_, &None) => {
|
||||||
|
if first.get() {
|
||||||
|
self.dst.push_str("[")
|
||||||
|
} else {
|
||||||
|
self.dst.push_str(", ")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(_, &Some(ref a)) => {
|
||||||
if first.get() {
|
if first.get() {
|
||||||
self.dst.push_str("[\n")
|
self.dst.push_str("[\n")
|
||||||
} else {
|
} else {
|
||||||
|
@ -411,13 +420,6 @@ impl<'a> Serializer<'a> {
|
||||||
self.dst.push_str(" ");
|
self.dst.push_str(" ");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => {
|
|
||||||
if first.get() {
|
|
||||||
self.dst.push_str("[")
|
|
||||||
} else {
|
|
||||||
self.dst.push_str(", ")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -456,41 +458,95 @@ impl<'a> Serializer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_str(&mut self, value: &str, is_key: bool) -> Result<(), Error> {
|
fn emit_str(&mut self, value: &str, is_key: bool) -> Result<(), Error> {
|
||||||
let do_pretty = if !is_key && self.settings.pretty_string {
|
/// Use ''' or '
|
||||||
// do pretty only if the block contains at least one newline
|
#[derive(PartialEq)]
|
||||||
value.contains('\n')
|
enum Multi {
|
||||||
// but not if it contains any of these, as they are not
|
False,
|
||||||
// representable
|
True,
|
||||||
&& !value.contains("'''")
|
Single,
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
if do_pretty {
|
|
||||||
drop(write!(self.dst, "'''\n"));
|
|
||||||
} else {
|
|
||||||
drop(write!(self.dst, "\""));
|
|
||||||
}
|
}
|
||||||
if do_pretty {
|
fn do_pretty(value: &str) -> Option<(Multi, String)> {
|
||||||
drop(write!(self.dst, "{}", value));
|
// For doing pretty prints we store in a new String
|
||||||
|
// because there are too many cases where pretty cannot
|
||||||
|
// work. We need to determine:
|
||||||
|
// - if we are a "multi-line" pretty (if there are \n)
|
||||||
|
// - if ['''] appears if multi or ['] if single
|
||||||
|
// - if there are any invalid control characters
|
||||||
|
//
|
||||||
|
// Doing it any other way would require multiple passes
|
||||||
|
// to determine if a pretty string works or not.
|
||||||
|
let mut out = String::with_capacity(value.len() * 2);
|
||||||
|
let mut multi = Multi::False;
|
||||||
|
// found consecutive single quotes
|
||||||
|
let mut max_found_singles = 0;
|
||||||
|
let mut found_singles = 0;
|
||||||
|
|
||||||
|
for ch in value.chars() {
|
||||||
|
if ch == '\'' {
|
||||||
|
found_singles += 1;
|
||||||
|
if found_singles >= 3 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if found_singles > max_found_singles {
|
||||||
|
max_found_singles = found_singles;
|
||||||
|
}
|
||||||
|
found_singles = 0
|
||||||
|
}
|
||||||
|
match ch {
|
||||||
|
'\t' => {},
|
||||||
|
'\n' => multi = Multi::True,
|
||||||
|
// note that the following are invalid: \b \f \r
|
||||||
|
c if c < '\u{1f}' => return None, // Invalid control character
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
out.push(ch);
|
||||||
|
}
|
||||||
|
if found_singles > max_found_singles {
|
||||||
|
max_found_singles = found_singles;
|
||||||
|
}
|
||||||
|
debug_assert!(max_found_singles < 3);
|
||||||
|
if multi == Multi::False && max_found_singles >= 1 {
|
||||||
|
// no newlines, but must use ''' because it has ' in it
|
||||||
|
multi = Multi::Single;
|
||||||
|
}
|
||||||
|
Some((multi, out))
|
||||||
|
}
|
||||||
|
|
||||||
|
let pretty = if !is_key && self.settings.pretty_string {
|
||||||
|
do_pretty(value)
|
||||||
} else {
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
if let Some((multi, s)) = pretty {
|
||||||
|
// A pretty string
|
||||||
|
match multi {
|
||||||
|
Multi::True => self.dst.push_str("'''\n"),
|
||||||
|
Multi::Single => self.dst.push_str("'''"),
|
||||||
|
Multi::False => self.dst.push('\''),
|
||||||
|
}
|
||||||
|
self.dst.push_str(&s);
|
||||||
|
match multi {
|
||||||
|
Multi::False => self.dst.push('\''),
|
||||||
|
_ => self.dst.push_str("'''"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not a pretty string
|
||||||
|
drop(write!(self.dst, "\""));
|
||||||
for ch in value.chars() {
|
for ch in value.chars() {
|
||||||
match ch {
|
match ch {
|
||||||
'\u{8}' => drop(write!(self.dst, "\\b")),
|
'\u{8}' => self.dst.push_str("\\b"),
|
||||||
'\u{9}' => drop(write!(self.dst, "\\t")),
|
'\u{9}' => self.dst.push_str("\\t"),
|
||||||
'\u{a}' => drop(write!(self.dst, "\\n")),
|
'\u{a}' => self.dst.push_str("\\n"),
|
||||||
'\u{c}' => drop(write!(self.dst, "\\f")),
|
'\u{c}' => self.dst.push_str("\\f"),
|
||||||
'\u{d}' => drop(write!(self.dst, "\\r")),
|
'\u{d}' => self.dst.push_str("\\r"),
|
||||||
'\u{22}' => drop(write!(self.dst, "\\\"")),
|
'\u{22}' => self.dst.push_str("\\\""),
|
||||||
'\u{5c}' => drop(write!(self.dst, "\\\\")),
|
'\u{5c}' => self.dst.push_str("\\\\"),
|
||||||
c if c < '\u{1f}' => drop(write!(self.dst, "\\u{:04X}", ch as u32)),
|
c if c < '\u{1f}' => drop(write!(self.dst, "\\u{:04X}", ch as u32)),
|
||||||
ch => drop(write!(self.dst, "{}", ch)),
|
ch => self.dst.push(ch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
self.dst.push_str("\"");
|
||||||
if do_pretty {
|
|
||||||
drop(write!(self.dst, "'''"));
|
|
||||||
} else {
|
|
||||||
drop(write!(self.dst, "\""));
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -703,13 +759,14 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> {
|
||||||
Err(Error::UnsupportedType)
|
Err(Error::UnsupportedType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_seq(mut self, _len: Option<usize>)
|
fn serialize_seq(mut self, len: Option<usize>)
|
||||||
-> Result<Self::SerializeSeq, Self::Error> {
|
-> Result<Self::SerializeSeq, Self::Error> {
|
||||||
self.array_type("array")?;
|
self.array_type("array")?;
|
||||||
Ok(SerializeSeq {
|
Ok(SerializeSeq {
|
||||||
ser: self,
|
ser: self,
|
||||||
first: Cell::new(true),
|
first: Cell::new(true),
|
||||||
type_: Cell::new(None),
|
type_: Cell::new(None),
|
||||||
|
len: len,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,6 +839,7 @@ impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> {
|
||||||
parent: &self.ser.state,
|
parent: &self.ser.state,
|
||||||
first: &self.first,
|
first: &self.first,
|
||||||
type_: &self.type_,
|
type_: &self.type_,
|
||||||
|
len: self.len,
|
||||||
},
|
},
|
||||||
settings: self.ser.settings.clone(),
|
settings: self.ser.settings.clone(),
|
||||||
})?;
|
})?;
|
||||||
|
@ -793,16 +851,16 @@ impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> {
|
||||||
match self.type_.get() {
|
match self.type_.get() {
|
||||||
Some("table") => return Ok(()),
|
Some("table") => return Ok(()),
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
match self.ser.settings.array {
|
match (self.len, &self.ser.settings.array) {
|
||||||
Some(ref a) => {
|
(Some(0...1), _) | (_, &None) => {
|
||||||
|
self.ser.dst.push_str("]");
|
||||||
|
},
|
||||||
|
(_, &Some(ref a)) => {
|
||||||
if a.trailing_comma {
|
if a.trailing_comma {
|
||||||
self.ser.dst.push_str(",");
|
self.ser.dst.push_str(",");
|
||||||
}
|
}
|
||||||
self.ser.dst.push_str("\n]");
|
self.ser.dst.push_str("\n]");
|
||||||
},
|
},
|
||||||
None => {
|
|
||||||
self.ser.dst.push_str("]");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -41,11 +41,12 @@ fn disable_pretty() {
|
||||||
const PRETTY_STD: &'static str = "\
|
const PRETTY_STD: &'static str = "\
|
||||||
[example]
|
[example]
|
||||||
array = [
|
array = [
|
||||||
\"item 1\",
|
'item 1',
|
||||||
\"item 2\",
|
'item 2',
|
||||||
]
|
]
|
||||||
empty = []
|
empty = []
|
||||||
oneline = \"this has no newlines.\"
|
one = ['one']
|
||||||
|
oneline = 'this has no newlines.'
|
||||||
text = '''
|
text = '''
|
||||||
this is the first line
|
this is the first line
|
||||||
this is the second line
|
this is the second line
|
||||||
|
@ -67,15 +68,21 @@ fn pretty_std() {
|
||||||
const PRETTY_INDENT_2: &'static str = "\
|
const PRETTY_INDENT_2: &'static str = "\
|
||||||
[example]
|
[example]
|
||||||
array = [
|
array = [
|
||||||
\"item 1\",
|
'item 1',
|
||||||
\"item 2\",
|
'item 2',
|
||||||
]
|
]
|
||||||
empty = []
|
empty = []
|
||||||
oneline = \"this has no newlines.\"
|
one = ['one']
|
||||||
|
oneline = 'this has no newlines.'
|
||||||
text = '''
|
text = '''
|
||||||
this is the first line
|
this is the first line
|
||||||
this is the second line
|
this is the second line
|
||||||
'''
|
'''
|
||||||
|
three = [
|
||||||
|
'one',
|
||||||
|
'two',
|
||||||
|
'three',
|
||||||
|
]
|
||||||
";
|
";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -88,6 +95,7 @@ fn pretty_indent_2() {
|
||||||
serializer.pretty_array_indent(2);
|
serializer.pretty_array_indent(2);
|
||||||
value.serialize(&mut serializer).unwrap();
|
value.serialize(&mut serializer).unwrap();
|
||||||
}
|
}
|
||||||
|
println!(">> Result:\n{}", result);
|
||||||
assert_eq!(toml, &result);
|
assert_eq!(toml, &result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,6 +176,7 @@ fn pretty_no_string() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const PRETTY_TRICKY: &'static str = r"[example]
|
const PRETTY_TRICKY: &'static str = r"[example]
|
||||||
|
single = '''this is a single line but has '' for no reason'''
|
||||||
text = '''
|
text = '''
|
||||||
this is the first line
|
this is the first line
|
||||||
This has a ''\' in it for no reason
|
This has a ''\' in it for no reason
|
||||||
|
|
Loading…
Reference in a new issue