zvariant_derive/lib.rs
1#![deny(rust_2018_idioms)]
2#![doc(
3 html_logo_url = "https://raw.githubusercontent.com/dbus2/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 r#type;
19mod utils;
20mod value;
21
22/// Derive macro to add [`Type`] implementation to structs and enums.
23///
24/// # Examples
25///
26/// For structs it works just like serde's [`Serialize`] and [`Deserialize`] macros:
27///
28/// ```
29/// use zvariant::{serialized::Context, to_bytes, Type, LE};
30/// use serde::{Deserialize, Serialize};
31///
32/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
33/// struct Struct<'s> {
34/// field1: u16,
35/// field2: i64,
36/// field3: &'s str,
37/// }
38///
39/// assert_eq!(Struct::SIGNATURE, "(qxs)");
40/// let s = Struct {
41/// field1: 42,
42/// field2: i64::max_value(),
43/// field3: "hello",
44/// };
45/// let ctxt = Context::new_dbus(LE, 0);
46/// let encoded = to_bytes(ctxt, &s).unwrap();
47/// let decoded: Struct = encoded.deserialize().unwrap().0;
48/// assert_eq!(decoded, s);
49/// ```
50///
51/// Same with enum, except that all variants of the enum must have the same number and types of
52/// fields (if any). If you want the encoding size of the (unit-type) enum to be dictated by
53/// `repr` attribute (like in the example below), you'll also need [serde_repr] crate.
54///
55/// ```
56/// use zvariant::{serialized::Context, to_bytes, Type, LE};
57/// use serde::{Deserialize, Serialize};
58/// use serde_repr::{Deserialize_repr, Serialize_repr};
59///
60/// #[repr(u8)]
61/// #[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq)]
62/// enum Enum {
63/// Variant1,
64/// Variant2,
65/// }
66/// assert_eq!(Enum::SIGNATURE, u8::SIGNATURE);
67/// let ctxt = Context::new_dbus(LE, 0);
68/// let encoded = to_bytes(ctxt, &Enum::Variant2).unwrap();
69/// let decoded: Enum = encoded.deserialize().unwrap().0;
70/// assert_eq!(decoded, Enum::Variant2);
71///
72/// #[repr(i64)]
73/// #[derive(Deserialize_repr, Serialize_repr, Type)]
74/// enum Enum2 {
75/// Variant1,
76/// Variant2,
77/// }
78/// assert_eq!(Enum2::SIGNATURE, i64::SIGNATURE);
79///
80/// // w/o repr attribute, u32 representation is chosen
81/// #[derive(Deserialize, Serialize, Type)]
82/// enum NoReprEnum {
83/// Variant1,
84/// Variant2,
85/// }
86/// assert_eq!(NoReprEnum::SIGNATURE, u32::SIGNATURE);
87///
88/// // Not-unit enums are represented as a structure, with the first field being a u32 denoting the
89/// // variant and the second as the actual value.
90/// #[derive(Deserialize, Serialize, Type)]
91/// enum NewType {
92/// Variant1(f64),
93/// Variant2(f64),
94/// }
95/// assert_eq!(NewType::SIGNATURE, "(ud)");
96///
97/// #[derive(Deserialize, Serialize, Type)]
98/// enum StructFields {
99/// Variant1(u16, i64, &'static str),
100/// Variant2 { field1: u16, field2: i64, field3: &'static str },
101/// }
102/// assert_eq!(StructFields::SIGNATURE, "(u(qxs))");
103/// ```
104///
105/// # Custom signatures
106///
107/// There are times when you'd find yourself wanting to specify a hardcoded signature yourself for
108/// the type. The `signature` attribute exists for this purpose. A typical use case is when you'd
109/// need to encode your type as a dictionary (signature `a{sv}`) type. For convenience, `dict` is
110/// an alias for `a{sv}`. Here is an example:
111///
112/// ```
113/// use zvariant::{
114/// serialized::Context, as_value, to_bytes, Type, LE,
115/// };
116/// use serde::{Deserialize, Serialize};
117///
118/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
119/// // `#[zvariant(signature = "a{sv}")]` would be the same.
120/// #[zvariant(signature = "dict")]
121/// struct Struct {
122/// #[serde(with = "as_value")]
123/// field1: u16,
124/// #[serde(with = "as_value")]
125/// field2: i64,
126/// #[serde(with = "as_value")]
127/// field3: String,
128/// }
129///
130/// assert_eq!(Struct::SIGNATURE, "a{sv}");
131/// let s = Struct {
132/// field1: 42,
133/// field2: i64::max_value(),
134/// field3: "hello".to_string(),
135/// };
136/// let ctxt = Context::new_dbus(LE, 0);
137/// let encoded = to_bytes(ctxt, &s).unwrap();
138/// let decoded: Struct = encoded.deserialize().unwrap().0;
139/// assert_eq!(decoded, s);
140/// ```
141///
142/// Another common use for custom signatures is (de)serialization of unit enums as strings:
143///
144/// ```
145/// use zvariant::{serialized::Context, to_bytes, Type, LE};
146/// use serde::{Deserialize, Serialize};
147///
148/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
149/// #[zvariant(signature = "s")]
150/// enum StrEnum {
151/// Variant1,
152/// Variant2,
153/// Variant3,
154/// }
155///
156/// assert_eq!(StrEnum::SIGNATURE, "s");
157/// let ctxt = Context::new_dbus(LE, 0);
158/// let encoded = to_bytes(ctxt, &StrEnum::Variant2).unwrap();
159/// assert_eq!(encoded.len(), 13);
160/// let decoded: StrEnum = encoded.deserialize().unwrap().0;
161/// assert_eq!(decoded, StrEnum::Variant2);
162/// ```
163///
164/// [`Type`]: https://docs.rs/zvariant/latest/zvariant/trait.Type.html
165/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
166/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
167/// [serde_repr]: https://crates.io/crates/serde_repr
168#[proc_macro_derive(Type, attributes(zbus, zvariant))]
169pub fn type_macro_derive(input: TokenStream) -> TokenStream {
170 let ast: DeriveInput = syn::parse(input).unwrap();
171 r#type::expand_derive(ast)
172 .unwrap_or_else(|err| err.to_compile_error())
173 .into()
174}
175
176/// Adds [`Serialize`] implementation to structs to be serialized as `a{sv}` type.
177///
178/// This macro serializes the deriving struct as a D-Bus dictionary type, where keys are strings and
179/// values are generic values. Such dictionary types are very commonly used with
180/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
181/// and GVariant.
182///
183/// # Alternative Approaches
184///
185/// There are two approaches to serializing structs as dictionaries:
186///
187/// 1. Using this macro (simpler, but less control).
188/// 2. Using the `Serialize` derive with `zvariant::as_value` (more verbose, but more control).
189///
190/// See the example below and the relevant [FAQ entry] in our book for more details on the
191/// alternative approach.
192///
193/// # Example
194///
195/// ## Approach #1
196///
197/// ```
198/// use zvariant::{SerializeDict, Type};
199///
200/// #[derive(Debug, Default, SerializeDict, Type)]
201/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
202/// pub struct MyStruct {
203/// field1: Option<u32>,
204/// field2: String,
205/// }
206/// ```
207///
208/// ## Approach #2
209///
210/// ```
211/// use serde::Serialize;
212/// use zvariant::{Type, as_value};
213///
214/// #[derive(Debug, Default, Serialize, Type)]
215/// #[zvariant(signature = "a{sv}")]
216/// #[serde(default, rename_all = "PascalCase")]
217/// pub struct MyStruct {
218/// #[serde(with = "as_value::optional", skip_serializing_if = "Option::is_none")]
219/// field1: Option<u32>,
220/// #[serde(with = "as_value")]
221/// field2: String,
222/// }
223/// ```
224///
225/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
226/// [FAQ entry]: https://dbus2.github.io/zbus/faq.html#how-to-use-a-struct-as-a-dictionary
227#[proc_macro_derive(SerializeDict, attributes(zbus, zvariant))]
228pub fn serialize_dict_macro_derive(input: TokenStream) -> TokenStream {
229 let input: DeriveInput = syn::parse(input).unwrap();
230 dict::expand_serialize_derive(input)
231 .unwrap_or_else(|err| err.to_compile_error())
232 .into()
233}
234
235/// Adds [`Deserialize`] implementation to structs to be deserialized from `a{sv}` type.
236///
237/// This macro deserializes a D-Bus dictionary type as a struct, where keys are strings and values
238/// are generic values. Such dictionary types are very commonly used with
239/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
240/// and GVariant.
241///
242/// # Alternative Approaches
243///
244/// There are two approaches to deserializing dictionaries as structs:
245///
246/// 1. Using this macro (simpler, but less control).
247/// 2. Using the `Deserialize` derive with `zvariant::as_value` (more verbose, but more control).
248///
249/// See the example below and the relevant [FAQ entry] in our book for more details on the
250/// alternative approach.
251///
252/// # Example
253///
254/// ## Approach #1
255///
256/// ```
257/// use zvariant::{DeserializeDict, Type};
258///
259/// #[derive(Debug, Default, DeserializeDict, Type)]
260/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
261/// pub struct MyStruct {
262/// field1: Option<u32>,
263/// field2: String,
264/// }
265/// ```
266///
267/// ## Approach #2
268///
269/// ```
270/// use serde::Deserialize;
271/// use zvariant::{Type, as_value};
272///
273/// #[derive(Debug, Default, Deserialize, Type)]
274/// #[zvariant(signature = "a{sv}")]
275/// #[serde(default, rename_all = "PascalCase")]
276/// pub struct MyStruct {
277/// #[serde(with = "as_value::optional")]
278/// field1: Option<u32>,
279/// #[serde(with = "as_value")]
280/// field2: String,
281/// }
282/// ```
283///
284/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
285/// [FAQ entry]: https://dbus2.github.io/zbus/faq.html#how-to-use-a-struct-as-a-dictionary
286#[proc_macro_derive(DeserializeDict, attributes(zbus, zvariant))]
287pub fn deserialize_dict_macro_derive(input: TokenStream) -> TokenStream {
288 let input: DeriveInput = syn::parse(input).unwrap();
289 dict::expand_deserialize_derive(input)
290 .unwrap_or_else(|err| err.to_compile_error())
291 .into()
292}
293
294/// Implements conversions for your type to/from [`Value`].
295///
296/// Implements `TryFrom<Value>` and `Into<Value>` for your type.
297///
298/// # Examples
299///
300/// Simple owned strutures:
301///
302/// ```
303/// use zvariant::{OwnedObjectPath, OwnedValue, Value};
304///
305/// #[derive(Clone, Value, OwnedValue)]
306/// struct OwnedStruct {
307/// owned_str: String,
308/// owned_path: OwnedObjectPath,
309/// }
310///
311/// let s = OwnedStruct {
312/// owned_str: String::from("hi"),
313/// owned_path: OwnedObjectPath::try_from("/blah").unwrap(),
314/// };
315/// let value = Value::from(s.clone());
316/// let _ = OwnedStruct::try_from(value).unwrap();
317/// let value = OwnedValue::try_from(s).unwrap();
318/// let s = OwnedStruct::try_from(value).unwrap();
319/// assert_eq!(s.owned_str, "hi");
320/// assert_eq!(s.owned_path.as_str(), "/blah");
321/// ```
322///
323/// Now for the more exciting case of unowned structures:
324///
325/// ```
326/// use zvariant::{ObjectPath, Str};
327/// # use zvariant::{OwnedValue, Value};
328/// #
329/// #[derive(Clone, Value, OwnedValue)]
330/// struct UnownedStruct<'a> {
331/// s: Str<'a>,
332/// path: ObjectPath<'a>,
333/// }
334///
335/// let hi = String::from("hi");
336/// let s = UnownedStruct {
337/// s: Str::from(&hi),
338/// path: ObjectPath::try_from("/blah").unwrap(),
339/// };
340/// let value = Value::from(s.clone());
341/// let s = UnownedStruct::try_from(value).unwrap();
342///
343/// let value = OwnedValue::try_from(s).unwrap();
344/// let s = UnownedStruct::try_from(value).unwrap();
345/// assert_eq!(s.s, "hi");
346/// assert_eq!(s.path, "/blah");
347/// ```
348///
349/// Generic structures also supported:
350///
351/// ```
352/// # use zvariant::{OwnedObjectPath, OwnedValue, Value};
353/// #
354/// #[derive(Clone, Value, OwnedValue)]
355/// struct GenericStruct<S, O> {
356/// field1: S,
357/// field2: O,
358/// }
359///
360/// let s = GenericStruct {
361/// field1: String::from("hi"),
362/// field2: OwnedObjectPath::try_from("/blah").unwrap(),
363/// };
364/// let value = Value::from(s.clone());
365/// let _ = GenericStruct::<String, OwnedObjectPath>::try_from(value).unwrap();
366/// let value = OwnedValue::try_from(s).unwrap();
367/// let s = GenericStruct::<String, OwnedObjectPath>::try_from(value).unwrap();
368/// assert_eq!(s.field1, "hi");
369/// assert_eq!(s.field2.as_str(), "/blah");
370/// ```
371///
372/// Enums also supported but currently only with unit variants:
373///
374/// ```
375/// # use zvariant::{OwnedValue, Value};
376/// #
377/// #[derive(Debug, PartialEq, Value, OwnedValue)]
378/// // Default representation is `u32`.
379/// #[repr(u8)]
380/// enum Enum {
381/// Variant1 = 0,
382/// Variant2,
383/// }
384///
385/// let value = Value::from(Enum::Variant1);
386/// let e = Enum::try_from(value).unwrap();
387/// assert_eq!(e, Enum::Variant1);
388/// assert_eq!(e as u8, 0);
389/// let value = OwnedValue::try_from(Enum::Variant2).unwrap();
390/// let e = Enum::try_from(value).unwrap();
391/// assert_eq!(e, Enum::Variant2);
392/// ```
393///
394/// String-encoded enums are also supported:
395///
396/// ```
397/// # use zvariant::{OwnedValue, Value};
398/// #
399/// #[derive(Debug, PartialEq, Value, OwnedValue)]
400/// #[zvariant(signature = "s")]
401/// enum StrEnum {
402/// Variant1,
403/// Variant2,
404/// }
405///
406/// let value = Value::from(StrEnum::Variant1);
407/// let e = StrEnum::try_from(value).unwrap();
408/// assert_eq!(e, StrEnum::Variant1);
409/// let value = OwnedValue::try_from(StrEnum::Variant2).unwrap();
410/// let e = StrEnum::try_from(value).unwrap();
411/// assert_eq!(e, StrEnum::Variant2);
412/// ```
413///
414/// # Renaming fields
415///
416/// ## Auto Renaming
417///
418/// The macro supports specifying a Serde-like `#[zvariant(rename_all = "case")]` attribute on
419/// structures. The attribute allows to rename all the fields from snake case to another case
420/// automatically.
421///
422/// Currently the macro supports the following values for `case`:
423///
424/// * `"lowercase"`
425/// * `"UPPERCASE"`
426/// * `"PascalCase"`
427/// * `"camelCase"`
428/// * `"snake_case"`
429/// * `"kebab-case"`
430///
431/// ## Individual Fields
432///
433/// It's still possible to specify custom names for individual fields using the
434/// `#[zvariant(rename = "another-name")]` attribute even when the `rename_all` attribute is
435/// present.
436///
437/// Here is an example using both `rename` and `rename_all`:
438///
439/// ```
440/// # use zvariant::{OwnedValue, Value, Dict};
441/// # use std::collections::HashMap;
442/// #
443/// #[derive(Clone, Value, OwnedValue)]
444/// #[zvariant(signature = "dict", rename_all = "PascalCase")]
445/// struct RenamedStruct {
446/// #[zvariant(rename = "MyValue")]
447/// field1: String,
448/// field2: String,
449/// }
450///
451/// let s = RenamedStruct {
452/// field1: String::from("hello"),
453/// field2: String::from("world")
454/// };
455/// let v = Value::from(s);
456/// let d = Dict::try_from(v).unwrap();
457/// let hm: HashMap<String, String> = HashMap::try_from(d).unwrap();
458/// assert_eq!(hm.get("MyValue").unwrap().as_str(), "hello");
459/// assert_eq!(hm.get("Field2").unwrap().as_str(), "world");
460/// ```
461///
462/// # Dictionary encoding
463///
464/// For treating your type as a dictionary, you can use the `signature = "dict"` attribute. See
465/// [`Type`] for more details and an example use. Please note that this macro can only handle
466/// `dict` or `a{sv}` values. All other values will be ignored.
467///
468/// [`Value`]: https://docs.rs/zvariant/latest/zvariant/enum.Value.html
469/// [`Type`]: derive.Type.html#custom-types
470#[proc_macro_derive(Value, attributes(zbus, zvariant))]
471pub fn value_macro_derive(input: TokenStream) -> TokenStream {
472 let ast: DeriveInput = syn::parse(input).unwrap();
473 value::expand_derive(ast, value::ValueType::Value)
474 .unwrap_or_else(|err| err.to_compile_error())
475 .into()
476}
477
478/// Implements conversions for your type to/from [`OwnedValue`].
479///
480/// Implements `TryFrom<OwnedValue>` and `TryInto<OwnedValue>` for your type.
481///
482/// See [`Value`] documentation for examples.
483///
484/// [`OwnedValue`]: https://docs.rs/zvariant/latest/zvariant/struct.OwnedValue.html
485#[proc_macro_derive(OwnedValue, attributes(zbus, zvariant))]
486pub fn owned_value_macro_derive(input: TokenStream) -> TokenStream {
487 let ast: DeriveInput = syn::parse(input).unwrap();
488 value::expand_derive(ast, value::ValueType::OwnedValue)
489 .unwrap_or_else(|err| err.to_compile_error())
490 .into()
491}