add """ for non-literals with newlines, clean up logic and add tests
This commit is contained in:
parent
dabe477d64
commit
54a4833bc1
92
src/ser.rs
92
src/ser.rs
|
@ -458,14 +458,21 @@ 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> {
|
||||||
/// Use ''' or '
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum Multi {
|
enum Type {
|
||||||
False,
|
NewlineTripple,
|
||||||
True,
|
OnelineTripple,
|
||||||
Single,
|
OnelineSingle,
|
||||||
}
|
}
|
||||||
fn do_pretty(value: &str) -> Option<(Multi, String)> {
|
|
||||||
|
enum Repr {
|
||||||
|
/// represent as a literal string (using '')
|
||||||
|
Literal(String, Type),
|
||||||
|
/// represent the std way (using "")
|
||||||
|
Std(Type),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_pretty(value: &str) -> Repr {
|
||||||
// For doing pretty prints we store in a new String
|
// For doing pretty prints we store in a new String
|
||||||
// because there are too many cases where pretty cannot
|
// because there are too many cases where pretty cannot
|
||||||
// work. We need to determine:
|
// work. We need to determine:
|
||||||
|
@ -476,16 +483,18 @@ impl<'a> Serializer<'a> {
|
||||||
// Doing it any other way would require multiple passes
|
// Doing it any other way would require multiple passes
|
||||||
// to determine if a pretty string works or not.
|
// to determine if a pretty string works or not.
|
||||||
let mut out = String::with_capacity(value.len() * 2);
|
let mut out = String::with_capacity(value.len() * 2);
|
||||||
let mut multi = Multi::False;
|
let mut ty = Type::OnelineSingle;
|
||||||
// found consecutive single quotes
|
// found consecutive single quotes
|
||||||
let mut max_found_singles = 0;
|
let mut max_found_singles = 0;
|
||||||
let mut found_singles = 0;
|
let mut found_singles = 0;
|
||||||
|
let mut can_be_pretty = true;
|
||||||
|
|
||||||
for ch in value.chars() {
|
for ch in value.chars() {
|
||||||
|
if can_be_pretty {
|
||||||
if ch == '\'' {
|
if ch == '\'' {
|
||||||
found_singles += 1;
|
found_singles += 1;
|
||||||
if found_singles >= 3 {
|
if found_singles >= 3 {
|
||||||
return None;
|
can_be_pretty = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if found_singles > max_found_singles {
|
if found_singles > max_found_singles {
|
||||||
|
@ -495,49 +504,71 @@ impl<'a> Serializer<'a> {
|
||||||
}
|
}
|
||||||
match ch {
|
match ch {
|
||||||
'\t' => {},
|
'\t' => {},
|
||||||
'\n' => multi = Multi::True,
|
'\n' => ty = Type::NewlineTripple,
|
||||||
// note that the following are invalid: \b \f \r
|
// note that the following are invalid: \b \f \r
|
||||||
c if c < '\u{1f}' => return None, // Invalid control character
|
c if c < '\u{1f}' => can_be_pretty = false, // Invalid control character
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
out.push(ch);
|
out.push(ch);
|
||||||
|
} else {
|
||||||
|
// the string cannot be represented as pretty,
|
||||||
|
// still check if it should be multiline
|
||||||
|
if ch == '\n' {
|
||||||
|
ty = Type::NewlineTripple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !can_be_pretty {
|
||||||
|
debug_assert!(ty != Type::OnelineTripple);
|
||||||
|
return Repr::Std(ty);
|
||||||
}
|
}
|
||||||
if found_singles > max_found_singles {
|
if found_singles > max_found_singles {
|
||||||
max_found_singles = found_singles;
|
max_found_singles = found_singles;
|
||||||
}
|
}
|
||||||
debug_assert!(max_found_singles < 3);
|
debug_assert!(max_found_singles < 3);
|
||||||
if multi == Multi::False && max_found_singles >= 1 {
|
if ty == Type::OnelineSingle && max_found_singles >= 1 {
|
||||||
// no newlines, but must use ''' because it has ' in it
|
// no newlines, but must use ''' because it has ' in it
|
||||||
multi = Multi::Single;
|
ty = Type::OnelineTripple;
|
||||||
}
|
}
|
||||||
Some((multi, out))
|
Repr::Literal(out, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
let pretty = if !is_key && self.settings.pretty_string {
|
let repr = if !is_key && self.settings.pretty_string {
|
||||||
do_pretty(value)
|
do_pretty(value)
|
||||||
} else {
|
} else {
|
||||||
None
|
Repr::Std(Type::OnelineSingle)
|
||||||
};
|
};
|
||||||
if let Some((multi, s)) = pretty {
|
match repr {
|
||||||
|
Repr::Literal(literal, ty) => {
|
||||||
// A pretty string
|
// A pretty string
|
||||||
match multi {
|
match ty {
|
||||||
Multi::True => self.dst.push_str("'''\n"),
|
Type::NewlineTripple => self.dst.push_str("'''\n"),
|
||||||
Multi::Single => self.dst.push_str("'''"),
|
Type::OnelineTripple => self.dst.push_str("'''"),
|
||||||
Multi::False => self.dst.push('\''),
|
Type::OnelineSingle => self.dst.push('\''),
|
||||||
}
|
}
|
||||||
self.dst.push_str(&s);
|
self.dst.push_str(&literal);
|
||||||
match multi {
|
match ty {
|
||||||
Multi::False => self.dst.push('\''),
|
Type::OnelineSingle => self.dst.push('\''),
|
||||||
_ => self.dst.push_str("'''"),
|
_ => self.dst.push_str("'''"),
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
// Not a pretty string
|
Repr::Std(ty) => {
|
||||||
drop(write!(self.dst, "\""));
|
match ty {
|
||||||
|
Type::NewlineTripple => self.dst.push_str("\"\"\"\n"),
|
||||||
|
Type::OnelineSingle => self.dst.push('"'),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
for ch in value.chars() {
|
for ch in value.chars() {
|
||||||
match ch {
|
match ch {
|
||||||
'\u{8}' => self.dst.push_str("\\b"),
|
'\u{8}' => self.dst.push_str("\\b"),
|
||||||
'\u{9}' => self.dst.push_str("\\t"),
|
'\u{9}' => self.dst.push_str("\\t"),
|
||||||
'\u{a}' => self.dst.push_str("\\n"),
|
'\u{a}' => {
|
||||||
|
match ty {
|
||||||
|
Type::NewlineTripple => self.dst.push('\n'),
|
||||||
|
Type::OnelineSingle => self.dst.push_str("\\n"),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
},
|
||||||
'\u{c}' => self.dst.push_str("\\f"),
|
'\u{c}' => self.dst.push_str("\\f"),
|
||||||
'\u{d}' => self.dst.push_str("\\r"),
|
'\u{d}' => self.dst.push_str("\\r"),
|
||||||
'\u{22}' => self.dst.push_str("\\\""),
|
'\u{22}' => self.dst.push_str("\\\""),
|
||||||
|
@ -546,7 +577,12 @@ impl<'a> Serializer<'a> {
|
||||||
ch => self.dst.push(ch),
|
ch => self.dst.push(ch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.dst.push_str("\"");
|
match ty {
|
||||||
|
Type::NewlineTripple => self.dst.push_str("\"\"\""),
|
||||||
|
Type::OnelineSingle => self.dst.push('"'),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,14 +175,32 @@ fn pretty_no_string() {
|
||||||
assert_eq!(toml, &result);
|
assert_eq!(toml, &result);
|
||||||
}
|
}
|
||||||
|
|
||||||
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'''
|
f = "\f"
|
||||||
text = '''
|
glass = '''
|
||||||
this is the first line
|
Nothing too unusual, except that I can eat glass in:
|
||||||
This has a ''\' in it for no reason
|
- Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα.
|
||||||
this is the third line
|
- Polish: Mogę jeść szkło, i mi nie szkodzi.
|
||||||
|
- Hindi: मैं काँच खा सकता हूँ, मुझे उस से कोई पीडा नहीं होती.
|
||||||
|
- Japanese: 私はガラスを食べられます。それは私を傷つけません。
|
||||||
'''
|
'''
|
||||||
";
|
r = "\r"
|
||||||
|
r_newline = """
|
||||||
|
\r
|
||||||
|
"""
|
||||||
|
single = '''this is a single line but has '' cuz it's tricky'''
|
||||||
|
single_tricky = "single line with ''' in it"
|
||||||
|
tabs = '''
|
||||||
|
this is pretty standard
|
||||||
|
except for some tabs right here
|
||||||
|
'''
|
||||||
|
text = """
|
||||||
|
this is the first line.
|
||||||
|
This has a ''' in it and \"\"\" cuz it's tricky yo
|
||||||
|
Also ' and \" because why not
|
||||||
|
this is the third line
|
||||||
|
"""
|
||||||
|
"##;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pretty_tricky() {
|
fn pretty_tricky() {
|
||||||
|
|
Loading…
Reference in a new issue