Skip to content

Commit

Permalink
Merge pull request #487 from epage/fix
Browse files Browse the repository at this point in the history
fix(toml)!: Polish API in prep for release
  • Loading branch information
epage authored Jan 23, 2023
2 parents 4a8f991 + 2069c72 commit b3e0389
Show file tree
Hide file tree
Showing 11 changed files with 232 additions and 153 deletions.
6 changes: 3 additions & 3 deletions crates/toml/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ harness = false

[[example]]
name = "decode"
required-features = ["parse", "derive"]
required-features = ["parse", "display"]

[[example]]
name = "enum_external"
required-features = ["parse", "derive"]
required-features = ["parse", "display"]

[[example]]
name = "toml2json"
required-features = ["parse", "derive"]
required-features = ["parse", "display"]
30 changes: 29 additions & 1 deletion crates/toml/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
/// 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
///
/// ```
Expand Down Expand Up @@ -98,6 +100,8 @@ impl std::fmt::Display for Error {
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,
Expand Down Expand Up @@ -200,7 +204,31 @@ impl<'de, 'a> serde::Deserializer<'de> for Deserializer<'a> {
}
}

/// Deserialization TOML value
/// 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,
Expand Down
29 changes: 15 additions & 14 deletions crates/toml/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
//!
//! A TOML document is represented with the [`Table`] type which maps `String` to the [`Value`] enum:
//!
//! ```rust,ignore
//! ```rust
//! # use toml::value::{Datetime, Array, Table};
//! pub enum Value {
//! String(String),
//! Integer(i64),
Expand Down Expand Up @@ -67,7 +68,7 @@
//!
//! 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 [`Value`] type itself to act as
//! use the [`Deserializer`], [`Serializer`], or [`Table`] type itself to act as
//! a deserializer/serializer for arbitrary types.
//!
//! An example of deserializing with TOML is:
Expand Down Expand Up @@ -149,29 +150,29 @@

pub mod map;
pub mod value;
#[doc(no_inline)]
pub use crate::value::Table;
#[doc(no_inline)]
pub use crate::value::Value;

pub mod ser;
#[doc(no_inline)]
#[cfg(feature = "display")]
pub use crate::ser::{to_string, to_string_pretty, Serializer};
pub mod de;
#[doc(no_inline)]
#[cfg(feature = "parse")]
pub use crate::de::{from_str, Deserializer, ValueDeserializer};
pub mod ser;

#[doc(hidden)]
pub mod macros;

mod edit;
#[cfg(feature = "display")]
mod fmt;
mod table;

mod edit;
#[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)]
Expand Down
9 changes: 5 additions & 4 deletions crates/toml/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ pub use serde::de::{Deserialize, IntoDeserializer};

use crate::value::{Array, Table, Value};

/// Construct a [`toml::Value`] from TOML syntax.
///
/// [`toml::Value`]: value/enum.Value.html
/// Construct a [`Table`] from TOML syntax.
///
/// ```rust
/// let cargo_toml = toml::toml! {
Expand Down Expand Up @@ -32,7 +30,10 @@ macro_rules! toml {
let table = $crate::value::Table::new();
let mut root = $crate::Value::Table(table);
$crate::toml_internal!(@toplevel root [] $($toml)+);
root
match root {
$crate::Value::Table(table) => table,
_ => unreachable!(),
}
}};
}

Expand Down
6 changes: 3 additions & 3 deletions crates/toml/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A map of String to toml::Value.
//! 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 [`LinkedHashMap`] instead.
//! feature of toml-rs to use [`IndexMap`] instead.
//!
//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
//! [`LinkedHashMap`]: https://docs.rs/linked-hash-map/*/linked_hash_map/struct.LinkedHashMap.html
//! [`IndexMap`]: https://docs.rs/indexmap

use crate::value::Value;
use serde::{de, ser};
Expand Down
43 changes: 42 additions & 1 deletion crates/toml/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
/// 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
///
/// ```
Expand Down Expand Up @@ -55,6 +57,8 @@ where
///
/// 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`].
#[cfg(feature = "display")]
pub fn to_string_pretty<T: ?Sized>(value: &T) -> Result<String, Error>
where
Expand Down Expand Up @@ -129,6 +133,8 @@ impl std::error::Error for Error {}
///
/// 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`].
#[non_exhaustive]
#[cfg(feature = "display")]
pub struct Serializer<'d> {
Expand Down Expand Up @@ -472,7 +478,7 @@ impl<'d> serde::ser::Serializer for Serializer<'d> {
}
}

/// Serialization for TOML values.
/// 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
Expand All @@ -481,6 +487,41 @@ impl<'d> serde::ser::Serializer for Serializer<'d> {
///
/// 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)
/// ```
#[non_exhaustive]
#[cfg(feature = "display")]
pub struct ValueSerializer<'d> {
Expand Down
114 changes: 114 additions & 0 deletions crates/toml/src/table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use std::fmt;

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 is backed by a BTreeMap, enable the `preserve_order` feature
/// to use a LinkedHashMap instead.
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 fmt::Display for Table {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> 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> de::IntoDeserializer<'de, crate::de::Error> for Table {
type Deserializer = Self;

fn into_deserializer(self) -> Self {
self
}
}
Loading

0 comments on commit b3e0389

Please sign in to comment.