From f17c00795f9b1fc1a7e0c636478e13d66fda737c Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Fri, 17 Sep 2021 16:17:28 -0300 Subject: Tweak MayBe to better align with Datafu This is a breaking change, but aligning with Datafu is useful. --- Cargo.toml | 2 +- README.md | 6 ++-- src/lib.rs | 65 ++++------------------------------ src/maybe.rs | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/maybe.rs | 13 +++---- 5 files changed, 127 insertions(+), 68 deletions(-) create mode 100644 src/maybe.rs diff --git a/Cargo.toml b/Cargo.toml index 6b44765..69db765 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde-util" -version = "0.1.0" +version = "0.2.0" authors = ["SoniEx2 "] license-file = "LICENSE.txt" description = "Soni's Serde Utilities" diff --git a/README.md b/README.md index 2a0dd34..9bb0377 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ Soni's Serde Utilities This crate provides some utilities for use with serde. -Currently, it provides `MayBe`, an `Option`-like that doesn't error if -something is present but doesn't match a `T`. For example, it enables the JSON: +Currently, it provides `MayBe`, a deserializable that doesn't error if +something doesn't match a `T`. For example, it enables the JSON: ```json { @@ -21,4 +21,4 @@ struct Foo { } ``` -as a `foo.bar.is_none()`. +as a `foo.bar.is_not()`. diff --git a/src/lib.rs b/src/lib.rs index 0002663..335a283 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,60 +1,9 @@ -use serde::Deserialize; +//! Soni's Serde Utilities. +//! +//! This crate provides some utilities for use with [`serde`]. -#[derive(Deserialize)] -#[serde(untagged)] -enum MaybeHelper { - Some(T), - None(serde::de::IgnoredAny), -} +pub use crate::maybe::MayBe; -/// Something that may be an `T`. -/// -/// Unlike `Option`, this places no restriction on whether something -/// can't be something else entirely. -/// -/// Can only be used with self-describing formats, like JSON. -#[derive(Deserialize)] -#[serde(from = "Option>")] -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct MayBe(pub Option); - -impl Default for MayBe { - fn default() -> Self { - Self(Default::default()) - } -} - -impl std::ops::Deref for MayBe { - type Target = Option; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::DerefMut for MayBe { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl From> for Option { - fn from(thing: MayBe) -> Option { - thing.0 - } -} - -impl From> for MayBe { - fn from(thing: Option) -> MayBe { - Self(thing) - } -} - -impl From>> for MayBe { - fn from(thing: Option>) -> MayBe { - Self(match thing { - Some(MaybeHelper::Some(v)) => Some(v), - _ => None, - }) - } -} +mod maybe; +// TODO: stuff for use with MayBe + Vec/HashMap/BTreeMap/etc +//mod container_utils; diff --git a/src/maybe.rs b/src/maybe.rs new file mode 100644 index 0000000..9b2c419 --- /dev/null +++ b/src/maybe.rs @@ -0,0 +1,109 @@ +use serde::Deserialize; +use serde::Deserializer; + +/// Something that may be an `T`. +/// +/// If a value cannot be deserialized as a `T`, this will *discard* the value +/// and provide an `MayBe::IsNot`. +/// +/// Can only be used with self-describing formats, like JSON. +#[derive(Deserialize)] +#[serde(untagged)] +#[derive(Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum MayBe { + Is(T), + #[serde(deserialize_with = "ignore_any")] + IsNot, +} + +fn ignore_any<'de, D: Deserializer<'de>>(d: D) -> Result<(), D::Error> { + serde::de::IgnoredAny::deserialize(d).map(|_| ()) +} + +// FIXME decide on Default +// This enum is analogous to Result, *not* Option. +// +//impl Default for MayBe { +// fn default() -> Self { +// MayBe::IsNot +// } +//} +//impl Default for MayBe { +// fn default() -> Self { +// MayBe::Is(T::default()) +// } +//} + +impl MayBe { + /// Returns whether the `MayBe` contains a `T`. + pub fn is(&self) -> bool { + matches!(self, MayBe::Is(_)) + } + + /// Returns whether the `MayBe` does not contain a `T`. + pub fn is_not(&self) -> bool { + !self.is() + } + + /// Converts this `MayBe` into an `Option`. + pub fn into_opt(self) -> Option { + self.into() + } + + /// Converts this `&MayBe` into an `Option<&T>`. + pub fn as_opt(&self) -> Option<&T> { + match self { + MayBe::Is(t) => Some(t), + MayBe::IsNot => None, + } + } + + /// Converts this `&mut MayBe` into an `Option<&mut T>`. + pub fn as_mut_opt(&mut self) -> Option<&mut T> { + match self { + MayBe::Is(t) => Some(t), + MayBe::IsNot => None, + } + } +} + +impl Clone for MayBe { + fn clone(&self) -> Self { + match self { + MayBe::Is(t) => MayBe::Is(t.clone()), + MayBe::IsNot => MayBe::IsNot, + } + } + + fn clone_from(&mut self, source: &Self) { + match (self, source) { + (MayBe::Is(a), MayBe::Is(b)) => a.clone_from(b), + (MayBe::IsNot, MayBe::IsNot) => (), + (a, b) => *a = b.clone(), + } + } +} + +impl From> for Option { + fn from(thing: MayBe) -> Option { + match thing { + MayBe::Is(t) => Some(t), + MayBe::IsNot => None, + } + } +} + +impl From> for MayBe { + fn from(thing: Option) -> MayBe { + match thing { + Some(t) => MayBe::Is(t), + None => MayBe::IsNot, + } + } +} + +impl From for MayBe { + fn from(thing: T) -> MayBe { + MayBe::Is(thing) + } +} diff --git a/tests/maybe.rs b/tests/maybe.rs index 68db667..1437195 100644 --- a/tests/maybe.rs +++ b/tests/maybe.rs @@ -7,23 +7,24 @@ use serde_util::MayBe; #[test] fn test_is_t() { let json = "256"; - assert!(from_str::>(json).unwrap().is_some()); + assert!(from_str::>(json).unwrap().is()); } #[test] fn test_is_not_t() { let json = "{}"; - assert!(from_str::>(json).unwrap().is_none()); + assert!(from_str::>(json).unwrap().is_not()); } #[test] fn test_is_missing() { #[derive(Deserialize)] struct Foo { - bar: MayBe, + #[serde(rename = "bar")] + _bar: MayBe, } let json = "{}"; - assert!(from_str::(json).unwrap().bar.is_none()); + assert!(from_str::(json).is_err()); } #[test] @@ -33,7 +34,7 @@ fn test_t_in_struct() { bar: MayBe, } let json = "{\"bar\": 123}"; - assert!(from_str::(json).unwrap().bar.is_some()); + assert!(from_str::(json).unwrap().bar.is()); } @@ -44,5 +45,5 @@ fn test_not_t_in_struct() { bar: MayBe, } let json = "{\"bar\": []}"; - assert!(from_str::(json).unwrap().bar.is_none()); + assert!(from_str::(json).unwrap().bar.is_not()); } -- cgit 1.4.1