feat: add display UI with splash screen, dashboard, and RSSI sparkline

Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:54 -06:00
1 parent 7064110293
commit 3102fc747a
17624 files changed
+4564928 -38

No files matched your search

+313
View File
@@ -0,0 +1,313 @@
//! Deserializing TOML into Rust structures.
//!
//! This module contains all the Serde support for deserializing TOML documents
//! into Rust structures. Note that some top-level functions here are also
//! provided at the top of the crate.
/// Deserializes a string into a type.
///
/// This function will attempt to interpret `s` as a TOML document and
/// deserialize `T` from the document.
///
/// To deserializes TOML values, instead of documents, see [`ValueDeserializer`].
///
/// # Examples
///
/// ```
/// use serde::Deserialize;
///
/// #[derive(Deserialize)]
/// struct Config {
/// title: String,
/// owner: Owner,
/// }
///
/// #[derive(Deserialize)]
/// struct Owner {
/// name: String,
/// }
///
/// let config: Config = toml::from_str(r#"
/// title = 'TOML Example'
///
/// [owner]
/// name = 'Lisa'
/// "#).unwrap();
///
/// assert_eq!(config.title, "TOML Example");
/// assert_eq!(config.owner.name, "Lisa");
/// ```
#[cfg(feature = "parse")]
pub fn from_str<T>(s: &'_ str) -> Result<T, Error>
where
T: serde::de::DeserializeOwned,
{
T::deserialize(Deserializer::new(s))
}
/// Errors that can occur when deserializing a type.
#[derive(PartialEq, Eq, Clone)]
pub struct Error {
inner: crate::edit::de::Error,
}
impl Error {
fn new(inner: crate::edit::de::Error) -> Self {
Self { inner }
}
pub(crate) fn add_key(&mut self, key: String) {
self.inner.add_key(key);
}
/// What went wrong
pub fn message(&self) -> &str {
self.inner.message()
}
/// The start/end index into the original document where the error occurred
#[cfg(feature = "parse")]
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.inner.span()
}
}
impl serde::de::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Error::new(crate::edit::de::Error::custom(msg))
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::error::Error for Error {}
/// Deserialization TOML document
///
/// To deserializes TOML values, instead of documents, see [`ValueDeserializer`].
#[cfg(feature = "parse")]
pub struct Deserializer<'a> {
input: &'a str,
}
#[cfg(feature = "parse")]
impl<'a> Deserializer<'a> {
/// Deserialization implementation for TOML.
pub fn new(input: &'a str) -> Self {
Self { input }
}
}
#[cfg(feature = "parse")]
impl<'de> serde::Deserializer<'de> for Deserializer<'_> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
let inner = toml_edit::de::Deserializer::parse(self.input).map_err(Error::new)?;
inner.deserialize_any(visitor).map_err(Error::new)
}
// `None` is interpreted as a missing field so be sure to implement `Some`
// as a present field.
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let inner = toml_edit::de::Deserializer::parse(self.input).map_err(Error::new)?;
inner.deserialize_option(visitor).map_err(Error::new)
}
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let inner = toml_edit::de::Deserializer::parse(self.input).map_err(Error::new)?;
inner
.deserialize_newtype_struct(name, visitor)
.map_err(Error::new)
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let inner = toml_edit::de::Deserializer::parse(self.input).map_err(Error::new)?;
inner
.deserialize_struct(name, fields, visitor)
.map_err(Error::new)
}
// Called when the type to deserialize is an enum, as opposed to a field in the type.
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let inner = toml_edit::de::Deserializer::parse(self.input).map_err(Error::new)?;
inner
.deserialize_enum(name, variants, visitor)
.map_err(Error::new)
}
serde::forward_to_deserialize_any! {
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
bytes byte_buf map unit
ignored_any unit_struct tuple_struct tuple identifier
}
}
/// Deserialization TOML [value][crate::Value]
///
/// # Example
///
/// ```
/// use serde::Deserialize;
///
/// #[derive(Deserialize)]
/// struct Config {
/// title: String,
/// owner: Owner,
/// }
///
/// #[derive(Deserialize)]
/// struct Owner {
/// name: String,
/// }
///
/// let config = Config::deserialize(toml::de::ValueDeserializer::new(
/// r#"{ title = 'TOML Example', owner = { name = 'Lisa' } }"#
/// )).unwrap();
///
/// assert_eq!(config.title, "TOML Example");
/// assert_eq!(config.owner.name, "Lisa");
/// ```
#[cfg(feature = "parse")]
pub struct ValueDeserializer<'a> {
input: &'a str,
}
#[cfg(feature = "parse")]
impl<'a> ValueDeserializer<'a> {
/// Deserialization implementation for TOML.
pub fn new(input: &'a str) -> Self {
Self { input }
}
}
#[cfg(feature = "parse")]
impl<'de> serde::Deserializer<'de> for ValueDeserializer<'_> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
let inner = self
.input
.parse::<toml_edit::de::ValueDeserializer>()
.map_err(Error::new)?;
inner.deserialize_any(visitor).map_err(Error::new)
}
// `None` is interpreted as a missing field so be sure to implement `Some`
// as a present field.
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let inner = self
.input
.parse::<toml_edit::de::ValueDeserializer>()
.map_err(Error::new)?;
inner.deserialize_option(visitor).map_err(Error::new)
}
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let inner = self
.input
.parse::<toml_edit::de::ValueDeserializer>()
.map_err(Error::new)?;
inner
.deserialize_newtype_struct(name, visitor)
.map_err(Error::new)
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let inner = self
.input
.parse::<toml_edit::de::ValueDeserializer>()
.map_err(Error::new)?;
inner
.deserialize_struct(name, fields, visitor)
.map_err(Error::new)
}
// Called when the type to deserialize is an enum, as opposed to a field in the type.
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let inner = self
.input
.parse::<toml_edit::de::ValueDeserializer>()
.map_err(Error::new)?;
inner
.deserialize_enum(name, variants, visitor)
.map_err(Error::new)
}
serde::forward_to_deserialize_any! {
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
bytes byte_buf map unit
ignored_any unit_struct tuple_struct tuple identifier
}
}
+91
View File
@@ -0,0 +1,91 @@
#[cfg(feature = "parse")]
pub(crate) mod de {
pub(crate) use toml_edit::de::Error;
}
#[cfg(not(feature = "parse"))]
pub(crate) mod de {
/// Errors that can occur when deserializing a type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Error {
inner: String,
}
impl Error {
/// Add key while unwinding
pub(crate) fn add_key(&mut self, _key: String) {}
/// What went wrong
pub(crate) fn message(&self) -> &str {
self.inner.as_str()
}
}
impl serde::de::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Error {
inner: msg.to_string(),
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::error::Error for Error {}
}
#[cfg(feature = "display")]
pub(crate) mod ser {
pub(crate) use toml_edit::ser::Error;
}
#[cfg(not(feature = "display"))]
pub(crate) mod ser {
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub(crate) enum Error {
UnsupportedType(Option<&'static str>),
UnsupportedNone,
KeyNotString,
Custom(String),
}
impl Error {
pub(crate) fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Error::Custom(msg.to_string())
}
}
impl serde::ser::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Self::custom(msg)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnsupportedType(Some(t)) => write!(formatter, "unsupported {t} type"),
Self::UnsupportedType(None) => write!(formatter, "unsupported rust type"),
Self::UnsupportedNone => "unsupported None value".fmt(formatter),
Self::KeyNotString => "map key was not a string".fmt(formatter),
Self::Custom(s) => s.fmt(formatter),
}
}
}
impl std::error::Error for Error {}
}
+66
View File
@@ -0,0 +1,66 @@
#[derive(Copy, Clone, Default)]
pub(crate) struct DocumentFormatter {
pub(crate) multiline_array: bool,
is_value: bool,
}
impl toml_edit::visit_mut::VisitMut for DocumentFormatter {
fn visit_document_mut(&mut self, node: &mut toml_edit::DocumentMut) {
toml_edit::visit_mut::visit_document_mut(self, node);
}
fn visit_item_mut(&mut self, node: &mut toml_edit::Item) {
let is_parent_value = self.is_value;
if !is_parent_value {
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,
};
self.is_value = other.is_value();
*node = other;
}
toml_edit::visit_mut::visit_item_mut(self, node);
self.is_value = is_parent_value;
}
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);
}
}
}
+185
View File
@@ -0,0 +1,185 @@
//! A [serde]-compatible [TOML]-parsing library
//!
//! TOML itself is a simple, ergonomic, and readable configuration format:
//!
//! ```toml
//! [package]
//! name = "toml"
//!
//! [dependencies]
//! serde = "1.0"
//! ```
//!
//! The TOML format tends to be relatively common throughout the Rust community
//! for configuration, notably being used by [Cargo], Rust's package manager.
//!
//! ## TOML values
//!
//! A TOML document is represented with the [`Table`] type which maps `String` to the [`Value`] enum:
//!
//! ```rust
//! # use toml::value::{Datetime, Array, Table};
//! pub enum Value {
//! String(String),
//! Integer(i64),
//! Float(f64),
//! Boolean(bool),
//! Datetime(Datetime),
//! Array(Array),
//! Table(Table),
//! }
//! ```
//!
//! ## Parsing TOML
//!
//! The easiest way to parse a TOML document is via the [`Table`] type:
//!
#![cfg_attr(not(feature = "parse"), doc = " ```ignore")]
#![cfg_attr(feature = "parse", doc = " ```")]
//! use toml::Table;
//!
//! let value = "foo = 'bar'".parse::<Table>().unwrap();
//!
//! assert_eq!(value["foo"].as_str(), Some("bar"));
//! ```
//!
//! The [`Table`] type implements a number of convenience methods and
//! traits; the example above uses [`FromStr`] to parse a [`str`] into a
//! [`Table`].
//!
//! ## Deserialization and Serialization
//!
//! This crate supports [`serde`] 1.0 with a number of
//! implementations of the `Deserialize`, `Serialize`, `Deserializer`, and
//! `Serializer` traits. Namely, you'll find:
//!
//! * `Deserialize for Table`
//! * `Serialize for Table`
//! * `Deserialize for Value`
//! * `Serialize for Value`
//! * `Deserialize for Datetime`
//! * `Serialize for Datetime`
//! * `Deserializer for de::Deserializer`
//! * `Serializer for ser::Serializer`
//! * `Deserializer for Table`
//! * `Deserializer for Value`
//!
//! This means that you can use Serde to deserialize/serialize the
//! [`Table`] type as well as [`Value`] and [`Datetime`] type in this crate. You can also
//! use the [`Deserializer`], [`Serializer`], or [`Table`] type itself to act as
//! a deserializer/serializer for arbitrary types.
//!
//! An example of deserializing with TOML is:
//!
#![cfg_attr(not(feature = "parse"), doc = " ```ignore")]
#![cfg_attr(feature = "parse", doc = " ```")]
//! use serde::Deserialize;
//!
//! #[derive(Deserialize)]
//! struct Config {
//! ip: String,
//! port: Option<u16>,
//! keys: Keys,
//! }
//!
//! #[derive(Deserialize)]
//! struct Keys {
//! github: String,
//! travis: Option<String>,
//! }
//!
//! let config: Config = toml::from_str(r#"
//! ip = '127.0.0.1'
//!
//! [keys]
//! github = 'xxxxxxxxxxxxxxxxx'
//! travis = 'yyyyyyyyyyyyyyyyy'
//! "#).unwrap();
//!
//! assert_eq!(config.ip, "127.0.0.1");
//! assert_eq!(config.port, None);
//! assert_eq!(config.keys.github, "xxxxxxxxxxxxxxxxx");
//! assert_eq!(config.keys.travis.as_ref().unwrap(), "yyyyyyyyyyyyyyyyy");
//! ```
//!
//! You can serialize types in a similar fashion:
//!
#![cfg_attr(not(feature = "display"), doc = " ```ignore")]
#![cfg_attr(feature = "display", doc = " ```")]
//! use serde::Serialize;
//!
//! #[derive(Serialize)]
//! struct Config {
//! ip: String,
//! port: Option<u16>,
//! keys: Keys,
//! }
//!
//! #[derive(Serialize)]
//! struct Keys {
//! github: String,
//! travis: Option<String>,
//! }
//!
//! let config = Config {
//! ip: "127.0.0.1".to_string(),
//! port: None,
//! keys: Keys {
//! github: "xxxxxxxxxxxxxxxxx".to_string(),
//! travis: Some("yyyyyyyyyyyyyyyyy".to_string()),
//! },
//! };
//!
//! let toml = toml::to_string(&config).unwrap();
//! ```
//!
//! [TOML]: https://github.com/toml-lang/toml
//! [Cargo]: https://crates.io/
//! [`serde`]: https://serde.rs/
//! [serde]: https://serde.rs/
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
// Makes rustc abort compilation if there are any unsafe blocks in the crate.
// Presence of this annotation is picked up by tools such as cargo-geiger
// and lets them ensure that there is indeed no unsafe code as opposed to
// something they couldn't detect (e.g. unsafe added via macro expansion, etc).
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![warn(clippy::print_stderr)]
#![warn(clippy::print_stdout)]
pub mod map;
pub mod value;
pub mod de;
pub mod ser;
#[doc(hidden)]
pub mod macros;
mod edit;
#[cfg(feature = "display")]
mod fmt;
mod table;
#[cfg(feature = "parse")]
#[doc(inline)]
pub use crate::de::{from_str, Deserializer};
#[cfg(feature = "display")]
#[doc(inline)]
pub use crate::ser::{to_string, to_string_pretty, Serializer};
#[doc(inline)]
pub use crate::value::Value;
pub use serde_spanned::Spanned;
pub use table::Table;
// Shortcuts for the module doc-comment
#[allow(unused_imports)]
use core::str::FromStr;
#[allow(unused_imports)]
use toml_datetime::Datetime;
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
pub struct ReadmeDoctests;
+455
View File
@@ -0,0 +1,455 @@
pub use serde::de::{Deserialize, IntoDeserializer};
use crate::value::{Array, Table, Value};
/// Construct a [`Table`] from TOML syntax.
///
/// ```rust
/// let cargo_toml = toml::toml! {
/// [package]
/// name = "toml"
///
/// [dependencies]
/// serde = "1.0"
///
/// [dev-dependencies]
/// serde_derive = "1.0"
/// serde_json = "1.0"
/// };
///
/// println!("{:#?}", cargo_toml);
/// ```
#[macro_export]
macro_rules! toml {
($($toml:tt)+) => {{
let table = $crate::value::Table::new();
let mut root = $crate::Value::Table(table);
$crate::toml_internal!(@toplevel root [] $($toml)+);
match root {
$crate::Value::Table(table) => table,
_ => unreachable!(),
}
}};
}
// TT-muncher to parse TOML syntax into a toml::Value.
//
// @toplevel -- Parse tokens outside of an inline table or inline array. In
// this state, `[table headers]` and `[[array headers]]` are
// allowed and `key = value` pairs are not separated by commas.
//
// @topleveldatetime -- Helper to parse a Datetime from string and insert it
// into a table, continuing in the @toplevel state.
//
// @path -- Turn a path segment into a string. Segments that look like idents
// are stringified, while quoted segments like `"cfg(windows)"`
// are not.
//
// @value -- Parse the value part of a `key = value` pair, which may be a
// primitive or inline table or inline array.
//
// @table -- Parse the contents of an inline table, returning them as a
// toml::Value::Table.
//
// @tabledatetime -- Helper to parse a Datetime from string and insert it
// into a table, continuing in the @table state.
//
// @array -- Parse the contents of an inline array, returning them as a
// toml::Value::Array.
//
// @arraydatetime -- Helper to parse a Datetime from string and push it into
// an array, continuing in the @array state.
//
// @trailingcomma -- Helper to append a comma to a sequence of tokens if the
// sequence is non-empty and does not already end in a trailing
// comma.
//
#[macro_export]
#[doc(hidden)]
macro_rules! toml_internal {
// Base case, no elements remaining.
(@toplevel $root:ident [$($path:tt)*]) => {};
// Parse negative number `key = -value`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = - $v:tt $($rest:tt)*) => {
$crate::toml_internal!(@toplevel $root [$($path)*] $($($k)-+).+ = (-$v) $($rest)*);
};
// Parse positive number `key = +value`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = + $v:tt $($rest:tt)*) => {
$crate::toml_internal!(@toplevel $root [$($path)*] $($($k)-+).+ = ($v) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Parse local datetime `key = 1979-05-27T00:32:00.999999`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
};
// Space instead of T.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
};
// Space instead of T.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
};
// Parse local date `key = 1979-05-27`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day) $($rest)*);
};
// Parse local time `key = 00:32:00.999999`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($hr : $min : $sec . $frac) $($rest)*);
};
// Parse local time `key = 07:32:00`.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
$crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($hr : $min : $sec) $($rest)*);
};
// Parse any other `key = value` including string, inline array, inline
// table, number, and boolean.
(@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $v:tt $($rest:tt)*) => {{
$crate::macros::insert_toml(
&mut $root,
&[$($path)* $(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
$crate::toml_internal!(@value $v));
$crate::toml_internal!(@toplevel $root [$($path)*] $($rest)*);
}};
// Parse array header `[[bin]]`.
(@toplevel $root:ident $oldpath:tt [[$($($path:tt)-+).+]] $($rest:tt)*) => {
$crate::macros::push_toml(
&mut $root,
&[$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+]);
$crate::toml_internal!(@toplevel $root [$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
};
// Parse table header `[patch.crates-io]`.
(@toplevel $root:ident $oldpath:tt [$($($path:tt)-+).+] $($rest:tt)*) => {
$crate::macros::insert_toml(
&mut $root,
&[$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+],
$crate::Value::Table($crate::value::Table::new()));
$crate::toml_internal!(@toplevel $root [$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
};
// Parse datetime from string and insert into table.
(@topleveldatetime $root:ident [$($path:tt)*] $($($k:tt)-+).+ = ($($datetime:tt)+) $($rest:tt)*) => {
$crate::macros::insert_toml(
&mut $root,
&[$($path)* $(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
$crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
$crate::toml_internal!(@toplevel $root [$($path)*] $($rest)*);
};
// Turn a path segment into a string.
(@path $ident:ident) => {
stringify!($ident)
};
// For a path segment that is not an ident, expect that it is already a
// quoted string, like in `[target."cfg(windows)".dependencies]`.
(@path $quoted:tt) => {
$quoted
};
// Construct a Value from an inline table.
(@value { $($inline:tt)* }) => {{
let mut table = $crate::Value::Table($crate::value::Table::new());
$crate::toml_internal!(@trailingcomma (@table table) $($inline)*);
table
}};
// Construct a Value from an inline array.
(@value [ $($inline:tt)* ]) => {{
let mut array = $crate::value::Array::new();
$crate::toml_internal!(@trailingcomma (@array array) $($inline)*);
$crate::Value::Array(array)
}};
(@value (-nan)) => {
$crate::Value::Float(::std::f64::NAN.copysign(-1.0))
};
(@value (nan)) => {
$crate::Value::Float(::std::f64::NAN.copysign(1.0))
};
(@value nan) => {
$crate::Value::Float(::std::f64::NAN.copysign(1.0))
};
(@value (-inf)) => {
$crate::Value::Float(::std::f64::NEG_INFINITY)
};
(@value (inf)) => {
$crate::Value::Float(::std::f64::INFINITY)
};
(@value inf) => {
$crate::Value::Float(::std::f64::INFINITY)
};
// Construct a Value from any other type, probably string or boolean or number.
(@value $v:tt) => {{
// TODO: Implement this with something like serde_json::to_value instead.
let de = $crate::macros::IntoDeserializer::<$crate::de::Error>::into_deserializer($v);
<$crate::Value as $crate::macros::Deserialize>::deserialize(de).unwrap()
}};
// Base case of inline table.
(@table $root:ident) => {};
// Parse negative number `key = -value`.
(@table $root:ident $($($k:tt)-+).+ = - $v:tt , $($rest:tt)*) => {
$crate::toml_internal!(@table $root $($($k)-+).+ = (-$v) , $($rest)*);
};
// Parse positive number `key = +value`.
(@table $root:ident $($($k:tt)-+).+ = + $v:tt , $($rest:tt)*) => {
$crate::toml_internal!(@table $root $($($k)-+).+ = ($v) , $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Parse local datetime `key = 1979-05-27T00:32:00.999999`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
};
// Space instead of T.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
};
// Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
};
// Space instead of T.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
};
// Parse local date `key = 1979-05-27`.
(@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day) $($rest)*);
};
// Parse local time `key = 00:32:00.999999`.
(@table $root:ident $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($hr : $min : $sec . $frac) $($rest)*);
};
// Parse local time `key = 07:32:00`.
(@table $root:ident $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($hr : $min : $sec) $($rest)*);
};
// Parse any other type, probably string or boolean or number.
(@table $root:ident $($($k:tt)-+).+ = $v:tt , $($rest:tt)*) => {
$crate::macros::insert_toml(
&mut $root,
&[$(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
$crate::toml_internal!(@value $v));
$crate::toml_internal!(@table $root $($rest)*);
};
// Parse a Datetime from string and continue in @table state.
(@tabledatetime $root:ident $($($k:tt)-+).+ = ($($datetime:tt)*) $($rest:tt)*) => {
$crate::macros::insert_toml(
&mut $root,
&[$(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
$crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
$crate::toml_internal!(@table $root $($rest)*);
};
// Base case of inline array.
(@array $root:ident) => {};
// Parse negative number `-value`.
(@array $root:ident - $v:tt , $($rest:tt)*) => {
$crate::toml_internal!(@array $root (-$v) , $($rest)*);
};
// Parse positive number `+value`.
(@array $root:ident + $v:tt , $($rest:tt)*) => {
$crate::toml_internal!(@array $root ($v) , $($rest)*);
};
// Parse offset datetime `1979-05-27T00:32:00.999999-07:00`.
(@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
};
// Parse offset datetime `1979-05-27T00:32:00-07:00`.
(@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Space instead of T.
(@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
};
// Parse local datetime `1979-05-27T00:32:00.999999`.
(@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
};
// Space instead of T.
(@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
};
// Parse offset datetime `1979-05-27T07:32:00Z` and local datetime `1979-05-27T07:32:00`.
(@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec) $($rest)*);
};
// Space instead of T.
(@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
};
// Parse local date `1979-05-27`.
(@array $root:ident $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day) $($rest)*);
};
// Parse local time `00:32:00.999999`.
(@array $root:ident $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($hr : $min : $sec . $frac) $($rest)*);
};
// Parse local time `07:32:00`.
(@array $root:ident $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
$crate::toml_internal!(@arraydatetime $root ($hr : $min : $sec) $($rest)*);
};
// Parse any other type, probably string or boolean or number.
(@array $root:ident $v:tt , $($rest:tt)*) => {
$root.push($crate::toml_internal!(@value $v));
$crate::toml_internal!(@array $root $($rest)*);
};
// Parse a Datetime from string and continue in @array state.
(@arraydatetime $root:ident ($($datetime:tt)*) $($rest:tt)*) => {
$root.push($crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
$crate::toml_internal!(@array $root $($rest)*);
};
// No trailing comma required if the tokens are empty.
(@trailingcomma ($($args:tt)*)) => {
$crate::toml_internal!($($args)*);
};
// Tokens end with a trailing comma, do not append another one.
(@trailingcomma ($($args:tt)*) ,) => {
$crate::toml_internal!($($args)* ,);
};
// Tokens end with something other than comma, append a trailing comma.
(@trailingcomma ($($args:tt)*) $last:tt) => {
$crate::toml_internal!($($args)* $last ,);
};
// Not yet at the last token.
(@trailingcomma ($($args:tt)*) $first:tt $($rest:tt)+) => {
$crate::toml_internal!(@trailingcomma ($($args)* $first) $($rest)+);
};
}
// Called when parsing a `key = value` pair.
// Inserts an entry into the table at the given path.
pub fn insert_toml(root: &mut Value, path: &[&str], value: Value) {
*traverse(root, path) = value;
}
// Called when parsing an `[[array header]]`.
// Pushes an empty table onto the array at the given path.
pub fn push_toml(root: &mut Value, path: &[&str]) {
let target = traverse(root, path);
if !target.is_array() {
*target = Value::Array(Array::new());
}
target
.as_array_mut()
.unwrap()
.push(Value::Table(Table::new()));
}
fn traverse<'a>(root: &'a mut Value, path: &[&str]) -> &'a mut Value {
let mut cur = root;
for &key in path {
// Lexical lifetimes :D
let cur1 = cur;
// From the TOML spec:
//
// > Each double-bracketed sub-table will belong to the most recently
// > defined table element above it.
let cur2 = if cur1.is_array() {
cur1.as_array_mut().unwrap().last_mut().unwrap()
} else {
cur1
};
// We are about to index into this value, so it better be a table.
if !cur2.is_table() {
*cur2 = Value::Table(Table::new());
}
if !cur2.as_table().unwrap().contains_key(key) {
// Insert an empty table for the next loop iteration to point to.
let empty = Value::Table(Table::new());
cur2.as_table_mut().unwrap().insert(key.to_owned(), empty);
}
// Step into the current table.
cur = cur2.as_table_mut().unwrap().get_mut(key).unwrap();
}
cur
}
+636
View File
@@ -0,0 +1,636 @@
// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A map of `String` to [Value].
//!
//! By default the map is backed by a [`BTreeMap`]. Enable the `preserve_order`
//! feature of toml-rs to use [`IndexMap`] instead.
//!
//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
//! [`IndexMap`]: https://docs.rs/indexmap
use crate::value::Value;
use serde::{de, ser};
use std::borrow::Borrow;
use std::fmt::{self, Debug};
use std::hash::Hash;
use std::iter::FromIterator;
use std::ops;
#[cfg(not(feature = "preserve_order"))]
use std::collections::{btree_map, BTreeMap};
#[cfg(feature = "preserve_order")]
use indexmap::{self, IndexMap};
/// Represents a TOML key/value type.
pub struct Map<K, V> {
map: MapImpl<K, V>,
}
#[cfg(not(feature = "preserve_order"))]
type MapImpl<K, V> = BTreeMap<K, V>;
#[cfg(feature = "preserve_order")]
type MapImpl<K, V> = IndexMap<K, V>;
impl Map<String, Value> {
/// Makes a new empty Map.
#[inline]
pub fn new() -> Self {
Map {
map: MapImpl::new(),
}
}
#[cfg(not(feature = "preserve_order"))]
/// Makes a new empty Map with the given initial capacity.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
// does not support with_capacity
let _ = capacity;
Map {
map: BTreeMap::new(),
}
}
#[cfg(feature = "preserve_order")]
/// Makes a new empty Map with the given initial capacity.
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Map {
map: IndexMap::with_capacity(capacity),
}
}
/// Clears the map, removing all values.
#[inline]
pub fn clear(&mut self) {
self.map.clear();
}
/// Returns a reference to the value corresponding to the key.
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
pub fn get<Q>(&self, key: &Q) -> Option<&Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash + ?Sized,
{
self.map.get(key)
}
/// Returns true if the map contains a value for the specified key.
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
pub fn contains_key<Q>(&self, key: &Q) -> bool
where
String: Borrow<Q>,
Q: Ord + Eq + Hash + ?Sized,
{
self.map.contains_key(key)
}
/// Returns a mutable reference to the value corresponding to the key.
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash + ?Sized,
{
self.map.get_mut(key)
}
/// Returns the key-value pair matching the given key.
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
pub fn get_key_value<Q>(&self, key: &Q) -> Option<(&String, &Value)>
where
String: Borrow<Q>,
Q: ?Sized + Ord + Eq + Hash,
{
self.map.get_key_value(key)
}
/// Inserts a key-value pair into the map.
///
/// If the map did not have this key present, `None` is returned.
///
/// If the map did have this key present, the value is updated, and the old
/// value is returned. The key is not updated, though; this matters for
/// types that can be `==` without being identical.
#[inline]
pub fn insert(&mut self, k: String, v: Value) -> Option<Value> {
self.map.insert(k, v)
}
/// Removes a key from the map, returning the value at the key if the key
/// was previously in the map.
///
/// The key may be any borrowed form of the map's key type, but the ordering
/// on the borrowed form *must* match the ordering on the key type.
#[inline]
pub fn remove<Q>(&mut self, key: &Q) -> Option<Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash + ?Sized,
{
#[cfg(not(feature = "preserve_order"))]
{
self.map.remove(key)
}
#[cfg(feature = "preserve_order")]
{
self.map.shift_remove(key)
}
}
/// Retains only the elements specified by the `keep` predicate.
///
/// In other words, remove all pairs `(k, v)` for which `keep(&k, &mut v)`
/// returns `false`.
///
/// The elements are visited in iteration order.
#[inline]
pub fn retain<F>(&mut self, mut keep: F)
where
F: FnMut(&str, &mut Value) -> bool,
{
self.map.retain(|key, value| keep(key.as_str(), value));
}
/// Gets the given key's corresponding entry in the map for in-place
/// manipulation.
pub fn entry<S>(&mut self, key: S) -> Entry<'_>
where
S: Into<String>,
{
#[cfg(feature = "preserve_order")]
use indexmap::map::Entry as EntryImpl;
#[cfg(not(feature = "preserve_order"))]
use std::collections::btree_map::Entry as EntryImpl;
match self.map.entry(key.into()) {
EntryImpl::Vacant(vacant) => Entry::Vacant(VacantEntry { vacant }),
EntryImpl::Occupied(occupied) => Entry::Occupied(OccupiedEntry { occupied }),
}
}
/// Returns the number of elements in the map.
#[inline]
pub fn len(&self) -> usize {
self.map.len()
}
/// Returns true if the map contains no elements.
#[inline]
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
/// Gets an iterator over the entries of the map.
#[inline]
pub fn iter(&self) -> Iter<'_> {
Iter {
iter: self.map.iter(),
}
}
/// Gets a mutable iterator over the entries of the map.
#[inline]
pub fn iter_mut(&mut self) -> IterMut<'_> {
IterMut {
iter: self.map.iter_mut(),
}
}
/// Gets an iterator over the keys of the map.
#[inline]
pub fn keys(&self) -> Keys<'_> {
Keys {
iter: self.map.keys(),
}
}
/// Gets an iterator over the values of the map.
#[inline]
pub fn values(&self) -> Values<'_> {
Values {
iter: self.map.values(),
}
}
}
impl Default for Map<String, Value> {
#[inline]
fn default() -> Self {
Map {
map: MapImpl::new(),
}
}
}
impl Clone for Map<String, Value> {
#[inline]
fn clone(&self) -> Self {
Map {
map: self.map.clone(),
}
}
}
impl PartialEq for Map<String, Value> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.map.eq(&other.map)
}
}
/// Access an element of this map. Panics if the given key is not present in the
/// map.
impl<Q> ops::Index<&Q> for Map<String, Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash + ?Sized,
{
type Output = Value;
fn index(&self, index: &Q) -> &Value {
self.map.index(index)
}
}
/// Mutably access an element of this map. Panics if the given key is not
/// present in the map.
impl<Q> ops::IndexMut<&Q> for Map<String, Value>
where
String: Borrow<Q>,
Q: Ord + Eq + Hash + ?Sized,
{
fn index_mut(&mut self, index: &Q) -> &mut Value {
self.map.get_mut(index).expect("no entry found for key")
}
}
impl Debug for Map<String, Value> {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
self.map.fmt(formatter)
}
}
impl ser::Serialize for Map<String, Value> {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(self.len()))?;
for (k, v) in self {
map.serialize_key(k)?;
map.serialize_value(v)?;
}
map.end()
}
}
impl<'de> de::Deserialize<'de> for Map<String, Value> {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Map<String, Value>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a map")
}
#[inline]
fn visit_unit<E>(self) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Map::new())
}
#[inline]
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
where
V: de::MapAccess<'de>,
{
let mut values = Map::new();
while let Some((key, value)) = visitor.next_entry()? {
values.insert(key, value);
}
Ok(values)
}
}
deserializer.deserialize_map(Visitor)
}
}
impl FromIterator<(String, Value)> for Map<String, Value> {
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = (String, Value)>,
{
Map {
map: FromIterator::from_iter(iter),
}
}
}
impl Extend<(String, Value)> for Map<String, Value> {
fn extend<T>(&mut self, iter: T)
where
T: IntoIterator<Item = (String, Value)>,
{
self.map.extend(iter);
}
}
macro_rules! delegate_iterator {
(($name:ident $($generics:tt)*) => $item:ty) => {
impl $($generics)* Iterator for $name $($generics)* {
type Item = $item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl $($generics)* DoubleEndedIterator for $name $($generics)* {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back()
}
}
impl $($generics)* ExactSizeIterator for $name $($generics)* {
#[inline]
fn len(&self) -> usize {
self.iter.len()
}
}
}
}
//////////////////////////////////////////////////////////////////////////////
/// A view into a single entry in a map, which may either be vacant or occupied.
/// This enum is constructed from the [`entry`] method on [`Map`].
///
/// [`entry`]: struct.Map.html#method.entry
/// [`Map`]: struct.Map.html
pub enum Entry<'a> {
/// A vacant Entry.
Vacant(VacantEntry<'a>),
/// An occupied Entry.
Occupied(OccupiedEntry<'a>),
}
/// A vacant Entry. It is part of the [`Entry`] enum.
///
/// [`Entry`]: enum.Entry.html
pub struct VacantEntry<'a> {
vacant: VacantEntryImpl<'a>,
}
/// An occupied Entry. It is part of the [`Entry`] enum.
///
/// [`Entry`]: enum.Entry.html
pub struct OccupiedEntry<'a> {
occupied: OccupiedEntryImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type VacantEntryImpl<'a> = btree_map::VacantEntry<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type VacantEntryImpl<'a> = indexmap::map::VacantEntry<'a, String, Value>;
#[cfg(not(feature = "preserve_order"))]
type OccupiedEntryImpl<'a> = btree_map::OccupiedEntry<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type OccupiedEntryImpl<'a> = indexmap::map::OccupiedEntry<'a, String, Value>;
impl<'a> Entry<'a> {
/// Returns a reference to this entry's key.
pub fn key(&self) -> &String {
match *self {
Entry::Vacant(ref e) => e.key(),
Entry::Occupied(ref e) => e.key(),
}
}
/// Ensures a value is in the entry by inserting the default if empty, and
/// returns a mutable reference to the value in the entry.
pub fn or_insert(self, default: Value) -> &'a mut Value {
match self {
Entry::Vacant(entry) => entry.insert(default),
Entry::Occupied(entry) => entry.into_mut(),
}
}
/// Ensures a value is in the entry by inserting the result of the default
/// function if empty, and returns a mutable reference to the value in the
/// entry.
pub fn or_insert_with<F>(self, default: F) -> &'a mut Value
where
F: FnOnce() -> Value,
{
match self {
Entry::Vacant(entry) => entry.insert(default()),
Entry::Occupied(entry) => entry.into_mut(),
}
}
}
impl<'a> VacantEntry<'a> {
/// Gets a reference to the key that would be used when inserting a value
/// through the `VacantEntry`.
#[inline]
pub fn key(&self) -> &String {
self.vacant.key()
}
/// Sets the value of the entry with the `VacantEntry`'s key, and returns a
/// mutable reference to it.
#[inline]
pub fn insert(self, value: Value) -> &'a mut Value {
self.vacant.insert(value)
}
}
impl<'a> OccupiedEntry<'a> {
/// Gets a reference to the key in the entry.
#[inline]
pub fn key(&self) -> &String {
self.occupied.key()
}
/// Gets a reference to the value in the entry.
#[inline]
pub fn get(&self) -> &Value {
self.occupied.get()
}
/// Gets a mutable reference to the value in the entry.
#[inline]
pub fn get_mut(&mut self) -> &mut Value {
self.occupied.get_mut()
}
/// Converts the entry into a mutable reference to its value.
#[inline]
pub fn into_mut(self) -> &'a mut Value {
self.occupied.into_mut()
}
/// Sets the value of the entry with the `OccupiedEntry`'s key, and returns
/// the entry's old value.
#[inline]
pub fn insert(&mut self, value: Value) -> Value {
self.occupied.insert(value)
}
/// Takes the value of the entry out of the map, and returns it.
#[inline]
pub fn remove(self) -> Value {
#[cfg(not(feature = "preserve_order"))]
{
self.occupied.remove()
}
#[cfg(feature = "preserve_order")]
{
self.occupied.shift_remove()
}
}
}
//////////////////////////////////////////////////////////////////////////////
impl<'a> IntoIterator for &'a Map<String, Value> {
type Item = (&'a String, &'a Value);
type IntoIter = Iter<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
Iter {
iter: self.map.iter(),
}
}
}
/// An iterator over a `toml::Map`'s entries.
pub struct Iter<'a> {
iter: IterImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type IterImpl<'a> = btree_map::Iter<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type IterImpl<'a> = indexmap::map::Iter<'a, String, Value>;
delegate_iterator!((Iter<'a>) => (&'a String, &'a Value));
//////////////////////////////////////////////////////////////////////////////
impl<'a> IntoIterator for &'a mut Map<String, Value> {
type Item = (&'a String, &'a mut Value);
type IntoIter = IterMut<'a>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IterMut {
iter: self.map.iter_mut(),
}
}
}
/// A mutable iterator over a `toml::Map`'s entries.
pub struct IterMut<'a> {
iter: IterMutImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type IterMutImpl<'a> = btree_map::IterMut<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type IterMutImpl<'a> = indexmap::map::IterMut<'a, String, Value>;
delegate_iterator!((IterMut<'a>) => (&'a String, &'a mut Value));
//////////////////////////////////////////////////////////////////////////////
impl IntoIterator for Map<String, Value> {
type Item = (String, Value);
type IntoIter = IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter {
iter: self.map.into_iter(),
}
}
}
/// An owning iterator over a `toml::Map`'s entries.
pub struct IntoIter {
iter: IntoIterImpl,
}
#[cfg(not(feature = "preserve_order"))]
type IntoIterImpl = btree_map::IntoIter<String, Value>;
#[cfg(feature = "preserve_order")]
type IntoIterImpl = indexmap::map::IntoIter<String, Value>;
delegate_iterator!((IntoIter) => (String, Value));
//////////////////////////////////////////////////////////////////////////////
/// An iterator over a `toml::Map`'s keys.
pub struct Keys<'a> {
iter: KeysImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type KeysImpl<'a> = btree_map::Keys<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type KeysImpl<'a> = indexmap::map::Keys<'a, String, Value>;
delegate_iterator!((Keys<'a>) => &'a String);
//////////////////////////////////////////////////////////////////////////////
/// An iterator over a `toml::Map`'s values.
pub struct Values<'a> {
iter: ValuesImpl<'a>,
}
#[cfg(not(feature = "preserve_order"))]
type ValuesImpl<'a> = btree_map::Values<'a, String, Value>;
#[cfg(feature = "preserve_order")]
type ValuesImpl<'a> = indexmap::map::Values<'a, String, Value>;
delegate_iterator!((Values<'a>) => &'a Value);
+107
View File
@@ -0,0 +1,107 @@
use super::write_document;
use super::{Error, Serializer};
use crate::fmt::DocumentFormatter;
type InnerSerializeDocumentSeq =
<toml_edit::ser::ValueSerializer as serde::Serializer>::SerializeSeq;
#[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,
}
}
}
impl serde::ser::SerializeSeq for SerializeDocumentArray<'_> {
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_element(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_document(self.dst, self.settings, self.inner.end())
}
}
impl serde::ser::SerializeTuple for SerializeDocumentArray<'_> {
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_element(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_document(self.dst, self.settings, self.inner.end())
}
}
impl serde::ser::SerializeTupleStruct for SerializeDocumentArray<'_> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_field(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_document(self.dst, self.settings, self.inner.end())
}
}
type InnerSerializeDocumentTupleVariant =
<toml_edit::ser::ValueSerializer as serde::Serializer>::SerializeTupleVariant;
#[doc(hidden)]
pub struct SerializeDocumentTupleVariant<'d> {
inner: InnerSerializeDocumentTupleVariant,
dst: &'d mut String,
settings: DocumentFormatter,
}
impl<'d> SerializeDocumentTupleVariant<'d> {
pub(crate) fn new(ser: Serializer<'d>, inner: InnerSerializeDocumentTupleVariant) -> Self {
Self {
inner,
dst: ser.dst,
settings: ser.settings,
}
}
}
impl serde::ser::SerializeTupleVariant for SerializeDocumentTupleVariant<'_> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_field(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_document(self.dst, self.settings, self.inner.end())
}
}
+118
View File
@@ -0,0 +1,118 @@
use super::write_document;
use super::{Error, Serializer};
use crate::fmt::DocumentFormatter;
type InnerSerializeDocumentTable =
<toml_edit::ser::ValueSerializer as serde::Serializer>::SerializeMap;
#[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,
}
}
}
impl serde::ser::SerializeMap for SerializeDocumentTable<'_> {
type Ok = ();
type Error = Error;
fn serialize_key<T>(&mut self, input: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_key(input).map_err(Error::wrap)
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_value(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_document(self.dst, self.settings, self.inner.end())
}
}
impl serde::ser::SerializeStruct for SerializeDocumentTable<'_> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_field(key, value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_document(self.dst, self.settings, self.inner.end())
}
}
impl serde::ser::SerializeStructVariant for SerializeDocumentTable<'_> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
serde::ser::SerializeStruct::serialize_field(self, key, value)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
serde::ser::SerializeStruct::end(self)
}
}
type InnerSerializeDocumentStructVariant =
<toml_edit::ser::ValueSerializer as serde::Serializer>::SerializeStructVariant;
#[doc(hidden)]
pub struct SerializeDocumentStructVariant<'d> {
inner: InnerSerializeDocumentStructVariant,
dst: &'d mut String,
settings: DocumentFormatter,
}
impl<'d> SerializeDocumentStructVariant<'d> {
pub(crate) fn new(ser: Serializer<'d>, inner: InnerSerializeDocumentStructVariant) -> Self {
Self {
inner,
dst: ser.dst,
settings: ser.settings,
}
}
}
impl serde::ser::SerializeStructVariant for SerializeDocumentStructVariant<'_> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_field(key, value).map_err(Error::wrap)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
write_document(self.dst, self.settings, self.inner.end())
}
}
+484
View File
@@ -0,0 +1,484 @@
//! 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.
#[cfg(feature = "display")]
mod array;
#[cfg(feature = "display")]
mod map;
#[cfg(feature = "display")]
mod ser_value;
#[cfg(feature = "display")]
pub use ser_value::ValueSerializer;
/// 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.
///
/// To serialize TOML values, instead of documents, see [`ValueSerializer`].
///
/// # Examples
///
/// ```
/// use serde::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)
/// ```
#[cfg(feature = "display")]
pub fn to_string<T>(value: &T) -> Result<String, Error>
where
T: serde::ser::Serialize + ?Sized,
{
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.
///
/// This is identical to `to_string` except the output string has a more
/// "pretty" output. See `Serializer::pretty` for more details.
///
/// To serialize TOML values, instead of documents, see [`ValueSerializer`].
///
/// For greater customization, instead serialize to a
/// [`toml_edit::DocumentMut`](https://docs.rs/toml_edit/latest/toml_edit/struct.DocumentMut.html).
#[cfg(feature = "display")]
pub fn to_string_pretty<T>(value: &T) -> Result<String, Error>
where
T: serde::ser::Serialize + ?Sized,
{
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(Clone, PartialEq, Eq)]
pub struct Error {
pub(crate) inner: crate::edit::ser::Error,
}
impl Error {
pub(crate) fn new(inner: impl std::fmt::Display) -> Self {
Self {
inner: crate::edit::ser::Error::Custom(inner.to_string()),
}
}
#[cfg(feature = "display")]
pub(crate) fn wrap(inner: crate::edit::ser::Error) -> Self {
Self { inner }
}
pub(crate) fn unsupported_type(t: Option<&'static str>) -> Self {
Self {
inner: crate::edit::ser::Error::UnsupportedType(t),
}
}
pub(crate) fn unsupported_none() -> Self {
Self {
inner: crate::edit::ser::Error::UnsupportedNone,
}
}
pub(crate) fn key_not_string() -> Self {
Self {
inner: crate::edit::ser::Error::KeyNotString,
}
}
}
impl serde::ser::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Error::new(msg)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::error::Error for Error {}
/// 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
/// 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.
///
/// To serialize TOML values, instead of documents, see [`ValueSerializer`].
#[cfg(feature = "display")]
pub struct Serializer<'d> {
dst: &'d mut String,
settings: crate::fmt::DocumentFormatter,
}
#[cfg(feature = "display")]
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: &'d mut String) -> Self {
Self {
dst,
settings: Default::default(),
}
}
/// Apply a default "pretty" policy to the document
///
/// For greater customization, instead serialize to a
/// [`toml_edit::DocumentMut`](https://docs.rs/toml_edit/latest/toml_edit/struct.DocumentMut.html).
pub fn pretty(dst: &'d mut String) -> Self {
let mut ser = Serializer::new(dst);
ser.settings.multiline_array = true;
ser
}
}
#[cfg(feature = "display")]
impl<'d> serde::ser::Serializer for Serializer<'d> {
type Ok = ();
type Error = Error;
type SerializeSeq = array::SerializeDocumentArray<'d>;
type SerializeTuple = array::SerializeDocumentArray<'d>;
type SerializeTupleStruct = array::SerializeDocumentArray<'d>;
type SerializeTupleVariant = array::SerializeDocumentTupleVariant<'d>;
type SerializeMap = map::SerializeDocumentTable<'d>;
type SerializeStruct = map::SerializeDocumentTable<'d>;
type SerializeStructVariant = map::SerializeDocumentStructVariant<'d>;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_bool(v),
)
}
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_i8(v),
)
}
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_i16(v),
)
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_i32(v),
)
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_i64(v),
)
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_u8(v),
)
}
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_u16(v),
)
}
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_u32(v),
)
}
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_u64(v),
)
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_f32(v),
)
}
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_f64(v),
)
}
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_char(v),
)
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_str(v),
)
}
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_bytes(v),
)
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_none(),
)
}
fn serialize_some<T>(self, v: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_some(v),
)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_unit(),
)
}
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
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,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_unit_variant(
name,
variant_index,
variant,
),
)
}
fn serialize_newtype_struct<T>(self, name: &'static str, v: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
write_document(
self.dst,
self.settings,
toml_edit::ser::ValueSerializer::new().serialize_newtype_struct(name, v),
)
}
fn serialize_newtype_variant<T>(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
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<usize>) -> Result<Self::SerializeSeq, Self::Error> {
let ser = toml_edit::ser::ValueSerializer::new()
.serialize_seq(len)
.map_err(Error::wrap)?;
let ser = array::SerializeDocumentArray::new(self, ser);
Ok(ser)
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
let ser = toml_edit::ser::ValueSerializer::new()
.serialize_tuple_variant(name, variant_index, variant, len)
.map_err(Error::wrap)?;
let ser = array::SerializeDocumentTupleVariant::new(self, ser);
Ok(ser)
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
let ser = toml_edit::ser::ValueSerializer::new()
.serialize_map(len)
.map_err(Error::wrap)?;
let ser = map::SerializeDocumentTable::new(self, ser);
Ok(ser)
}
fn serialize_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
self.serialize_map(Some(len))
}
fn serialize_struct_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
let ser = toml_edit::ser::ValueSerializer::new()
.serialize_struct_variant(name, variant_index, variant, len)
.map_err(Error::wrap)?;
let ser = map::SerializeDocumentStructVariant::new(self, ser);
Ok(ser)
}
}
#[cfg(feature = "display")]
pub(crate) fn write_document(
dst: &mut String,
mut settings: crate::fmt::DocumentFormatter,
value: Result<toml_edit::Value, crate::edit::ser::Error>,
) -> Result<(), Error> {
use std::fmt::Write;
use toml_edit::visit_mut::VisitMut as _;
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));
}
};
settings.visit_table_mut(&mut table);
let doc: toml_edit::DocumentMut = table.into();
write!(dst, "{doc}").unwrap();
Ok(())
}
+101
View File
@@ -0,0 +1,101 @@
use super::write_value;
use super::{Error, ValueSerializer};
type InnerSerializeValueSeq = <toml_edit::ser::ValueSerializer as serde::Serializer>::SerializeSeq;
#[doc(hidden)]
pub struct SerializeValueArray<'d> {
inner: InnerSerializeValueSeq,
dst: &'d mut String,
}
impl<'d> SerializeValueArray<'d> {
pub(crate) fn new(ser: ValueSerializer<'d>, inner: InnerSerializeValueSeq) -> Self {
Self {
inner,
dst: ser.dst,
}
}
}
impl serde::ser::SerializeSeq for SerializeValueArray<'_> {
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_element(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_value(self.dst, self.inner.end())
}
}
impl serde::ser::SerializeTuple for SerializeValueArray<'_> {
type Ok = ();
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_element(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_value(self.dst, self.inner.end())
}
}
impl serde::ser::SerializeTupleStruct for SerializeValueArray<'_> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_field(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_value(self.dst, self.inner.end())
}
}
type InnerSerializeValueTupleVariant =
<toml_edit::ser::ValueSerializer as serde::Serializer>::SerializeTupleVariant;
#[doc(hidden)]
pub struct SerializeValueTupleVariant<'d> {
inner: InnerSerializeValueTupleVariant,
dst: &'d mut String,
}
impl<'d> SerializeValueTupleVariant<'d> {
pub(crate) fn new(ser: ValueSerializer<'d>, inner: InnerSerializeValueTupleVariant) -> Self {
Self {
inner,
dst: ser.dst,
}
}
}
impl serde::ser::SerializeTupleVariant for SerializeValueTupleVariant<'_> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_field(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_value(self.dst, self.inner.end())
}
}
+95
View File
@@ -0,0 +1,95 @@
use super::write_value;
use super::{Error, ValueSerializer};
type InnerSerializeValueTable =
<toml_edit::ser::ValueSerializer as serde::Serializer>::SerializeMap;
#[doc(hidden)]
pub struct SerializeValueTable<'d> {
inner: InnerSerializeValueTable,
dst: &'d mut String,
}
impl<'d> SerializeValueTable<'d> {
pub(crate) fn new(ser: ValueSerializer<'d>, inner: InnerSerializeValueTable) -> Self {
Self {
inner,
dst: ser.dst,
}
}
}
impl serde::ser::SerializeMap for SerializeValueTable<'_> {
type Ok = ();
type Error = Error;
fn serialize_key<T>(&mut self, input: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_key(input).map_err(Error::wrap)
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_value(value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_value(self.dst, self.inner.end())
}
}
impl serde::ser::SerializeStruct for SerializeValueTable<'_> {
type Ok = ();
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_field(key, value).map_err(Error::wrap)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
write_value(self.dst, self.inner.end())
}
}
type InnerSerializeValueStructVariant =
<toml_edit::ser::ValueSerializer as serde::Serializer>::SerializeStructVariant;
#[doc(hidden)]
pub struct SerializeValueStructVariant<'d> {
inner: InnerSerializeValueStructVariant,
dst: &'d mut String,
}
impl<'d> SerializeValueStructVariant<'d> {
pub(crate) fn new(ser: ValueSerializer<'d>, inner: InnerSerializeValueStructVariant) -> Self {
Self {
inner,
dst: ser.dst,
}
}
}
impl serde::ser::SerializeStructVariant for SerializeValueStructVariant<'_> {
type Ok = ();
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.inner.serialize_field(key, value).map_err(Error::wrap)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
write_value(self.dst, self.inner.end())
}
}
+328
View File
@@ -0,0 +1,328 @@
mod array;
mod map;
use super::Error;
/// Serialization for TOML [values][crate::Value].
///
/// 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.
///
/// # Examples
///
/// ```
/// use serde::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 mut value = String::new();
/// serde::Serialize::serialize(
/// &config,
/// toml::ser::ValueSerializer::new(&mut value)
/// ).unwrap();
/// println!("{}", value)
/// ```
#[cfg(feature = "display")]
pub struct ValueSerializer<'d> {
dst: &'d mut String,
}
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 }
}
}
impl<'d> serde::ser::Serializer for ValueSerializer<'d> {
type Ok = ();
type Error = Error;
type SerializeSeq = array::SerializeValueArray<'d>;
type SerializeTuple = array::SerializeValueArray<'d>;
type SerializeTupleStruct = array::SerializeValueArray<'d>;
type SerializeTupleVariant = array::SerializeValueTupleVariant<'d>;
type SerializeMap = map::SerializeValueTable<'d>;
type SerializeStruct = map::SerializeValueTable<'d>;
type SerializeStructVariant = map::SerializeValueStructVariant<'d>;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_bool(v),
)
}
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_i8(v),
)
}
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_i16(v),
)
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_i32(v),
)
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_i64(v),
)
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_u8(v),
)
}
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_u16(v),
)
}
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_u32(v),
)
}
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_u64(v),
)
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_f32(v),
)
}
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_f64(v),
)
}
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_char(v),
)
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_str(v),
)
}
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_bytes(v),
)
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_none(),
)
}
fn serialize_some<T>(self, v: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_some(v),
)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_unit(),
)
}
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
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<Self::Ok, Self::Error> {
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_unit_variant(
name,
variant_index,
variant,
),
)
}
fn serialize_newtype_struct<T>(self, name: &'static str, v: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_newtype_struct(name, v),
)
}
fn serialize_newtype_variant<T>(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
write_value(
self.dst,
toml_edit::ser::ValueSerializer::new().serialize_newtype_variant(
name,
variant_index,
variant,
value,
),
)
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
let ser = toml_edit::ser::ValueSerializer::new()
.serialize_seq(len)
.map_err(Error::wrap)?;
let ser = array::SerializeValueArray::new(self, ser);
Ok(ser)
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
let ser = toml_edit::ser::ValueSerializer::new()
.serialize_tuple_variant(name, variant_index, variant, len)
.map_err(Error::wrap)?;
let ser = array::SerializeValueTupleVariant::new(self, ser);
Ok(ser)
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
let ser = toml_edit::ser::ValueSerializer::new()
.serialize_map(len)
.map_err(Error::wrap)?;
let ser = map::SerializeValueTable::new(self, ser);
Ok(ser)
}
fn serialize_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
self.serialize_map(Some(len))
}
fn serialize_struct_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
let ser = toml_edit::ser::ValueSerializer::new()
.serialize_struct_variant(name, variant_index, variant, len)
.map_err(Error::wrap)?;
let ser = map::SerializeValueStructVariant::new(self, ser);
Ok(ser)
}
}
pub(crate) fn write_value(
dst: &mut String,
value: Result<toml_edit::Value, crate::edit::ser::Error>,
) -> Result<(), Error> {
use std::fmt::Write;
let value = value.map_err(Error::wrap)?;
write!(dst, "{value}").unwrap();
Ok(())
}
+115
View File
@@ -0,0 +1,115 @@
use serde::de;
use serde::ser;
use crate::map::Map;
use crate::Value;
/// Type representing a TOML table, payload of the `Value::Table` variant.
///
/// By default it entries are stored in
/// [lexicographic order](https://doc.rust-lang.org/std/primitive.str.html#impl-Ord-for-str)
/// of the keys. Enable the `preserve_order` feature to store entries in the order they appear in
/// the source file.
pub type Table = Map<String, Value>;
impl Table {
/// Convert a `T` into `toml::Table`.
///
/// This conversion can fail if `T`'s implementation of `Serialize` decides to
/// fail, or if `T` contains a map with non-string keys.
pub fn try_from<T>(value: T) -> Result<Self, crate::ser::Error>
where
T: ser::Serialize,
{
value.serialize(crate::value::TableSerializer)
}
/// Interpret a `toml::Table` as an instance of type `T`.
///
/// This conversion can fail if the structure of the `Table` does not match the structure
/// expected by `T`, for example if `T` is a bool which can't be mapped to a `Table`. It can
/// also fail if the structure is correct but `T`'s implementation of `Deserialize` decides
/// that something is wrong with the data, for example required struct fields are missing from
/// the TOML map or some number is too big to fit in the expected primitive type.
pub fn try_into<'de, T>(self) -> Result<T, crate::de::Error>
where
T: de::Deserialize<'de>,
{
de::Deserialize::deserialize(self)
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for Table {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
crate::ser::to_string(self)
.expect("Unable to represent value as string")
.fmt(f)
}
}
#[cfg(feature = "parse")]
impl std::str::FromStr for Table {
type Err = crate::de::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
crate::from_str(s)
}
}
impl<'de> de::Deserializer<'de> for Table {
type Error = crate::de::Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, crate::de::Error>
where
V: de::Visitor<'de>,
{
Value::Table(self).deserialize_any(visitor)
}
#[inline]
fn deserialize_enum<V>(
self,
name: &'static str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, crate::de::Error>
where
V: de::Visitor<'de>,
{
Value::Table(self).deserialize_enum(name, variants, visitor)
}
// `None` is interpreted as a missing field so be sure to implement `Some`
// as a present field.
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, crate::de::Error>
where
V: de::Visitor<'de>,
{
Value::Table(self).deserialize_option(visitor)
}
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, crate::de::Error>
where
V: de::Visitor<'de>,
{
Value::Table(self).deserialize_newtype_struct(name, visitor)
}
serde::forward_to_deserialize_any! {
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit seq
bytes byte_buf map unit_struct tuple_struct struct
tuple ignored_any identifier
}
}
impl de::IntoDeserializer<'_, crate::de::Error> for Table {
type Deserializer = Self;
fn into_deserializer(self) -> Self {
self
}
}
+1495
View File
File diff suppressed because it is too large. Load diff