1 | use crate::{utils::*, Signature}; |
2 | use serde::de::{Deserialize, DeserializeSeed}; |
3 | use std::{ |
4 | marker::PhantomData, |
5 | net::{IpAddr, Ipv4Addr, Ipv6Addr}, |
6 | path::{Path, PathBuf}, |
7 | rc::Rc, |
8 | sync::{Arc, Mutex, RwLock}, |
9 | time::Duration, |
10 | }; |
11 | |
12 | /// Trait implemented by all serializable types. |
13 | /// |
14 | /// This very simple trait provides the signature for the implementing type. Since the [D-Bus type |
15 | /// system] relies on these signatures, our [serialization and deserialization] API requires this |
16 | /// trait in addition to [`Serialize`] and [`Deserialize`], respectively. |
17 | /// |
18 | /// Implementation is provided for all the [basic types] and blanket implementations for common |
19 | /// container types, such as, arrays, slices, tuples, [`Vec`] and [`HashMap`]. For easy |
20 | /// implementation for custom types, use `Type` derive macro from [zvariant_derive] crate. |
21 | /// |
22 | /// If your type's signature cannot be determined statically, you should implement the |
23 | /// [DynamicType] trait instead, which is otherwise automatically implemented if you implement this |
24 | /// trait. |
25 | /// |
26 | /// [D-Bus type system]: https://dbus.freedesktop.org/doc/dbus-specification.html#type-system |
27 | /// [serialization and deserialization]: index.html#functions |
28 | /// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html |
29 | /// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html |
30 | /// [basic types]: trait.Basic.html |
31 | /// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html |
32 | /// [`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html |
33 | /// [zvariant_derive]: https://docs.rs/zvariant_derive/latest/zvariant_derive/ |
34 | pub trait Type { |
35 | /// Get the signature for the implementing type. |
36 | /// |
37 | /// # Example |
38 | /// |
39 | /// ``` |
40 | /// use std::collections::HashMap; |
41 | /// use zvariant::Type; |
42 | /// |
43 | /// assert_eq!(u32::signature(), "u" ); |
44 | /// assert_eq!(String::signature(), "s" ); |
45 | /// assert_eq!(<(u32, &str, u64)>::signature(), "(ust)" ); |
46 | /// assert_eq!(<(u32, &str, &[u64])>::signature(), "(usat)" ); |
47 | /// assert_eq!(<HashMap<u8, &str>>::signature(), "a{ys}" ); |
48 | /// ``` |
49 | fn signature() -> Signature<'static>; |
50 | } |
51 | |
52 | /// Types with dynamic signatures. |
53 | /// |
54 | /// Prefer implementing [Type] if possible, but if the actual signature of your type cannot be |
55 | /// determined until runtime, you can implement this type to support serialization. You should |
56 | /// also implement [DynamicDeserialize] for deserialization. |
57 | pub trait DynamicType { |
58 | /// Get the signature for the implementing type. |
59 | /// |
60 | /// See [Type::signature] for details. |
61 | fn dynamic_signature(&self) -> Signature<'_>; |
62 | } |
63 | |
64 | /// Types that deserialize based on dynamic signatures. |
65 | /// |
66 | /// Prefer implementing [Type] and [Deserialize] if possible, but if the actual signature of your |
67 | /// type cannot be determined until runtime, you should implement this type to support |
68 | /// deserialization given a signature. |
69 | pub trait DynamicDeserialize<'de>: DynamicType { |
70 | /// A [DeserializeSeed] implementation for this type. |
71 | type Deserializer: DeserializeSeed<'de, Value = Self> + DynamicType; |
72 | |
73 | /// Get a deserializer compatible with this signature. |
74 | fn deserializer_for_signature<S>(signature: S) -> zvariant::Result<Self::Deserializer> |
75 | where |
76 | S: TryInto<Signature<'de>>, |
77 | S::Error: Into<zvariant::Error>; |
78 | } |
79 | |
80 | impl<T> DynamicType for T |
81 | where |
82 | T: Type + ?Sized, |
83 | { |
84 | fn dynamic_signature(&self) -> Signature<'_> { |
85 | <T as Type>::signature() |
86 | } |
87 | } |
88 | |
89 | impl<T> Type for PhantomData<T> |
90 | where |
91 | T: Type + ?Sized, |
92 | { |
93 | fn signature() -> Signature<'static> { |
94 | T::signature() |
95 | } |
96 | } |
97 | |
98 | impl<'de, T> DynamicDeserialize<'de> for T |
99 | where |
100 | T: Type + ?Sized + Deserialize<'de>, |
101 | { |
102 | type Deserializer = PhantomData<T>; |
103 | |
104 | fn deserializer_for_signature<S>(signature: S) -> zvariant::Result<Self::Deserializer> |
105 | where |
106 | S: TryInto<Signature<'de>>, |
107 | S::Error: Into<zvariant::Error>, |
108 | { |
109 | let mut expected = <T as Type>::signature(); |
110 | let original = signature.try_into().map_err(Into::into)?; |
111 | |
112 | if original == expected { |
113 | return Ok(PhantomData); |
114 | } |
115 | |
116 | let mut signature = original.as_ref(); |
117 | while expected.len() < signature.len() |
118 | && signature.starts_with(STRUCT_SIG_START_CHAR) |
119 | && signature.ends_with(STRUCT_SIG_END_CHAR) |
120 | { |
121 | signature = signature.slice(1..signature.len() - 1); |
122 | } |
123 | |
124 | while signature.len() < expected.len() |
125 | && expected.starts_with(STRUCT_SIG_START_CHAR) |
126 | && expected.ends_with(STRUCT_SIG_END_CHAR) |
127 | { |
128 | expected = expected.slice(1..expected.len() - 1); |
129 | } |
130 | |
131 | if signature == expected { |
132 | Ok(PhantomData) |
133 | } else { |
134 | let expected = <T as Type>::signature(); |
135 | Err(zvariant::Error::SignatureMismatch( |
136 | original.to_owned(), |
137 | format!("` {expected}`" ), |
138 | )) |
139 | } |
140 | } |
141 | } |
142 | |
143 | macro_rules! array_type { |
144 | ($arr:ty) => { |
145 | impl<T> Type for $arr |
146 | where |
147 | T: Type, |
148 | { |
149 | #[inline] |
150 | fn signature() -> Signature<'static> { |
151 | Signature::from_string_unchecked(format!("a{}" , T::signature())) |
152 | } |
153 | } |
154 | }; |
155 | } |
156 | |
157 | array_type!([T]); |
158 | array_type!(Vec<T>); |
159 | |
160 | impl<T, S> Type for std::collections::HashSet<T, S> |
161 | where |
162 | T: Type + Eq + Hash, |
163 | S: BuildHasher, |
164 | { |
165 | #[inline ] |
166 | fn signature() -> Signature<'static> { |
167 | <[T]>::signature() |
168 | } |
169 | } |
170 | |
171 | #[cfg (feature = "arrayvec" )] |
172 | impl<T, const CAP: usize> Type for arrayvec::ArrayVec<T, CAP> |
173 | where |
174 | T: Type, |
175 | { |
176 | #[inline ] |
177 | fn signature() -> Signature<'static> { |
178 | <[T]>::signature() |
179 | } |
180 | } |
181 | |
182 | #[cfg (feature = "arrayvec" )] |
183 | impl<const CAP: usize> Type for arrayvec::ArrayString<CAP> { |
184 | #[inline ] |
185 | fn signature() -> Signature<'static> { |
186 | <&str>::signature() |
187 | } |
188 | } |
189 | |
190 | #[cfg (feature = "heapless" )] |
191 | impl<T, const CAP: usize> Type for heapless::Vec<T, CAP> |
192 | where |
193 | T: Type, |
194 | { |
195 | #[inline ] |
196 | fn signature() -> Signature<'static> { |
197 | <[T]>::signature() |
198 | } |
199 | } |
200 | |
201 | #[cfg (feature = "heapless" )] |
202 | impl<const CAP: usize> Type for heapless::String<CAP> { |
203 | #[inline ] |
204 | fn signature() -> Signature<'static> { |
205 | <&str>::signature() |
206 | } |
207 | } |
208 | |
209 | // Empty type deserves empty signature |
210 | impl Type for () { |
211 | #[inline ] |
212 | fn signature() -> Signature<'static> { |
213 | Signature::from_static_str_unchecked(signature:"" ) |
214 | } |
215 | } |
216 | |
217 | macro_rules! deref_impl { |
218 | ( |
219 | $type:ty, |
220 | <$($desc:tt)+ |
221 | ) => { |
222 | impl <$($desc)+ { |
223 | #[inline] |
224 | fn signature() -> Signature<'static> { |
225 | <$type>::signature() |
226 | } |
227 | } |
228 | }; |
229 | } |
230 | |
231 | deref_impl!(T, <T: ?Sized + Type> Type for &T); |
232 | deref_impl!(T, <T: ?Sized + Type> Type for &mut T); |
233 | deref_impl!(T, <T: ?Sized + Type + ToOwned> Type for Cow<'_, T>); |
234 | deref_impl!(T, <T: ?Sized + Type> Type for Arc<T>); |
235 | deref_impl!(T, <T: ?Sized + Type> Type for Mutex<T>); |
236 | deref_impl!(T, <T: ?Sized + Type> Type for RwLock<T>); |
237 | deref_impl!(T, <T: ?Sized + Type> Type for Box<T>); |
238 | deref_impl!(T, <T: ?Sized + Type> Type for Rc<T>); |
239 | |
240 | #[cfg (all(feature = "gvariant" , not(feature = "option-as-array" )))] |
241 | impl<T> Type for Option<T> |
242 | where |
243 | T: Type, |
244 | { |
245 | #[inline ] |
246 | fn signature() -> Signature<'static> { |
247 | Signature::from_string_unchecked(format!("m{}" , T::signature())) |
248 | } |
249 | } |
250 | |
251 | #[cfg (feature = "option-as-array" )] |
252 | impl<T> Type for Option<T> |
253 | where |
254 | T: Type, |
255 | { |
256 | #[inline ] |
257 | fn signature() -> Signature<'static> { |
258 | Signature::from_string_unchecked(format!("a{}" , T::signature())) |
259 | } |
260 | } |
261 | |
262 | //////////////////////////////////////////////////////////////////////////////// |
263 | |
264 | macro_rules! tuple_impls { |
265 | ($($len:expr => ($($n:tt $name:ident)+))+) => { |
266 | $( |
267 | impl<$($name),+> Type for ($($name,)+) |
268 | where |
269 | $($name: Type,)+ |
270 | { |
271 | fn signature() -> Signature<'static> { |
272 | let mut sig = String::with_capacity(255); |
273 | sig.push(STRUCT_SIG_START_CHAR); |
274 | $( |
275 | sig.push_str($name::signature().as_str()); |
276 | )+ |
277 | sig.push(STRUCT_SIG_END_CHAR); |
278 | |
279 | Signature::from_string_unchecked(sig) |
280 | } |
281 | } |
282 | )+ |
283 | } |
284 | } |
285 | |
286 | tuple_impls! { |
287 | 1 => (0 T0) |
288 | 2 => (0 T0 1 T1) |
289 | 3 => (0 T0 1 T1 2 T2) |
290 | 4 => (0 T0 1 T1 2 T2 3 T3) |
291 | 5 => (0 T0 1 T1 2 T2 3 T3 4 T4) |
292 | 6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5) |
293 | 7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6) |
294 | 8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7) |
295 | 9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8) |
296 | 10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9) |
297 | 11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10) |
298 | 12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11) |
299 | 13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12) |
300 | 14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13) |
301 | 15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14) |
302 | 16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15) |
303 | } |
304 | |
305 | //////////////////////////////////////////////////////////////////////////////// |
306 | |
307 | // Arrays are serialized as tuples/structs by Serde so we treat them as such too even though |
308 | // it's very strange. Slices and arrayvec::ArrayVec can be used anyway so I guess it's no big |
309 | // deal. |
310 | impl<T, const N: usize> Type for [T; N] |
311 | where |
312 | T: Type, |
313 | { |
314 | #[allow (clippy::reversed_empty_ranges)] |
315 | fn signature() -> Signature<'static> { |
316 | let mut sig: String = String::with_capacity(255); |
317 | sig.push(STRUCT_SIG_START_CHAR); |
318 | for _ in 0..N { |
319 | sig.push_str(T::signature().as_str()); |
320 | } |
321 | sig.push(STRUCT_SIG_END_CHAR); |
322 | |
323 | Signature::from_string_unchecked(signature:sig) |
324 | } |
325 | } |
326 | |
327 | //////////////////////////////////////////////////////////////////////////////// |
328 | |
329 | use std::{ |
330 | borrow::Cow, |
331 | collections::{BTreeMap, HashMap}, |
332 | hash::{BuildHasher, Hash}, |
333 | time::SystemTime, |
334 | }; |
335 | |
336 | macro_rules! map_impl { |
337 | ($ty:ident < K $(: $kbound1:ident $(+ $kbound2:ident)*)*, V $(, $typaram:ident : $bound:ident)* >) => { |
338 | impl<K, V $(, $typaram)*> Type for $ty<K, V $(, $typaram)*> |
339 | where |
340 | K: Type $(+ $kbound1 $(+ $kbound2)*)*, |
341 | V: Type, |
342 | $($typaram: $bound,)* |
343 | { |
344 | #[inline] |
345 | fn signature() -> Signature<'static> { |
346 | Signature::from_string_unchecked(format!("a{{{}{}}}" , K::signature(), V::signature())) |
347 | } |
348 | } |
349 | } |
350 | } |
351 | |
352 | map_impl!(BTreeMap<K: Ord, V>); |
353 | map_impl!(HashMap<K: Eq + Hash, V, H: BuildHasher>); |
354 | |
355 | impl Type for Duration { |
356 | fn signature() -> Signature<'static> { |
357 | <(u64, u32)>::signature() |
358 | } |
359 | } |
360 | |
361 | impl Type for SystemTime { |
362 | #[inline ] |
363 | fn signature() -> Signature<'static> { |
364 | <( |
365 | // seconds |
366 | u64, |
367 | // nano |
368 | u32, |
369 | )>::signature() |
370 | } |
371 | } |
372 | |
373 | impl Type for Ipv4Addr { |
374 | #[inline ] |
375 | fn signature() -> Signature<'static> { |
376 | <[u8; 4]>::signature() |
377 | } |
378 | } |
379 | |
380 | impl Type for Ipv6Addr { |
381 | #[inline ] |
382 | fn signature() -> Signature<'static> { |
383 | <[u8; 16]>::signature() |
384 | } |
385 | } |
386 | |
387 | impl Type for IpAddr { |
388 | #[inline ] |
389 | fn signature() -> Signature<'static> { |
390 | <(u32, &[u8])>::signature() |
391 | } |
392 | } |
393 | |
394 | // BitFlags |
395 | #[cfg (feature = "enumflags2" )] |
396 | impl<F> Type for enumflags2::BitFlags<F> |
397 | where |
398 | F: Type + enumflags2::BitFlag, |
399 | { |
400 | #[inline ] |
401 | fn signature() -> Signature<'static> { |
402 | F::signature() |
403 | } |
404 | } |
405 | |
406 | #[cfg (feature = "serde_bytes" )] |
407 | impl Type for serde_bytes::Bytes { |
408 | fn signature() -> Signature<'static> { |
409 | Signature::from_static_str_unchecked("ay" ) |
410 | } |
411 | } |
412 | |
413 | #[cfg (feature = "serde_bytes" )] |
414 | impl Type for serde_bytes::ByteBuf { |
415 | fn signature() -> Signature<'static> { |
416 | Signature::from_static_str_unchecked("ay" ) |
417 | } |
418 | } |
419 | |
420 | #[allow (unused)] |
421 | macro_rules! static_str_type { |
422 | ($ty:ty) => { |
423 | impl Type for $ty { |
424 | fn signature() -> Signature<'static> { |
425 | <&str>::signature() |
426 | } |
427 | } |
428 | }; |
429 | } |
430 | |
431 | static_str_type!(Path); |
432 | static_str_type!(PathBuf); |
433 | |
434 | #[cfg (feature = "uuid" )] |
435 | impl Type for uuid::Uuid { |
436 | fn signature() -> Signature<'static> { |
437 | Signature::from_static_str_unchecked("ay" ) |
438 | } |
439 | } |
440 | |
441 | #[cfg (feature = "url" )] |
442 | static_str_type!(url::Url); |
443 | |
444 | // FIXME: Ignoring the `serde-human-readable` feature of `time` crate in these impls: |
445 | // https://github.com/time-rs/time/blob/f9398b9598757508ca3815694f23203843e0011b/src/serde/mod.rs#L110 |
446 | #[cfg (feature = "time" )] |
447 | impl Type for time::Date { |
448 | fn signature() -> Signature<'static> { |
449 | // Serialized as a (year, ordinal) tuple: |
450 | // https://github.com/time-rs/time/blob/f9398b9598757508ca3815694f23203843e0011b/src/serde/mod.rs#L92 |
451 | <(i32, u16)>::signature() |
452 | } |
453 | } |
454 | |
455 | #[cfg (feature = "time" )] |
456 | impl Type for time::Duration { |
457 | fn signature() -> Signature<'static> { |
458 | // Serialized as a (whole seconds, nanoseconds) tuple: |
459 | // https://github.com/time-rs/time/blob/f9398b9598757508ca3815694f23203843e0011b/src/serde/mod.rs#L119 |
460 | <(i64, i32)>::signature() |
461 | } |
462 | } |
463 | |
464 | #[cfg (feature = "time" )] |
465 | impl Type for time::OffsetDateTime { |
466 | fn signature() -> Signature<'static> { |
467 | // Serialized as a tuple: |
468 | // https://github.com/time-rs/time/blob/f9398b9598757508ca3815694f23203843e0011b/src/serde/mod.rs#L155 |
469 | <( |
470 | // year |
471 | i32, |
472 | // ordinal |
473 | u16, |
474 | // hour |
475 | u8, |
476 | // minute |
477 | u8, |
478 | // second |
479 | u8, |
480 | // nanosecond |
481 | u32, |
482 | // offset.whole_hours |
483 | i8, |
484 | // offset.minutes_past_hour |
485 | i8, |
486 | // offset.seconds_past_minute |
487 | i8, |
488 | )>::signature() |
489 | } |
490 | } |
491 | |
492 | #[cfg (feature = "time" )] |
493 | impl Type for time::PrimitiveDateTime { |
494 | fn signature() -> Signature<'static> { |
495 | // Serialized as a tuple: |
496 | // https://github.com/time-rs/time/blob/f9398b9598757508ca3815694f23203843e0011b/src/serde/mod.rs#L200 |
497 | <( |
498 | // year |
499 | i32, |
500 | // ordinal |
501 | u16, |
502 | // hour |
503 | u8, |
504 | // minute |
505 | u8, |
506 | // second |
507 | u8, |
508 | // nanosecond |
509 | u32, |
510 | )>::signature() |
511 | } |
512 | } |
513 | |
514 | #[cfg (feature = "time" )] |
515 | impl Type for time::Time { |
516 | fn signature() -> Signature<'static> { |
517 | // Serialized as a tuple: |
518 | // https://github.com/time-rs/time/blob/f9398b9598757508ca3815694f23203843e0011b/src/serde/mod.rs#L246 |
519 | <( |
520 | // hour |
521 | u8, |
522 | // minute |
523 | u8, |
524 | // second |
525 | u8, |
526 | // nanosecond |
527 | u32, |
528 | )>::signature() |
529 | } |
530 | } |
531 | |
532 | #[cfg (feature = "time" )] |
533 | impl Type for time::UtcOffset { |
534 | fn signature() -> Signature<'static> { |
535 | // Serialized as a (whole hours, minutes past hour, seconds past minute) tuple: |
536 | // https://github.com/time-rs/time/blob/f9398b9598757508ca3815694f23203843e0011b/src/serde/mod.rs#L282 |
537 | <(i8, i8, i8)>::signature() |
538 | } |
539 | } |
540 | |
541 | #[cfg (feature = "time" )] |
542 | impl Type for time::Weekday { |
543 | fn signature() -> Signature<'static> { |
544 | // Serialized as number from Monday: |
545 | // https://github.com/time-rs/time/blob/f9398b9598757508ca3815694f23203843e0011b/src/serde/mod.rs#L312 |
546 | u8::signature() |
547 | } |
548 | } |
549 | |
550 | #[cfg (feature = "time" )] |
551 | impl Type for time::Month { |
552 | fn signature() -> Signature<'static> { |
553 | // Serialized as month number: |
554 | // https://github.com/time-rs/time/blob/f9398b9598757508ca3815694f23203843e0011b/src/serde/mod.rs#L337 |
555 | u8::signature() |
556 | } |
557 | } |
558 | |
559 | #[cfg (feature = "chrono" )] |
560 | impl<Tz: chrono::TimeZone> Type for chrono::DateTime<Tz> { |
561 | fn signature() -> Signature<'static> { |
562 | <&str>::signature() |
563 | } |
564 | } |
565 | |
566 | #[cfg (feature = "chrono" )] |
567 | static_str_type!(chrono::NaiveDateTime); |
568 | #[cfg (feature = "chrono" )] |
569 | static_str_type!(chrono::NaiveTime); |
570 | |
571 | // TODO: Blanket implementation for more types: https://github.com/serde-rs/serde/blob/master/serde/src/ser/impls.rs |
572 | |