1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#![doc(
    html_logo_url = "https://storage.googleapis.com/fdo-gitlab-uploads/project/avatar/3213/zbus-logomark.png"
)]

//! This crate provides derive macros helpers for zvariant.

extern crate proc_macro;

use proc_macro::TokenStream;
use syn::{self, DeriveInput};

mod dict;
mod r#type;
mod utils;

/// Derive macro to add [`Type`] implementation to structs and enums.
///
/// # Examples
///
/// For structs it works just like serde's [`Serialize`] and [`Deserialize`] macros:
///
/// ```
/// use zvariant::{EncodingContext, from_slice, to_bytes};
/// use zvariant::Type;
/// use zvariant_derive::Type;
/// use serde::{Deserialize, Serialize};
/// use byteorder::LE;
///
/// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
/// struct Struct<'s> {
///     field1: u16,
///     field2: i64,
///     field3: &'s str,
/// }
///
/// assert_eq!(Struct::signature(), "(qxs)");
/// let s = Struct {
///     field1: 42,
///     field2: i64::max_value(),
///     field3: "hello",
/// };
/// let ctxt = EncodingContext::<LE>::new_dbus(0);
/// let encoded = to_bytes(ctxt, &s).unwrap();
/// let decoded: Struct = from_slice(&encoded, ctxt).unwrap();
/// assert_eq!(decoded, s);
/// ```
///
/// Same with enum, except that only enums with unit variants are supported. If you want the
/// encoding size of the enum to be dictated by `repr` attribute (like in the example below),
/// you'll also need [serde_repr] crate.
///
/// ```
/// use zvariant::{EncodingContext, from_slice, to_bytes};
/// use zvariant::Type;
/// use zvariant_derive::Type;
/// use serde::{Deserialize, Serialize};
/// use serde_repr::{Deserialize_repr, Serialize_repr};
/// use byteorder::LE;
///
/// #[repr(u8)]
/// #[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq)]
/// enum Enum {
///     Variant1,
///     Variant2,
/// }
/// assert_eq!(Enum::signature(), u8::signature());
/// let ctxt = EncodingContext::<LE>::new_dbus(0);
/// let encoded = to_bytes(ctxt, &Enum::Variant2).unwrap();
/// let decoded: Enum = from_slice(&encoded, ctxt).unwrap();
/// assert_eq!(decoded, Enum::Variant2);
///
/// #[repr(i64)]
/// #[derive(Deserialize_repr, Serialize_repr, Type)]
/// enum Enum2 {
///     Variant1,
///     Variant2,
/// }
/// assert_eq!(Enum2::signature(), i64::signature());
///
/// // w/o repr attribute, u32 representation is chosen
/// #[derive(Deserialize, Serialize, Type)]
/// enum NoReprEnum {
///     Variant1,
///     Variant2,
/// }
/// assert_eq!(NoReprEnum::signature(), u32::signature());
/// ```
///
/// [`Type`]: https://docs.rs/zvariant/2.0.0/zvariant/trait.Type.html
/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
/// [serde_repr]: https://crates.io/crates/serde_repr
#[proc_macro_derive(Type)]
pub fn type_macro_derive(input: TokenStream) -> TokenStream {
    let ast: DeriveInput = syn::parse(input).unwrap();

    r#type::expand_derive(ast)
}

/// Derive macro to add [`Type`] implementation to structs serialized as `a{sv}` type.
///
/// # Examples
///
/// ```
/// use zvariant::{Signature, Type};
/// use zvariant_derive::TypeDict;
///
/// #[derive(TypeDict)]
/// struct Struct {
///     field: u32,
/// }
///
/// assert_eq!(Struct::signature(), Signature::from_str_unchecked("a{sv}"));
/// ```
///
/// [`Type`]: ../zvariant/trait.Type.html
#[proc_macro_derive(TypeDict)]
pub fn type_dict_macro_derive(input: TokenStream) -> TokenStream {
    let ast: DeriveInput = syn::parse(input).unwrap();

    dict::expand_type_derive(ast)
}

/// Adds [`Serialize`] implementation to structs to be serialized as `a{sv}` type.
///
/// This macro serializes the deriving struct as a D-Bus dictionary type, where keys are strings and
/// values are generic values. Such dictionary types are very commonly used with
/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
/// and GVariant.
///
/// # Examples
///
/// For structs it works just like serde's [`Serialize`] macros:
///
/// ```
/// use zvariant::{EncodingContext, to_bytes};
/// use zvariant_derive::{SerializeDict, TypeDict};
///
/// #[derive(SerializeDict, TypeDict)]
/// struct Struct {
///     field1: u16,
///     #[zvariant(rename = "another-name")]
///     field2: i64,
///     optional_field: Option<String>,
/// }
/// ```
///
/// The serialized D-Bus version of `Struct {42, 77, None}`
/// will be `{"field1": Value::U16(42), "another-name": Value::I64(77)}`.
///
/// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html
#[proc_macro_derive(SerializeDict, attributes(zvariant))]
pub fn serialize_dict_macro_derive(input: TokenStream) -> TokenStream {
    let input: DeriveInput = syn::parse(input).unwrap();

    dict::expand_serialize_derive(input)
}

/// Adds [`Deserialize`] implementation to structs to be deserialized from `a{sv}` type.
///
/// This macro deserializes a D-Bus dictionary type as a struct, where keys are strings and values
/// are generic values. Such dictionary types are very commonly used with
/// [D-Bus](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties)
/// and GVariant.
///
/// # Examples
///
/// For structs it works just like serde's [`Deserialize`] macros:
///
/// ```
/// use zvariant::{EncodingContext, to_bytes};
/// use zvariant_derive::{DeserializeDict, TypeDict};
///
/// #[derive(DeserializeDict, TypeDict)]
/// struct Struct {
///     field1: u16,
///     #[zvariant(rename = "another-name")]
///     field2: i64,
///     optional_field: Option<String>,
/// }
/// ```
///
/// The deserialized D-Bus dictionary `{"field1": Value::U16(42), "another-name": Value::I64(77)}`
/// will be `Struct {42, 77, None}`.
///
/// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
#[proc_macro_derive(DeserializeDict, attributes(zvariant))]
pub fn deserialize_dict_macro_derive(input: TokenStream) -> TokenStream {
    let input: DeriveInput = syn::parse(input).unwrap();

    dict::expand_deserialize_derive(input)
}