//! Serializing Rust structures into TOML. //! //! 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_derive::Serialize; //! # use std::collections::HashMap; //! #[derive(Serialize)] //! struct Manifest { //! package: Package, //! #[serde(serialize_with = "toml::ser::tables_last")] //! dependencies: HashMap<String, Dependency>, //! } //! # 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;
usecrate::datetime; use serde::ser;
/// 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. pubfn to_vec<T: ?Sized>(value: &T) -> Result<Vec<u8>, Error> where
T: ser::Serialize,
{
to_string(value).map(|e| e.into_bytes())
}
/// Serialize the given data structure as a String of TOML. /// /// 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. /// /// # Examples /// /// ``` /// use serde_derive::Serialize; /// /// #[derive(Serialize)] /// struct Config { /// database: Database, /// } /// /// #[derive(Serialize)] /// struct Database { /// ip: String, /// port: Vec<u16>, /// connection_max: u32, /// enabled: bool, /// } /// /// let config = Config { /// database: Database { /// ip: "192.168.1.1".to_string(), /// port: vec![8001, 8002, 8003], /// connection_max: 5000, /// enabled: false, /// }, /// }; /// /// let toml = toml::to_string(&config).unwrap(); /// println!("{}", toml) /// ``` pubfn to_string<T: ?Sized>(value: &T) -> Result<String, Error> where
T: ser::Serialize,
{ letmut dst = String::with_capacity(128);
value.serialize(&mut Serializer::new(&>mut dst))?;
Ok(dst)
}
/// Serialize the given data structure as a "pretty" String of TOML. /// /// This is identical to `to_string` except the output string has a more /// "pretty" output. See `Serializer::pretty` for more details. pubfn to_string_pretty<T: ?Sized>(value: &T) -> Result<String, Error> where
T: ser::Serialize,
{ letmut dst = String::with_capacity(128);
value.serialize(&mut Serializer::pretty(&mut dst))?;
Ok(dst)
}
/// Errors that can occur when serializing a type. #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pubenum 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,
/// A serialized number was invalid.
NumberInvalid,
/// None was attempted to be serialized, but it's not supported.
UnsupportedNone,
/// A custom error which could be generated when serializing a particular /// type.
Custom(String),
}
#[derive(Debug, Default, Clone)] /// Internal place for holding array settings struct ArraySettings {
indent: usize,
trailing_comma: bool,
}
/// Serialization implementation for TOML. /// /// 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. pubstruct Serializer<'a> {
dst: &'a mut String,
state: State<'a>,
settings: Rc<Settings>,
}
impl<'a> Serializer<'a> { /// 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`. pubfn new(dst: &'a mut String) -> Serializer<'a> {
Serializer {
dst,
state: State::End,
settings: Rc::new(Settings::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` pubfn pretty(dst: &'a mut String) -> Serializer<'a> {
Serializer {
dst,
state: State::End,
settings: Rc::new(Settings {
array: Some(ArraySettings::pretty()),
string: Some(StringSettings::pretty()),
}),
}
}
/// 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 /// ''' /// ``` pubfn pretty_string(&mutself, value: bool) -> &mutSelf {
Rc::get_mut(&mutself.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 /// """ /// ``` pubfn pretty_string_literal(&mutself, value: bool) -> &='color:red'>mutSelf { let use_default = iflet Some(refmut s) = Rc::get_mut(&mutself.settings).unwrap().string {
s.literal = value; false
} else { true
};
/// 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", /// ] /// ``` pubfn pretty_array(&mutself, value: bool) -> &mutSelf {
Rc::get_mut(&mutself.settings).unwrap().array = if value {
Some(ArraySettings::pretty())
} else {
None
}; self
}
/// Set the indent for pretty arrays /// /// See `Serializer::pretty_array` for more details. pubfn pretty_array_indent(&mutself, value: usize) -> &'color:red'>mutSelf { let use_default = iflet Some(refmut a) = Rc::get_mut(&mutself.settings).unwrap().array {
a.indent = value; false
} else { true
};
/// Specify whether to use a trailing comma when serializing pretty arrays /// /// See `Serializer::pretty_array` for more details. pubfn pretty_array_trailing_comma(&mutself, value: bool) -> & style='color:red'>mutSelf { let use_default = iflet Some(refmut a) = Rc::get_mut(&mutself.settings).unwrap().array {
a.trailing_comma = value; false
} else { true
};
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. letmut out = String::with_capacity(value.len() * 2); letmut ty = Type::OnelineSingle; // found consecutive single quotes letmut max_found_singles = 0; letmut found_singles = 0; letmut 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(())
}
// Unlike [..]s, we can't omit [[..]] ancestors, so be sure to emit table // headers for them. letmut p = state; iflet State::Array { first, parent, .. } = *state { if first.get() {
p = parent;
}
} whilelet State::Table { first, parent, .. } = *p {
p = parent; if !first.get() { break;
} iflet 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');
} elseiflet 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(())
}
impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> { 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>;
impl<'a, 'b> ser::Serializer for DateStrEmitter<'a, 'b> { 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>;
impl ser::Serializer for StringExtractor { type Ok = String; type Error = Error; type SerializeSeq = ser::Impossible<String, Error>; type SerializeTuple = ser::Impossible<String, Error>; type SerializeTupleStruct = ser::Impossible<String, Error>; type SerializeTupleVariant = ser::Impossible<String, Error>; type SerializeMap = ser::Impossible<String, Error>; type SerializeStruct = ser::Impossible<String, Error>; type SerializeStructVariant = ser::Impossible<String, Error>;
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!(),
}
}
}
/// 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_derive::Serialize; /// # use std::collections::HashMap; /// #[derive(Serialize)] /// struct Manifest { /// package: Package, /// #[serde(serialize_with = "toml::ser::tables_last")] /// dependencies: HashMap<String, Dependency>, /// } /// # type Package = String; /// # type Dependency = String; /// # fn main() {} /// ``` pubfn tables_last<'a, I, K, V, S>(data: &'a I, serializer: S) -> Result<S::Ok, S::Error> where
&'a I: IntoIterator<Item = (K, V)>,
K: ser::Serialize,
V: ser::Serialize,
S: ser::Serializer,
{ use serde::ser::SerializeMap;
letmut map = serializer.serialize_map(None)?; for (k, v) in data { iflet Category::Primitive = v.serialize(Categorize::new())? {
map.serialize_entry(&k, &v)?;
}
} for (k, v) in data { iflet Category::Array = v.serialize(Categorize::new())? {
map.serialize_entry(&k, &v)?;
}
} for (k, v) in data { iflet Category::Table = v.serialize(Categorize::new())? {
map.serialize_entry(&k, &v)?;
}
}
map.end()
}
impl<E: ser::Error> ser::Serializer for Categorize<E> { 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<Category, E>;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.