From 7293402f6ac8644955f22423bad8bce2bdc166db Mon Sep 17 00:00:00 2001 From: Romain Ruetschi Date: Wed, 15 May 2024 21:58:24 +0200 Subject: [PATCH] Add helper to deserialize into `Option` or `Result` --- src/de.rs | 4 +-- src/de/helpers.rs | 64 +++++++++++++++++++++++++++++++++++++++++ tests/sum_types.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 136 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..8280657 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,66 @@ 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`]. +pub type Option = serde_with::FromInto>; + +#[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`]. +pub type Result = serde_with::FromInto>; + +#[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)); +}