summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2021-09-16 10:51:39 -0300
committerSoniEx2 <endermoneymod@gmail.com>2021-09-16 10:51:39 -0300
commit134727652dc3a65bc5cf6a4d3cfb1c24fd023d5e (patch)
treeaf84264ada4c309cbf78a48e9030748a7ecac110
[Project] Soni's Serde Utilities
Some utilities for use with serde.
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml17
-rw-r--r--LICENSE.txt22
-rw-r--r--README.md24
-rw-r--r--src/lib.rs60
-rw-r--r--tests/maybe.rs48
6 files changed, 173 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96ef6c0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/target
+Cargo.lock
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..6b44765
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "serde-util"
+version = "0.1.0"
+authors = ["SoniEx2 <endermoneymod@gmail.com>"]
+license-file = "LICENSE.txt"
+description = "Soni's Serde Utilities"
+edition = "2018"
+repository = "https://soniex2.autistic.space/git-repos/serde-util.git"
+readme = "README.md"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+serde = { version = "1.0", features = ["derive"] }
+
+[dev-dependencies]
+serde_json = "1.0"
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..33c1f7f
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2021 Soni L.
+
+Permission is hereby granted, free of charge, to any person ("You") obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+This license shall be void if You bring a copyright lawsuit, related or
+unrelated to the Software, against the copyright holder.
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2a0dd34
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
+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:
+
+```json
+{
+  "bar": []
+}
+```
+
+to successfully deserialize into the Rust struct:
+
+```rust
+#[derive(Deserialize)]
+struct Foo {
+  bar: MayBe<f64>,
+}
+```
+
+as a `foo.bar.is_none()`.
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..0002663
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,60 @@
+use serde::Deserialize;
+
+#[derive(Deserialize)]
+#[serde(untagged)]
+enum MaybeHelper<T> {
+    Some(T),
+    None(serde::de::IgnoredAny),
+}
+
+/// 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,
+        })
+    }
+}
diff --git a/tests/maybe.rs b/tests/maybe.rs
new file mode 100644
index 0000000..68db667
--- /dev/null
+++ b/tests/maybe.rs
@@ -0,0 +1,48 @@
+use serde::Deserialize;
+
+use serde_json::from_str;
+
+use serde_util::MayBe;
+
+#[test]
+fn test_is_t() {
+    let json = "256";
+    assert!(from_str::<MayBe<f64>>(json).unwrap().is_some());
+}
+
+#[test]
+fn test_is_not_t() {
+    let json = "{}";
+    assert!(from_str::<MayBe<f64>>(json).unwrap().is_none());
+}
+
+#[test]
+fn test_is_missing() {
+    #[derive(Deserialize)]
+    struct Foo {
+        bar: MayBe<f64>,
+    }
+    let json = "{}";
+    assert!(from_str::<Foo>(json).unwrap().bar.is_none());
+}
+
+#[test]
+fn test_t_in_struct() {
+    #[derive(Deserialize)]
+    struct Foo {
+        bar: MayBe<f64>,
+    }
+    let json = "{\"bar\": 123}";
+    assert!(from_str::<Foo>(json).unwrap().bar.is_some());
+}
+
+
+#[test]
+fn test_not_t_in_struct() {
+    #[derive(Deserialize)]
+    struct Foo {
+        bar: MayBe<f64>,
+    }
+    let json = "{\"bar\": []}";
+    assert!(from_str::<Foo>(json).unwrap().bar.is_none());
+}