From bf5d33f3ba991afe398d58fb4fed38ec72d6f4c7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 13 Aug 2019 16:31:41 +0200 Subject: Rework API to resemble that of serde_json --- serde_dhall/src/lib.rs | 200 ++++++++++++++++++++++++-------------------- serde_dhall/src/serde.rs | 3 +- serde_dhall/tests/traits.rs | 8 +- 3 files changed, 115 insertions(+), 96 deletions(-) (limited to 'serde_dhall') diff --git a/serde_dhall/src/lib.rs b/serde_dhall/src/lib.rs index 8e1af82..19aabec 100644 --- a/serde_dhall/src/lib.rs +++ b/serde_dhall/src/lib.rs @@ -5,28 +5,27 @@ //! //! You can think of Dhall as: JSON + types + imports + functions //! -//! For a description of the dhall language, examples, tutorials, and more, see the [language +//! For a description of the Dhall language, examples, tutorials, and more, see the [language //! website][dhall]. //! -//! This crate provides support for consuming dhall files the same way you would consume JSON or -//! YAML. It uses the [Serde][serde] serialization library to provide drop-in support for dhall +//! This crate provides support for consuming Dhall files the same way you would consume JSON or +//! YAML. It uses the [Serde][serde] serialization library to provide drop-in support for Dhall //! for any datatype that supports serde (and that's a lot of them !). //! -//! This library is limited to deserializing (reading) dhall values; serializing (writing) -//! values to dhall is not supported for now. +//! This library is limited to deserializing (reading) Dhall values; serializing (writing) +//! values to Dhall is not supported for now. //! //! # Examples //! //! ### Custom datatype //! //! If you have a custom datatype for which you derived [serde::Deserialize], chances are -//! you will be able to derive [StaticType][de::StaticType] for it as well. -//! This gives you access to a dhall representation of your datatype that can be outputted -//! to users, and allows easy type-safe deserializing. +//! you will be able to derive [StaticType] for it as well. +//! This allows easy type-safe deserializing. //! //! ```edition2018 //! use serde::Deserialize; -//! use serde_dhall::de::StaticType; +//! use serde_dhall::{de::Error, StaticType}; //! //! #[derive(Debug, Deserialize, StaticType)] //! struct Point { @@ -34,71 +33,78 @@ //! y: u64, //! } //! -//! fn main() { -//! // Some dhall data +//! fn main() -> Result<(), Error> { +//! // Some Dhall data //! let data = "{ x = 1, y = 1 + 1 }"; //! -//! // Convert the dhall string to a Point. -//! let point: Point = -//! serde_dhall::de::from_str_auto_type(&data) -//! .expect("An error ocurred !"); +//! // Convert the Dhall string to a Point. +//! let point: Point = serde_dhall::from_str_auto_type(data)?; +//! assert_eq!(point.x, 1); +//! assert_eq!(point.y, 2); //! -//! // Prints "point = Point { x: 1, y: 2 }" -//! println!("point = {:?}", point); +//! // Invalid data fails the type validation +//! let invalid_data = "{ x = 1, z = 0.3 }"; +//! assert!(serde_dhall::from_str_auto_type::(invalid_data).is_err()); +//! +//! Ok(()) //! } //! ``` //! //! ### Loosely typed //! //! If you used to consume JSON or YAML in a loosely typed way, you can continue to do so -//! with dhall. You only need to replace [serde_json::from_str] or [serde_yaml::from_str] -//! with [serde_dhall::de::from_str][de::from_str]. -//! More generally, if the [StaticType][de::StaticType] derive doesn't suit your -//! needs, you can still deserialize any valid dhall file that serde can handle. +//! with Dhall. You only need to replace [serde_json::from_str] or [serde_yaml::from_str] +//! with [serde_dhall::from_str][from_str]. +//! More generally, if the [StaticType] derive doesn't suit your +//! needs, you can still deserialize any valid Dhall file that serde can handle. //! //! [serde_json::from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html //! [serde_yaml::from_str]: https://docs.serde.rs/serde_yaml/fn.from_str.html //! //! ```edition2018 +//! # fn main() -> serde_dhall::de::Result<()> { //! use std::collections::BTreeMap; //! -//! let mut map = BTreeMap::new(); -//! map.insert("x".to_string(), 1); -//! map.insert("y".to_string(), 2); -//! -//! // Some dhall data +//! // Some Dhall data //! let data = "{ x = 1, y = 1 + 1 } : { x: Natural, y: Natural }"; //! //! // Deserialize it to a Rust type. -//! let deserialized_map: BTreeMap = -//! serde_dhall::de::from_str(&data, None) -//! .expect("Failed reading the data !"); -//! assert_eq!(map, deserialized_map); +//! let deserialized_map: BTreeMap = serde_dhall::from_str(data)?; +//! +//! let mut expected_map = BTreeMap::new(); +//! expected_map.insert("x".to_string(), 1); +//! expected_map.insert("y".to_string(), 2); +//! +//! assert_eq!(deserialized_map, expected_map); +//! # Ok(()) +//! # } //! ``` //! -//! You can of course specify a dhall type that the input should match. +//! You can alternatively specify a Dhall type that the input should match. //! //! ```edition2018 +//! # fn main() -> serde_dhall::de::Result<()> { //! use std::collections::BTreeMap; //! -//! let mut map = BTreeMap::new(); -//! map.insert("x".to_string(), 1); -//! map.insert("y".to_string(), 2); +//! // Parse a Dhall type +//! let point_type_str = "{ x: Natural, y: Natural }"; +//! let point_type = serde_dhall::from_str(point_type_str)?; //! -//! // Some dhall data +//! // Some Dhall data //! let point_data = "{ x = 1, y = 1 + 1 }"; -//! let point_type_data = "{ x: Natural, y: Natural }"; -//! -//! // Construct a type -//! let point_type = -//! serde_dhall::de::from_str(point_type_data, None) -//! .expect("Could not parse the Point type"); //! -//! // Deserialize it to a Rust type. +//! // Deserialize the data to a Rust type. This ensures that +//! // the data matches the point type. //! let deserialized_map: BTreeMap = -//! serde_dhall::de::from_str(&point_data, Some(&point_type)) -//! .expect("Failed reading the data !"); -//! assert_eq!(map, deserialized_map); +//! serde_dhall::from_str_check_type(point_data, &point_type)?; +//! +//! let mut expected_map = BTreeMap::new(); +//! expected_map.insert("x".to_string(), 1); +//! expected_map.insert("y".to_string(), 2); +//! +//! assert_eq!(deserialized_map, expected_map); +//! # Ok(()) +//! # } //! ``` //! //! [dhall]: https://dhall-lang.org/ @@ -108,9 +114,16 @@ mod serde; mod static_type; +#[doc(inline)] +pub use de::{from_str, from_str_auto_type, from_str_check_type}; +#[doc(hidden)] +pub use dhall_proc_macros::StaticType; +pub use static_type::StaticType; +#[doc(inline)] pub use value::Value; -mod value { +// A Dhall value. +pub mod value { use dhall::core::thunk::{Thunk, TypeThunk}; use dhall::core::value::Value as DhallValue; use dhall::phase::{NormalizedSubExpr, Parsed, Type, Typed}; @@ -197,47 +210,44 @@ mod value { } } -mod error { - use dhall::error::Error as DhallError; +/// Deserialize Dhall data to a Rust data structure. +pub mod de { + use super::StaticType; + use super::Value; + pub use error::{Error, Result}; - pub type Result = std::result::Result; + mod error { + use dhall::error::Error as DhallError; - #[derive(Debug)] - #[non_exhaustive] - pub enum Error { - Dhall(DhallError), - Deserialize(String), - } + pub type Result = std::result::Result; - impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Error::Dhall(err) => write!(f, "{}", err), - Error::Deserialize(err) => write!(f, "{}", err), + #[derive(Debug)] + #[non_exhaustive] + pub enum Error { + Dhall(DhallError), + Deserialize(String), + } + + impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Error::Dhall(err) => write!(f, "{}", err), + Error::Deserialize(err) => write!(f, "{}", err), + } } } - } - impl std::error::Error for Error {} + impl std::error::Error for Error {} - impl serde::de::Error for Error { - fn custom(msg: T) -> Self - where - T: std::fmt::Display, - { - Error::Deserialize(msg.to_string()) + impl serde::de::Error for Error { + fn custom(msg: T) -> Self + where + T: std::fmt::Display, + { + Error::Deserialize(msg.to_string()) + } } } -} - -/// Deserialization of Dhall expressions into Rust -pub mod de { - #[doc(hidden)] - pub use dhall_proc_macros::StaticType; - - pub use super::error::{Error, Result}; - pub use super::static_type::StaticType; - pub use super::Value; /// A data structure that can be deserialized from a Dhall expression /// @@ -247,37 +257,45 @@ pub mod de { /// This trait cannot be implemented manually. // TODO: seal trait pub trait Deserialize: Sized { - /// See [dhall::de::from_str][crate::de::from_str] + /// See [serde_dhall::from_str][crate::from_str] fn from_dhall(v: &Value) -> Result; } - /// Deserialize an instance of type T from a string of Dhall text. + /// Deserialize an instance of type `T` from a string of Dhall text. /// /// This will recursively resolve all imports in the expression, and /// typecheck it before deserialization. Relative imports will be resolved relative to the /// provided file. More control over this process is not yet available /// but will be in a coming version of this crate. + pub fn from_str(s: &str) -> Result + where + T: Deserialize, + { + T::from_dhall(&Value::from_str(s, None)?) + } + + /// Deserialize an instance of type `T` from a string of Dhall text, + /// additionally checking that it matches the supplied type. /// - /// If a type is provided, this additionally checks that the provided - /// expression has that type. - pub fn from_str(s: &str, ty: Option<&Value>) -> Result + /// Like [from_str], but this additionally checks that + /// the type of the provided expression matches the supplied type. + pub fn from_str_check_type(s: &str, ty: &Value) -> Result where T: Deserialize, { - T::from_dhall(&Value::from_str(s, ty)?) + T::from_dhall(&Value::from_str(s, Some(ty))?) } - /// Deserialize an instance of type T from a string of Dhall text, - /// additionally checking that it matches the type of T. + /// Deserialize an instance of type `T` from a string of Dhall text, + /// additionally checking that it matches the type of `T`. /// - /// This will recursively resolve all imports in the expression, and - /// typecheck it before deserialization. Relative imports will be resolved relative to the - /// provided file. More control over this process is not yet available - /// but will be in a coming version of this crate. + /// Like [from_str], but this additionally checks that + /// the type of the provided expression matches the output type `T`. The [StaticType] trait + /// captures Rust types that are valid Dhall types. pub fn from_str_auto_type(s: &str) -> Result where T: Deserialize + StaticType, { - from_str(s, Some(&::static_type())) + from_str_check_type(s, &::static_type()) } } diff --git a/serde_dhall/src/serde.rs b/serde_dhall/src/serde.rs index 1f639e6..10d5a17 100644 --- a/serde_dhall/src/serde.rs +++ b/serde_dhall/src/serde.rs @@ -1,4 +1,5 @@ -use crate::de::{Deserialize, Error, Result, Value}; +use crate::de::{Deserialize, Error, Result}; +use crate::Value; use dhall_syntax::{ExprF, SubExpr, X}; use std::borrow::Cow; diff --git a/serde_dhall/tests/traits.rs b/serde_dhall/tests/traits.rs index 40ac1d7..55be63b 100644 --- a/serde_dhall/tests/traits.rs +++ b/serde_dhall/tests/traits.rs @@ -1,10 +1,10 @@ #![feature(proc_macro_hygiene)] -use serde_dhall::de::{from_str, StaticType, Value}; +use serde_dhall::{from_str, StaticType, Value}; #[test] fn test_static_type() { fn parse(s: &str) -> Value { - from_str(s, None).unwrap() + from_str(s).unwrap() } assert_eq!(bool::static_type(), parse("Bool")); @@ -15,14 +15,14 @@ fn test_static_type() { parse("{ _1: Bool, _2: List Text }") ); - #[derive(serde_dhall::de::StaticType)] + #[derive(serde_dhall::StaticType)] #[allow(dead_code)] struct A { field1: bool, field2: Option, } assert_eq!( - ::static_type(), + ::static_type(), parse("{ field1: Bool, field2: Optional Bool }") ); -- cgit v1.2.3