zvariant_derive/lib.rs
1#![deny(rust_2018_idioms)]
2#![doc(
3 html_logo_url = "https://raw.githubusercontent.com/z-galaxy/zbus/9f7a90d2b594ddc48b7a5f39fda5e00cd56a7dfb/logo.png"
4)]
5#![doc = include_str!("../README.md")]
6#![doc(test(attr(
7 warn(unused),
8 deny(warnings),
9 allow(dead_code),
10 // W/o this, we seem to get some bogus warning about `extern crate zbus`.
11 allow(unused_extern_crates),
12)))]
13
14use proc_macro::TokenStream;
15use syn::DeriveInput;
16
17mod utils;
18
19/// Derive macro to add [`Type`] implementation to structs and enums.
20///
21/// # Examples
22///
23/// For structs it works just like serde's [`Serialize`] and [`Deserialize`] macros:
24///
25/// ```
26/// use zvariant::{serialized::Context, to_bytes, Type, LE};
27/// use serde::{Deserialize, Serialize};
28///
29/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
30/// struct Struct<'s> {
31/// field1: u16,
32/// field2: i64,
33/// field3: &'s str,
34/// }
35///
36/// assert_eq!(Struct::SIGNATURE, "(qxs)");
37/// let s = Struct {
38/// field1: 42,
39/// field2: i64::max_value(),
40/// field3: "hello",
41/// };
42/// let ctxt = Context::new_dbus(LE, 0);
43/// let encoded = to_bytes(ctxt, &s).unwrap();
44/// let decoded: Struct = encoded.deserialize().unwrap().0;
45/// assert_eq!(decoded, s);
46/// ```
47///
48/// Same with enum, except that all variants of the enum must have the same number and types of
49/// fields (if any). If you want the encoding size of the (unit-type) enum to be dictated by
50/// `repr` attribute (like in the example below), you'll also need [serde_repr] crate.
51///
52/// ```
53/// use zvariant::{serialized::Context, to_bytes, Type, LE};
54/// use serde::{Deserialize, Serialize};
55/// use serde_repr::{Deserialize_repr, Serialize_repr};
56///
57/// #[repr(u8)]
58/// #[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq)]
59/// enum Enum {
60/// Variant1,
61/// Variant2,
62/// }
63/// assert_eq!(Enum::SIGNATURE, u8::SIGNATURE);
64/// let ctxt = Context::new_dbus(LE, 0);
65/// let encoded = to_bytes(ctxt, &Enum::Variant2).unwrap();
66/// let decoded: Enum = encoded.deserialize().unwrap().0;
67/// assert_eq!(decoded, Enum::Variant2);
68///
69/// #[repr(i64)]
70/// #[derive(Deserialize_repr, Serialize_repr, Type)]
71/// enum Enum2 {
72/// Variant1,
73/// Variant2,
74/// }
75/// assert_eq!(Enum2::SIGNATURE, i64::SIGNATURE);
76///
77/// // w/o repr attribute, u32 representation is chosen
78/// #[derive(Deserialize, Serialize, Type)]
79/// enum NoReprEnum {
80/// Variant1,
81/// Variant2,
82/// }
83/// assert_eq!(NoReprEnum::SIGNATURE, u32::SIGNATURE);
84///
85/// // Not-unit enums are represented as a structure, with the first field being a u32 denoting the
86/// // variant and the second as the actual value.
87/// #[derive(Deserialize, Serialize, Type)]
88/// enum NewType {
89/// Variant1(f64),
90/// Variant2(f64),
91/// }
92/// assert_eq!(NewType::SIGNATURE, "(ud)");
93///
94/// #[derive(Deserialize, Serialize, Type)]
95/// enum StructFields {
96/// Variant1(u16, i64, &'static str),
97/// Variant2 { field1: u16, field2: i64, field3: &'static str },
98/// }
99/// assert_eq!(StructFields::SIGNATURE, "(u(qxs))");
100/// ```
101///
102/// # Custom signatures
103///
104/// There are times when you'd find yourself wanting to specify a hardcoded signature yourself for
105/// the type. The `signature` attribute exists for this purpose. A typical use case is when you'd
106/// need to encode your type as a dictionary (signature `a{sv}`) type. For convenience, `dict` is
107/// an alias for `a{sv}`. Here is an example:
108///
109/// ```
110/// use zvariant::{
111/// serialized::Context, as_value, to_bytes, Type, LE,
112/// };
113/// use serde::{Deserialize, Serialize};
114///
115/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
116/// // `#[zvariant(signature = "a{sv}")]` would be the same.
117/// #[zvariant(signature = "dict")]
118/// struct Struct {
119/// #[serde(with = "as_value")]
120/// field1: u16,
121/// #[serde(with = "as_value")]
122/// field2: i64,
123/// #[serde(with = "as_value")]
124/// field3: String,
125/// }
126///
127/// assert_eq!(Struct::SIGNATURE, "a{sv}");
128/// let s = Struct {
129/// field1: 42,
130/// field2: i64::max_value(),
131/// field3: "hello".to_string(),
132/// };
133/// let ctxt = Context::new_dbus(LE, 0);
134/// let encoded = to_bytes(ctxt, &s).unwrap();
135/// let decoded: Struct = encoded.deserialize().unwrap().0;
136/// assert_eq!(decoded, s);
137/// ```
138///
139/// Another common use for custom signatures is (de)serialization of unit enums as strings:
140///
141/// ```
142/// use zvariant::{serialized::Context, to_bytes, Type, LE};
143/// use serde::{Deserialize, Serialize};
144///
145/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
146/// #[zvariant(signature = "s")]
147/// enum StrEnum {
148/// Variant1,
149/// Variant2,
150/// Variant3,
151/// }
152///
153/// assert_eq!(StrEnum::SIGNATURE, "s");
154/// let ctxt = Context::new_dbus(LE, 0);
155/// let encoded = to_bytes(ctxt, &StrEnum::Variant2).unwrap();
156/// assert_eq!(encoded.len(), 13);
157/// let decoded: StrEnum = encoded.deserialize().unwrap().0;
158/// assert_eq!(decoded, StrEnum::Variant2);
159/// ```
160///
161/// # Custom crate path
162///
163/// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export,
164/// you can specify the crate path using the `crate` attribute:
165///
166/// ```
167/// use zvariant::Type;
168///
169/// #[derive(Type)]
170/// #[zvariant(crate = "zvariant")]
171/// struct MyStruct {
172/// field: String,
173/// }
174/// ```
175///
176/// [`Type`]: https://docs.rs/zvariant/latest/zvariant/trait.Type.html
177/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
178/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
179/// [serde_repr]: https://crates.io/crates/serde_repr
180#[proc_macro_derive(Type, attributes(zbus, zvariant))]
181pub fn type_macro_derive(input: TokenStream) -> TokenStream {
182 let ast: DeriveInput = syn::parse(input).unwrap();
183 zvariant_utils::derive::expand_type_derive(ast, &utils::config())
184 .unwrap_or_else(|err| err.to_compile_error())
185 .into()
186}
187
188/// Adds [`Serialize`] implementation to structs to be serialized as a D-Bus dictionary type.
189///
190/// The dictionary type is determined by the `signature` attribute. The default is `a{sv}`
191/// (string keys, variant values), but nested forms like `a{sa{sv}}` and `a{oa{sv}}` are also
192/// supported — fields whose value type is itself a dict (or any non-`Variant` type) are
193/// serialized directly through their own `Serialize` impl rather than wrapped as a variant.
194///
195/// Such dictionary types are very commonly used with
196/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
197/// and GVariant.
198///
199/// # Alternative Approaches
200///
201/// There are two approaches to serializing structs as dictionaries:
202///
203/// 1. Using this macro (simpler, but less control).
204/// 2. Using the `Serialize` derive with `zvariant::as_value` (more verbose, but more control).
205///
206/// See the example below and the relevant [FAQ entry] in our book for more details on the
207/// alternative approach.
208///
209/// # Example
210///
211/// ## Approach #1
212///
213/// ```
214/// use zvariant::{SerializeDict, Type};
215///
216/// #[derive(Debug, Default, SerializeDict, Type)]
217/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
218/// pub struct MyStruct {
219/// field1: Option<u32>,
220/// field2: String,
221/// }
222/// ```
223///
224/// ## Approach #2
225///
226/// ```
227/// use serde::Serialize;
228/// use zvariant::{Type, as_value};
229///
230/// #[derive(Debug, Default, Serialize, Type)]
231/// #[zvariant(signature = "a{sv}")]
232/// #[serde(default, rename_all = "PascalCase")]
233/// pub struct MyStruct {
234/// #[serde(with = "as_value::optional", skip_serializing_if = "Option::is_none")]
235/// field1: Option<u32>,
236/// #[serde(with = "as_value")]
237/// field2: String,
238/// }
239/// ```
240///
241/// ## Nested dictionaries
242///
243/// To represent shapes like `a{sa{sv}}` (the body type of
244/// `org.freedesktop.DBus.ObjectManager.GetManagedObjects` and similar APIs), nest one
245/// `SerializeDict`/`DeserializeDict` struct inside another:
246///
247/// ```
248/// use zvariant::{DeserializeDict, SerializeDict, Type};
249///
250/// #[derive(SerializeDict, DeserializeDict, Type, Default)]
251/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
252/// pub struct AdapterProperties {
253/// address: Option<String>,
254/// name: Option<String>,
255/// }
256///
257/// #[derive(SerializeDict, DeserializeDict, Type, Default)]
258/// #[zvariant(signature = "a{sa{sv}}")]
259/// pub struct InterfaceProperties {
260/// #[zvariant(rename = "org.bluez.Adapter1")]
261/// adapter: Option<AdapterProperties>,
262/// }
263/// ```
264///
265/// # Custom crate path
266///
267/// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export,
268/// you can specify the crate path using the `crate` attribute:
269///
270/// ```
271/// use zvariant::{SerializeDict, Type};
272///
273/// #[derive(SerializeDict, Type)]
274/// #[zvariant(signature = "a{sv}", crate = "zvariant")]
275/// struct MyStruct {
276/// field: String,
277/// }
278/// ```
279///
280/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
281/// [FAQ entry]: https://z-galaxy.github.io/zbus/faq.html#how-to-use-a-struct-as-a-dictionary
282#[proc_macro_derive(SerializeDict, attributes(zbus, zvariant))]
283pub fn serialize_dict_macro_derive(input: TokenStream) -> TokenStream {
284 let input: DeriveInput = syn::parse(input).unwrap();
285 zvariant_utils::derive::expand_serialize_dict_derive(input, &utils::config())
286 .unwrap_or_else(|err| err.to_compile_error())
287 .into()
288}
289
290/// Adds [`Deserialize`] implementation to structs to be deserialized from a D-Bus dictionary type.
291///
292/// The dictionary type is determined by the `signature` attribute. The default is `a{sv}`
293/// (string keys, variant values), but nested forms like `a{sa{sv}}` and `a{oa{sv}}` are also
294/// supported — fields whose value type is itself a dict (or any non-`Variant` type) are
295/// deserialized directly through their own `Deserialize` impl rather than unwrapped from a
296/// variant. See [`SerializeDict`] for a nested example.
297///
298/// Such dictionary types are very commonly used with
299/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
300/// and GVariant.
301///
302/// # Alternative Approaches
303///
304/// There are two approaches to deserializing dictionaries as structs:
305///
306/// 1. Using this macro (simpler, but less control).
307/// 2. Using the `Deserialize` derive with `zvariant::as_value` (more verbose, but more control).
308///
309/// See the example below and the relevant [FAQ entry] in our book for more details on the
310/// alternative approach.
311///
312/// # Example
313///
314/// ## Approach #1
315///
316/// ```
317/// use zvariant::{DeserializeDict, Type};
318///
319/// #[derive(Debug, Default, DeserializeDict, Type)]
320/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
321/// pub struct MyStruct {
322/// field1: Option<u32>,
323/// field2: String,
324/// }
325/// ```
326///
327/// ## Approach #2
328///
329/// ```
330/// use serde::Deserialize;
331/// use zvariant::{Type, as_value};
332///
333/// #[derive(Debug, Default, Deserialize, Type)]
334/// #[zvariant(signature = "a{sv}")]
335/// #[serde(default, rename_all = "PascalCase")]
336/// pub struct MyStruct {
337/// #[serde(with = "as_value::optional")]
338/// field1: Option<u32>,
339/// #[serde(with = "as_value")]
340/// field2: String,
341/// }
342/// ```
343///
344/// # Custom crate path
345///
346/// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export,
347/// you can specify the crate path using the `crate` attribute:
348///
349/// ```
350/// use zvariant::{DeserializeDict, Type};
351///
352/// #[derive(DeserializeDict, Type)]
353/// #[zvariant(signature = "a{sv}", crate = "zvariant")]
354/// struct MyStruct {
355/// field: String,
356/// }
357/// ```
358///
359/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
360/// [FAQ entry]: https://z-galaxy.github.io/zbus/faq.html#how-to-use-a-struct-as-a-dictionary
361#[proc_macro_derive(DeserializeDict, attributes(zbus, zvariant))]
362pub fn deserialize_dict_macro_derive(input: TokenStream) -> TokenStream {
363 let input: DeriveInput = syn::parse(input).unwrap();
364 zvariant_utils::derive::expand_deserialize_dict_derive(input, &utils::config())
365 .unwrap_or_else(|err| err.to_compile_error())
366 .into()
367}
368
369/// Implements conversions for your type to/from [`Value`].
370///
371/// Implements `TryFrom<Value>` and `Into<Value>` for your type.
372///
373/// # Examples
374///
375/// Simple owned strutures:
376///
377/// ```
378/// use zvariant::{OwnedObjectPath, OwnedValue, Value};
379///
380/// #[derive(Clone, Value, OwnedValue)]
381/// struct OwnedStruct {
382/// owned_str: String,
383/// owned_path: OwnedObjectPath,
384/// }
385///
386/// let s = OwnedStruct {
387/// owned_str: String::from("hi"),
388/// owned_path: OwnedObjectPath::try_from("/blah").unwrap(),
389/// };
390/// let value = Value::from(s.clone());
391/// let _ = OwnedStruct::try_from(value).unwrap();
392/// let value = OwnedValue::try_from(s).unwrap();
393/// let s = OwnedStruct::try_from(value).unwrap();
394/// assert_eq!(s.owned_str, "hi");
395/// assert_eq!(s.owned_path.as_str(), "/blah");
396/// ```
397///
398/// Now for the more exciting case of unowned structures:
399///
400/// ```
401/// use zvariant::{ObjectPath, Str};
402/// # use zvariant::{OwnedValue, Value};
403/// #
404/// #[derive(Clone, Value, OwnedValue)]
405/// struct UnownedStruct<'a> {
406/// s: Str<'a>,
407/// path: ObjectPath<'a>,
408/// }
409///
410/// let hi = String::from("hi");
411/// let s = UnownedStruct {
412/// s: Str::from(&hi),
413/// path: ObjectPath::try_from("/blah").unwrap(),
414/// };
415/// let value = Value::from(s.clone());
416/// let s = UnownedStruct::try_from(value).unwrap();
417///
418/// let value = OwnedValue::try_from(s).unwrap();
419/// let s = UnownedStruct::try_from(value).unwrap();
420/// assert_eq!(s.s, "hi");
421/// assert_eq!(s.path, "/blah");
422/// ```
423///
424/// Generic structures also supported:
425///
426/// ```
427/// # use zvariant::{OwnedObjectPath, OwnedValue, Value};
428/// #
429/// #[derive(Clone, Value, OwnedValue)]
430/// struct GenericStruct<S, O> {
431/// field1: S,
432/// field2: O,
433/// }
434///
435/// let s = GenericStruct {
436/// field1: String::from("hi"),
437/// field2: OwnedObjectPath::try_from("/blah").unwrap(),
438/// };
439/// let value = Value::from(s.clone());
440/// let _ = GenericStruct::<String, OwnedObjectPath>::try_from(value).unwrap();
441/// let value = OwnedValue::try_from(s).unwrap();
442/// let s = GenericStruct::<String, OwnedObjectPath>::try_from(value).unwrap();
443/// assert_eq!(s.field1, "hi");
444/// assert_eq!(s.field2.as_str(), "/blah");
445/// ```
446///
447/// Enums also supported but currently only with unit variants:
448///
449/// ```
450/// # use zvariant::{OwnedValue, Value};
451/// #
452/// #[derive(Debug, PartialEq, Value, OwnedValue)]
453/// // Default representation is `u32`.
454/// #[repr(u8)]
455/// enum Enum {
456/// Variant1 = 0,
457/// Variant2,
458/// }
459///
460/// let value = Value::from(Enum::Variant1);
461/// let e = Enum::try_from(value).unwrap();
462/// assert_eq!(e, Enum::Variant1);
463/// assert_eq!(e as u8, 0);
464/// let value = OwnedValue::try_from(Enum::Variant2).unwrap();
465/// let e = Enum::try_from(value).unwrap();
466/// assert_eq!(e, Enum::Variant2);
467/// ```
468///
469/// String-encoded enums are also supported:
470///
471/// ```
472/// # use zvariant::{OwnedValue, Value};
473/// #
474/// #[derive(Debug, PartialEq, Value, OwnedValue)]
475/// #[zvariant(signature = "s")]
476/// enum StrEnum {
477/// Variant1,
478/// Variant2,
479/// }
480///
481/// let value = Value::from(StrEnum::Variant1);
482/// let e = StrEnum::try_from(value).unwrap();
483/// assert_eq!(e, StrEnum::Variant1);
484/// let value = OwnedValue::try_from(StrEnum::Variant2).unwrap();
485/// let e = StrEnum::try_from(value).unwrap();
486/// assert_eq!(e, StrEnum::Variant2);
487/// ```
488///
489/// # Renaming fields
490///
491/// ## Auto Renaming
492///
493/// The macro supports specifying a Serde-like `#[zvariant(rename_all = "case")]` attribute on
494/// structures. The attribute allows to rename all the fields from snake case to another case
495/// automatically.
496///
497/// Currently the macro supports the following values for `case`:
498///
499/// * `"lowercase"`
500/// * `"UPPERCASE"`
501/// * `"PascalCase"`
502/// * `"camelCase"`
503/// * `"snake_case"`
504/// * `"kebab-case"`
505///
506/// ## Individual Fields
507///
508/// It's still possible to specify custom names for individual fields using the
509/// `#[zvariant(rename = "another-name")]` attribute even when the `rename_all` attribute is
510/// present.
511///
512/// Here is an example using both `rename` and `rename_all`:
513///
514/// ```
515/// # use zvariant::{OwnedValue, Value, Dict};
516/// # use std::collections::HashMap;
517/// #
518/// #[derive(Clone, Value, OwnedValue)]
519/// #[zvariant(signature = "dict", rename_all = "PascalCase")]
520/// struct RenamedStruct {
521/// #[zvariant(rename = "MyValue")]
522/// field1: String,
523/// field2: String,
524/// }
525///
526/// let s = RenamedStruct {
527/// field1: String::from("hello"),
528/// field2: String::from("world")
529/// };
530/// let v = Value::from(s);
531/// let d = Dict::try_from(v).unwrap();
532/// let hm: HashMap<String, String> = HashMap::try_from(d).unwrap();
533/// assert_eq!(hm.get("MyValue").unwrap().as_str(), "hello");
534/// assert_eq!(hm.get("Field2").unwrap().as_str(), "world");
535/// ```
536///
537/// # Dictionary encoding
538///
539/// For treating your type as a dictionary, you can use the `signature = "dict"` attribute. See
540/// [`Type`] for more details and an example use. Please note that this macro can only handle
541/// `dict` or `a{sv}` values. All other values will be ignored.
542///
543/// # Custom crate path
544///
545/// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export,
546/// you can specify the crate path using the `crate` attribute:
547///
548/// ```
549/// use zvariant::Value;
550///
551/// #[derive(Clone, Value)]
552/// #[zvariant(crate = "zvariant")]
553/// struct MyStruct {
554/// field: String,
555/// }
556/// ```
557///
558/// [`Value`]: https://docs.rs/zvariant/latest/zvariant/enum.Value.html
559/// [`Type`]: crate::Type#custom-signatures
560#[proc_macro_derive(Value, attributes(zbus, zvariant))]
561pub fn value_macro_derive(input: TokenStream) -> TokenStream {
562 let ast: DeriveInput = syn::parse(input).unwrap();
563 zvariant_utils::derive::expand_value_derive(
564 ast,
565 zvariant_utils::derive::ValueType::Value,
566 &utils::config(),
567 )
568 .unwrap_or_else(|err| err.to_compile_error())
569 .into()
570}
571
572/// Implements conversions for your type to/from [`OwnedValue`].
573///
574/// Implements `TryFrom<OwnedValue>` and `TryInto<OwnedValue>` for your type.
575///
576/// See [`Value`] documentation for examples.
577///
578/// [`OwnedValue`]: https://docs.rs/zvariant/latest/zvariant/struct.OwnedValue.html
579#[proc_macro_derive(OwnedValue, attributes(zbus, zvariant))]
580pub fn owned_value_macro_derive(input: TokenStream) -> TokenStream {
581 let ast: DeriveInput = syn::parse(input).unwrap();
582 zvariant_utils::derive::expand_value_derive(
583 ast,
584 zvariant_utils::derive::ValueType::OwnedValue,
585 &utils::config(),
586 )
587 .unwrap_or_else(|err| err.to_compile_error())
588 .into()
589}
590
591/// Constructs a const [`Signature`] with compile-time validation.
592///
593/// This macro creates a `Signature` from a string literal at compile time, validating
594/// that the signature string is valid D-Bus signature. Invalid signatures will cause
595/// a compilation error.
596///
597/// # Examples
598///
599/// ## Basic usage
600///
601/// ```
602/// use zvariant::signature;
603///
604/// // Create signatures for basic types
605/// let sig = signature!("s"); // String signature
606/// assert_eq!(sig.to_string(), "s");
607///
608/// let sig = signature!("i"); // 32-bit integer signature
609/// assert_eq!(sig.to_string(), "i");
610/// ```
611///
612/// ## Container types
613///
614/// ```
615/// use zvariant::signature;
616///
617/// // Array of strings
618/// let sig = signature!("as");
619/// assert_eq!(sig.to_string(), "as");
620///
621/// // Dictionary mapping strings to variants
622/// let sig = signature!("a{sv}");
623/// assert_eq!(sig.to_string(), "a{sv}");
624///
625/// // Structures
626/// let sig = signature!("(isx)");
627/// assert_eq!(sig.to_string(), "(isx)");
628/// ```
629///
630/// ## Const signatures
631///
632/// The macro can be used to create const signatures, which is especially useful
633/// for defining signatures at compile time:
634///
635/// ```
636/// use zvariant::{signature, Signature};
637///
638/// const MY_SIGNATURE: Signature = signature!("a{sv}");
639///
640/// fn process_data(_data: &str) {
641/// assert_eq!(MY_SIGNATURE.to_string(), "a{sv}");
642/// }
643/// ```
644///
645/// ## Using the `dict` alias
646///
647/// For convenience, `dict` is an alias for `a{sv}` (string-to-variant dictionary):
648///
649/// ```
650/// use zvariant::signature;
651///
652/// let sig = signature!("dict");
653/// assert_eq!(sig.to_string(), "a{sv}");
654/// ```
655///
656/// ## Compile-time validation
657///
658/// Invalid signatures will be caught at compile time:
659///
660/// ```compile_fail
661/// use zvariant::signature;
662///
663/// // This will fail to compile because 'z' is not a valid D-Bus type
664/// let sig = signature!("z");
665/// ```
666///
667/// [`Signature`]: https://docs.rs/zvariant/latest/zvariant/enum.Signature.html
668#[proc_macro]
669pub fn signature(input: TokenStream) -> TokenStream {
670 // The `signature!` macro has always emitted hardcoded `::zvariant` paths (it never used
671 // proc-macro-crate detection); keep that behaviour.
672 zvariant_utils::derive::expand_signature_macro(input.into(), "e::quote! { ::zvariant })
673 .unwrap_or_else(|err| err.to_compile_error())
674 .into()
675}