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