1 | use std::{ |
2 | fmt::Display, |
3 | ops::{Deref, DerefMut}, |
4 | }; |
5 | |
6 | use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; |
7 | |
8 | use 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>`. |
24 | pub trait NoneValue { |
25 | type NoneType; |
26 | |
27 | /// The none-equivalent value. |
28 | fn null_value() -> Self::NoneType; |
29 | } |
30 | |
31 | impl<T> NoneValue for T |
32 | where |
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)] |
77 | pub struct Optional<T>(Option<T>); |
78 | |
79 | impl<T> Type for Optional<T> |
80 | where |
81 | T: Type, |
82 | { |
83 | fn signature() -> crate::Signature<'static> { |
84 | T::signature() |
85 | } |
86 | } |
87 | |
88 | impl<T> Serialize for Optional<T> |
89 | where |
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: &T) => value.serialize(serializer), |
103 | None => T::null_value().serialize(serializer), |
104 | } |
105 | } |
106 | } |
107 | |
108 | impl<'de, T, E> Deserialize<'de> for Optional<T> |
109 | where |
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: impl Deserialize<'de> + TryInto<…> + PartialEq = <<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(op:de::Error::custom)?))) |
127 | } |
128 | } |
129 | } |
130 | |
131 | impl<T> From<Option<T>> for Optional<T> { |
132 | fn from(value: Option<T>) -> Self { |
133 | Optional(value) |
134 | } |
135 | } |
136 | |
137 | impl<T> From<Optional<T>> for Option<T> { |
138 | fn from(value: Optional<T>) -> Self { |
139 | value.0 |
140 | } |
141 | } |
142 | |
143 | impl<T> Deref for Optional<T> { |
144 | type Target = Option<T>; |
145 | |
146 | fn deref(&self) -> &Self::Target { |
147 | &self.0 |
148 | } |
149 | } |
150 | |
151 | impl<T> DerefMut for Optional<T> { |
152 | fn deref_mut(&mut self) -> &mut Self::Target { |
153 | &mut self.0 |
154 | } |
155 | } |
156 | |
157 | impl<T> Default for Optional<T> { |
158 | fn default() -> Self { |
159 | Self(None) |
160 | } |
161 | } |
162 | |
163 | #[cfg (test)] |
164 | mod 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 | } |
181 | |