Skip to main content

s2_common/
maybe.rs

1use serde::{Deserialize, Serialize};
2
3/// The [`Maybe`] type represents an optional that might or might not be specified.
4///
5/// An [`Option`] is deserialized as [`None`] if either the value is not specified or the value is
6/// `null`. [`Maybe`] allows us to distinguish between the two.
7///
8/// The [`Deserialize`] impl always produces `Specified` via the blanket `From<T>` impl.
9/// `Unspecified` is only produced by `Default::default()`, which serde invokes for
10/// absent fields annotated with `#[serde(default)]`.
11///
12/// # Examples
13///
14/// ```
15/// use s2_common::maybe::Maybe;
16///
17/// #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
18/// pub struct MyStruct {
19///     #[serde(default)]
20///     pub field: Maybe<Option<u32>>,
21/// }
22///
23/// assert_eq!(
24///     MyStruct { field: Maybe::Unspecified },
25///     serde_json::from_str("{}").unwrap(),
26/// );
27///
28/// assert_eq!(
29///     MyStruct { field: Maybe::Specified(None) },
30///     serde_json::from_str(r#"{ "field": null }"#).unwrap(),
31/// );
32///
33/// assert_eq!(
34///     MyStruct { field: Maybe::Specified(Some(10)) },
35///     serde_json::from_str(r#"{ "field": 10 }"#).unwrap(),
36/// );
37/// ```
38#[derive(Debug, Clone, Default, PartialEq, Eq)]
39pub enum Maybe<T> {
40    #[default]
41    Unspecified,
42    Specified(T),
43}
44
45impl<T> Maybe<T> {
46    pub fn is_unspecified(&self) -> bool {
47        matches!(self, Self::Unspecified)
48    }
49
50    pub fn map<U, F>(self, f: F) -> Maybe<U>
51    where
52        F: FnOnce(T) -> U,
53    {
54        match self {
55            Self::Unspecified => Maybe::Unspecified,
56            Self::Specified(x) => Maybe::Specified(f(x)),
57        }
58    }
59
60    pub fn unwrap_or_default(self) -> T
61    where
62        T: Default,
63    {
64        match self {
65            Self::Unspecified => T::default(),
66            Self::Specified(x) => x,
67        }
68    }
69}
70
71impl<T> Maybe<Option<T>> {
72    pub fn map_opt<U, F>(self, f: F) -> Maybe<Option<U>>
73    where
74        F: FnOnce(T) -> U,
75    {
76        self.map(|opt| opt.map(f))
77    }
78
79    pub fn try_map_opt<U, E, F>(self, f: F) -> Result<Maybe<Option<U>>, E>
80    where
81        F: FnOnce(T) -> Result<U, E>,
82    {
83        match self {
84            Maybe::Unspecified => Ok(Maybe::Unspecified),
85            Maybe::Specified(opt) => match opt {
86                Some(value) => f(value).map(|converted| Maybe::Specified(Some(converted))),
87                None => Ok(Maybe::Specified(None)),
88            },
89        }
90    }
91
92    pub fn opt_or_default_mut(&mut self) -> &mut T
93    where
94        T: Default,
95    {
96        match self {
97            Maybe::Unspecified | Maybe::Specified(None) => {
98                *self = Self::Specified(Some(T::default()));
99                match self {
100                    Self::Specified(Some(x)) => x,
101                    _ => unreachable!(),
102                }
103            }
104            Maybe::Specified(Some(x)) => x,
105        }
106    }
107}
108
109impl<T> From<T> for Maybe<T> {
110    fn from(value: T) -> Self {
111        Self::Specified(value)
112    }
113}
114
115impl<T: Serialize> Serialize for Maybe<T> {
116    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
117    where
118        S: serde::Serializer,
119    {
120        match self {
121            Self::Unspecified => serializer.serialize_none(),
122            Self::Specified(v) => v.serialize(serializer),
123        }
124    }
125}
126
127impl<'de, T: Deserialize<'de>> Deserialize<'de> for Maybe<T> {
128    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
129    where
130        D: serde::Deserializer<'de>,
131    {
132        let v = T::deserialize(deserializer)?;
133        Ok(v.into())
134    }
135}