zvariant/
type.rs

1use crate::{utils::*, Signature};
2use serde::de::{Deserialize, DeserializeSeed};
3use 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/
34pub 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.
57pub 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.
69pub 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
80impl<T> DynamicType for T
81where
82    T: Type + ?Sized,
83{
84    fn dynamic_signature(&self) -> Signature<'_> {
85        <T as Type>::signature()
86    }
87}
88
89impl<T> Type for PhantomData<T>
90where
91    T: Type + ?Sized,
92{
93    fn signature() -> Signature<'static> {
94        T::signature()
95    }
96}
97
98impl<'de, T> DynamicDeserialize<'de> for T
99where
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
143macro_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
157array_type!([T]);
158array_type!(Vec<T>);
159
160impl<T, S> Type for std::collections::HashSet<T, S>
161where
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")]
172impl<T, const CAP: usize> Type for arrayvec::ArrayVec<T, CAP>
173where
174    T: Type,
175{
176    #[inline]
177    fn signature() -> Signature<'static> {
178        <[T]>::signature()
179    }
180}
181
182#[cfg(feature = "arrayvec")]
183impl<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")]
191impl<T, const CAP: usize> Type for heapless::Vec<T, CAP>
192where
193    T: Type,
194{
195    #[inline]
196    fn signature() -> Signature<'static> {
197        <[T]>::signature()
198    }
199}
200
201#[cfg(feature = "heapless")]
202impl<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
210impl Type for () {
211    #[inline]
212    fn signature() -> Signature<'static> {
213        Signature::from_static_str_unchecked("")
214    }
215}
216
217macro_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
231deref_impl!(T, <T: ?Sized + Type> Type for &T);
232deref_impl!(T, <T: ?Sized + Type> Type for &mut T);
233deref_impl!(T, <T: ?Sized + Type + ToOwned> Type for Cow<'_, T>);
234deref_impl!(T, <T: ?Sized + Type> Type for Arc<T>);
235deref_impl!(T, <T: ?Sized + Type> Type for Mutex<T>);
236deref_impl!(T, <T: ?Sized + Type> Type for RwLock<T>);
237deref_impl!(T, <T: ?Sized + Type> Type for Box<T>);
238deref_impl!(T, <T: ?Sized + Type> Type for Rc<T>);
239
240#[cfg(all(feature = "gvariant", not(feature = "option-as-array")))]
241impl<T> Type for Option<T>
242where
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")]
252impl<T> Type for Option<T>
253where
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
264macro_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
286tuple_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.
310impl<T, const N: usize> Type for [T; N]
311where
312    T: Type,
313{
314    #[allow(clippy::reversed_empty_ranges)]
315    fn signature() -> Signature<'static> {
316        let mut sig = 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(sig)
324    }
325}
326
327////////////////////////////////////////////////////////////////////////////////
328
329use std::{
330    borrow::Cow,
331    collections::{BTreeMap, HashMap},
332    hash::{BuildHasher, Hash},
333    time::SystemTime,
334};
335
336macro_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
352map_impl!(BTreeMap<K: Ord, V>);
353map_impl!(HashMap<K: Eq + Hash, V, H: BuildHasher>);
354
355impl Type for Duration {
356    fn signature() -> Signature<'static> {
357        <(u64, u32)>::signature()
358    }
359}
360
361impl Type for SystemTime {
362    #[inline]
363    fn signature() -> Signature<'static> {
364        <(
365            // seconds
366            u64,
367            // nano
368            u32,
369        )>::signature()
370    }
371}
372
373impl Type for Ipv4Addr {
374    #[inline]
375    fn signature() -> Signature<'static> {
376        <[u8; 4]>::signature()
377    }
378}
379
380impl Type for Ipv6Addr {
381    #[inline]
382    fn signature() -> Signature<'static> {
383        <[u8; 16]>::signature()
384    }
385}
386
387impl Type for IpAddr {
388    #[inline]
389    fn signature() -> Signature<'static> {
390        <(u32, &[u8])>::signature()
391    }
392}
393
394// BitFlags
395#[cfg(feature = "enumflags2")]
396impl<F> Type for enumflags2::BitFlags<F>
397where
398    F: Type + enumflags2::BitFlag,
399{
400    #[inline]
401    fn signature() -> Signature<'static> {
402        F::signature()
403    }
404}
405
406#[cfg(feature = "serde_bytes")]
407impl 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")]
414impl Type for serde_bytes::ByteBuf {
415    fn signature() -> Signature<'static> {
416        Signature::from_static_str_unchecked("ay")
417    }
418}
419
420#[allow(unused)]
421macro_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
431static_str_type!(Path);
432static_str_type!(PathBuf);
433
434#[cfg(feature = "uuid")]
435impl Type for uuid::Uuid {
436    fn signature() -> Signature<'static> {
437        Signature::from_static_str_unchecked("ay")
438    }
439}
440
441#[cfg(feature = "url")]
442static_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")]
447impl 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")]
456impl 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")]
465impl 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")]
493impl 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")]
515impl 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")]
533impl 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")]
542impl 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")]
551impl 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")]
560impl<Tz: chrono::TimeZone> Type for chrono::DateTime<Tz> {
561    fn signature() -> Signature<'static> {
562        <&str>::signature()
563    }
564}
565
566#[cfg(feature = "chrono")]
567static_str_type!(chrono::NaiveDateTime);
568#[cfg(feature = "chrono")]
569static_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