zvariant/
type.rs

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