diff options
-rw-r--r-- | serde_dhall/src/deserialize.rs | 34 | ||||
-rw-r--r-- | serde_dhall/src/lib.rs | 102 | ||||
-rw-r--r-- | serde_dhall/src/shortcuts.rs | 19 | ||||
-rw-r--r-- | serde_dhall/src/static_type.rs | 47 |
4 files changed, 128 insertions, 74 deletions
diff --git a/serde_dhall/src/deserialize.rs b/serde_dhall/src/deserialize.rs index 7bb0051..b9b711c 100644 --- a/serde_dhall/src/deserialize.rs +++ b/serde_dhall/src/deserialize.rs @@ -10,26 +10,44 @@ use crate::{Error, ErrorKind, Result, Value}; pub trait Sealed {} -/// A data structure that can be deserialized from a Dhall expression +/// A data structure that can be deserialized from a Dhall expression. /// -/// This is automatically implemented for any type that [serde][serde] -/// can deserialize. +/// This is automatically implemented for any type that [serde] can deserialize. +/// In fact, this trait cannot be implemented manually. To implement it for your type, +/// use serde's derive mechanism. /// -/// This trait cannot be implemented manually. +/// # Example /// -/// TODO +/// ```rust +/// # fn main() -> serde_dhall::Result<()> { +/// use serde::Deserialize; +/// +/// // Use serde's derive +/// #[derive(Deserialize)] +/// struct Point { +/// x: u64, +/// y: u64, +/// } +/// +/// // Convert a Dhall string to a Point. +/// let point: Point = serde_dhall::from_str("{ x = 1, y = 1 + 1 }")?; +/// # Ok(()) +/// # } +/// ``` +/// +/// [serde]: https://serde.rs pub trait Deserialize: Sealed + Sized { #[doc(hidden)] fn from_dhall(v: &Value) -> Result<Self>; } -impl<'a, T> Sealed for T where T: serde::Deserialize<'a> {} +impl<T> Sealed for T where T: serde::de::DeserializeOwned {} struct Deserializer<'a>(Cow<'a, SimpleValue>); -impl<'a, T> Deserialize for T +impl<T> Deserialize for T where - T: serde::Deserialize<'a>, + T: serde::de::DeserializeOwned, { fn from_dhall(v: &Value) -> Result<Self> { let sval = v.to_simple_value().ok_or_else(|| { diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index fe1f4b2..71274b6 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -1,5 +1,5 @@ #![doc(html_root_url = "https://docs.rs/serde_dhall/0.4.0")] -// TODO #![warn(missing_docs)] #![warn(missing_doc_code_examples)] +#![warn(missing_docs, missing_doc_code_examples)] //! [Dhall][dhall] is a programmable configuration language that provides a non-repetitive //! alternative to JSON and YAML. //! @@ -47,7 +47,7 @@ //! # fn main() -> serde_dhall::Result<()> { //! use serde::Deserialize; //! -//! #[derive(Debug, Deserialize)] +//! #[derive(Deserialize)] //! struct Point { //! x: u64, //! y: u64, @@ -65,28 +65,6 @@ //! # } //! ``` //! -//! # Type correspondence -//! -//! The following Dhall types correspond to the following Rust types: -//! -//! Dhall | Rust -//! -------|------ -//! `Bool` | `bool` -//! `Natural` | `u64`, `u32`, ... -//! `Integer` | `i64`, `i32`, ... -//! `Double` | `f64`, `f32`, ... -//! `Text` | `String` -//! `List T` | `Vec<T>` -//! `Optional T` | `Option<T>` -//! `{ x: T, y: U }` | structs -//! `{ _1: T, _2: U }` | `(T, U)`, structs -//! `{ x: T, y: T }` | `HashMap<String, T>`, structs -//! `< x: T \| y: U >` | enums -//! `T -> U` | unsupported -//! `Prelude.JSON.Type` | unsupported -//! `Prelude.Map.Type T U` | unsupported -//! -//! //! # Replacing `serde_json` or `serde_yaml` //! //! If you used to consume JSON or YAML, you only need to replace [`serde_json::from_str`] or @@ -99,16 +77,45 @@ //! # Additional Dhall typechecking //! //! When deserializing, normal type checking is done to ensure that the returned value is a valid -//! Dhall value, and that it can be deserialized into the required Rust type. However types are -//! first-class in Dhall, and this library allows you to additionally check that some input data +//! Dhall value. However types are +//! first-class in Dhall, and this library allows you to additionally check that the input data //! matches a given Dhall type. That way, a type error will be caught on the Dhall side, and have //! pretty and explicit errors that point to the source file. //! -//! There are two ways to typecheck a Dhall value: you can provide the type as Dhall text or you -//! can let Rust infer it for you. +//! There are two ways to typecheck a Dhall value in this way: you can provide the type manually or +//! you can let Rust infer it for you. +//! +//! To let Rust infer the appropriate Dhall type, use the [StaticType](trait.StaticType.html) +//! trait. +//! +//! ```rust +//! # fn main() -> serde_dhall::Result<()> { +//! use serde::Deserialize; +//! use serde_dhall::StaticType; +//! +//! #[derive(Deserialize, StaticType)] +//! struct Point { +//! x: u64, +//! y: u64, +//! } +//! +//! // Some Dhall data +//! let data = "{ x = 1, y = 1 + 1 }"; +//! +//! // Convert the Dhall string to a Point. +//! let point: Point = serde_dhall::from_str_static_type(data)?; +//! assert_eq!(point.x, 1); +//! assert_eq!(point.y, 2); +//! +//! // Invalid data fails the type validation +//! let invalid_data = "{ x = 1, z = 0.3 }"; +//! assert!(serde_dhall::from_str_static_type::<Point>(invalid_data).is_err()); +//! # Ok(()) +//! # } +//! ``` //! -//! To provide a type written in Dhall, first parse it into a [`SimpleType`](enum.SimpleType.html), then -//! pass it to [`from_str_manual_type`](fn.from_str_manual_type.html). +//! To provide a type manually, you need a [`SimpleType`](enum.SimpleType.html) value. You +//! can parse it from some Dhall text like you would parse any other value. //! //! ```rust //! # fn main() -> serde_dhall::Result<()> { @@ -136,41 +143,20 @@ //! # } //! ``` //! -//! You can also let Rust infer the appropriate Dhall type, using the -//! [StaticType](trait.StaticType.html) trait. -//! -//! ```rust -//! # fn main() -> serde_dhall::Result<()> { -//! use serde::Deserialize; -//! use serde_dhall::StaticType; -//! -//! #[derive(Debug, Deserialize, StaticType)] -//! struct Point { -//! x: u64, -//! y: u64, -//! } +//! # Controlling deserialization //! -//! // Some Dhall data -//! let data = "{ x = 1, y = 1 + 1 }"; -//! -//! // Convert the Dhall string to a Point. -//! let point: Point = serde_dhall::from_str_static_type(data)?; -//! assert_eq!(point.x, 1); -//! assert_eq!(point.y, 2); -//! -//! // Invalid data fails the type validation -//! let invalid_data = "{ x = 1, z = 0.3 }"; -//! assert!(serde_dhall::from_str_static_type::<Point>(invalid_data).is_err()); -//! # Ok(()) -//! # } -//! ``` +//! If you need more control over the process of reading Dhall values, e.g. disabling +//! imports, see the [`options`] module. //! +//! [`options`]: options/index.html //! [dhall]: https://dhall-lang.org/ //! [serde]: https://docs.serde.rs/serde/ //! [serde::Deserialize]: https://docs.serde.rs/serde/trait.Deserialize.html #[cfg(doctest)] -doc_comment::doctest!("../../README.md"); +mod test_readme { + doc_comment::doctest!("../../README.md"); +} /// Finer-grained control over deserializing Dhall values pub mod options; diff --git a/serde_dhall/src/shortcuts.rs b/serde_dhall/src/shortcuts.rs index 4aba9d1..cd31402 100644 --- a/serde_dhall/src/shortcuts.rs +++ b/serde_dhall/src/shortcuts.rs @@ -19,9 +19,12 @@ macro_rules! gen_doc { (@tck_info2, manual) => {" against the supplied type"}; (@tck_info2, static) => {" against the type of `T`"}; - (@tck_req, none) => {""}; - (@tck_req, manual) => {""}; - (@tck_req, static) => {"`T` must implement the [`StaticType`] trait.\n"}; + (@tck_req, $src:tt, none) => {""}; + (@tck_req, $src:tt, manual) => {""}; + (@tck_req, $src:tt, static) => { + concat!("`T` must implement the [`StaticType`] trait. Use [`from_", stringify!($src), + "_manual_type`] to provide a type manually.\n") + }; (@tck_comment, $src:tt, none) => { concat!("For additional type safety, prefer [`from_", stringify!($src), "_static_type`] @@ -37,7 +40,7 @@ macro_rules! gen_doc { ($src:tt, $ty:tt) => {concat!(" Deserialize an instance of type `T` from ", gen_doc!(@source_desc, $src), gen_doc!(@tck_info1, $ty),". -", gen_doc!(@tck_req, $ty), " +", gen_doc!(@tck_req, $src, $ty), " This will recursively resolve all imports in the expression, and typecheck it", gen_doc!(@tck_info2, $ty)," before deserialization. Relative imports will be resolved relative to the current directory. See [`options`] for more control over this process. @@ -66,7 +69,7 @@ macro_rules! gen_example { use serde::Deserialize; // We use serde's derive feature -#[derive(Debug, Deserialize)] +#[derive(Deserialize)] struct Point { x: u64, y: u64, @@ -109,7 +112,7 @@ assert_eq!(deserialized_map, expected_map); use serde::Deserialize; use serde_dhall::StaticType; -#[derive(Debug, Deserialize, StaticType)] +#[derive(Deserialize, StaticType)] struct Point { x: u64, y: u64, @@ -132,7 +135,7 @@ assert!(serde_dhall::from_str_static_type::<Point>(invalid_data).is_err()); use serde::Deserialize; // We use serde's derive feature -#[derive(Debug, Deserialize)] +#[derive(Deserialize)] struct Point { x: u64, y: u64, @@ -160,7 +163,7 @@ let deserialized_map: HashMap<String, usize> = use serde::Deserialize; use serde_dhall::StaticType; -#[derive(Debug, Deserialize, StaticType)] +#[derive(Deserialize, StaticType)] struct Point { x: u64, y: u64, diff --git a/serde_dhall/src/static_type.rs b/serde_dhall/src/static_type.rs index 19f1202..020dfce 100644 --- a/serde_dhall/src/static_type.rs +++ b/serde_dhall/src/static_type.rs @@ -29,7 +29,54 @@ use crate::SimpleType; /// # Ok(()) /// # } /// ``` +/// +/// # Type correspondence +/// +/// The following Dhall types correspond to the following Rust types: +/// +/// Dhall | Rust +/// -------|------ +/// `Bool` | `bool` +/// `Natural` | `u64`, `u32`, ... +/// `Integer` | `i64`, `i32`, ... +/// `Double` | `f64`, `f32`, ... +/// `Text` | `String` +/// `List T` | `Vec<T>` +/// `Optional T` | `Option<T>` +/// `{ x: T, y: U }` | structs +/// `{ _1: T, _2: U }` | `(T, U)`, structs +/// `{ x: T, y: T }` | `HashMap<String, T>`, structs +/// `< x: T \| y: U >` | enums +/// `T -> U` | unsupported +/// `Prelude.JSON.Type` | unsupported +/// `Prelude.Map.Type T U` | unsupported pub trait StaticType { + /// Return the Dhall type that represents this type. + /// + /// # Example + /// + /// ```rust + /// # fn main() -> serde_dhall::Result<()> { + /// use serde::Deserialize; + /// use serde_dhall::{SimpleType, StaticType}; + /// + /// // Using `derive(StaticType)` here would give it the type `{ _1: List Natural }`. + /// #[derive(Deserialize)] + /// #[serde(transparent)] + /// struct Foo(Vec<u64>); + /// + /// impl StaticType for Foo { + /// fn static_type() -> SimpleType { + /// SimpleType::List(Box::new(SimpleType::Natural)) + /// } + /// } + /// + /// let foo: Foo = serde_dhall::from_str_static_type("[ 1, 2 ]")?; + /// + /// assert_eq!(foo.0, vec![1, 2]); + /// # Ok(()) + /// # } + /// ``` fn static_type() -> SimpleType; } |