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}