diff --git a/src/array.rs b/src/array.rs index c956ee00..1eac0ec3 100644 --- a/src/array.rs +++ b/src/array.rs @@ -304,6 +304,12 @@ impl Array { } } +impl std::fmt::Display for Array { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + crate::encode::Encode::encode(self, f, ("", "")) + } +} + impl> Extend for Array { fn extend>(&mut self, iter: T) { for value in iter { diff --git a/src/encode.rs b/src/encode.rs index c2c61b21..8d81ff9c 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -1,48 +1,235 @@ use std::fmt::{Display, Formatter, Result, Write}; -use itertools::Itertools; - use crate::datetime::*; use crate::document::Document; use crate::inline_table::DEFAULT_INLINE_KEY_DECOR; use crate::key::Key; -use crate::repr::{DecorDisplay, Formatted, Repr, ValueRepr}; +use crate::repr::{Formatted, Repr, ValueRepr}; use crate::table::{DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_TABLE_DECOR}; -use crate::value::DEFAULT_VALUE_DECOR; +use crate::value::{DEFAULT_LEADING_VALUE_DECOR, DEFAULT_VALUE_DECOR}; use crate::{Array, InlineTable, Item, Table, Value}; -impl<'d, D: Display> Display for DecorDisplay<'d, D> { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { +pub(crate) trait Encode { + fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result; +} + +impl Encode for Key { + fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result { + let repr = self.to_repr(); write!( - f, + buf, "{}{}{}", - self.decor.prefix().unwrap_or(self.default.0), - self.inner, - self.decor.suffix().unwrap_or(self.default.1) + self.decor().prefix().unwrap_or(default_decor.0), + repr, + self.decor().suffix().unwrap_or(default_decor.1) ) } } -impl Display for Repr { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - self.as_raw().fmt(f) +impl<'k> Encode for &'k [&'k Key] { + fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result { + for (i, key) in self.iter().enumerate() { + let first = i == 0; + let last = i + 1 == self.len(); + + let prefix = if first { + default_decor.0 + } else { + DEFAULT_KEY_PATH_DECOR.0 + }; + let suffix = if last { + default_decor.1 + } else { + DEFAULT_KEY_PATH_DECOR.1 + }; + + if !first { + write!(buf, ".")?; + } + key.encode(buf, (prefix, suffix))?; + } + Ok(()) } } -impl Display for Formatted +impl Encode for Formatted where T: ValueRepr, { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { + fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result { let repr = self.to_repr(); write!( - f, - "{}", - self.decor().display(repr.as_ref(), DEFAULT_VALUE_DECOR) + buf, + "{}{}{}", + self.decor().prefix().unwrap_or(default_decor.0), + repr, + self.decor().suffix().unwrap_or(default_decor.1) + ) + } +} + +impl Encode for Array { + fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result { + write!(buf, "{}[", self.decor().prefix().unwrap_or(default_decor.0))?; + + for (i, elem) in self.iter().enumerate() { + let inner_decor; + if i == 0 { + inner_decor = DEFAULT_LEADING_VALUE_DECOR; + } else { + inner_decor = DEFAULT_VALUE_DECOR; + write!(buf, ",")?; + } + elem.encode(buf, inner_decor)?; + } + if self.trailing_comma() && !self.is_empty() { + write!(buf, ",")?; + } + + write!(buf, "{}", self.trailing())?; + write!(buf, "]{}", self.decor().suffix().unwrap_or(default_decor.1)) + } +} + +impl Encode for InlineTable { + fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result { + write!( + buf, + "{}{{", + self.decor().prefix().unwrap_or(default_decor.0) + )?; + write!(buf, "{}", self.preamble)?; + + let children = self.get_values(); + for (i, (key_path, value)) in children.into_iter().enumerate() { + if i != 0 { + write!(buf, ",")?; + } + key_path.as_slice().encode(buf, DEFAULT_INLINE_KEY_DECOR)?; + write!(buf, "=")?; + value.encode(buf, DEFAULT_VALUE_DECOR)?; + } + + write!( + buf, + "}}{}", + self.decor().suffix().unwrap_or(default_decor.1) ) } } +impl Encode for Value { + fn encode(&self, buf: &mut dyn Write, default_decor: (&str, &str)) -> Result { + match self { + Value::String(repr) => repr.encode(buf, default_decor), + Value::Integer(repr) => repr.encode(buf, default_decor), + Value::Float(repr) => repr.encode(buf, default_decor), + Value::Boolean(repr) => repr.encode(buf, default_decor), + Value::Datetime(repr) => repr.encode(buf, default_decor), + Value::Array(array) => array.encode(buf, default_decor), + Value::InlineTable(table) => table.encode(buf, default_decor), + } + } +} + +impl Display for Document { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let mut path = Vec::new(); + let mut last_position = 0; + let mut tables = Vec::new(); + visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| { + if let Some(pos) = t.position { + last_position = pos; + } + tables.push((last_position, t, p.clone(), is_array)); + Ok(()) + }) + .unwrap(); + + tables.sort_by_key(|&(id, _, _, _)| id); + for (_, table, path, is_array) in tables { + visit_table(f, table, &path, is_array)?; + } + self.trailing.fmt(f) + } +} + +fn visit_nested_tables<'t, F>( + table: &'t Table, + path: &mut Vec<&'t Key>, + is_array_of_tables: bool, + callback: &mut F, +) -> Result +where + F: FnMut(&'t Table, &Vec<&'t Key>, bool) -> Result, +{ + callback(table, path, is_array_of_tables)?; + + for kv in table.items.values() { + match kv.value { + Item::Table(ref t) if !t.is_dotted() => { + path.push(&kv.key); + visit_nested_tables(t, path, false, callback)?; + path.pop(); + } + Item::ArrayOfTables(ref a) => { + for t in a.iter() { + path.push(&kv.key); + visit_nested_tables(t, path, true, callback)?; + path.pop(); + } + } + _ => {} + } + } + Ok(()) +} + +fn visit_table( + buf: &mut dyn Write, + table: &Table, + path: &[&Key], + is_array_of_tables: bool, +) -> Result { + let children = table.get_values(); + + if path.is_empty() { + // don't print header for the root node + } else if is_array_of_tables { + write!( + buf, + "{}[[", + table.decor.prefix().unwrap_or(DEFAULT_TABLE_DECOR.0) + )?; + path.encode(buf, DEFAULT_KEY_PATH_DECOR)?; + writeln!( + buf, + "]]{}", + table.decor.suffix().unwrap_or(DEFAULT_TABLE_DECOR.1) + )?; + } else if !(table.implicit && children.is_empty()) { + write!( + buf, + "{}[", + table.decor.prefix().unwrap_or(DEFAULT_TABLE_DECOR.0) + )?; + path.encode(buf, DEFAULT_KEY_PATH_DECOR)?; + writeln!( + buf, + "]{}", + table.decor.suffix().unwrap_or(DEFAULT_TABLE_DECOR.1) + )?; + } + // print table body + for (key_path, value) in children { + key_path.as_slice().encode(buf, DEFAULT_KEY_DECOR)?; + write!(buf, "=")?; + value.encode(buf, DEFAULT_VALUE_DECOR)?; + writeln!(buf)?; + } + Ok(()) +} + impl ValueRepr for String { fn to_repr(&self) -> Repr { to_string_repr(self, None, None) @@ -250,212 +437,3 @@ impl ValueRepr for Datetime { Repr::new_unchecked(self.to_string()) } } - -impl Display for Key { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - // HACK: For now, leaving off decor since we don't know the defaults to use in this context - self.to_repr().as_ref().fmt(f) - } -} - -impl Display for Value { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - match *self { - Value::String(ref repr) => write!(f, "{}", repr), - Value::Integer(ref repr) => write!(f, "{}", repr), - Value::Float(ref repr) => write!(f, "{}", repr), - Value::Boolean(ref repr) => write!(f, "{}", repr), - Value::Datetime(ref repr) => write!(f, "{}", repr), - Value::Array(ref array) => write!(f, "{}", array), - Value::InlineTable(ref table) => write!(f, "{}", table), - } - } -} - -impl Display for Array { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!( - f, - "{}[", - self.decor().prefix().unwrap_or(DEFAULT_VALUE_DECOR.0) - )?; - write!(f, "{}", self.iter().join(","))?; - if self.trailing_comma() && !self.is_empty() { - write!(f, ",")?; - } - write!(f, "{}", self.trailing())?; - write!( - f, - "]{}", - self.decor().suffix().unwrap_or(DEFAULT_VALUE_DECOR.1) - ) - } -} - -impl Display for InlineTable { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!( - f, - "{}{{", - self.decor().prefix().unwrap_or(DEFAULT_VALUE_DECOR.0) - )?; - write!(f, "{}", self.preamble)?; - - let children = self.get_values(); - for (i, (key_path, value)) in children.into_iter().enumerate() { - let key = key_path_display(&key_path, DEFAULT_INLINE_KEY_DECOR); - if i > 0 { - write!(f, ",")?; - } - write!(f, "{}={}", key, value)?; - } - - write!( - f, - "}}{}", - self.decor().suffix().unwrap_or(DEFAULT_VALUE_DECOR.1) - ) - } -} - -impl Table { - fn visit_nested_tables<'t, F>( - &'t self, - path: &mut Vec<&'t Key>, - is_array_of_tables: bool, - callback: &mut F, - ) -> Result - where - F: FnMut(&'t Table, &Vec<&'t Key>, bool) -> Result, - { - callback(self, path, is_array_of_tables)?; - - for kv in self.items.values() { - match kv.value { - Item::Table(ref t) if !t.is_dotted() => { - path.push(&kv.key); - t.visit_nested_tables(path, false, callback)?; - path.pop(); - } - Item::ArrayOfTables(ref a) => { - for t in a.iter() { - path.push(&kv.key); - t.visit_nested_tables(path, true, callback)?; - path.pop(); - } - } - _ => {} - } - } - Ok(()) - } -} - -fn visit_table( - f: &mut dyn Write, - table: &Table, - path: &[&Key], - is_array_of_tables: bool, -) -> Result { - let children = table.get_values(); - - if path.is_empty() { - // don't print header for the root node - } else if is_array_of_tables { - write!( - f, - "{}[[", - table.decor.prefix().unwrap_or(DEFAULT_TABLE_DECOR.0) - )?; - write!( - f, - "{}", - path.iter() - .map(|k| k.decor.display(k, DEFAULT_KEY_PATH_DECOR)) - .join(".") - )?; - writeln!( - f, - "]]{}", - table.decor.suffix().unwrap_or(DEFAULT_TABLE_DECOR.1) - )?; - } else if !(table.implicit && children.is_empty()) { - write!( - f, - "{}[", - table.decor.prefix().unwrap_or(DEFAULT_TABLE_DECOR.0) - )?; - write!( - f, - "{}", - path.iter() - .map(|k| k.decor.display(k, DEFAULT_KEY_PATH_DECOR)) - .join(".") - )?; - writeln!( - f, - "]{}", - table.decor.suffix().unwrap_or(DEFAULT_TABLE_DECOR.1) - )?; - } - // print table body - for (key_path, value) in children { - let key = key_path_display(&key_path, DEFAULT_KEY_DECOR); - writeln!(f, "{}={}", key, value)?; - } - Ok(()) -} - -fn key_path_display(key_path: &[&Key], default: (&'static str, &'static str)) -> impl Display { - key_path - .iter() - .enumerate() - .map(|(ki, k)| { - let prefix = if ki == 0 { - default.0 - } else { - DEFAULT_KEY_PATH_DECOR.0 - }; - let suffix = if ki + 1 == key_path.len() { - default.1 - } else { - DEFAULT_KEY_PATH_DECOR.1 - }; - k.decor.display(k, (prefix, suffix)) - }) - .join(".") -} - -impl Display for Table { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let mut path = Vec::new(); - - self.visit_nested_tables(&mut path, false, &mut |t, path, is_array| { - visit_table(f, t, path, is_array) - })?; - Ok(()) - } -} - -impl Display for Document { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - let mut path = Vec::new(); - let mut last_position = 0; - let mut tables = Vec::new(); - self.as_table() - .visit_nested_tables(&mut path, false, &mut |t, p, is_array| { - if let Some(pos) = t.position { - last_position = pos; - } - tables.push((last_position, t, p.clone(), is_array)); - Ok(()) - }) - .unwrap(); - - tables.sort_by_key(|&(id, _, _, _)| id); - for (_, table, path, is_array) in tables { - visit_table(f, table, &path, is_array)?; - } - self.trailing.fmt(f) - } -} diff --git a/src/inline_table.rs b/src/inline_table.rs index ff15b02a..51c8ffa9 100644 --- a/src/inline_table.rs +++ b/src/inline_table.rs @@ -270,6 +270,12 @@ impl InlineTable { } } +impl std::fmt::Display for InlineTable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + crate::encode::Encode::encode(self, f, ("", "")) + } +} + impl, V: Into> Extend<(K, V)> for InlineTable { fn extend>(&mut self, iter: T) { for (key, value) in iter { diff --git a/src/key.rs b/src/key.rs index 57f9fa10..e671c83a 100644 --- a/src/key.rs +++ b/src/key.rs @@ -99,6 +99,12 @@ impl Key { } } +impl std::fmt::Display for Key { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + crate::encode::Encode::encode(self, f, ("", "")) + } +} + impl FromStr for Key { type Err = parser::TomlError; diff --git a/src/repr.rs b/src/repr.rs index 961147a2..56f8df6d 100644 --- a/src/repr.rs +++ b/src/repr.rs @@ -62,6 +62,15 @@ where } } +impl std::fmt::Display for Formatted +where + T: ValueRepr, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + crate::encode::Encode::encode(self, f, ("", "")) + } +} + pub trait ValueRepr: crate::private::Sealed { /// The TOML representation of the value fn to_repr(&self) -> Repr; @@ -86,6 +95,12 @@ impl Repr { } } +impl std::fmt::Display for Repr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_raw().fmt(f) + } +} + /// A prefix and suffix, /// /// Including comments, whitespaces and newlines. @@ -119,27 +134,4 @@ impl Decor { pub fn suffix(&self) -> Option<&str> { self.suffix.as_deref() } - - /// Render a value with its decor - pub(crate) fn display<'d, D: std::fmt::Display + std::fmt::Debug>( - &'d self, - inner: &'d D, - default: (&'static str, &'static str), - ) -> DecorDisplay<'d, D> { - DecorDisplay { - inner, - decor: self, - default, - } - } -} - -/// Render a prefix and suffix, -/// -/// Including comments, whitespaces and newlines. -#[derive(Debug)] -pub(crate) struct DecorDisplay<'d, D> { - pub(crate) inner: &'d D, - pub(crate) decor: &'d Decor, - pub(crate) default: (&'static str, &'static str), } diff --git a/src/table.rs b/src/table.rs index 5b8dd6bc..09feae81 100644 --- a/src/table.rs +++ b/src/table.rs @@ -295,6 +295,21 @@ impl Table { } } +impl std::fmt::Display for Table { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use crate::encode::Encode; + let children = self.get_values(); + // print table body + for (key_path, value) in children { + key_path.as_slice().encode(f, DEFAULT_KEY_DECOR)?; + write!(f, "=")?; + value.encode(f, DEFAULT_VALUE_DECOR)?; + writeln!(f)?; + } + Ok(()) + } +} + impl, V: Into> Extend<(K, V)> for Table { fn extend>(&mut self, iter: T) { for (key, value) in iter { diff --git a/src/value.rs b/src/value.rs index c33c0bdb..8e22947d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -192,7 +192,7 @@ impl Value { /// # Example /// ```rust /// let mut v = toml_edit::Value::from(42); - /// assert_eq!(&v.to_string(), " 42"); + /// assert_eq!(&v.to_string(), "42"); /// let d = v.decorated(" ", " "); /// assert_eq!(&d.to_string(), " 42 "); /// ``` @@ -323,9 +323,27 @@ impl, V: Into> FromIterator<(K, V)> for Value { } } +impl std::fmt::Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + crate::encode::Encode::encode(self, f, ("", "")) + } +} + // `key1 = value1` pub(crate) const DEFAULT_VALUE_DECOR: (&str, &str) = (" ", ""); // `{ key = value }` pub(crate) const DEFAULT_TRAILING_VALUE_DECOR: (&str, &str) = (" ", " "); // `[value1, value2]` pub(crate) const DEFAULT_LEADING_VALUE_DECOR: (&str, &str) = ("", ""); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_iter_formatting() { + let features = vec!["node".to_owned(), "mouth".to_owned()]; + let features: Value = features.iter().cloned().collect(); + assert_eq!(features.to_string(), r#"["node", "mouth"]"#); + } +}