Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(toml)!: Polish API in prep for release #487

Merged
merged 13 commits into from
Jan 23, 2023
Merged
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