summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2021-09-17 16:17:28 -0300
committerSoniEx2 <endermoneymod@gmail.com>2021-09-17 16:17:28 -0300
commitf17c00795f9b1fc1a7e0c636478e13d66fda737c (patch)
tree1bdd38fa760c0da944ef2fe8964de782ed7f7267
parent134727652dc3a65bc5cf6a4d3cfb1c24fd023d5e (diff)
Tweak MayBe to better align with Datafu
This is a breaking change, but aligning with Datafu is useful.
-rw-r--r--Cargo.toml2
-rw-r--r--README.md6
-rw-r--r--src/lib.rs65
-rw-r--r--src/maybe.rs109
-rw-r--r--tests/maybe.rs13
5 files changed, 127 insertions, 68 deletions
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 <endermoneymod@gmail.com>"]
 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<T>`, an `Option<T>`-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<T>`, 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<T> {
-    Some(T),
-    None(serde::de::IgnoredAny),
-}
+pub use crate::maybe::MayBe;
 
-/// Something that may be an `T`.
-///
-/// Unlike `Option<T>`, 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<MaybeHelper<T>>")]
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(transparent)]
-pub struct MayBe<T>(pub Option<T>);
-
-impl<T> Default for MayBe<T> {
-    fn default() -> Self {
-        Self(Default::default())
-    }
-}
-
-impl<T> std::ops::Deref for MayBe<T> {
-    type Target = Option<T>;
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl<T> std::ops::DerefMut for MayBe<T> {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.0
-    }
-}
-
-impl<T> From<MayBe<T>> for Option<T> {
-    fn from(thing: MayBe<T>) -> Option<T> {
-        thing.0
-    }
-}
-
-impl<T> From<Option<T>> for MayBe<T> {
-    fn from(thing: Option<T>) -> MayBe<T> {
-        Self(thing)
-    }
-}
-
-impl<T> From<Option<MaybeHelper<T>>> for MayBe<T> {
-    fn from(thing: Option<MaybeHelper<T>>) -> MayBe<T> {
-        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<T> {
+    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<T> Default for MayBe<T> {
+//    fn default() -> Self {
+//        MayBe::IsNot
+//    }
+//}
+//impl<T: Default> Default for MayBe<T> {
+//    fn default() -> Self {
+//        MayBe::Is(T::default())
+//    }
+//}
+
+impl<T> MayBe<T> {
+    /// 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<T>` into an `Option<T>`.
+    pub fn into_opt(self) -> Option<T> {
+        self.into()
+    }
+
+    /// Converts this `&MayBe<T>` 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<T>` 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<T: Clone> Clone for MayBe<T> {
+    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<T> From<MayBe<T>> for Option<T> {
+    fn from(thing: MayBe<T>) -> Option<T> {
+        match thing {
+            MayBe::Is(t) => Some(t),
+            MayBe::IsNot => None,
+        }
+    }
+}
+
+impl<T> From<Option<T>> for MayBe<T> {
+    fn from(thing: Option<T>) -> MayBe<T> {
+        match thing {
+            Some(t) => MayBe::Is(t),
+            None => MayBe::IsNot,
+        }
+    }
+}
+
+impl<T> From<T> for MayBe<T> {
+    fn from(thing: T) -> MayBe<T> {
+        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::<MayBe<f64>>(json).unwrap().is_some());
+    assert!(from_str::<MayBe<f64>>(json).unwrap().is());
 }
 
 #[test]
 fn test_is_not_t() {
     let json = "{}";
-    assert!(from_str::<MayBe<f64>>(json).unwrap().is_none());
+    assert!(from_str::<MayBe<f64>>(json).unwrap().is_not());
 }
 
 #[test]
 fn test_is_missing() {
     #[derive(Deserialize)]
     struct Foo {
-        bar: MayBe<f64>,
+        #[serde(rename = "bar")]
+        _bar: MayBe<f64>,
     }
     let json = "{}";
-    assert!(from_str::<Foo>(json).unwrap().bar.is_none());
+    assert!(from_str::<Foo>(json).is_err());
 }
 
 #[test]
@@ -33,7 +34,7 @@ fn test_t_in_struct() {
         bar: MayBe<f64>,
     }
     let json = "{\"bar\": 123}";
-    assert!(from_str::<Foo>(json).unwrap().bar.is_some());
+    assert!(from_str::<Foo>(json).unwrap().bar.is());
 }
 
 
@@ -44,5 +45,5 @@ fn test_not_t_in_struct() {
         bar: MayBe<f64>,
     }
     let json = "{\"bar\": []}";
-    assert!(from_str::<Foo>(json).unwrap().bar.is_none());
+    assert!(from_str::<Foo>(json).unwrap().bar.is_not());
 }