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