zvariant/
optional.rs

1use std::{
2    fmt::Display,
3    ops::{Deref, DerefMut},
4};
5
6use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
7
8use crate::Type;
9
10/// Type that uses a special value to be used as none.
11///
12/// See [`Optional`] documentation for the rationale for this trait's existence.
13///
14/// # Caveats
15///
16/// Since use of default values as none is typical, this trait is implemented for all types that
17/// implement [`Default`] for convenience. Unfortunately, this means you can not implement this
18/// trait manually for types that implement [`Default`].
19///
20/// Moreoever, since `bool` implements [`Default`], `NoneValue` gets implemented for `bool` as well.
21/// However, this is unsound since its not possible to distinguish between `false` and `None` in
22/// this case. This is why you'll get a panic on trying to serialize or deserialize an
23/// `Optionanl<bool>`.
24pub trait NoneValue {
25    type NoneType;
26
27    /// The none-equivalent value.
28    fn null_value() -> Self::NoneType;
29}
30
31impl<T> NoneValue for T
32where
33    T: Default,
34{
35    type NoneType = Self;
36
37    fn null_value() -> Self {
38        Default::default()
39    }
40}
41
42/// An optional value.
43///
44/// Since D-Bus doesn't have the concept of nullability, it uses a special value (typically the
45/// default value) as the null value. For example [this signal][ts] uses empty strings for null
46/// values. Serde has built-in support for `Option` but unfortunately that doesn't work for us.
47/// Hence the need for this type.
48///
49/// The serialization and deserialization of `Optional` relies on [`NoneValue`] implementation of
50/// the underlying type.
51///
52/// # Examples
53///
54/// ```
55/// use zvariant::{serialized::Context, Optional, to_bytes, LE};
56///
57/// // `Null` case.
58/// let ctxt = Context::new_dbus(LE, 0);
59/// let s = Optional::<&str>::default();
60/// let encoded = to_bytes(ctxt, &s).unwrap();
61/// assert_eq!(encoded.bytes(), &[0, 0, 0, 0, 0]);
62/// let s: Optional<&str> = encoded.deserialize().unwrap().0;
63/// assert_eq!(*s, None);
64///
65/// // `Some` case.
66/// let s = Optional::from(Some("hello"));
67/// let encoded = to_bytes(ctxt, &s).unwrap();
68/// assert_eq!(encoded.len(), 10);
69/// // The first byte is the length of the string in Little-Endian format.
70/// assert_eq!(encoded[0], 5);
71/// let s: Optional<&str> = encoded.deserialize().unwrap().0;
72/// assert_eq!(*s, Some("hello"));
73/// ```
74///
75/// [ts]: https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-name-owner-changed
76#[derive(Clone, Debug, PartialEq, Eq, Hash)]
77pub struct Optional<T>(Option<T>);
78
79impl<T> Type for Optional<T>
80where
81    T: Type,
82{
83    fn signature() -> crate::Signature<'static> {
84        T::signature()
85    }
86}
87
88impl<T> Serialize for Optional<T>
89where
90    T: Type + NoneValue + Serialize,
91    <T as NoneValue>::NoneType: Serialize,
92{
93    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
94    where
95        S: Serializer,
96    {
97        if T::signature() == bool::signature() {
98            panic!("`Optional<bool>` type is not supported");
99        }
100
101        match &self.0 {
102            Some(value) => value.serialize(serializer),
103            None => T::null_value().serialize(serializer),
104        }
105    }
106}
107
108impl<'de, T, E> Deserialize<'de> for Optional<T>
109where
110    T: Type + NoneValue + Deserialize<'de>,
111    <T as NoneValue>::NoneType: Deserialize<'de> + TryInto<T, Error = E> + PartialEq,
112    E: Display,
113{
114    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
115    where
116        D: Deserializer<'de>,
117    {
118        if T::signature() == bool::signature() {
119            panic!("`Optional<bool>` type is not supported");
120        }
121
122        let value = <<T as NoneValue>::NoneType>::deserialize(deserializer)?;
123        if value == T::null_value() {
124            Ok(Optional(None))
125        } else {
126            Ok(Optional(Some(value.try_into().map_err(de::Error::custom)?)))
127        }
128    }
129}
130
131impl<T> From<Option<T>> for Optional<T> {
132    fn from(value: Option<T>) -> Self {
133        Optional(value)
134    }
135}
136
137impl<T> From<Optional<T>> for Option<T> {
138    fn from(value: Optional<T>) -> Self {
139        value.0
140    }
141}
142
143impl<T> Deref for Optional<T> {
144    type Target = Option<T>;
145
146    fn deref(&self) -> &Self::Target {
147        &self.0
148    }
149}
150
151impl<T> DerefMut for Optional<T> {
152    fn deref_mut(&mut self) -> &mut Self::Target {
153        &mut self.0
154    }
155}
156
157impl<T> Default for Optional<T> {
158    fn default() -> Self {
159        Self(None)
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use std::panic::catch_unwind;
166
167    #[test]
168    fn bool_in_optional() {
169        // Ensure trying to encode/decode `bool` in `Optional` fails.
170        use crate::{to_bytes, Optional, LE};
171
172        let ctxt = crate::serialized::Context::new_dbus(LE, 0);
173        let res = catch_unwind(|| to_bytes(ctxt, &Optional::<bool>::default()));
174        assert!(res.is_err());
175
176        let data = crate::serialized::Data::new([0, 0, 0, 0].as_slice(), ctxt);
177        let res = catch_unwind(|| data.deserialize::<Optional<bool>>());
178        assert!(res.is_err());
179    }
180}