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