From c2e0eed41e94028b9dce232314285e4da1aa9b41 Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Thu, 16 May 2024 09:03:35 +0200 Subject: [PATCH] Add helper to deserialize into `Option` or `Result` --- src/de.rs | 4 +- src/de/helpers.rs | 141 +++++++++++++++++++++++++++++++++++++++++++++ tests/sum_types.rs | 72 ++++++++++++++++++++++- 3 files changed, 213 insertions(+), 4 deletions(-) diff --git a/src/de.rs b/src/de.rs index dfc177e..ab5497e 100644 --- a/src/de.rs +++ b/src/de.rs @@ -6,12 +6,12 @@ mod error; pub use error::Error; mod helpers; -pub use helpers::{As, Integer, Same}; +pub use helpers::{As, Integer, Option, Result, Same}; mod deserializer; #[doc(hidden)] -pub fn decode_value(value: Value) -> Result +pub fn decode_value(value: Value) -> std::result::Result where T: DeserializeOwned, { diff --git a/src/de/helpers.rs b/src/de/helpers.rs index 1499c88..37cc5fc 100644 --- a/src/de/helpers.rs +++ b/src/de/helpers.rs @@ -1,4 +1,5 @@ use num_bigint::BigInt; +use serde::{Deserialize, Serialize}; pub use serde_with::{As, Same}; @@ -71,3 +72,143 @@ pub use serde_with::{As, Same}; /// itf::from_value::>(json.clone()).unwrap(); /// ``` pub type Integer = serde_with::TryFromInto; + +/// Helper for `serde` to deserialize types isomorphic to [`std::option::Option`]. +/// +/// To be used in conjunction with [`As`]. +/// +/// ## Example +/// +/// ```rust +/// use serde::Deserialize; +/// use serde_json::json; +/// +/// use itf::de::{self, As}; +/// +/// +/// #[derive(Debug, PartialEq, Deserialize)] +/// struct FooOption { +/// #[serde(with = "As::>")] +/// foo: Option, +/// } +/// +/// let some_itf = json!({ +/// "foo": { +/// "tag": "Some", +/// "value": 42, +/// } +/// }); +/// +/// let some_foo = itf::from_value::(some_itf).unwrap(); +/// assert_eq!(some_foo, FooOption { foo: Some(42) }); +/// +/// let none_itf = json!({ +/// "foo": { +/// "tag": "None", +/// "value": {}, +/// } +/// }); +/// +/// let none_foo = itf::from_value::(none_itf).unwrap(); +/// assert_eq!(none_foo, FooOption { foo: None }); +/// ``` +pub type Option = serde_with::FromInto>; + +/// A type isomorphic to [`std::option::Option`]. +/// +/// Can either be used directly or with [`As`] together with [`Option`]. +#[derive( + Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, +)] +#[serde(tag = "tag", content = "value")] +pub enum QuintOption { + #[default] + None, + Some(T), +} + +impl From> for QuintOption { + fn from(opt: std::option::Option) -> Self { + match opt { + Some(value) => QuintOption::Some(value), + None => QuintOption::None, + } + } +} + +impl From> for std::option::Option { + fn from(opt: QuintOption) -> Self { + match opt { + QuintOption::Some(value) => Some(value), + QuintOption::None => None, + } + } +} + +/// Helper for `serde` to deserialize types isomorphic to [`std::result::Result`]. +/// +/// To be used in conjunction with [`As`]. +/// +/// ## Example +/// +/// ```rust +/// use serde::Deserialize; +/// use serde_json::json; +/// +/// use itf::de::{self, As}; +/// +/// #[derive(Debug, PartialEq, Deserialize)] +/// struct FooResult { +/// #[serde(with = "As::>")] +/// foo: Result, +/// } +/// +/// let ok_itf = json!({ +/// "foo": { +/// "tag": "Ok", +/// "value": 42, +/// } +/// }); +/// +/// let ok = itf::from_value::(ok_itf).unwrap(); +/// assert_eq!(ok.foo, Ok(42)); +/// +/// let err_itf = json!({ +/// "foo": { +/// "tag": "Err", +/// "value": 42, +/// } +/// }); +/// +/// let err = itf::from_value::(err_itf).unwrap(); +/// assert_eq!(err.foo, Err(42)); +/// ``` +pub type Result = serde_with::FromInto>; + +/// A type isomorphic to [`std::result::Result`]. +/// +/// Can either be used directly or with [`As`] together with [`Result`]. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[serde(tag = "tag", content = "value")] +pub enum QuintResult { + Ok(T), + Err(E), +} + +impl From> for QuintResult { + fn from(opt: std::result::Result) -> Self { + match opt { + Ok(value) => QuintResult::Ok(value), + Err(e) => QuintResult::Err(e), + } + } +} + +impl From> for std::result::Result { + fn from(opt: QuintResult) -> Self { + match opt { + QuintResult::Ok(value) => Ok(value), + QuintResult::Err(e) => Err(e), + } + } +} diff --git a/tests/sum_types.rs b/tests/sum_types.rs index eb1b2bc..c9f4565 100644 --- a/tests/sum_types.rs +++ b/tests/sum_types.rs @@ -28,7 +28,7 @@ fn parse_trace() { } #[test] -fn test_deserialize_some() { +fn test_deserialize_adhoc_some() { let some_itf = json!({ "tag": "Some", "value": {"#bigint": "1"}, @@ -39,7 +39,7 @@ fn test_deserialize_some() { } #[test] -fn test_deserialize_none() { +fn test_deserialize_adhoc_none() { let none_itf = json!({ "tag": "None", "value": {}, @@ -93,3 +93,71 @@ fn test_deserialize_enum() { let foobar = itf::from_value::(foobar_itf).unwrap(); assert_eq!(foobar, Enum::FooBar("hello".to_string(), 42.into(), true)); } + +#[derive(Debug, PartialEq, Deserialize)] +struct FooOption { + #[serde(with = "As::>")] + foo: Option, +} + +#[test] +#[allow(clippy::disallowed_names)] +fn test_deserialize_option_some() { + let some_itf = json!({ + "foo": { + "tag": "Some", + "value": 42, + } + }); + + let some_foo = itf::from_value::(some_itf).unwrap(); + assert_eq!(some_foo, FooOption { foo: Some(42) }); +} + +#[test] +#[allow(clippy::disallowed_names)] +fn test_deserialize_option_none() { + let none_itf = json!({ + "foo": { + "tag": "None", + "value": {}, + } + }); + + let none_foo = itf::from_value::(none_itf).unwrap(); + assert_eq!(none_foo, FooOption { foo: None }); +} + +#[derive(Debug, PartialEq, Deserialize)] +struct FooResult { + #[serde(with = "As::>")] + foo: Result, +} + +#[test] +#[allow(clippy::disallowed_names)] +fn test_deserialize_result_ok() { + let ok_itf = json!({ + "foo": { + "tag": "Ok", + "value": 42, + } + }); + + let ok = itf::from_value::(ok_itf).unwrap(); + assert_eq!(ok.foo, Ok(42)); +} + +#[test] +#[allow(clippy::disallowed_names)] +fn test_deserialize_result_err() { + let err_itf = json!({ + "foo": { + "tag": "Err", + "value": 42, + } + }); + + let err = itf::from_value::(err_itf).unwrap(); + assert_eq!(err.foo, Err(42)); +}