From 6e99df4f72334079eb53b22ae3671e6b58157d61 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Jan 2023 09:24:14 -0600 Subject: [PATCH] feat(toml)!: Update generator by using `toml_edit` This is the other main half of #340. Still have deprecations and tests left Note that strings are rendered differently, see #287 By extension this also finishes up #396. BREAKING CHANGES - `impl Display for toml::Value` now renders as values, not documents, see instead `Table` - `toml::ser::Serializer` only serializes documents, instead see `toml::ser::ValueSerializer` - `toml::ser::tables_last` is removed, no longer needed - `toml::ser::to_vec` is removed to mirror the loss of `from_slice` - atm `toml::ser::to_string_pretty` just causes larger arrays to be indented - `toml::ser::Error` is now opaque - `toml::ser::Serializer::pretty_string` was removed - `toml::ser::Serializer::pretty_string_literal` was removed - `toml::ser::Serializer::pretty_array` was removed - `toml::ser::Serializer::pretty_array_indent` was removed - `toml::ser::Serializer::pretty_array_trailing_comma` was removed - `toml::ser::Serializer` is now used used by value, rather than `&mut` Fixes #396 --- crates/toml/src/fmt.rs | 68 + crates/toml/src/lib.rs | 4 +- crates/toml/src/ser.rs | 2083 +++++------------ crates/toml/src/value.rs | 89 +- crates/toml/tests/testsuite/display_tricky.rs | 2 +- crates/toml/tests/testsuite/pretty.rs | 236 +- crates/toml/tests/testsuite/serde.rs | 10 +- crates/toml/tests/testsuite/tables_last.rs | 26 +- 8 files changed, 790 insertions(+), 1728 deletions(-) create mode 100644 crates/toml/src/fmt.rs diff --git a/crates/toml/src/fmt.rs b/crates/toml/src/fmt.rs new file mode 100644 index 00000000..2623f04f --- /dev/null +++ b/crates/toml/src/fmt.rs @@ -0,0 +1,68 @@ +#[derive(Copy, Clone)] +pub(crate) struct DocumentFormatter { + pub(crate) multiline_array: bool, +} + +impl Default for DocumentFormatter { + fn default() -> Self { + Self { + multiline_array: false, + } + } +} + +impl toml_edit::visit_mut::VisitMut for DocumentFormatter { + fn visit_document_mut(&mut self, node: &mut toml_edit::Document) { + toml_edit::visit_mut::visit_document_mut(self, node); + } + + fn visit_item_mut(&mut self, node: &mut toml_edit::Item) { + let other = std::mem::take(node); + let other = match other.into_table().map(toml_edit::Item::Table) { + Ok(i) => i, + Err(i) => i, + }; + let other = match other + .into_array_of_tables() + .map(toml_edit::Item::ArrayOfTables) + { + Ok(i) => i, + Err(i) => i, + }; + *node = other; + + toml_edit::visit_mut::visit_item_mut(self, node); + } + + fn visit_table_mut(&mut self, node: &mut toml_edit::Table) { + node.decor_mut().clear(); + + // Empty tables could be semantically meaningful, so make sure they are not implicit + if !node.is_empty() { + node.set_implicit(true); + } + + toml_edit::visit_mut::visit_table_mut(self, node); + } + + fn visit_value_mut(&mut self, node: &mut toml_edit::Value) { + node.decor_mut().clear(); + + toml_edit::visit_mut::visit_value_mut(self, node); + } + + fn visit_array_mut(&mut self, node: &mut toml_edit::Array) { + toml_edit::visit_mut::visit_array_mut(self, node); + + if !self.multiline_array || (0..=1).contains(&node.len()) { + node.set_trailing(""); + node.set_trailing_comma(false); + } else { + for item in node.iter_mut() { + item.decor_mut().set_prefix("\n "); + } + node.set_trailing("\n"); + node.set_trailing_comma(true); + } + } +} diff --git a/crates/toml/src/lib.rs b/crates/toml/src/lib.rs index d68d9bea..e0c3b813 100644 --- a/crates/toml/src/lib.rs +++ b/crates/toml/src/lib.rs @@ -153,7 +153,7 @@ pub use crate::value::Value; pub mod ser; #[doc(no_inline)] -pub use crate::ser::{to_string, to_string_pretty, to_vec, Serializer}; +pub use crate::ser::{to_string, to_string_pretty, Serializer}; pub mod de; #[doc(no_inline)] pub use crate::de::{from_str, Deserializer, ValueDeserializer}; @@ -161,6 +161,8 @@ pub use crate::de::{from_str, Deserializer, ValueDeserializer}; #[doc(hidden)] pub mod macros; +mod fmt; + pub use serde_spanned::Spanned; // Shortcuts for the module doc-comment diff --git a/crates/toml/src/ser.rs b/crates/toml/src/ser.rs index 74bacb6b..cda9e1de 100644 --- a/crates/toml/src/ser.rs +++ b/crates/toml/src/ser.rs @@ -3,48 +3,8 @@ //! This module contains all the Serde support for serializing Rust structures //! into TOML documents (as strings). Note that some top-level functions here //! are also provided at the top of the crate. -//! -//! Note that the TOML format has a restriction that if a table itself contains -//! tables, all keys with non-table values must be emitted first. This is -//! typically easy to ensure happens when you're defining a `struct` as you can -//! reorder the fields manually, but when working with maps (such as `BTreeMap` -//! or `HashMap`) this can lead to serialization errors. In those situations you -//! may use the `tables_last` function in this module like so: -//! -//! ```rust -//! # use serde::Serialize; -//! # use std::collections::HashMap; -//! #[derive(Serialize)] -//! struct Manifest { -//! package: Package, -//! #[serde(serialize_with = "toml::ser::tables_last")] -//! dependencies: HashMap, -//! } -//! # type Package = String; -//! # type Dependency = String; -//! # fn main() {} -//! ``` - -use std::cell::Cell; -use std::error; -use std::fmt::{self, Write}; -use std::marker; -use std::rc::Rc; - -use serde::ser; -use toml_datetime::__unstable as datetime; - -/// Serialize the given data structure as a TOML byte vector. -/// -/// Serialization can fail if `T`'s implementation of `Serialize` decides to -/// fail, if `T` contains a map with non-string keys, or if `T` attempts to -/// serialize an unsupported datatype such as an enum, tuple, or tuple struct. -pub fn to_vec(value: &T) -> Result, Error> -where - T: ser::Serialize, -{ - to_string(value).map(|e| e.into_bytes()) -} + +use crate::fmt::DocumentFormatter; /// Serialize the given data structure as a String of TOML. /// @@ -84,11 +44,12 @@ where /// ``` pub fn to_string(value: &T) -> Result where - T: ser::Serialize, + T: serde::ser::Serialize, { - let mut dst = String::with_capacity(128); - value.serialize(&mut Serializer::new(&mut dst))?; - Ok(dst) + let mut output = String::new(); + let serializer = Serializer::new(&mut output); + value.serialize(serializer)?; + Ok(output) } /// Serialize the given data structure as a "pretty" String of TOML. @@ -97,92 +58,68 @@ where /// "pretty" output. See `Serializer::pretty` for more details. pub fn to_string_pretty(value: &T) -> Result where - T: ser::Serialize, + T: serde::ser::Serialize, { - let mut dst = String::with_capacity(128); - value.serialize(&mut Serializer::pretty(&mut dst))?; - Ok(dst) + let mut output = String::new(); + let serializer = Serializer::pretty(&mut output); + value.serialize(serializer)?; + Ok(output) } /// Errors that can occur when serializing a type. -#[derive(Debug, PartialEq, Eq, Clone)] -#[non_exhaustive] -pub enum Error { - /// Indicates that a Rust type was requested to be serialized but it was not - /// supported. - /// - /// Currently the TOML format does not support serializing types such as - /// enums, tuples and tuple structs. - UnsupportedType, - - /// The key of all TOML maps must be strings, but serialization was - /// attempted where the key of a map was not a string. - KeyNotString, - - /// An error that we never omit but keep for backwards compatibility - #[doc(hidden)] - KeyNewline, - - /// An array had to be homogeneous, but now it is allowed to be heterogeneous. - #[doc(hidden)] - ArrayMixedType, - - /// All values in a TOML table must be emitted before further tables are - /// emitted. If a value is emitted *after* a table then this error is - /// generated. - ValueAfterTable, - - /// A serialized date was invalid. - DateInvalid, +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Error { + pub(crate) inner: toml_edit::ser::Error, +} - /// A serialized number was invalid. - NumberInvalid, +impl Error { + pub(crate) fn new(inner: impl std::fmt::Display) -> Self { + Self { + inner: toml_edit::ser::Error::Custom(inner.to_string()), + } + } - /// None was attempted to be serialized, but it's not supported. - UnsupportedNone, + pub(crate) fn wrap(inner: toml_edit::ser::Error) -> Self { + Self { inner } + } - /// A custom error which could be generated when serializing a particular - /// type. - Custom(String), -} + pub(crate) fn unsupported_type(t: Option<&'static str>) -> Self { + Self { + inner: toml_edit::ser::Error::UnsupportedType(t), + } + } -#[derive(Debug, Default, Clone)] -/// Internal place for holding array settings -struct ArraySettings { - indent: usize, - trailing_comma: bool, -} + pub(crate) fn unsupported_none() -> Self { + Self { + inner: toml_edit::ser::Error::UnsupportedNone, + } + } -impl ArraySettings { - fn pretty() -> ArraySettings { - ArraySettings { - indent: 4, - trailing_comma: true, + pub(crate) fn key_not_string() -> Self { + Self { + inner: toml_edit::ser::Error::KeyNotString, } } } -#[derive(Debug, Default, Clone)] -/// String settings -struct StringSettings { - /// Whether to use literal strings when possible - literal: bool, +impl serde::ser::Error for Error { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + Error::new(msg) + } } -impl StringSettings { - fn pretty() -> StringSettings { - StringSettings { literal: true } +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.inner.fmt(f) } } -#[derive(Debug, Default, Clone)] -/// Internal struct for holding serialization settings -struct Settings { - array: Option, - string: Option, -} +impl std::error::Error for Error {} -/// Serialization implementation for TOML. +/// Serialization for TOML documents. /// /// This structure implements serialization support for TOML to serialize an /// arbitrary type to TOML. Note that the TOML format does not support all @@ -191,718 +128,250 @@ struct Settings { /// /// Currently a serializer always writes its output to an in-memory `String`, /// which is passed in when creating the serializer itself. -pub struct Serializer<'a> { - dst: &'a mut String, - state: State<'a>, - settings: Rc, -} - -#[derive(Debug, Copy, Clone)] -enum ArrayState { - Started, - StartedAsATable, -} - -#[derive(Debug, Clone)] -enum State<'a> { - Table { - key: &'a str, - parent: &'a State<'a>, - first: &'a Cell, - table_emitted: &'a Cell, - }, - Array { - parent: &'a State<'a>, - first: &'a Cell, - type_: &'a Cell>, - len: Option, - }, - End, -} - -#[doc(hidden)] -pub struct SerializeSeq<'a, 'b> { - ser: &'b mut Serializer<'a>, - first: Cell, - type_: Cell>, - len: Option, -} - -#[doc(hidden)] -pub enum SerializeTable<'a, 'b> { - Datetime(&'b mut Serializer<'a>), - Table { - ser: &'b mut Serializer<'a>, - key: String, - first: Cell, - table_emitted: Cell, - }, +#[non_exhaustive] +pub struct Serializer<'d> { + dst: &'d mut String, + settings: DocumentFormatter, } -impl<'a> Serializer<'a> { +impl<'d> Serializer<'d> { /// Creates a new serializer which will emit TOML into the buffer provided. /// /// The serializer can then be used to serialize a type after which the data /// will be present in `dst`. - pub fn new(dst: &'a mut String) -> Serializer<'a> { - Serializer { + pub fn new(dst: &'d mut String) -> Self { + Self { dst, - state: State::End, - settings: Rc::new(Settings::default()), + settings: Default::default(), } } - /// Instantiate a "pretty" formatter - /// - /// By default this will use: - /// - /// - pretty strings: strings with newlines will use the `'''` syntax. See - /// `Serializer::pretty_string` - /// - pretty arrays: each item in arrays will be on a newline, have an indentation of 4 and - /// have a trailing comma. See `Serializer::pretty_array` - pub fn pretty(dst: &'a mut String) -> Serializer<'a> { - Serializer { - dst, - state: State::End, - settings: Rc::new(Settings { - array: Some(ArraySettings::pretty()), - string: Some(StringSettings::pretty()), - }), - } + /// Apply a default "pretty" policy to the document + pub fn pretty(dst: &'d mut String) -> Self { + let mut ser = Serializer::new(dst); + ser.settings.multiline_array = true; + ser } - - /// Enable or Disable pretty strings - /// - /// If enabled, literal strings will be used when possible and strings with - /// one or more newlines will use triple quotes (i.e.: `'''` or `"""`) - /// - /// # Examples - /// - /// Instead of: - /// - /// ```toml,ignore - /// single = "no newlines" - /// text = "\nfoo\nbar\n" - /// ``` - /// - /// You will have: - /// - /// ```toml,ignore - /// single = 'no newlines' - /// text = ''' - /// foo - /// bar - /// ''' - /// ``` - pub fn pretty_string(&mut self, value: bool) -> &mut Self { - Rc::get_mut(&mut self.settings).unwrap().string = if value { - Some(StringSettings::pretty()) - } else { - None - }; - self - } - - /// Enable or Disable Literal strings for pretty strings - /// - /// If enabled, literal strings will be used when possible and strings with - /// one or more newlines will use triple quotes (i.e.: `'''` or `"""`) - /// - /// If disabled, literal strings will NEVER be used and strings with one or - /// more newlines will use `"""` - /// - /// # Examples - /// - /// Instead of: - /// - /// ```toml,ignore - /// single = "no newlines" - /// text = "\nfoo\nbar\n" - /// ``` - /// - /// You will have: - /// - /// ```toml,ignore - /// single = "no newlines" - /// text = """ - /// foo - /// bar - /// """ - /// ``` - pub fn pretty_string_literal(&mut self, value: bool) -> &mut Self { - let use_default = if let Some(ref mut s) = Rc::get_mut(&mut self.settings).unwrap().string { - s.literal = value; - false - } else { - true - }; - - if use_default { - let mut string = StringSettings::pretty(); - string.literal = value; - Rc::get_mut(&mut self.settings).unwrap().string = Some(string); - } - self - } - - /// Enable or Disable pretty arrays - /// - /// If enabled, arrays will always have each item on their own line. - /// - /// Some specific features can be controlled via other builder methods: - /// - /// - `Serializer::pretty_array_indent`: set the indent to a value other - /// than 4. - /// - `Serializer::pretty_array_trailing_comma`: enable/disable the trailing - /// comma on the last item. - /// - /// # Examples - /// - /// Instead of: - /// - /// ```toml,ignore - /// array = ["foo", "bar"] - /// ``` - /// - /// You will have: - /// - /// ```toml,ignore - /// array = [ - /// "foo", - /// "bar", - /// ] - /// ``` - pub fn pretty_array(&mut self, value: bool) -> &mut Self { - Rc::get_mut(&mut self.settings).unwrap().array = if value { - Some(ArraySettings::pretty()) - } else { - None - }; - self - } - - /// Set the indent for pretty arrays - /// - /// See `Serializer::pretty_array` for more details. - pub fn pretty_array_indent(&mut self, value: usize) -> &mut Self { - let use_default = if let Some(ref mut a) = Rc::get_mut(&mut self.settings).unwrap().array { - a.indent = value; - false - } else { - true - }; - - if use_default { - let mut array = ArraySettings::pretty(); - array.indent = value; - Rc::get_mut(&mut self.settings).unwrap().array = Some(array); - } - self - } - - /// Specify whether to use a trailing comma when serializing pretty arrays - /// - /// See `Serializer::pretty_array` for more details. - pub fn pretty_array_trailing_comma(&mut self, value: bool) -> &mut Self { - let use_default = if let Some(ref mut a) = Rc::get_mut(&mut self.settings).unwrap().array { - a.trailing_comma = value; - false - } else { - true - }; - - if use_default { - let mut array = ArraySettings::pretty(); - array.trailing_comma = value; - Rc::get_mut(&mut self.settings).unwrap().array = Some(array); - } - self - } - - fn display(&mut self, t: T, type_: ArrayState) -> Result<(), Error> { - self.emit_key(type_)?; - write!(self.dst, "{}", t).map_err(ser::Error::custom)?; - if let State::Table { .. } = self.state { - self.dst.push('\n'); - } - Ok(()) - } - - fn emit_key(&mut self, type_: ArrayState) -> Result<(), Error> { - self.array_type(type_)?; - let state = self.state.clone(); - self._emit_key(&state) - } - - // recursive implementation of `emit_key` above - fn _emit_key(&mut self, state: &State<'_>) -> Result<(), Error> { - match *state { - State::End => Ok(()), - State::Array { - parent, - first, - type_, - len, - } => { - assert!(type_.get().is_some()); - if first.get() { - self._emit_key(parent)?; - } - self.emit_array(first, len) - } - State::Table { - parent, - first, - table_emitted, - key, - } => { - if table_emitted.get() { - return Err(Error::ValueAfterTable); - } - if first.get() { - self.emit_table_header(parent)?; - first.set(false); - } - self.escape_key(key)?; - self.dst.push_str(" = "); - Ok(()) - } - } - } - - fn emit_array(&mut self, first: &Cell, len: Option) -> Result<(), Error> { - match (len, &self.settings.array) { - (Some(0..=1), _) | (_, &None) => { - if first.get() { - self.dst.push('[') - } else { - self.dst.push_str(", ") - } - } - (_, &Some(ref a)) => { - if first.get() { - self.dst.push_str("[\n") - } else { - self.dst.push_str(",\n") - } - for _ in 0..a.indent { - self.dst.push(' '); - } - } - } - Ok(()) - } - - fn array_type(&mut self, type_: ArrayState) -> Result<(), Error> { - let prev = match self.state { - State::Array { type_, .. } => type_, - _ => return Ok(()), - }; - if prev.get().is_none() { - prev.set(Some(type_)); - } - Ok(()) - } - - fn escape_key(&mut self, key: &str) -> Result<(), Error> { - let ok = !key.is_empty() - && key - .chars() - .all(|c| matches!(c,'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_')); - if ok { - write!(self.dst, "{}", key).map_err(ser::Error::custom)?; - } else { - self.emit_str(key, true)?; - } - Ok(()) - } - - fn emit_str(&mut self, value: &str, is_key: bool) -> Result<(), Error> { - #[derive(PartialEq)] - enum Type { - NewlineTripple, - OnelineTripple, - OnelineSingle, - } - - 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 - // 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 ty = Type::OnelineSingle; - // found consecutive single quotes - let mut max_found_singles = 0; - let mut found_singles = 0; - let mut can_be_pretty = true; - - for ch in value.chars() { - if can_be_pretty { - if ch == '\'' { - found_singles += 1; - if found_singles >= 3 { - can_be_pretty = false; - } - } else { - if found_singles > max_found_singles { - max_found_singles = found_singles; - } - found_singles = 0 - } - match ch { - '\t' => {} - '\n' => ty = Type::NewlineTripple, - // Escape codes are needed if any ascii control - // characters are present, including \b \f \r. - c if c <= '\u{1f}' || c == '\u{7f}' => can_be_pretty = false, - _ => {} - } - 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 && found_singles > 0 && value.ends_with('\'') { - // We cannot escape the ending quote so we must use """ - can_be_pretty = false; - } - if !can_be_pretty { - debug_assert!(ty != Type::OnelineTripple); - return Repr::Std(ty); - } - if found_singles > max_found_singles { - max_found_singles = found_singles; - } - debug_assert!(max_found_singles < 3); - if ty == Type::OnelineSingle && max_found_singles >= 1 { - // no newlines, but must use ''' because it has ' in it - ty = Type::OnelineTripple; - } - Repr::Literal(out, ty) - } - - let repr = if !is_key && self.settings.string.is_some() { - match (&self.settings.string, do_pretty(value)) { - (&Some(StringSettings { literal: false, .. }), Repr::Literal(_, ty)) => { - Repr::Std(ty) - } - (_, r) => r, - } - } else { - Repr::Std(Type::OnelineSingle) - }; - match repr { - Repr::Literal(literal, ty) => { - // A pretty string - match ty { - Type::NewlineTripple => self.dst.push_str("'''\n"), - Type::OnelineTripple => self.dst.push_str("'''"), - Type::OnelineSingle => self.dst.push('\''), - } - self.dst.push_str(&literal); - match ty { - Type::OnelineSingle => self.dst.push('\''), - _ => self.dst.push_str("'''"), - } - } - Repr::Std(ty) => { - match ty { - Type::NewlineTripple => self.dst.push_str("\"\"\"\n"), - // note: OnelineTripple can happen if do_pretty wants to do - // '''it's one line''' - // but settings.string.literal == false - Type::OnelineSingle | Type::OnelineTripple => self.dst.push('"'), - } - for ch in value.chars() { - match ch { - '\u{8}' => self.dst.push_str("\\b"), - '\u{9}' => self.dst.push_str("\\t"), - '\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{d}' => self.dst.push_str("\\r"), - '\u{22}' => self.dst.push_str("\\\""), - '\u{5c}' => self.dst.push_str("\\\\"), - c if c <= '\u{1f}' || c == '\u{7f}' => { - write!(self.dst, "\\u{:04X}", ch as u32).map_err(ser::Error::custom)?; - } - ch => self.dst.push(ch), - } - } - match ty { - Type::NewlineTripple => self.dst.push_str("\"\"\""), - Type::OnelineSingle | Type::OnelineTripple => self.dst.push('"'), - } - } - } - Ok(()) - } - - fn emit_table_header(&mut self, state: &State<'_>) -> Result<(), Error> { - let array_of_tables = match *state { - State::End => return Ok(()), - State::Array { .. } => true, - _ => false, - }; - - // Unlike [..]s, we can't omit [[..]] ancestors, so be sure to emit table - // headers for them. - let mut p = state; - if let State::Array { first, parent, .. } = *state { - if first.get() { - p = parent; - } - } - while let State::Table { first, parent, .. } = *p { - p = parent; - if !first.get() { - break; - } - if let State::Array { - parent: &State::Table { .. }, - .. - } = *parent - { - self.emit_table_header(parent)?; - break; - } - } - - match *state { - State::Table { first, .. } => { - if !first.get() { - // Newline if we are a table that is not the first - // table in the document. - self.dst.push('\n'); - } - } - State::Array { parent, first, .. } => { - if !first.get() { - // Always newline if we are not the first item in the - // table-array - self.dst.push('\n'); - } else if let State::Table { first, .. } = *parent { - if !first.get() { - // Newline if we are not the first item in the document - self.dst.push('\n'); - } - } - } - _ => {} - } - self.dst.push('['); - if array_of_tables { - self.dst.push('['); - } - self.emit_key_part(state)?; - if array_of_tables { - self.dst.push(']'); - } - self.dst.push_str("]\n"); - Ok(()) - } - - fn emit_key_part(&mut self, key: &State<'_>) -> Result { - match *key { - State::Array { parent, .. } => self.emit_key_part(parent), - State::End => Ok(true), - State::Table { - key, - parent, - table_emitted, - .. - } => { - table_emitted.set(true); - let first = self.emit_key_part(parent)?; - if !first { - self.dst.push('.'); - } - self.escape_key(key)?; - Ok(false) - } - } - } -} - -macro_rules! serialize_float { - ($this:expr, $v:expr) => {{ - $this.emit_key(ArrayState::Started)?; - match ($v.is_sign_negative(), $v.is_nan(), $v == 0.0) { - (true, true, _) => write!($this.dst, "-nan"), - (false, true, _) => write!($this.dst, "nan"), - (true, false, true) => write!($this.dst, "-0.0"), - (false, false, true) => write!($this.dst, "0.0"), - (_, false, false) => write!($this.dst, "{}", $v).and_then(|_| { - if $v % 1.0 == 0.0 { - write!($this.dst, ".0") - } else { - Ok(()) - } - }), - } - .map_err(ser::Error::custom)?; - - if let State::Table { .. } = $this.state { - $this.dst.push_str("\n"); - } - return Ok(()); - }}; } -impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> { +impl<'d> serde::ser::Serializer for Serializer<'d> { type Ok = (); type Error = Error; - type SerializeSeq = SerializeSeq<'a, 'b>; - type SerializeTuple = SerializeSeq<'a, 'b>; - type SerializeTupleStruct = SerializeSeq<'a, 'b>; - type SerializeTupleVariant = SerializeSeq<'a, 'b>; - type SerializeMap = SerializeTable<'a, 'b>; - type SerializeStruct = SerializeTable<'a, 'b>; - type SerializeStructVariant = ser::Impossible<(), Error>; - - fn serialize_bool(self, v: bool) -> Result<(), Self::Error> { - self.display(v, ArrayState::Started) - } - - fn serialize_i8(self, v: i8) -> Result<(), Self::Error> { - self.display(v, ArrayState::Started) - } - - fn serialize_i16(self, v: i16) -> Result<(), Self::Error> { - self.display(v, ArrayState::Started) + type SerializeSeq = SerializeDocumentArray<'d>; + type SerializeTuple = SerializeDocumentArray<'d>; + type SerializeTupleStruct = SerializeDocumentArray<'d>; + type SerializeTupleVariant = SerializeDocumentArray<'d>; + type SerializeMap = SerializeDocumentTable<'d>; + type SerializeStruct = SerializeDocumentTable<'d>; + type SerializeStructVariant = serde::ser::Impossible; + + fn serialize_bool(self, v: bool) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_bool(v), + ) + } + + fn serialize_i8(self, v: i8) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_i8(v), + ) + } + + fn serialize_i16(self, v: i16) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_i16(v), + ) + } + + fn serialize_i32(self, v: i32) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_i32(v), + ) + } + + fn serialize_i64(self, v: i64) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_i64(v), + ) + } + + fn serialize_u8(self, v: u8) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_u8(v), + ) + } + + fn serialize_u16(self, v: u16) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_u16(v), + ) + } + + fn serialize_u32(self, v: u32) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_u32(v), + ) + } + + fn serialize_u64(self, v: u64) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_u64(v), + ) + } + + fn serialize_f32(self, v: f32) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_f32(v), + ) + } + + fn serialize_f64(self, v: f64) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_f64(v), + ) + } + + fn serialize_char(self, v: char) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_char(v), + ) } - fn serialize_i32(self, v: i32) -> Result<(), Self::Error> { - self.display(v, ArrayState::Started) + fn serialize_str(self, v: &str) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_str(v), + ) } - fn serialize_i64(self, v: i64) -> Result<(), Self::Error> { - self.display(v, ArrayState::Started) + fn serialize_bytes(self, v: &[u8]) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_bytes(v), + ) } - fn serialize_u8(self, v: u8) -> Result<(), Self::Error> { - self.display(v, ArrayState::Started) - } - - fn serialize_u16(self, v: u16) -> Result<(), Self::Error> { - self.display(v, ArrayState::Started) - } - - fn serialize_u32(self, v: u32) -> Result<(), Self::Error> { - self.display(v, ArrayState::Started) - } - - fn serialize_u64(self, v: u64) -> Result<(), Self::Error> { - self.display(v, ArrayState::Started) - } - - fn serialize_f32(self, v: f32) -> Result<(), Self::Error> { - serialize_float!(self, v) - } - - fn serialize_f64(self, v: f64) -> Result<(), Self::Error> { - serialize_float!(self, v) - } - - fn serialize_char(self, v: char) -> Result<(), Self::Error> { - let mut buf = [0; 4]; - self.serialize_str(v.encode_utf8(&mut buf)) - } - - fn serialize_str(self, value: &str) -> Result<(), Self::Error> { - self.emit_key(ArrayState::Started)?; - self.emit_str(value, false)?; - if let State::Table { .. } = self.state { - self.dst.push('\n'); - } - Ok(()) - } - - fn serialize_bytes(self, value: &[u8]) -> Result<(), Self::Error> { - use serde::ser::Serialize; - value.serialize(self) - } - - fn serialize_none(self) -> Result<(), Self::Error> { - Err(Error::UnsupportedNone) + fn serialize_none(self) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_none(), + ) } - fn serialize_some(self, value: &T) -> Result<(), Self::Error> + fn serialize_some(self, v: &T) -> Result where - T: ser::Serialize, + T: serde::ser::Serialize, { - value.serialize(self) + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_some(v), + ) } - fn serialize_unit(self) -> Result<(), Self::Error> { - Err(Error::UnsupportedType) + fn serialize_unit(self) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_unit(), + ) } - fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Self::Error> { - Err(Error::UnsupportedType) + fn serialize_unit_struct(self, name: &'static str) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_unit_struct(name), + ) } fn serialize_unit_variant( self, - _name: &'static str, - _variant_index: u32, + name: &'static str, + variant_index: u32, variant: &'static str, - ) -> Result<(), Self::Error> { - self.serialize_str(variant) + ) -> Result { + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_unit_variant( + name, + variant_index, + variant, + ), + ) } fn serialize_newtype_struct( self, - _name: &'static str, - value: &T, - ) -> Result<(), Self::Error> + name: &'static str, + v: &T, + ) -> Result where - T: ser::Serialize, + T: serde::ser::Serialize, { - value.serialize(self) + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_newtype_struct(name, v), + ) } fn serialize_newtype_variant( self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _value: &T, - ) -> Result<(), Self::Error> + name: &'static str, + variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result where - T: ser::Serialize, + T: serde::ser::Serialize, { - Err(Error::UnsupportedType) + write_document( + self.dst, + self.settings, + toml_edit::ser::ValueSerializer::new().serialize_newtype_variant( + name, + variant_index, + variant, + value, + ), + ) } fn serialize_seq(self, len: Option) -> Result { - self.array_type(ArrayState::Started)?; - Ok(SerializeSeq { - ser: self, - first: Cell::new(true), - type_: Cell::new(None), - len, - }) + let ser = toml_edit::ser::ValueSerializer::new() + .serialize_seq(len) + .map_err(Error::wrap)?; + let ser = SerializeDocumentArray::new(self, ser); + Ok(ser) } fn serialize_tuple(self, len: usize) -> Result { @@ -927,570 +396,437 @@ impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> { self.serialize_seq(Some(len)) } - fn serialize_map(self, _len: Option) -> Result { - self.array_type(ArrayState::StartedAsATable)?; - Ok(SerializeTable::Table { - ser: self, - key: String::new(), - first: Cell::new(true), - table_emitted: Cell::new(false), - }) + fn serialize_map(self, len: Option) -> Result { + let ser = toml_edit::ser::ValueSerializer::new() + .serialize_map(len) + .map_err(Error::wrap)?; + let ser = SerializeDocumentTable::new(self, ser); + Ok(ser) } fn serialize_struct( self, - name: &'static str, - _len: usize, + _name: &'static str, + len: usize, ) -> Result { - if name == datetime::NAME { - self.array_type(ArrayState::Started)?; - Ok(SerializeTable::Datetime(self)) - } else { - self.array_type(ArrayState::StartedAsATable)?; - Ok(SerializeTable::Table { - ser: self, - key: String::new(), - first: Cell::new(true), - table_emitted: Cell::new(false), - }) - } + self.serialize_map(Some(len)) } fn serialize_struct_variant( self, - _name: &'static str, + name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { - Err(Error::UnsupportedType) + Err(Error::unsupported_type(Some(name))) } } -impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> { - type Ok = (); - type Error = Error; +type InnerSerializeDocumentSeq = + ::SerializeSeq; - fn serialize_element(&mut self, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - value.serialize(&mut Serializer { - dst: &mut *self.ser.dst, - state: State::Array { - parent: &self.ser.state, - first: &self.first, - type_: &self.type_, - len: self.len, - }, - settings: self.ser.settings.clone(), - })?; - self.first.set(false); - Ok(()) - } - - fn end(self) -> Result<(), Error> { - match self.type_.get() { - Some(ArrayState::StartedAsATable) => return Ok(()), - Some(ArrayState::Started) => match (self.len, &self.ser.settings.array) { - (Some(0..=1), _) | (_, &None) => { - self.ser.dst.push(']'); - } - (_, &Some(ref a)) => { - if a.trailing_comma { - self.ser.dst.push(','); - } - self.ser.dst.push_str("\n]"); - } - }, - None => { - assert!(self.first.get()); - self.ser.emit_key(ArrayState::Started)?; - self.ser.dst.push_str("[]") - } - } - if let State::Table { .. } = self.ser.state { - self.ser.dst.push('\n'); +#[doc(hidden)] +pub struct SerializeDocumentArray<'d> { + inner: InnerSerializeDocumentSeq, + dst: &'d mut String, + settings: DocumentFormatter, +} + +impl<'d> SerializeDocumentArray<'d> { + pub(crate) fn new(ser: Serializer<'d>, inner: InnerSerializeDocumentSeq) -> Self { + Self { + inner, + dst: ser.dst, + settings: ser.settings, } - Ok(()) } } -impl<'a, 'b> ser::SerializeTuple for SerializeSeq<'a, 'b> { +impl<'d> serde::ser::SerializeSeq for SerializeDocumentArray<'d> { type Ok = (); type Error = Error; fn serialize_element(&mut self, value: &T) -> Result<(), Error> where - T: ser::Serialize, + T: serde::ser::Serialize, { - ser::SerializeSeq::serialize_element(self, value) + self.inner.serialize_element(value).map_err(Error::wrap) } - fn end(self) -> Result<(), Error> { - ser::SerializeSeq::end(self) + fn end(self) -> Result { + write_document(self.dst, self.settings, self.inner.end()) } } -impl<'a, 'b> ser::SerializeTupleVariant for SerializeSeq<'a, 'b> { +impl<'d> serde::ser::SerializeTuple for SerializeDocumentArray<'d> { type Ok = (); type Error = Error; - fn serialize_field(&mut self, value: &T) -> Result<(), Error> + fn serialize_element(&mut self, value: &T) -> Result<(), Error> where - T: ser::Serialize, + T: serde::ser::Serialize, { - ser::SerializeSeq::serialize_element(self, value) + self.inner.serialize_element(value).map_err(Error::wrap) } - fn end(self) -> Result<(), Error> { - ser::SerializeSeq::end(self) + fn end(self) -> Result { + write_document(self.dst, self.settings, self.inner.end()) } } -impl<'a, 'b> ser::SerializeTupleStruct for SerializeSeq<'a, 'b> { +impl<'d> serde::ser::SerializeTupleVariant for SerializeDocumentArray<'d> { type Ok = (); type Error = Error; fn serialize_field(&mut self, value: &T) -> Result<(), Error> where - T: ser::Serialize, + T: serde::ser::Serialize, { - ser::SerializeSeq::serialize_element(self, value) + self.inner.serialize_field(value).map_err(Error::wrap) } - fn end(self) -> Result<(), Error> { - ser::SerializeSeq::end(self) + fn end(self) -> Result { + write_document(self.dst, self.settings, self.inner.end()) } } -impl<'a, 'b> ser::SerializeMap for SerializeTable<'a, 'b> { +impl<'d> serde::ser::SerializeTupleStruct for SerializeDocumentArray<'d> { type Ok = (); type Error = Error; - fn serialize_key(&mut self, input: &T) -> Result<(), Error> + fn serialize_field(&mut self, value: &T) -> Result<(), Error> where - T: ser::Serialize, + T: serde::ser::Serialize, { - match *self { - SerializeTable::Datetime(_) => panic!(), // shouldn't be possible - SerializeTable::Table { ref mut key, .. } => { - key.truncate(0); - *key = input.serialize(StringExtractor)?; - } - } - Ok(()) + self.inner.serialize_field(value).map_err(Error::wrap) } - fn serialize_value(&mut self, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - match *self { - SerializeTable::Datetime(_) => panic!(), // shouldn't be possible - SerializeTable::Table { - ref mut ser, - ref key, - ref first, - ref table_emitted, - .. - } => { - let res = value.serialize(&mut Serializer { - dst: &mut *ser.dst, - state: State::Table { - key, - parent: &ser.state, - first, - table_emitted, - }, - settings: ser.settings.clone(), - }); - match res { - Ok(()) => first.set(false), - Err(Error::UnsupportedNone) => {} - Err(e) => return Err(e), - } - } - } - Ok(()) - } - - fn end(self) -> Result<(), Error> { - match self { - SerializeTable::Datetime(_) => panic!(), // shouldn't be possible - SerializeTable::Table { ser, first, .. } => { - if first.get() { - let state = ser.state.clone(); - ser.emit_table_header(&state)?; - } - } - } - Ok(()) + fn end(self) -> Result { + write_document(self.dst, self.settings, self.inner.end()) } } -impl<'a, 'b> ser::SerializeStruct for SerializeTable<'a, 'b> { - type Ok = (); - type Error = Error; +type InnerSerializeDocumentTable = + ::SerializeMap; - fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Error> - where - T: ser::Serialize, - { - match *self { - SerializeTable::Datetime(ref mut ser) => { - if key == datetime::FIELD { - value.serialize(DateStrEmitter(ser))?; - } else { - return Err(Error::DateInvalid); - } - } - SerializeTable::Table { - ref mut ser, - ref first, - ref table_emitted, - .. - } => { - let res = value.serialize(&mut Serializer { - dst: &mut *ser.dst, - state: State::Table { - key, - parent: &ser.state, - first, - table_emitted, - }, - settings: ser.settings.clone(), - }); - match res { - Ok(()) => first.set(false), - Err(Error::UnsupportedNone) => {} - Err(e) => return Err(e), - } - } - } - Ok(()) - } - - fn end(self) -> Result<(), Error> { - match self { - SerializeTable::Datetime(_) => {} - SerializeTable::Table { ser, first, .. } => { - if first.get() { - let state = ser.state.clone(); - ser.emit_table_header(&state)?; - } - } +#[doc(hidden)] +pub struct SerializeDocumentTable<'d> { + inner: InnerSerializeDocumentTable, + dst: &'d mut String, + settings: DocumentFormatter, +} + +impl<'d> SerializeDocumentTable<'d> { + pub(crate) fn new(ser: Serializer<'d>, inner: InnerSerializeDocumentTable) -> Self { + Self { + inner, + dst: ser.dst, + settings: ser.settings, } - Ok(()) } } -struct DateStrEmitter<'a, 'b>(&'b mut Serializer<'a>); - -impl<'a, 'b> ser::Serializer for DateStrEmitter<'a, 'b> { +impl<'d> serde::ser::SerializeMap for SerializeDocumentTable<'d> { type Ok = (); type Error = Error; - type SerializeSeq = ser::Impossible<(), Error>; - type SerializeTuple = ser::Impossible<(), Error>; - type SerializeTupleStruct = ser::Impossible<(), Error>; - type SerializeTupleVariant = ser::Impossible<(), Error>; - type SerializeMap = ser::Impossible<(), Error>; - type SerializeStruct = ser::Impossible<(), Error>; - type SerializeStructVariant = ser::Impossible<(), Error>; - - fn serialize_bool(self, _v: bool) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_i8(self, _v: i8) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_i16(self, _v: i16) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - fn serialize_i32(self, _v: i32) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_i64(self, _v: i64) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_u8(self, _v: u8) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_u16(self, _v: u16) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_u32(self, _v: u32) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_u64(self, _v: u64) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_f32(self, _v: f32) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_f64(self, _v: f64) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_char(self, _v: char) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_str(self, value: &str) -> Result<(), Self::Error> { - self.0.display(value, ArrayState::Started)?; - Ok(()) - } - - fn serialize_bytes(self, _value: &[u8]) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_none(self) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } - - fn serialize_some(self, _value: &T) -> Result<(), Self::Error> + fn serialize_key(&mut self, input: &T) -> Result<(), Self::Error> where - T: ser::Serialize, + T: serde::ser::Serialize, { - Err(Error::DateInvalid) + self.inner.serialize_key(input).map_err(Error::wrap) } - fn serialize_unit(self) -> Result<(), Self::Error> { - Err(Error::DateInvalid) + fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> + where + T: serde::ser::Serialize, + { + self.inner.serialize_value(value).map_err(Error::wrap) } - fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Self::Error> { - Err(Error::DateInvalid) + fn end(self) -> Result { + write_document(self.dst, self.settings, self.inner.end()) } +} - fn serialize_unit_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - ) -> Result<(), Self::Error> { - Err(Error::DateInvalid) - } +impl<'d> serde::ser::SerializeStruct for SerializeDocumentTable<'d> { + type Ok = (); + type Error = Error; - fn serialize_newtype_struct( - self, - _name: &'static str, - _value: &T, + fn serialize_field( + &mut self, + key: &'static str, + value: &T, ) -> Result<(), Self::Error> where - T: ser::Serialize, + T: serde::ser::Serialize, { - Err(Error::DateInvalid) + self.inner.serialize_field(key, value).map_err(Error::wrap) } - fn serialize_newtype_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _value: &T, - ) -> Result<(), Self::Error> - where - T: ser::Serialize, - { - Err(Error::DateInvalid) + fn end(self) -> Result { + write_document(self.dst, self.settings, self.inner.end()) } +} - fn serialize_seq(self, _len: Option) -> Result { - Err(Error::DateInvalid) - } +fn write_document<'d>( + dst: &'d mut String, + mut settings: DocumentFormatter, + value: Result, +) -> Result<(), Error> { + use std::fmt::Write; - fn serialize_tuple(self, _len: usize) -> Result { - Err(Error::DateInvalid) - } + let value = value.map_err(Error::wrap)?; + let mut table = match toml_edit::Item::Value(value).into_table() { + Ok(i) => i, + Err(_) => { + return Err(Error::unsupported_type(None)); + } + }; - fn serialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - Err(Error::DateInvalid) - } + use toml_edit::visit_mut::VisitMut as _; + settings.visit_table_mut(&mut table); - fn serialize_tuple_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - Err(Error::DateInvalid) - } + let doc: toml_edit::Document = table.into(); + write!(dst, "{}", doc).unwrap(); - fn serialize_map(self, _len: Option) -> Result { - Err(Error::DateInvalid) - } + Ok(()) +} - fn serialize_struct( - self, - _name: &'static str, - _len: usize, - ) -> Result { - Err(Error::DateInvalid) - } +/// Serialization for TOML values. +/// +/// This structure implements serialization support for TOML to serialize an +/// arbitrary type to TOML. Note that the TOML format does not support all +/// datatypes in Rust, such as enums, tuples, and tuple structs. These types +/// will generate an error when serialized. +/// +/// Currently a serializer always writes its output to an in-memory `String`, +/// which is passed in when creating the serializer itself. +#[non_exhaustive] +pub struct ValueSerializer<'d> { + dst: &'d mut String, +} - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> Result { - Err(Error::DateInvalid) +impl<'d> ValueSerializer<'d> { + /// Creates a new serializer which will emit TOML into the buffer provided. + /// + /// The serializer can then be used to serialize a type after which the data + /// will be present in `dst`. + pub fn new(dst: &'d mut String) -> Self { + Self { dst } } } -struct StringExtractor; - -impl ser::Serializer for StringExtractor { - type Ok = String; +impl<'d> serde::ser::Serializer for ValueSerializer<'d> { + type Ok = (); type Error = Error; - type SerializeSeq = ser::Impossible; - type SerializeTuple = ser::Impossible; - type SerializeTupleStruct = ser::Impossible; - type SerializeTupleVariant = ser::Impossible; - type SerializeMap = ser::Impossible; - type SerializeStruct = ser::Impossible; - type SerializeStructVariant = ser::Impossible; + type SerializeSeq = SerializeValueArray<'d>; + type SerializeTuple = SerializeValueArray<'d>; + type SerializeTupleStruct = SerializeValueArray<'d>; + type SerializeTupleVariant = SerializeValueArray<'d>; + type SerializeMap = SerializeValueTable<'d>; + type SerializeStruct = SerializeValueTable<'d>; + type SerializeStructVariant = serde::ser::Impossible; - fn serialize_bool(self, _v: bool) -> Result { - Err(Error::KeyNotString) + fn serialize_bool(self, v: bool) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_bool(v), + ) } - fn serialize_i8(self, _v: i8) -> Result { - Err(Error::KeyNotString) + fn serialize_i8(self, v: i8) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_i8(v), + ) } - fn serialize_i16(self, _v: i16) -> Result { - Err(Error::KeyNotString) + fn serialize_i16(self, v: i16) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_i16(v), + ) } - fn serialize_i32(self, _v: i32) -> Result { - Err(Error::KeyNotString) + fn serialize_i32(self, v: i32) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_i32(v), + ) } - fn serialize_i64(self, _v: i64) -> Result { - Err(Error::KeyNotString) + fn serialize_i64(self, v: i64) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_i64(v), + ) } - fn serialize_u8(self, _v: u8) -> Result { - Err(Error::KeyNotString) + fn serialize_u8(self, v: u8) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_u8(v), + ) } - fn serialize_u16(self, _v: u16) -> Result { - Err(Error::KeyNotString) + fn serialize_u16(self, v: u16) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_u16(v), + ) } - fn serialize_u32(self, _v: u32) -> Result { - Err(Error::KeyNotString) + fn serialize_u32(self, v: u32) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_u32(v), + ) } - fn serialize_u64(self, _v: u64) -> Result { - Err(Error::KeyNotString) + fn serialize_u64(self, v: u64) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_u64(v), + ) } - fn serialize_f32(self, _v: f32) -> Result { - Err(Error::KeyNotString) + fn serialize_f32(self, v: f32) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_f32(v), + ) } - fn serialize_f64(self, _v: f64) -> Result { - Err(Error::KeyNotString) + fn serialize_f64(self, v: f64) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_f64(v), + ) } - fn serialize_char(self, _v: char) -> Result { - Err(Error::KeyNotString) + fn serialize_char(self, v: char) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_char(v), + ) } - fn serialize_str(self, value: &str) -> Result { - Ok(value.to_string()) + fn serialize_str(self, v: &str) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_str(v), + ) } - fn serialize_bytes(self, _value: &[u8]) -> Result { - Err(Error::KeyNotString) + fn serialize_bytes(self, v: &[u8]) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_bytes(v), + ) } - fn serialize_none(self) -> Result { - Err(Error::KeyNotString) + fn serialize_none(self) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_none(), + ) } - fn serialize_some(self, _value: &T) -> Result + fn serialize_some(self, v: &T) -> Result where - T: ser::Serialize, + T: serde::ser::Serialize, { - Err(Error::KeyNotString) + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_some(v), + ) } - fn serialize_unit(self) -> Result { - Err(Error::KeyNotString) + fn serialize_unit(self) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_unit(), + ) } - fn serialize_unit_struct(self, _name: &'static str) -> Result { - Err(Error::KeyNotString) + fn serialize_unit_struct(self, name: &'static str) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_unit_struct(name), + ) } fn serialize_unit_variant( self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - ) -> Result { - Err(Error::KeyNotString) + name: &'static str, + variant_index: u32, + variant: &'static str, + ) -> Result { + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_unit_variant( + name, + variant_index, + variant, + ), + ) } fn serialize_newtype_struct( self, - _name: &'static str, - value: &T, - ) -> Result + name: &'static str, + v: &T, + ) -> Result where - T: ser::Serialize, + T: serde::ser::Serialize, { - value.serialize(self) + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_newtype_struct(name, v), + ) } fn serialize_newtype_variant( self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _value: &T, - ) -> Result + name: &'static str, + variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result where - T: ser::Serialize, + T: serde::ser::Serialize, { - Err(Error::KeyNotString) + write_value( + self.dst, + toml_edit::ser::ValueSerializer::new().serialize_newtype_variant( + name, + variant_index, + variant, + value, + ), + ) } - fn serialize_seq(self, _len: Option) -> Result { - Err(Error::KeyNotString) + fn serialize_seq(self, len: Option) -> Result { + let ser = toml_edit::ser::ValueSerializer::new() + .serialize_seq(len) + .map_err(Error::wrap)?; + let ser = SerializeValueArray::new(self, ser); + Ok(ser) } - fn serialize_tuple(self, _len: usize) -> Result { - Err(Error::KeyNotString) + fn serialize_tuple(self, len: usize) -> Result { + self.serialize_seq(Some(len)) } fn serialize_tuple_struct( self, _name: &'static str, - _len: usize, + len: usize, ) -> Result { - Err(Error::KeyNotString) + self.serialize_seq(Some(len)) } fn serialize_tuple_variant( @@ -1498,356 +834,189 @@ impl ser::Serializer for StringExtractor { _name: &'static str, _variant_index: u32, _variant: &'static str, - _len: usize, + len: usize, ) -> Result { - Err(Error::KeyNotString) + self.serialize_seq(Some(len)) } - fn serialize_map(self, _len: Option) -> Result { - Err(Error::KeyNotString) + fn serialize_map(self, len: Option) -> Result { + let ser = toml_edit::ser::ValueSerializer::new() + .serialize_map(len) + .map_err(Error::wrap)?; + let ser = SerializeValueTable::new(self, ser); + Ok(ser) } fn serialize_struct( self, _name: &'static str, - _len: usize, + len: usize, ) -> Result { - Err(Error::KeyNotString) + self.serialize_map(Some(len)) } fn serialize_struct_variant( self, - _name: &'static str, + name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { - Err(Error::KeyNotString) + Err(Error::unsupported_type(Some(name))) } } -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Error::UnsupportedType => "unsupported Rust type".fmt(f), - Error::KeyNotString => "map key was not a string".fmt(f), - Error::ValueAfterTable => "values must be emitted before tables".fmt(f), - Error::DateInvalid => "a serialized date was invalid".fmt(f), - Error::NumberInvalid => "a serialized number was invalid".fmt(f), - Error::UnsupportedNone => "unsupported None value".fmt(f), - Error::Custom(ref s) => s.fmt(f), - Error::KeyNewline => unreachable!(), - Error::ArrayMixedType => unreachable!(), - } - } -} - -impl error::Error for Error {} - -impl ser::Error for Error { - fn custom(msg: T) -> Error { - Error::Custom(msg.to_string()) - } -} +type InnerSerializeValueSeq = ::SerializeSeq; -enum Category { - Primitive, - Array, - Table, +#[doc(hidden)] +pub struct SerializeValueArray<'d> { + inner: InnerSerializeValueSeq, + dst: &'d mut String, } -/// Convenience function to serialize items in a map in an order valid with -/// TOML. -/// -/// TOML carries the restriction that keys in a table must be serialized last if -/// their value is a table itself. This isn't always easy to guarantee, so this -/// helper can be used like so: -/// -/// ```rust -/// # use serde::Serialize; -/// # use std::collections::HashMap; -/// #[derive(Serialize)] -/// struct Manifest { -/// package: Package, -/// #[serde(serialize_with = "toml::ser::tables_last")] -/// dependencies: HashMap, -/// } -/// # type Package = String; -/// # type Dependency = String; -/// # fn main() {} -/// ``` -pub fn tables_last<'a, I, K, V, S>(data: &'a I, serializer: S) -> Result -where - &'a I: IntoIterator, - K: ser::Serialize, - V: ser::Serialize, - S: ser::Serializer, -{ - use serde::ser::SerializeMap; - - let mut map = serializer.serialize_map(None)?; - for (k, v) in data { - if let Category::Primitive = v.serialize(Categorize::new())? { - map.serialize_entry(&k, &v)?; - } - } - for (k, v) in data { - if let Category::Array = v.serialize(Categorize::new())? { - map.serialize_entry(&k, &v)?; +impl<'d> SerializeValueArray<'d> { + pub(crate) fn new(ser: ValueSerializer<'d>, inner: InnerSerializeValueSeq) -> Self { + Self { + inner, + dst: ser.dst, } } - for (k, v) in data { - if let Category::Table = v.serialize(Categorize::new())? { - map.serialize_entry(&k, &v)?; - } - } - map.end() -} - -struct Categorize(marker::PhantomData); - -impl Categorize { - fn new() -> Self { - Categorize(marker::PhantomData) - } } -impl ser::Serializer for Categorize { - type Ok = Category; - type Error = E; - type SerializeSeq = Self; - type SerializeTuple = Self; - type SerializeTupleStruct = Self; - type SerializeTupleVariant = Self; - type SerializeMap = Self; - type SerializeStruct = Self; - type SerializeStructVariant = ser::Impossible; - - fn serialize_bool(self, _: bool) -> Result { - Ok(Category::Primitive) - } - - fn serialize_i8(self, _: i8) -> Result { - Ok(Category::Primitive) - } - - fn serialize_i16(self, _: i16) -> Result { - Ok(Category::Primitive) - } - - fn serialize_i32(self, _: i32) -> Result { - Ok(Category::Primitive) - } - - fn serialize_i64(self, _: i64) -> Result { - Ok(Category::Primitive) - } - - fn serialize_u8(self, _: u8) -> Result { - Ok(Category::Primitive) - } - - fn serialize_u16(self, _: u16) -> Result { - Ok(Category::Primitive) - } - - fn serialize_u32(self, _: u32) -> Result { - Ok(Category::Primitive) - } - - fn serialize_u64(self, _: u64) -> Result { - Ok(Category::Primitive) - } - - fn serialize_f32(self, _: f32) -> Result { - Ok(Category::Primitive) - } - - fn serialize_f64(self, _: f64) -> Result { - Ok(Category::Primitive) - } - - fn serialize_char(self, _: char) -> Result { - Ok(Category::Primitive) - } - - fn serialize_str(self, _: &str) -> Result { - Ok(Category::Primitive) - } - - fn serialize_bytes(self, _: &[u8]) -> Result { - Ok(Category::Array) - } - - fn serialize_none(self) -> Result { - Err(ser::Error::custom("unsupported")) - } - - fn serialize_some(self, v: &T) -> Result { - v.serialize(self) - } - - fn serialize_unit(self) -> Result { - Err(ser::Error::custom("unsupported")) - } - - fn serialize_unit_struct(self, _: &'static str) -> Result { - Err(ser::Error::custom("unsupported")) - } - - fn serialize_unit_variant( - self, - _: &'static str, - _: u32, - _: &'static str, - ) -> Result { - Err(ser::Error::custom("unsupported")) - } - - fn serialize_newtype_struct( - self, - _: &'static str, - v: &T, - ) -> Result { - v.serialize(self) - } - - fn serialize_newtype_variant( - self, - _: &'static str, - _: u32, - _: &'static str, - _: &T, - ) -> Result { - Err(ser::Error::custom("unsupported")) - } - - fn serialize_seq(self, _: Option) -> Result { - Ok(self) - } - - fn serialize_tuple(self, _: usize) -> Result { - Ok(self) - } - - fn serialize_tuple_struct( - self, - _: &'static str, - _: usize, - ) -> Result { - Ok(self) - } - - fn serialize_tuple_variant( - self, - _: &'static str, - _: u32, - _: &'static str, - _: usize, - ) -> Result { - Ok(self) - } - - fn serialize_map(self, _: Option) -> Result { - Ok(self) - } +impl<'d> serde::ser::SerializeSeq for SerializeValueArray<'d> { + type Ok = (); + type Error = Error; - fn serialize_struct(self, _: &'static str, _: usize) -> Result { - Ok(self) + fn serialize_element(&mut self, value: &T) -> Result<(), Error> + where + T: serde::ser::Serialize, + { + self.inner.serialize_element(value).map_err(Error::wrap) } - fn serialize_struct_variant( - self, - _: &'static str, - _: u32, - _: &'static str, - _: usize, - ) -> Result { - Err(ser::Error::custom("unsupported")) + fn end(self) -> Result { + write_value(self.dst, self.inner.end()) } } -impl ser::SerializeSeq for Categorize { - type Ok = Category; - type Error = E; +impl<'d> serde::ser::SerializeTuple for SerializeValueArray<'d> { + type Ok = (); + type Error = Error; - fn serialize_element(&mut self, _: &T) -> Result<(), Self::Error> { - Ok(()) + fn serialize_element(&mut self, value: &T) -> Result<(), Error> + where + T: serde::ser::Serialize, + { + self.inner.serialize_element(value).map_err(Error::wrap) } fn end(self) -> Result { - Ok(Category::Array) + write_value(self.dst, self.inner.end()) } } -impl ser::SerializeTuple for Categorize { - type Ok = Category; - type Error = E; +impl<'d> serde::ser::SerializeTupleVariant for SerializeValueArray<'d> { + type Ok = (); + type Error = Error; - fn serialize_element(&mut self, _: &T) -> Result<(), Self::Error> { - Ok(()) + fn serialize_field(&mut self, value: &T) -> Result<(), Error> + where + T: serde::ser::Serialize, + { + self.inner.serialize_field(value).map_err(Error::wrap) } fn end(self) -> Result { - Ok(Category::Array) + write_value(self.dst, self.inner.end()) } } -impl ser::SerializeTupleVariant for Categorize { - type Ok = Category; - type Error = E; +impl<'d> serde::ser::SerializeTupleStruct for SerializeValueArray<'d> { + type Ok = (); + type Error = Error; - fn serialize_field(&mut self, _: &T) -> Result<(), Self::Error> { - Ok(()) + fn serialize_field(&mut self, value: &T) -> Result<(), Error> + where + T: serde::ser::Serialize, + { + self.inner.serialize_field(value).map_err(Error::wrap) } fn end(self) -> Result { - Ok(Category::Array) + write_value(self.dst, self.inner.end()) } } -impl ser::SerializeTupleStruct for Categorize { - type Ok = Category; - type Error = E; +type InnerSerializeValueTable = + ::SerializeMap; - fn serialize_field(&mut self, _: &T) -> Result<(), Self::Error> { - Ok(()) - } +#[doc(hidden)] +pub struct SerializeValueTable<'d> { + inner: InnerSerializeValueTable, + dst: &'d mut String, +} - fn end(self) -> Result { - Ok(Category::Array) +impl<'d> SerializeValueTable<'d> { + pub(crate) fn new(ser: ValueSerializer<'d>, inner: InnerSerializeValueTable) -> Self { + Self { + inner, + dst: ser.dst, + } } } -impl ser::SerializeMap for Categorize { - type Ok = Category; - type Error = E; +impl<'d> serde::ser::SerializeMap for SerializeValueTable<'d> { + type Ok = (); + type Error = Error; - fn serialize_key(&mut self, _: &T) -> Result<(), Self::Error> { - Ok(()) + fn serialize_key(&mut self, input: &T) -> Result<(), Self::Error> + where + T: serde::ser::Serialize, + { + self.inner.serialize_key(input).map_err(Error::wrap) } - fn serialize_value(&mut self, _: &T) -> Result<(), Self::Error> { - Ok(()) + fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> + where + T: serde::ser::Serialize, + { + self.inner.serialize_value(value).map_err(Error::wrap) } fn end(self) -> Result { - Ok(Category::Table) + write_value(self.dst, self.inner.end()) } } -impl ser::SerializeStruct for Categorize { - type Ok = Category; - type Error = E; +impl<'d> serde::ser::SerializeStruct for SerializeValueTable<'d> { + type Ok = (); + type Error = Error; - fn serialize_field(&mut self, _: &'static str, _: &T) -> Result<(), Self::Error> + fn serialize_field( + &mut self, + key: &'static str, + value: &T, + ) -> Result<(), Self::Error> where - T: ser::Serialize, + T: serde::ser::Serialize, { - Ok(()) + self.inner.serialize_field(key, value).map_err(Error::wrap) } fn end(self) -> Result { - Ok(Category::Table) + write_value(self.dst, self.inner.end()) } } + +fn write_value<'d>( + dst: &'d mut String, + value: Result, +) -> Result<(), Error> { + use std::fmt::Write; + + let value = value.map_err(Error::wrap)?; + + write!(dst, "{}", value).unwrap(); + + Ok(()) +} diff --git a/crates/toml/src/value.rs b/crates/toml/src/value.rs index 775b76d7..71e3b5d4 100644 --- a/crates/toml/src/value.rs +++ b/crates/toml/src/value.rs @@ -482,9 +482,12 @@ where impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - crate::ser::to_string(self) - .expect("Unable to represent value as string") - .fmt(f) + use serde::Serialize as _; + + let mut output = String::new(); + let serializer = crate::ser::ValueSerializer::new(&mut output); + self.serialize(serializer).unwrap(); + output.fmt(f) } } @@ -895,11 +898,11 @@ impl ser::Serializer for ValueSerializer { } fn serialize_unit(self) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(Some("unit"))) } - fn serialize_unit_struct(self, _name: &'static str) -> Result { - Err(crate::ser::Error::UnsupportedType) + fn serialize_unit_struct(self, name: &'static str) -> Result { + Err(crate::ser::Error::unsupported_type(Some(name))) } fn serialize_unit_variant( @@ -924,7 +927,7 @@ impl ser::Serializer for ValueSerializer { fn serialize_newtype_variant( self, - _name: &'static str, + name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T, @@ -932,11 +935,11 @@ impl ser::Serializer for ValueSerializer { where T: ser::Serialize, { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(Some(name))) } fn serialize_none(self) -> Result { - Err(crate::ser::Error::UnsupportedNone) + Err(crate::ser::Error::unsupported_none()) } fn serialize_some(self, value: &T) -> Result @@ -993,12 +996,12 @@ impl ser::Serializer for ValueSerializer { fn serialize_struct_variant( self, - _name: &'static str, + name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(Some(name))) } } @@ -1017,76 +1020,76 @@ impl ser::Serializer for TableSerializer { type SerializeStructVariant = ser::Impossible; fn serialize_bool(self, _value: bool) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_i8(self, _value: i8) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_i16(self, _value: i16) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_i32(self, _value: i32) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_i64(self, _value: i64) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_u8(self, _value: u8) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_u16(self, _value: u16) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_u32(self, _value: u32) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_u64(self, _value: u64) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_f32(self, _value: f32) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_f64(self, _value: f64) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_char(self, _value: char) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_str(self, _value: &str) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_bytes(self, _value: &[u8]) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_unit(self) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_unit_struct(self, _name: &'static str) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_unit_variant( self, - _name: &'static str, + name: &'static str, _variant_index: u32, _variant: &'static str, ) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(Some(name))) } fn serialize_newtype_struct( @@ -1102,7 +1105,7 @@ impl ser::Serializer for TableSerializer { fn serialize_newtype_variant( self, - _name: &'static str, + name: &'static str, _variant_index: u32, _variant: &'static str, _value: &T, @@ -1110,11 +1113,11 @@ impl ser::Serializer for TableSerializer { where T: ser::Serialize, { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(Some(name))) } fn serialize_none(self) -> Result { - Err(crate::ser::Error::UnsupportedNone) + Err(crate::ser::Error::unsupported_none()) } fn serialize_some(self, value: &T) -> Result @@ -1125,29 +1128,29 @@ impl ser::Serializer for TableSerializer { } fn serialize_seq(self, _len: Option) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_tuple(self, _len: usize) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(None)) } fn serialize_tuple_struct( self, - _name: &'static str, + name: &'static str, _len: usize, ) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(Some(name))) } fn serialize_tuple_variant( self, - _name: &'static str, + name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(Some(name))) } fn serialize_map(self, _len: Option) -> Result { @@ -1167,12 +1170,12 @@ impl ser::Serializer for TableSerializer { fn serialize_struct_variant( self, - _name: &'static str, + name: &'static str, _variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { - Err(crate::ser::Error::UnsupportedType) + Err(crate::ser::Error::unsupported_type(Some(name))) } } @@ -1260,7 +1263,7 @@ impl ser::SerializeMap for SerializeMap { { match Value::try_from(key)? { Value::String(s) => self.next_key = Some(s), - _ => return Err(crate::ser::Error::KeyNotString), + _ => return Err(crate::ser::Error::key_not_string()), }; Ok(()) } @@ -1275,7 +1278,9 @@ impl ser::SerializeMap for SerializeMap { Ok(value) => { self.map.insert(key, value); } - Err(crate::ser::Error::UnsupportedNone) => {} + Err(crate::ser::Error { + inner: toml_edit::ser::Error::UnsupportedNone, + }) => {} Err(e) => return Err(e), } Ok(()) diff --git a/crates/toml/tests/testsuite/display_tricky.rs b/crates/toml/tests/testsuite/display_tricky.rs index 3fd04fbf..379ae913 100644 --- a/crates/toml/tests/testsuite/display_tricky.rs +++ b/crates/toml/tests/testsuite/display_tricky.rs @@ -50,6 +50,6 @@ fn both_ends() { ) .unwrap(); - let recipe_toml = toml::Value::try_from(recipe_fails).unwrap(); + let recipe_toml = toml::Table::try_from(recipe_fails).unwrap(); recipe_toml.to_string(); } diff --git a/crates/toml/tests/testsuite/pretty.rs b/crates/toml/tests/testsuite/pretty.rs index dab72c8a..0b06fa2b 100644 --- a/crates/toml/tests/testsuite/pretty.rs +++ b/crates/toml/tests/testsuite/pretty.rs @@ -6,7 +6,10 @@ const NO_PRETTY: &str = "\ array = [\"item 1\", \"item 2\"] empty = [] oneline = \"this has no newlines.\" -text = \"\\nthis is the first line\\nthis is the second line\\n\" +text = ''' + +this is the first line\\nthis is the second line +''' "; #[test] @@ -14,43 +17,23 @@ fn no_pretty() { let toml = NO_PRETTY; let value: toml::Value = toml::from_str(toml).unwrap(); let mut result = String::with_capacity(128); - value - .serialize(&mut toml::Serializer::new(&mut result)) - .unwrap(); - println!("EXPECTED:\n{}", toml); - println!("\nRESULT:\n{}", result); - assert_eq!(toml, &result); -} - -#[test] -fn disable_pretty() { - let toml = NO_PRETTY; - let value: toml::Value = toml::from_str(toml).unwrap(); - let mut result = String::with_capacity(128); - { - let mut serializer = toml::Serializer::pretty(&mut result); - serializer.pretty_string(false); - serializer.pretty_array(false); - value.serialize(&mut serializer).unwrap(); - } - println!("EXPECTED:\n{}", toml); - println!("\nRESULT:\n{}", result); - assert_eq!(toml, &result); + value.serialize(toml::Serializer::new(&mut result)).unwrap(); + assert_eq(toml, &result); } const PRETTY_STD: &str = "\ [example] array = [ - 'item 1', - 'item 2', + \"item 1\", + \"item 2\", ] empty = [] -one = ['one'] -oneline = 'this has no newlines.' -text = ''' +one = [\"one\"] +oneline = \"this has no newlines.\" +text = \"\"\" this is the first line this is the second line -''' +\"\"\" "; #[test] @@ -59,139 +42,30 @@ fn pretty_std() { let value: toml::Value = toml::from_str(toml).unwrap(); let mut result = String::with_capacity(128); value - .serialize(&mut toml::Serializer::pretty(&mut result)) + .serialize(toml::Serializer::pretty(&mut result)) .unwrap(); - println!("EXPECTED:\n{}", toml); - println!("\nRESULT:\n{}", result); assert_eq(toml, &result); } -const PRETTY_INDENT_2: &str = "\ -[example] -array = [ - 'item 1', - 'item 2', -] -empty = [] -one = ['one'] -oneline = 'this has no newlines.' -text = ''' -this is the first line -this is the second line -''' -three = [ - 'one', - 'two', - 'three', -] -"; - -#[test] -fn pretty_indent_2() { - let toml = PRETTY_INDENT_2; - let value: toml::Value = toml::from_str(toml).unwrap(); - let mut result = String::with_capacity(128); - { - let mut serializer = toml::Serializer::pretty(&mut result); - serializer.pretty_array_indent(2); - value.serialize(&mut serializer).unwrap(); - } - println!(">> Result:\n{}", result); - assert_eq!(toml, &result); -} - -const PRETTY_INDENT_2_OTHER: &str = "\ -[example] -array = [ - \"item 1\", - \"item 2\", -] -empty = [] -oneline = \"this has no newlines.\" -text = \"\\nthis is the first line\\nthis is the second line\\n\" -"; - -#[test] -/// Test pretty indent when gotten the other way -fn pretty_indent_2_other() { - let toml = PRETTY_INDENT_2_OTHER; - let value: toml::Value = toml::from_str(toml).unwrap(); - let mut result = String::with_capacity(128); - { - let mut serializer = toml::Serializer::new(&mut result); - serializer.pretty_array_indent(2); - value.serialize(&mut serializer).unwrap(); - } - assert_eq!(toml, &result); -} - -const PRETTY_ARRAY_NO_COMMA: &str = "\ -[example] -array = [ - \"item 1\", - \"item 2\" -] -empty = [] -oneline = \"this has no newlines.\" -text = \"\\nthis is the first line\\nthis is the second line\\n\" -"; -#[test] -/// Test pretty indent when gotten the other way -fn pretty_indent_array_no_comma() { - let toml = PRETTY_ARRAY_NO_COMMA; - let value: toml::Value = toml::from_str(toml).unwrap(); - let mut result = String::with_capacity(128); - { - let mut serializer = toml::Serializer::new(&mut result); - serializer.pretty_array_trailing_comma(false); - value.serialize(&mut serializer).unwrap(); - } - assert_eq!(toml, &result); -} - -const PRETTY_NO_STRING: &str = "\ -[example] -array = [ - \"item 1\", - \"item 2\", -] -empty = [] -oneline = \"this has no newlines.\" -text = \"\\nthis is the first line\\nthis is the second line\\n\" -"; -#[test] -/// Test pretty indent when gotten the other way -fn pretty_no_string() { - let toml = PRETTY_NO_STRING; - let value: toml::Value = toml::from_str(toml).unwrap(); - let mut result = String::with_capacity(128); - { - let mut serializer = toml::Serializer::pretty(&mut result); - serializer.pretty_string(false); - value.serialize(&mut serializer).unwrap(); - } - assert_eq!(toml, &result); -} - const PRETTY_TRICKY: &str = r##"[example] f = "\f" -glass = ''' +glass = """ Nothing too unusual, except that I can eat glass in: - Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα. - 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 = "this is a single line but has '' cuz it's tricky" single_tricky = "single line with ''' in it" -tabs = ''' +tabs = """ this is pretty standard - except for some tabs right here -''' +\texcept for some \btabs right here +""" text = """ this is the first line. This has a ''' in it and \"\"\" cuz it's tricky yo @@ -206,24 +80,22 @@ fn pretty_tricky() { let value: toml::Value = toml::from_str(toml).unwrap(); let mut result = String::with_capacity(128); value - .serialize(&mut toml::Serializer::pretty(&mut result)) + .serialize(toml::Serializer::pretty(&mut result)) .unwrap(); - println!("EXPECTED:\n{}", toml); - println!("\nRESULT:\n{}", result); - assert_eq!(toml, &result); + assert_eq(toml, &result); } const PRETTY_TABLE_ARRAY: &str = r##"[[array]] -key = 'foo' +key = "foo" [[array]] -key = 'bar' +key = "bar" [abc] -doc = 'this is a table' +doc = "this is a table" [example] -single = 'this is a single line string' +single = "this is a single line string" "##; #[test] @@ -232,11 +104,9 @@ fn pretty_table_array() { let value: toml::Value = toml::from_str(toml).unwrap(); let mut result = String::with_capacity(128); value - .serialize(&mut toml::Serializer::pretty(&mut result)) + .serialize(toml::Serializer::pretty(&mut result)) .unwrap(); - println!("EXPECTED:\n{}", toml); - println!("\nRESULT:\n{}", result); - assert_eq!(toml, &result); + assert_eq(toml, &result); } const TABLE_ARRAY: &str = r##"[[array]] @@ -257,56 +127,6 @@ fn table_array() { let toml = TABLE_ARRAY; let value: toml::Value = toml::from_str(toml).unwrap(); let mut result = String::with_capacity(128); - value - .serialize(&mut toml::Serializer::new(&mut result)) - .unwrap(); - println!("EXPECTED:\n{}", toml); - println!("\nRESULT:\n{}", result); - assert_eq(toml, &result); -} - -const PRETTY_TRICKY_NON_LITERAL: &str = r##"[example] -f = "\f" -glass = """ -Nothing too unusual, except that I can eat glass in: -- Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα. -- Polish: Mogę jeść szkło, i mi nie szkodzi. -- Hindi: मैं काँच खा सकता हूँ, मुझे उस से कोई पीडा नहीं होती. -- Japanese: 私はガラスを食べられます。それは私を傷つけません。 -""" -plain = """ -This has a couple of lines -Because it likes to. -""" -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 -\texcept for some \ttabs 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 fourth line -""" -"##; - -#[test] -fn pretty_tricky_non_literal() { - let toml = PRETTY_TRICKY_NON_LITERAL; - let value: toml::Value = toml::from_str(toml).unwrap(); - let mut result = String::with_capacity(128); - { - let mut serializer = toml::Serializer::pretty(&mut result); - serializer.pretty_string_literal(false); - value.serialize(&mut serializer).unwrap(); - } - println!("EXPECTED:\n{}", toml); - println!("\nRESULT:\n{}", result); + value.serialize(toml::Serializer::new(&mut result)).unwrap(); assert_eq(toml, &result); } diff --git a/crates/toml/tests/testsuite/serde.rs b/crates/toml/tests/testsuite/serde.rs index bb5ec8cb..a9dd3e5a 100644 --- a/crates/toml/tests/testsuite/serde.rs +++ b/crates/toml/tests/testsuite/serde.rs @@ -28,14 +28,12 @@ macro_rules! equivalent { assert_eq!(literal, t!(toml.clone().try_into())); // Through a string equivalent - println!("to_string(literal)"); - snapbox::assert_eq(toml.to_string(), t!(toml::to_string(&literal))); - println!("to_string(toml)"); - snapbox::assert_eq(toml.to_string(), t!(toml::to_string(&toml))); + println!("to_string"); + snapbox::assert_eq(t!(toml::to_string(&toml)), t!(toml::to_string(&literal))); println!("literal, from_str(toml)"); - assert_eq!(literal, t!(toml::from_str(&toml.to_string()))); + assert_eq!(literal, t!(toml::from_str(&t!(toml::to_string(&toml))))); println!("toml, from_str(toml)"); - assert_eq!(toml, t!(toml::from_str(&toml.to_string()))); + assert_eq!(toml, t!(toml::from_str(&t!(toml::to_string(&toml))))); }}; } diff --git a/crates/toml/tests/testsuite/tables_last.rs b/crates/toml/tests/testsuite/tables_last.rs index 32a94a9f..3339e0a0 100644 --- a/crates/toml/tests/testsuite/tables_last.rs +++ b/crates/toml/tests/testsuite/tables_last.rs @@ -2,21 +2,21 @@ use std::collections::HashMap; use serde::Serialize; -#[derive(Serialize)] -struct A { - #[serde(serialize_with = "toml::ser::tables_last")] - vals: HashMap<&'static str, Value>, -} - -#[derive(Serialize)] -#[serde(untagged)] -enum Value { - Map(HashMap<&'static str, &'static str>), - Int(i32), -} - #[test] fn always_works() { + // Ensure this works without the removed "toml::ser::tables_last" + #[derive(Serialize)] + struct A { + vals: HashMap<&'static str, Value>, + } + + #[derive(Serialize)] + #[serde(untagged)] + enum Value { + Map(HashMap<&'static str, &'static str>), + Int(i32), + } + let mut a = A { vals: HashMap::new(), };