• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::borrow::Cow;
2 use std::fmt::{Display, Formatter, Result, Write};
3 
4 use toml_datetime::Datetime;
5 
6 use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
7 use crate::key::Key;
8 use crate::repr::{Formatted, Repr, ValueRepr};
9 use crate::table::{
10     DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_ROOT_DECOR, DEFAULT_TABLE_DECOR,
11 };
12 use crate::value::{
13     DEFAULT_LEADING_VALUE_DECOR, DEFAULT_TRAILING_VALUE_DECOR, DEFAULT_VALUE_DECOR,
14 };
15 use crate::DocumentMut;
16 use crate::{Array, InlineTable, Item, Table, Value};
17 
encode_key(this: &Key, buf: &mut dyn Write, input: Option<&str>) -> Result18 pub(crate) fn encode_key(this: &Key, buf: &mut dyn Write, input: Option<&str>) -> Result {
19     if let Some(input) = input {
20         let repr = this
21             .as_repr()
22             .map(Cow::Borrowed)
23             .unwrap_or_else(|| Cow::Owned(this.default_repr()));
24         repr.encode(buf, input)?;
25     } else {
26         let repr = this.display_repr();
27         write!(buf, "{}", repr)?;
28     };
29 
30     Ok(())
31 }
32 
encode_key_path( this: &[Key], buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result33 fn encode_key_path(
34     this: &[Key],
35     buf: &mut dyn Write,
36     input: Option<&str>,
37     default_decor: (&str, &str),
38 ) -> Result {
39     let leaf_decor = this.last().expect("always at least one key").leaf_decor();
40     for (i, key) in this.iter().enumerate() {
41         let dotted_decor = key.dotted_decor();
42 
43         let first = i == 0;
44         let last = i + 1 == this.len();
45 
46         if first {
47             leaf_decor.prefix_encode(buf, input, default_decor.0)?;
48         } else {
49             write!(buf, ".")?;
50             dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
51         }
52 
53         encode_key(key, buf, input)?;
54 
55         if last {
56             leaf_decor.suffix_encode(buf, input, default_decor.1)?;
57         } else {
58             dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
59         }
60     }
61     Ok(())
62 }
63 
encode_key_path_ref( this: &[&Key], buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result64 pub(crate) fn encode_key_path_ref(
65     this: &[&Key],
66     buf: &mut dyn Write,
67     input: Option<&str>,
68     default_decor: (&str, &str),
69 ) -> Result {
70     let leaf_decor = this.last().expect("always at least one key").leaf_decor();
71     for (i, key) in this.iter().enumerate() {
72         let dotted_decor = key.dotted_decor();
73 
74         let first = i == 0;
75         let last = i + 1 == this.len();
76 
77         if first {
78             leaf_decor.prefix_encode(buf, input, default_decor.0)?;
79         } else {
80             write!(buf, ".")?;
81             dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
82         }
83 
84         encode_key(key, buf, input)?;
85 
86         if last {
87             leaf_decor.suffix_encode(buf, input, default_decor.1)?;
88         } else {
89             dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
90         }
91     }
92     Ok(())
93 }
94 
encode_formatted<T: ValueRepr>( this: &Formatted<T>, buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result95 pub(crate) fn encode_formatted<T: ValueRepr>(
96     this: &Formatted<T>,
97     buf: &mut dyn Write,
98     input: Option<&str>,
99     default_decor: (&str, &str),
100 ) -> Result {
101     let decor = this.decor();
102     decor.prefix_encode(buf, input, default_decor.0)?;
103 
104     if let Some(input) = input {
105         let repr = this
106             .as_repr()
107             .map(Cow::Borrowed)
108             .unwrap_or_else(|| Cow::Owned(this.default_repr()));
109         repr.encode(buf, input)?;
110     } else {
111         let repr = this.display_repr();
112         write!(buf, "{}", repr)?;
113     };
114 
115     decor.suffix_encode(buf, input, default_decor.1)?;
116     Ok(())
117 }
118 
encode_array( this: &Array, buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result119 pub(crate) fn encode_array(
120     this: &Array,
121     buf: &mut dyn Write,
122     input: Option<&str>,
123     default_decor: (&str, &str),
124 ) -> Result {
125     let decor = this.decor();
126     decor.prefix_encode(buf, input, default_decor.0)?;
127     write!(buf, "[")?;
128 
129     for (i, elem) in this.iter().enumerate() {
130         let inner_decor;
131         if i == 0 {
132             inner_decor = DEFAULT_LEADING_VALUE_DECOR;
133         } else {
134             inner_decor = DEFAULT_VALUE_DECOR;
135             write!(buf, ",")?;
136         }
137         encode_value(elem, buf, input, inner_decor)?;
138     }
139     if this.trailing_comma() && !this.is_empty() {
140         write!(buf, ",")?;
141     }
142 
143     this.trailing().encode_with_default(buf, input, "")?;
144     write!(buf, "]")?;
145     decor.suffix_encode(buf, input, default_decor.1)?;
146 
147     Ok(())
148 }
149 
encode_table( this: &InlineTable, buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result150 pub(crate) fn encode_table(
151     this: &InlineTable,
152     buf: &mut dyn Write,
153     input: Option<&str>,
154     default_decor: (&str, &str),
155 ) -> Result {
156     let decor = this.decor();
157     decor.prefix_encode(buf, input, default_decor.0)?;
158     write!(buf, "{{")?;
159     this.preamble().encode_with_default(buf, input, "")?;
160 
161     let children = this.get_values();
162     let len = children.len();
163     for (i, (key_path, value)) in children.into_iter().enumerate() {
164         if i != 0 {
165             write!(buf, ",")?;
166         }
167         let inner_decor = if i == len - 1 {
168             DEFAULT_TRAILING_VALUE_DECOR
169         } else {
170             DEFAULT_VALUE_DECOR
171         };
172         encode_key_path_ref(&key_path, buf, input, DEFAULT_INLINE_KEY_DECOR)?;
173         write!(buf, "=")?;
174         encode_value(value, buf, input, inner_decor)?;
175     }
176 
177     write!(buf, "}}")?;
178     decor.suffix_encode(buf, input, default_decor.1)?;
179 
180     Ok(())
181 }
182 
encode_value( this: &Value, buf: &mut dyn Write, input: Option<&str>, default_decor: (&str, &str), ) -> Result183 pub(crate) fn encode_value(
184     this: &Value,
185     buf: &mut dyn Write,
186     input: Option<&str>,
187     default_decor: (&str, &str),
188 ) -> Result {
189     match this {
190         Value::String(repr) => encode_formatted(repr, buf, input, default_decor),
191         Value::Integer(repr) => encode_formatted(repr, buf, input, default_decor),
192         Value::Float(repr) => encode_formatted(repr, buf, input, default_decor),
193         Value::Boolean(repr) => encode_formatted(repr, buf, input, default_decor),
194         Value::Datetime(repr) => encode_formatted(repr, buf, input, default_decor),
195         Value::Array(array) => encode_array(array, buf, input, default_decor),
196         Value::InlineTable(table) => encode_table(table, buf, input, default_decor),
197     }
198 }
199 
200 impl Display for DocumentMut {
fmt(&self, f: &mut Formatter<'_>) -> Result201     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
202         let decor = self.decor();
203         decor.prefix_encode(f, None, DEFAULT_ROOT_DECOR.0)?;
204 
205         let mut path = Vec::new();
206         let mut last_position = 0;
207         let mut tables = Vec::new();
208         visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
209             if let Some(pos) = t.position() {
210                 last_position = pos;
211             }
212             tables.push((last_position, t, p.clone(), is_array));
213             Ok(())
214         })
215         .unwrap();
216 
217         tables.sort_by_key(|&(id, _, _, _)| id);
218         let mut first_table = true;
219         for (_, table, path, is_array) in tables {
220             visit_table(f, None, table, &path, is_array, &mut first_table)?;
221         }
222         decor.suffix_encode(f, None, DEFAULT_ROOT_DECOR.1)?;
223         self.trailing().encode_with_default(f, None, "")
224     }
225 }
226 
visit_nested_tables<'t, F>( table: &'t Table, path: &mut Vec<Key>, is_array_of_tables: bool, callback: &mut F, ) -> Result where F: FnMut(&'t Table, &Vec<Key>, bool) -> Result,227 fn visit_nested_tables<'t, F>(
228     table: &'t Table,
229     path: &mut Vec<Key>,
230     is_array_of_tables: bool,
231     callback: &mut F,
232 ) -> Result
233 where
234     F: FnMut(&'t Table, &Vec<Key>, bool) -> Result,
235 {
236     if !table.is_dotted() {
237         callback(table, path, is_array_of_tables)?;
238     }
239 
240     for kv in table.items.values() {
241         match kv.value {
242             Item::Table(ref t) => {
243                 let key = kv.key.clone();
244                 path.push(key);
245                 visit_nested_tables(t, path, false, callback)?;
246                 path.pop();
247             }
248             Item::ArrayOfTables(ref a) => {
249                 for t in a.iter() {
250                     let key = kv.key.clone();
251                     path.push(key);
252                     visit_nested_tables(t, path, true, callback)?;
253                     path.pop();
254                 }
255             }
256             _ => {}
257         }
258     }
259     Ok(())
260 }
261 
visit_table( buf: &mut dyn Write, input: Option<&str>, table: &Table, path: &[Key], is_array_of_tables: bool, first_table: &mut bool, ) -> Result262 fn visit_table(
263     buf: &mut dyn Write,
264     input: Option<&str>,
265     table: &Table,
266     path: &[Key],
267     is_array_of_tables: bool,
268     first_table: &mut bool,
269 ) -> Result {
270     let children = table.get_values();
271     // We are intentionally hiding implicit tables without any tables nested under them (ie
272     // `table.is_empty()` which is in contrast to `table.get_values().is_empty()`).  We are
273     // trusting the user that an empty implicit table is not semantically meaningful
274     //
275     // This allows a user to delete all tables under this implicit table and the implicit table
276     // will disappear.
277     //
278     // However, this means that users need to take care in deciding what tables get marked as
279     // implicit.
280     let is_visible_std_table = !(table.implicit && children.is_empty());
281 
282     if path.is_empty() {
283         // don't print header for the root node
284         if !children.is_empty() {
285             *first_table = false;
286         }
287     } else if is_array_of_tables {
288         let default_decor = if *first_table {
289             *first_table = false;
290             ("", DEFAULT_TABLE_DECOR.1)
291         } else {
292             DEFAULT_TABLE_DECOR
293         };
294         table.decor.prefix_encode(buf, input, default_decor.0)?;
295         write!(buf, "[[")?;
296         encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
297         write!(buf, "]]")?;
298         table.decor.suffix_encode(buf, input, default_decor.1)?;
299         writeln!(buf)?;
300     } else if is_visible_std_table {
301         let default_decor = if *first_table {
302             *first_table = false;
303             ("", DEFAULT_TABLE_DECOR.1)
304         } else {
305             DEFAULT_TABLE_DECOR
306         };
307         table.decor.prefix_encode(buf, input, default_decor.0)?;
308         write!(buf, "[")?;
309         encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
310         write!(buf, "]")?;
311         table.decor.suffix_encode(buf, input, default_decor.1)?;
312         writeln!(buf)?;
313     }
314     // print table body
315     for (key_path, value) in children {
316         encode_key_path_ref(&key_path, buf, input, DEFAULT_KEY_DECOR)?;
317         write!(buf, "=")?;
318         encode_value(value, buf, input, DEFAULT_VALUE_DECOR)?;
319         writeln!(buf)?;
320     }
321     Ok(())
322 }
323 
324 impl ValueRepr for String {
to_repr(&self) -> Repr325     fn to_repr(&self) -> Repr {
326         to_string_repr(self, None, None)
327     }
328 }
329 
to_string_repr( value: &str, style: Option<StringStyle>, literal: Option<bool>, ) -> Repr330 pub(crate) fn to_string_repr(
331     value: &str,
332     style: Option<StringStyle>,
333     literal: Option<bool>,
334 ) -> Repr {
335     let (style, literal) = infer_style(value, style, literal);
336 
337     let mut output = String::with_capacity(value.len() * 2);
338     if literal {
339         output.push_str(style.literal_start());
340         output.push_str(value);
341         output.push_str(style.literal_end());
342     } else {
343         output.push_str(style.standard_start());
344         for ch in value.chars() {
345             match ch {
346                 '\u{8}' => output.push_str("\\b"),
347                 '\u{9}' => output.push_str("\\t"),
348                 '\u{a}' => match style {
349                     StringStyle::NewlineTriple => output.push('\n'),
350                     StringStyle::OnelineSingle => output.push_str("\\n"),
351                     StringStyle::OnelineTriple => unreachable!(),
352                 },
353                 '\u{c}' => output.push_str("\\f"),
354                 '\u{d}' => output.push_str("\\r"),
355                 '\u{22}' => output.push_str("\\\""),
356                 '\u{5c}' => output.push_str("\\\\"),
357                 c if c <= '\u{1f}' || c == '\u{7f}' => {
358                     write!(output, "\\u{:04X}", ch as u32).unwrap();
359                 }
360                 ch => output.push(ch),
361             }
362         }
363         output.push_str(style.standard_end());
364     }
365 
366     Repr::new_unchecked(output)
367 }
368 
369 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
370 pub(crate) enum StringStyle {
371     NewlineTriple,
372     OnelineTriple,
373     OnelineSingle,
374 }
375 
376 impl StringStyle {
377     fn literal_start(self) -> &'static str {
378         match self {
379             Self::NewlineTriple => "'''\n",
380             Self::OnelineTriple => "'''",
381             Self::OnelineSingle => "'",
382         }
383     }
384     fn literal_end(self) -> &'static str {
385         match self {
386             Self::NewlineTriple => "'''",
387             Self::OnelineTriple => "'''",
388             Self::OnelineSingle => "'",
389         }
390     }
391 
392     fn standard_start(self) -> &'static str {
393         match self {
394             Self::NewlineTriple => "\"\"\"\n",
395             // note: OnelineTriple can happen if do_pretty wants to do
396             // '''it's one line'''
397             // but literal == false
398             Self::OnelineTriple | Self::OnelineSingle => "\"",
399         }
400     }
401 
standard_end(self) -> &'static str402     fn standard_end(self) -> &'static str {
403         match self {
404             Self::NewlineTriple => "\"\"\"",
405             // note: OnelineTriple can happen if do_pretty wants to do
406             // '''it's one line'''
407             // but literal == false
408             Self::OnelineTriple | Self::OnelineSingle => "\"",
409         }
410     }
411 }
412 
infer_style( value: &str, style: Option<StringStyle>, literal: Option<bool>, ) -> (StringStyle, bool)413 fn infer_style(
414     value: &str,
415     style: Option<StringStyle>,
416     literal: Option<bool>,
417 ) -> (StringStyle, bool) {
418     match (style, literal) {
419         (Some(style), Some(literal)) => (style, literal),
420         (None, Some(literal)) => (infer_all_style(value).0, literal),
421         (Some(style), None) => {
422             let literal = infer_literal(value);
423             (style, literal)
424         }
425         (None, None) => infer_all_style(value),
426     }
427 }
428 
infer_literal(value: &str) -> bool429 fn infer_literal(value: &str) -> bool {
430     #[cfg(feature = "parse")]
431     {
432         use winnow::stream::ContainsToken as _;
433         (value.contains('"') | value.contains('\\'))
434             && value
435                 .chars()
436                 .all(|c| crate::parser::strings::LITERAL_CHAR.contains_token(c))
437     }
438     #[cfg(not(feature = "parse"))]
439     {
440         false
441     }
442 }
443 
infer_all_style(value: &str) -> (StringStyle, bool)444 fn infer_all_style(value: &str) -> (StringStyle, bool) {
445     // We need to determine:
446     // - if we are a "multi-line" pretty (if there are \n)
447     // - if ['''] appears if multi or ['] if single
448     // - if there are any invalid control characters
449     //
450     // Doing it any other way would require multiple passes
451     // to determine if a pretty string works or not.
452     let mut ty = StringStyle::OnelineSingle;
453     // found consecutive single quotes
454     let mut max_found_singles = 0;
455     let mut found_singles = 0;
456     let mut prefer_literal = false;
457     let mut can_be_pretty = true;
458 
459     for ch in value.chars() {
460         if can_be_pretty {
461             if ch == '\'' {
462                 found_singles += 1;
463                 if found_singles >= 3 {
464                     can_be_pretty = false;
465                 }
466             } else {
467                 if found_singles > max_found_singles {
468                     max_found_singles = found_singles;
469                 }
470                 found_singles = 0;
471             }
472             match ch {
473                 '\t' => {}
474                 '"' => {
475                     prefer_literal = true;
476                 }
477                 '\\' => {
478                     prefer_literal = true;
479                 }
480                 '\n' => ty = StringStyle::NewlineTriple,
481                 // Escape codes are needed if any ascii control
482                 // characters are present, including \b \f \r.
483                 c if c <= '\u{1f}' || c == '\u{7f}' => can_be_pretty = false,
484                 _ => {}
485             }
486         } else {
487             // the string cannot be represented as pretty,
488             // still check if it should be multiline
489             if ch == '\n' {
490                 ty = StringStyle::NewlineTriple;
491             }
492         }
493     }
494     if found_singles > 0 && value.ends_with('\'') {
495         // We cannot escape the ending quote so we must use """
496         can_be_pretty = false;
497     }
498     if !prefer_literal {
499         can_be_pretty = false;
500     }
501     if !can_be_pretty {
502         debug_assert!(ty != StringStyle::OnelineTriple);
503         return (ty, false);
504     }
505     if found_singles > max_found_singles {
506         max_found_singles = found_singles;
507     }
508     debug_assert!(max_found_singles < 3);
509     if ty == StringStyle::OnelineSingle && max_found_singles >= 1 {
510         // no newlines, but must use ''' because it has ' in it
511         ty = StringStyle::OnelineTriple;
512     }
513     (ty, true)
514 }
515 
516 impl ValueRepr for i64 {
to_repr(&self) -> Repr517     fn to_repr(&self) -> Repr {
518         Repr::new_unchecked(self.to_string())
519     }
520 }
521 
522 impl ValueRepr for f64 {
to_repr(&self) -> Repr523     fn to_repr(&self) -> Repr {
524         to_f64_repr(*self)
525     }
526 }
527 
to_f64_repr(f: f64) -> Repr528 fn to_f64_repr(f: f64) -> Repr {
529     let repr = match (f.is_sign_negative(), f.is_nan(), f == 0.0) {
530         (true, true, _) => "-nan".to_owned(),
531         (false, true, _) => "nan".to_owned(),
532         (true, false, true) => "-0.0".to_owned(),
533         (false, false, true) => "0.0".to_owned(),
534         (_, false, false) => {
535             if f % 1.0 == 0.0 {
536                 format!("{}.0", f)
537             } else {
538                 format!("{}", f)
539             }
540         }
541     };
542     Repr::new_unchecked(repr)
543 }
544 
545 impl ValueRepr for bool {
to_repr(&self) -> Repr546     fn to_repr(&self) -> Repr {
547         Repr::new_unchecked(self.to_string())
548     }
549 }
550 
551 impl ValueRepr for Datetime {
to_repr(&self) -> Repr552     fn to_repr(&self) -> Repr {
553         Repr::new_unchecked(self.to_string())
554     }
555 }
556 
557 #[cfg(test)]
558 mod test {
559     use super::*;
560     use proptest::prelude::*;
561 
562     proptest! {
563         #[test]
564         #[cfg(feature = "parse")]
565         fn parseable_string(string in "\\PC*") {
566             let string = Value::from(string);
567             let encoded = string.to_string();
568             let _: Value = encoded.parse().unwrap_or_else(|err| {
569                 panic!("error: {err}
570 
571 string:
572 ```
573 {string}
574 ```
575 ")
576             });
577         }
578     }
579 
580     proptest! {
581         #[test]
582         #[cfg(feature = "parse")]
583         fn parseable_key(string in "\\PC*") {
584             let string = Key::new(string);
585             let encoded = string.to_string();
586             let _: Key = encoded.parse().unwrap_or_else(|err| {
587                 panic!("error: {err}
588 
589 string:
590 ```
591 {string}
592 ```
593 ")
594             });
595         }
596     }
597 }
598