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::{SerializeDict, DeserializeDict, serialized::Context, to_bytes, Type, LE};
114///
115/// #[derive(DeserializeDict, SerializeDict, Type, PartialEq, Debug)]
116/// // `#[zvariant(signature = "a{sv}")]` would be the same.
117/// #[zvariant(signature = "dict")]
118/// struct Struct {
119/// field1: u16,
120/// field2: i64,
121/// field3: String,
122/// }
123///
124/// assert_eq!(Struct::signature(), "a{sv}");
125/// let s = Struct {
126/// field1: 42,
127/// field2: i64::max_value(),
128/// field3: "hello".to_string(),
129/// };
130/// let ctxt = Context::new_dbus(LE, 0);
131/// let encoded = to_bytes(ctxt, &s).unwrap();
132/// let decoded: Struct = encoded.deserialize().unwrap().0;
133/// assert_eq!(decoded, s);
134/// ```
135///
136/// Another common use for custom signatures is (de)serialization of unit enums as strings:
137///
138/// ```
139/// use zvariant::{serialized::Context, to_bytes, Type, LE};
140/// use serde::{Deserialize, Serialize};
141///
142/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
143/// #[zvariant(signature = "s")]
144/// enum StrEnum {
145/// Variant1,
146/// Variant2,
147/// Variant3,
148/// }
149///
150/// assert_eq!(StrEnum::signature(), "s");
151/// let ctxt = Context::new_dbus(LE, 0);
152/// let encoded = to_bytes(ctxt, &StrEnum::Variant2).unwrap();
153/// assert_eq!(encoded.len(), 13);
154/// let decoded: StrEnum = encoded.deserialize().unwrap().0;
155/// assert_eq!(decoded, StrEnum::Variant2);
156/// ```
157///
158/// [`Type`]: https://docs.rs/zvariant/latest/zvariant/trait.Type.html
159/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
160/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
161/// [serde_repr]: https://crates.io/crates/serde_repr
162#[proc_macro_derive(Type, attributes(zvariant))]
163pub fn type_macro_derive(input: TokenStream) -> TokenStream {
164 let ast: DeriveInput = syn::parse(input).unwrap();
165 r#type::expand_derive(ast)
166 .unwrap_or_else(|err| err.to_compile_error())
167 .into()
168}
169
170/// Adds [`Serialize`] implementation to structs to be serialized as `a{sv}` type.
171///
172/// This macro serializes the deriving struct as a D-Bus dictionary type, where keys are strings and
173/// values are generic values. Such dictionary types are very commonly used with
174/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
175/// and GVariant.
176///
177/// # Examples
178///
179/// For structs it works just like serde's [`Serialize`] macros:
180///
181/// ```
182/// use zvariant::{SerializeDict, Type};
183///
184/// #[derive(SerializeDict, Type)]
185/// #[zvariant(signature = "a{sv}")]
186/// struct Struct {
187/// field1: u16,
188/// #[zvariant(rename = "another-name")]
189/// field2: i64,
190/// optional_field: Option<String>,
191/// }
192/// ```
193///
194/// The serialized D-Bus version of `Struct {42, 77, None}`
195/// will be `{"field1": Value::U16(42), "another-name": Value::I64(77)}`.
196///
197/// # Auto renaming fields
198///
199/// The macro supports specifying a Serde-like `#[zvariant(rename_all = "case")]` attribute on
200/// structures. The attribute allows to rename all the fields from snake case to another case
201/// automatically:
202///
203/// ```
204/// use zvariant::{SerializeDict, Type};
205///
206/// #[derive(SerializeDict, Type)]
207/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
208/// struct Struct {
209/// field1: u16,
210/// #[zvariant(rename = "another-name")]
211/// field2: i64,
212/// optional_field: Option<String>,
213/// }
214/// ```
215///
216/// It's still possible to specify custom names for individual fields using the
217/// `#[zvariant(rename = "another-name")]` attribute even when the `rename_all` attribute is
218/// present.
219///
220/// Currently the macro supports the following values for `case`:
221///
222/// * `"lowercase"`
223/// * `"UPPERCASE"`
224/// * `"PascalCase"`
225/// * `"camelCase"`
226/// * `"snake_case"`
227/// * `"kebab-case"`
228///
229/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
230#[proc_macro_derive(SerializeDict, attributes(zvariant))]
231pub fn serialize_dict_macro_derive(input: TokenStream) -> TokenStream {
232 let input: DeriveInput = syn::parse(input).unwrap();
233 dict::expand_serialize_derive(input)
234 .unwrap_or_else(|err| err.to_compile_error())
235 .into()
236}
237
238/// Adds [`Deserialize`] implementation to structs to be deserialized from `a{sv}` type.
239///
240/// This macro deserializes a D-Bus dictionary type as a struct, where keys are strings and values
241/// are generic values. Such dictionary types are very commonly used with
242/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
243/// and GVariant.
244///
245/// # Examples
246///
247/// For structs it works just like serde's [`Deserialize`] macros:
248///
249/// ```
250/// use zvariant::{DeserializeDict, Type};
251///
252/// #[derive(DeserializeDict, Type)]
253/// #[zvariant(signature = "a{sv}")]
254/// ##[allow(unused)]
255/// struct Struct {
256/// field1: u16,
257/// #[zvariant(rename = "another-name")]
258/// field2: i64,
259/// optional_field: Option<String>,
260/// }
261/// ```
262///
263/// The deserialized D-Bus dictionary `{"field1": Value::U16(42), "another-name": Value::I64(77)}`
264/// will be `Struct {42, 77, None}`.
265///
266/// # Auto renaming fields
267///
268/// The macro supports specifying a Serde-like `#[zvariant(rename_all = "case")]` attribute on
269/// structures. The attribute allows to rename all the fields from snake case to another case
270/// automatically:
271///
272/// ```
273/// use zvariant::{SerializeDict, Type};
274///
275/// #[derive(SerializeDict, Type)]
276/// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")]
277/// struct Struct {
278/// field1: u16,
279/// #[zvariant(rename = "another-name")]
280/// field2: i64,
281/// optional_field: Option<String>,
282/// }
283/// ```
284///
285/// It's still possible to specify custom names for individual fields using the
286/// `#[zvariant(rename = "another-name")]` attribute even when the `rename_all` attribute is
287/// present.
288///
289/// Currently the macro supports the following values for `case`:
290///
291/// * `"lowercase"`
292/// * `"UPPERCASE"`
293/// * `"PascalCase"`
294/// * `"camelCase"`
295/// * `"snake_case"`
296/// * `"kebab-case"`
297///
298/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
299#[proc_macro_derive(DeserializeDict, attributes(zvariant))]
300pub fn deserialize_dict_macro_derive(input: TokenStream) -> TokenStream {
301 let input: DeriveInput = syn::parse(input).unwrap();
302 dict::expand_deserialize_derive(input)
303 .unwrap_or_else(|err| err.to_compile_error())
304 .into()
305}
306
307/// Implements conversions for your type to/from [`Value`].
308///
309/// Implements `TryFrom<Value>` and `Into<Value>` for your type.
310///
311/// # Examples
312///
313/// Simple owned strutures:
314///
315/// ```
316/// use zvariant::{OwnedObjectPath, OwnedValue, Value};
317///
318/// #[derive(Clone, Value, OwnedValue)]
319/// struct OwnedStruct {
320/// owned_str: String,
321/// owned_path: OwnedObjectPath,
322/// }
323///
324/// let s = OwnedStruct {
325/// owned_str: String::from("hi"),
326/// owned_path: OwnedObjectPath::try_from("/blah").unwrap(),
327/// };
328/// let value = Value::from(s.clone());
329/// let _ = OwnedStruct::try_from(value).unwrap();
330/// let value = OwnedValue::try_from(s).unwrap();
331/// let s = OwnedStruct::try_from(value).unwrap();
332/// assert_eq!(s.owned_str, "hi");
333/// assert_eq!(s.owned_path.as_str(), "/blah");
334/// ```
335///
336/// Now for the more exciting case of unowned structures:
337///
338/// ```
339/// use zvariant::{ObjectPath, Str};
340/// # use zvariant::{OwnedValue, Value};
341/// #
342/// #[derive(Clone, Value, OwnedValue)]
343/// struct UnownedStruct<'a> {
344/// s: Str<'a>,
345/// path: ObjectPath<'a>,
346/// }
347///
348/// let hi = String::from("hi");
349/// let s = UnownedStruct {
350/// s: Str::from(&hi),
351/// path: ObjectPath::try_from("/blah").unwrap(),
352/// };
353/// let value = Value::from(s.clone());
354/// let s = UnownedStruct::try_from(value).unwrap();
355///
356/// let value = OwnedValue::try_from(s).unwrap();
357/// let s = UnownedStruct::try_from(value).unwrap();
358/// assert_eq!(s.s, "hi");
359/// assert_eq!(s.path, "/blah");
360/// ```
361///
362/// Generic structures also supported:
363///
364/// ```
365/// # use zvariant::{OwnedObjectPath, OwnedValue, Value};
366/// #
367/// #[derive(Clone, Value, OwnedValue)]
368/// struct GenericStruct<S, O> {
369/// field1: S,
370/// field2: O,
371/// }
372///
373/// let s = GenericStruct {
374/// field1: String::from("hi"),
375/// field2: OwnedObjectPath::try_from("/blah").unwrap(),
376/// };
377/// let value = Value::from(s.clone());
378/// let _ = GenericStruct::<String, OwnedObjectPath>::try_from(value).unwrap();
379/// let value = OwnedValue::try_from(s).unwrap();
380/// let s = GenericStruct::<String, OwnedObjectPath>::try_from(value).unwrap();
381/// assert_eq!(s.field1, "hi");
382/// assert_eq!(s.field2.as_str(), "/blah");
383/// ```
384///
385/// Enums also supported but currently only simple ones w/ an integer representation:
386///
387/// ```
388/// # use zvariant::{OwnedValue, Value};
389/// #
390/// #[derive(Debug, PartialEq, Value, OwnedValue)]
391/// #[repr(u8)]
392/// enum Enum {
393/// Variant1 = 1,
394/// Variant2 = 2,
395/// }
396///
397/// let value = Value::from(Enum::Variant1);
398/// let e = Enum::try_from(value).unwrap();
399/// assert_eq!(e, Enum::Variant1);
400/// let value = OwnedValue::try_from(Enum::Variant2).unwrap();
401/// let e = Enum::try_from(value).unwrap();
402/// assert_eq!(e, Enum::Variant2);
403/// ```
404///
405/// # Dictionary encoding
406///
407/// For treating your type as a dictionary, you can use the `signature = "dict"` attribute. See
408/// [`Type`] for more details and an example use. Please note that this macro can only handle
409/// `dict` or `a{sv}` values. All other values will be ignored.
410///
411/// [`Value`]: https://docs.rs/zvariant/latest/zvariant/enum.Value.html
412/// [`Type`]: derive.Type.html#custom-types
413#[proc_macro_derive(Value)]
414pub fn value_macro_derive(input: TokenStream) -> TokenStream {
415 let ast: DeriveInput = syn::parse(input).unwrap();
416 value::expand_derive(ast, value::ValueType::Value)
417 .unwrap_or_else(|err| err.to_compile_error())
418 .into()
419}
420
421/// Implements conversions for your type to/from [`OwnedValue`].
422///
423/// Implements `TryFrom<OwnedValue>` and `TryInto<OwnedValue>` for your type.
424///
425/// See [`Value`] documentation for examples.
426///
427/// [`OwnedValue`]: https://docs.rs/zvariant/latest/zvariant/struct.OwnedValue.html
428#[proc_macro_derive(OwnedValue)]
429pub fn owned_value_macro_derive(input: TokenStream) -> TokenStream {
430 let ast: DeriveInput = syn::parse(input).unwrap();
431 value::expand_derive(ast, value::ValueType::OwnedValue)
432 .unwrap_or_else(|err| err.to_compile_error())
433 .into()
434}