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
12pub trait Type {
35 fn signature() -> Signature<'static>;
50}
51
52pub trait DynamicType {
58 fn dynamic_signature(&self) -> Signature<'_>;
62}
63
64pub trait DynamicDeserialize<'de>: DynamicType {
70 type Deserializer: DeserializeSeed<'de, Value = Self> + DynamicType;
72
73 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
209impl 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
262macro_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
305impl<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
327use 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 u64,
367 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#[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#[cfg(feature = "time")]
447impl Type for time::Date {
448 fn signature() -> Signature<'static> {
449 <(i32, u16)>::signature()
452 }
453}
454
455#[cfg(feature = "time")]
456impl Type for time::Duration {
457 fn signature() -> Signature<'static> {
458 <(i64, i32)>::signature()
461 }
462}
463
464#[cfg(feature = "time")]
465impl Type for time::OffsetDateTime {
466 fn signature() -> Signature<'static> {
467 <(
470 i32,
472 u16,
474 u8,
476 u8,
478 u8,
480 u32,
482 i8,
484 i8,
486 i8,
488 )>::signature()
489 }
490}
491
492#[cfg(feature = "time")]
493impl Type for time::PrimitiveDateTime {
494 fn signature() -> Signature<'static> {
495 <(
498 i32,
500 u16,
502 u8,
504 u8,
506 u8,
508 u32,
510 )>::signature()
511 }
512}
513
514#[cfg(feature = "time")]
515impl Type for time::Time {
516 fn signature() -> Signature<'static> {
517 <(
520 u8,
522 u8,
524 u8,
526 u32,
528 )>::signature()
529 }
530}
531
532#[cfg(feature = "time")]
533impl Type for time::UtcOffset {
534 fn signature() -> Signature<'static> {
535 <(i8, i8, i8)>::signature()
538 }
539}
540
541#[cfg(feature = "time")]
542impl Type for time::Weekday {
543 fn signature() -> Signature<'static> {
544 u8::signature()
547 }
548}
549
550#[cfg(feature = "time")]
551impl Type for time::Month {
552 fn signature() -> Signature<'static> {
553 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