summary refs log tree commit diff stats
path: root/src/maybe.rs
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 /src/maybe.rs
parent134727652dc3a65bc5cf6a4d3cfb1c24fd023d5e (diff)
Tweak MayBe to better align with Datafu
This is a breaking change, but aligning with Datafu is useful.
Diffstat (limited to 'src/maybe.rs')
-rw-r--r--src/maybe.rs109
1 files changed, 109 insertions, 0 deletions
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)
+    }
+}