Skip to main content

jsonld_syntax/
nullable.rs

1use std::fmt;
2
3/// Error returned by [`Nullable::try_unwrap`] when the value is `null`.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
5#[error("nullable value was null")]
6pub struct NullError;
7
8/// Value that can be `null`.
9///
10/// `Option` is used throughout this crate for entries that may or may not be
11/// present. A JSON-LD entry can also be present with the explicit value
12/// `null`, which is meaningful: it unsets whatever the surrounding context
13/// inherited. Hence this separate type, so that `Option<Nullable<T>>` can tell
14/// an absent entry from an entry set to `null`.
15#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
16pub enum Nullable<T> {
17    /// The `null` value.
18    Null,
19
20    /// Any other value.
21    Some(T),
22}
23
24impl<T> Nullable<T> {
25    /// Checks if the value is `null`.
26    #[inline(always)]
27    pub fn is_null(&self) -> bool {
28        matches!(self, Nullable::Null)
29    }
30
31    /// Checks if the value is not `null`.
32    #[inline(always)]
33    pub fn is_some(&self) -> bool {
34        matches!(self, Nullable::Some(_))
35    }
36
37    /// Returns the inner value, or [`NullError`] if `null`.
38    ///
39    /// # Errors
40    ///
41    /// Returns an error when the value is null.
42    #[inline(always)]
43    pub fn try_unwrap(self) -> Result<T, NullError> {
44        match self {
45            Nullable::Some(t) => Ok(t),
46            Nullable::Null => Err(NullError),
47        }
48    }
49
50    /// Converts `&Nullable<T>` into `Nullable<&T>`.
51    #[inline(always)]
52    pub fn as_ref(&self) -> Nullable<&T> {
53        match self {
54            Nullable::Null => Nullable::Null,
55            Nullable::Some(t) => Nullable::Some(t),
56        }
57    }
58
59    /// Converts `&Nullable<T>` into `Nullable<&T::Target>` by dereferencing
60    /// the inner value.
61    pub fn as_deref(&self) -> Nullable<&T::Target>
62    where
63        T: std::ops::Deref,
64    {
65        match self {
66            Self::Null => Nullable::Null,
67            Self::Some(t) => Nullable::Some(t),
68        }
69    }
70
71    /// Converts this value into an `Option`, mapping `null` to `None`.
72    #[inline(always)]
73    pub fn option(self) -> Option<T> {
74        match self {
75            Nullable::Null => None,
76            Nullable::Some(t) => Some(t),
77        }
78    }
79
80    /// Maps the inner value with the given function, keeping `null` as `null`.
81    #[inline(always)]
82    pub fn map<F, U>(self, f: F) -> Nullable<U>
83    where
84        F: FnOnce(T) -> U,
85    {
86        match self {
87            Nullable::Null => Nullable::Null,
88            Nullable::Some(t) => Nullable::Some(f(t)),
89        }
90    }
91
92    /// Converts the contained value, keeping null as null.
93    pub fn cast<U>(self) -> Nullable<U>
94    where
95        T: Into<U>,
96    {
97        match self {
98            Self::Null => Nullable::Null,
99            Self::Some(t) => Nullable::Some(t.into()),
100        }
101    }
102
103    /// Returns the contained value, or `default` if it is null.
104    pub fn unwrap_or(self, default: T) -> T {
105        match self {
106            Self::Null => default,
107            Self::Some(t) => t,
108        }
109    }
110
111    /// Returns the contained value, or the default one if it is null.
112    pub fn unwrap_or_default(self) -> T
113    where
114        T: Default,
115    {
116        match self {
117            Self::Null => T::default(),
118            Self::Some(t) => t,
119        }
120    }
121}
122
123impl<T> From<T> for Nullable<T> {
124    fn from(value: T) -> Self {
125        Self::Some(value)
126    }
127}
128
129impl<T> From<Option<T>> for Nullable<T> {
130    fn from(value: Option<T>) -> Self {
131        match value {
132            Some(t) => Self::Some(t),
133            None => Self::Null,
134        }
135    }
136}
137
138impl<T: Clone> Nullable<&T> {
139    /// Clones the referenced inner value.
140    #[inline(always)]
141    #[must_use]
142    pub fn cloned(&self) -> Nullable<T> {
143        match self {
144            Nullable::Null => Nullable::Null,
145            Nullable::Some(t) => Nullable::Some((*t).clone()),
146        }
147    }
148}
149
150impl<T: fmt::Display> fmt::Display for Nullable<T> {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        match self {
153            Self::Null => write!(f, "null"),
154            Self::Some(v) => v.fmt(f),
155        }
156    }
157}
158
159impl<T: contextual::DisplayWithContext<V>, V> contextual::DisplayWithContext<V> for Nullable<T> {
160    fn fmt_with(&self, vocabulary: &V, f: &mut fmt::Formatter) -> fmt::Result {
161        match self {
162            Self::Null => write!(f, "null"),
163            Self::Some(v) => v.fmt_with(vocabulary, f),
164        }
165    }
166}
167
168#[cfg(feature = "serde")]
169impl<T: serde::Serialize> serde::Serialize for Nullable<T> {
170    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
171    where
172        S: serde::Serializer,
173    {
174        match self {
175            Self::Null => serializer.serialize_none(),
176            Self::Some(t) => serializer.serialize_some(t),
177        }
178    }
179}
180
181#[cfg(feature = "serde")]
182impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for Nullable<T> {
183    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
184    where
185        D: serde::Deserializer<'de>,
186    {
187        Ok(Option::<T>::deserialize(deserializer)?.into())
188    }
189}
190
191#[cfg(feature = "serde")]
192impl<T> Nullable<T> {
193    /// Deserializes a `Nullable<T>` and wraps it in `Some`.
194    ///
195    /// Meant as the `deserialize_with` of an `Option<Nullable<T>>` field that
196    /// also carries `#[serde(default)]`: an absent entry then yields `None`,
197    /// while an entry present with the value `null` yields
198    /// `Some(Nullable::Null)`. Serde's own handling of `Option` would collapse
199    /// both cases to `None`.
200    ///
201    /// # Errors
202    ///
203    /// Returns the deserializer's error when the value is neither absent, null, nor a valid `T`.
204    pub fn optional<'de, D>(deserializer: D) -> Result<Option<Self>, D::Error>
205    where
206        T: serde::Deserialize<'de>,
207        D: serde::Deserializer<'de>,
208    {
209        use serde::Deserialize;
210        Ok(Some(Option::<T>::deserialize(deserializer)?.into()))
211    }
212}