zvariant_derive/
lib.rs

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