zvariant/type/
mod.rs

1mod dynamic;
2pub use dynamic::{DynamicDeserialize, DynamicType};
3#[cfg(feature = "serde_bytes")]
4mod bytes;
5#[cfg(feature = "enumflags2")]
6mod enumflags2;
7mod libstd;
8mod net;
9mod paths;
10mod time;
11#[cfg(feature = "uuid")]
12mod uuid;
13
14use crate::Signature;
15
16/// Trait implemented by all serializable types.
17///
18/// This very simple trait provides the signature for the implementing type. Since the [D-Bus type
19/// system] relies on these signatures, our [serialization] and [deserialization] API requires this
20/// trait in addition to [`trait@serde::Serialize`] and [`serde::de::Deserialize`], respectively.
21///
22/// Implementation is provided for all the [basic types](crate::Basic) and blanket implementations
23/// for common container types, such as, arrays, slices, tuples, [`Vec`] and
24/// [`std::collections::HashMap`]. For easy implementation for custom types, use `Type` derive macro
25/// from [zvariant_derive] crate.
26///
27/// If your type's signature cannot be determined statically, you should implement the
28/// [`DynamicType`] trait instead, which is otherwise automatically implemented if you implement
29/// this trait.
30///
31/// [D-Bus type system]: https://dbus.freedesktop.org/doc/dbus-specification.html#type-system
32/// [serialization]: zvariant#functions
33/// [deserialization]: zvariant::serialized::Data::deserialize
34/// [zvariant_derive]: https://docs.rs/zvariant_derive/latest/zvariant_derive/
35pub trait Type {
36    /// The signature for the implementing type, in parsed format.
37    ///
38    /// # Example
39    ///
40    /// ```
41    /// use std::collections::HashMap;
42    /// use zvariant::{Type, signature::{Child, Signature}};
43    ///
44    /// assert_eq!(u32::SIGNATURE, &Signature::U32);
45    /// assert_eq!(String::SIGNATURE, &Signature::Str);
46    /// assert_eq!(
47    ///     <(u32, &str, u64)>::SIGNATURE,
48    ///     &Signature::static_structure(&[&Signature::U32, &Signature::Str, &Signature::U64]),
49    /// );
50    /// assert_eq!(
51    ///     <(u32, &str, &[u64])>::SIGNATURE,
52    ///     &Signature::static_structure(&[
53    ///         &Signature::U32,
54    ///         &Signature::Str,
55    ///         &Signature::Array(Child::Static { child: &Signature::U64 }),
56    ///     ]),
57    /// );
58    /// assert_eq!(
59    ///     <HashMap<u8, &str>>::SIGNATURE,
60    ///     &Signature::static_dict(&Signature::U8, &Signature::Str),
61    /// );
62    /// ```
63    const SIGNATURE: &'static Signature;
64}
65
66/// Implements the [`Type`] trait by delegating the signature to a simpler type (usually a tuple).
67/// Tests that ensure that the two types are serialize-compatible are auto-generated.
68///
69/// Example:
70/// ```no_compile
71/// impl_type_with_repr! {
72///    // Duration is serialized as a (u64, u32) pair.
73///    Duration => (u64, u32) {
74///        // The macro auto-generates tests for us,
75///        // so we need to provide a test name.
76///        duration {
77///            // Sample values used to test serialize compatibility.
78///            samples = [Duration::ZERO, Duration::MAX],
79///            // Converts our type into the simpler "repr" type.
80///            repr(d) = (d.as_secs(), d.subsec_nanos()),
81///        }
82///    }
83/// }
84/// ```
85#[macro_export]
86macro_rules! impl_type_with_repr {
87    ($($ty:ident)::+ $(<$typaram:ident $(: $($tbound:ident)::+)?>)? => $repr:ty {
88        $test_mod:ident $(<$($typaram_sample:ident = $typaram_sample_value:ty),*>)? {
89            $(signature = $signature:literal,)?
90            samples = $samples:expr,
91            repr($sample_ident:ident) = $into_repr:expr,
92        }
93    }) => {
94        impl $(<$typaram $(: $($tbound)::+)?>)? $crate::Type for $($ty)::+ $(<$typaram>)? {
95            const SIGNATURE: &'static $crate::Signature = <$repr>::SIGNATURE;
96        }
97
98        #[cfg(test)]
99        #[allow(unused_imports)]
100        mod $test_mod {
101            use super::*;
102            use $crate::{serialized::Context, to_bytes, LE};
103
104            $($(type $typaram_sample = $typaram_sample_value;)*)?
105            type Ty = $($ty)::+$(<$typaram>)?;
106
107            const _: fn() = || {
108                fn assert_impl_all<'de, T: ?Sized + serde::Serialize + serde::Deserialize<'de>>() {}
109                assert_impl_all::<Ty>();
110            };
111
112            #[test]
113            fn type_can_be_deserialized_from_encoded_type() {
114                let ctx = Context::new_dbus(LE, 0);
115                let samples = $samples;
116                let _: &[Ty] = &samples;
117
118                for $sample_ident in samples {
119                    let encoded = to_bytes(ctx, &$sample_ident).unwrap();
120                    let (decoded, _): (Ty, _) = encoded.deserialize().unwrap();
121                    assert_eq!($sample_ident, decoded);
122                }
123            }
124
125            #[test]
126            fn repr_can_be_deserialized_from_encoded_type() {
127                let ctx = Context::new_dbus(LE, 0);
128                let samples = $samples;
129                let _: &[Ty] = &samples;
130
131                for $sample_ident in samples {
132                    let repr: $repr = $into_repr;
133                    let encoded = to_bytes(ctx, &$sample_ident).unwrap();
134                    let (decoded, _): ($repr, _) = encoded.deserialize().unwrap();
135                    assert_eq!(repr, decoded);
136                }
137            }
138
139            #[test]
140            fn type_can_be_deserialized_from_encoded_repr() {
141                let ctx = Context::new_dbus(LE, 0);
142                let samples = $samples;
143                let _: &[Ty] = &samples;
144
145                for $sample_ident in samples {
146                    let repr: $repr = $into_repr;
147                    let encoded = to_bytes(ctx, &repr).unwrap();
148                    let (decoded, _): (Ty, _) = encoded.deserialize().unwrap();
149                    assert_eq!($sample_ident, decoded);
150                }
151            }
152
153            #[test]
154            fn encoding_of_type_and_repr_match() {
155                let ctx = Context::new_dbus(LE, 0);
156                let samples = $samples;
157                let _: &[Ty] = &samples;
158
159                for $sample_ident in samples {
160                    let repr: $repr = $into_repr;
161                    let encoded = to_bytes(ctx, &$sample_ident).unwrap();
162                    let encoded_repr = to_bytes(ctx, &repr).unwrap();
163                    assert_eq!(encoded.bytes(), encoded_repr.bytes());
164                }
165            }
166
167            $(
168                #[test]
169                fn signature_equals() {
170                    assert_eq!(<Ty as $crate::Type>::SIGNATURE, $signature);
171                }
172            )?
173        }
174    };
175}
176
177#[macro_export]
178#[allow(unused)]
179macro_rules! static_str_type {
180    ($ty:ty) => {
181        impl Type for $ty {
182            const SIGNATURE: &'static Signature = &Signature::Str;
183        }
184    };
185}