zvariant_derive-4.1.2/.cargo_vcs_info.json0000644000000001550000000000100142350ustar { "git": { "sha1": "6f7efc9448cf02fb97dd5920da3de45164854a63" }, "path_in_vcs": "zvariant_derive" }zvariant_derive-4.1.2/Cargo.toml0000644000000025360000000000100122400ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.75" name = "zvariant_derive" version = "4.1.2" authors = ["Zeeshan Ali Khan "] description = "D-Bus & GVariant encoding & decoding" readme = "README.md" keywords = [ "D-Bus", "DBus", "IPC", "GVariant", ] categories = [ "data-structures", "encoding", "parsing", ] license = "MIT" repository = "https://github.com/dbus2/zbus/" [lib] proc-macro = true [dependencies.proc-macro-crate] version = "3.1.0" [dependencies.proc-macro2] version = "1.0.81" [dependencies.quote] version = "1.0.36" [dependencies.syn] version = "2.0.64" features = [ "extra-traits", "full", ] [dependencies.zvariant_utils] version = "=2.0.0" [dev-dependencies.enumflags2] version = "0.7.9" features = ["serde"] [dev-dependencies.serde] version = "1.0.200" features = ["derive"] [dev-dependencies.serde_repr] version = "0.1.19" zvariant_derive-4.1.2/Cargo.toml.orig000064400000000000000000000016101046102023000157110ustar 00000000000000[package] name = "zvariant_derive" # Keep major and minor version in sync with zvariant crate version = "4.1.2" authors = ["Zeeshan Ali Khan "] edition = "2021" rust-version = "1.75" description = "D-Bus & GVariant encoding & decoding" repository = "https://github.com/dbus2/zbus/" keywords = ["D-Bus", "DBus", "IPC", "GVariant"] license = "MIT" categories = ["data-structures", "encoding", "parsing"] readme = "README.md" [lib] proc-macro = true [dependencies] proc-macro2 = "1.0.81" syn = { version = "2.0.64", features = ["extra-traits", "full"] } quote = "1.0.36" proc-macro-crate = "3.1.0" zvariant_utils = { path = "../zvariant_utils", version = "=2.0.0" } [dev-dependencies] zvariant = { path = "../zvariant", features = ["enumflags2"] } enumflags2 = { version = "0.7.9", features = ["serde"] } serde = { version = "1.0.200", features = ["derive"] } serde_repr = "0.1.19" zvariant_derive-4.1.2/LICENSE000064400000000000000000000020701046102023000140300ustar 00000000000000Copyright (c) 2024 Zeeshan Ali Khan & zbus contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. zvariant_derive-4.1.2/README.md000064400000000000000000000017411046102023000143060ustar 00000000000000# zvariant_derive [![](https://docs.rs/zvariant_derive/badge.svg)](https://docs.rs/zvariant_derive/) [![](https://img.shields.io/crates/v/zvariant_derive)](https://crates.io/crates/zvariant_derive) This crate provides derive macros helpers for [`zvariant`]. The `zvariant` crate re-exports these macros for your convenience so you do not need to use this crate directly. **Status:** Stable. ## Example code ```rust use zvariant::{serialized::Context, to_bytes, Type, LE}; use serde::{Deserialize, Serialize}; #[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 = Context::new_dbus(LE, 0); let encoded = to_bytes(ctxt, &s).unwrap(); let decoded: Struct = encoded.deserialize().unwrap().0; assert_eq!(decoded, s); ``` [`zvariant`]: https://crates.io/crates/zvariant zvariant_derive-4.1.2/src/dict.rs000064400000000000000000000167551046102023000151220ustar 00000000000000use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, ToTokens}; use syn::{punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, Error, Field}; use zvariant_utils::{case, macros}; use crate::utils::*; fn dict_name_for_field( f: &Field, rename_attr: Option, rename_all_attr: Option<&str>, ) -> Result { if let Some(name) = rename_attr { Ok(name) } else { let ident = f.ident.as_ref().unwrap().to_string(); match rename_all_attr { Some("lowercase") => Ok(ident.to_ascii_lowercase()), Some("UPPERCASE") => Ok(ident.to_ascii_uppercase()), Some("PascalCase") => Ok(case::pascal_or_camel_case(&ident, true)), Some("camelCase") => Ok(case::pascal_or_camel_case(&ident, false)), Some("snake_case") => Ok(case::snake_case(&ident)), None => Ok(ident), Some(other) => Err(Error::new( f.span(), format!("invalid `rename_all` attribute value {other}"), )), } } } pub fn expand_serialize_derive(input: DeriveInput) -> Result { let (name, data) = match input.data { Data::Struct(data) => (input.ident, data), _ => return Err(Error::new(input.span(), "only structs supported")), }; let StructAttributes { rename_all, .. } = StructAttributes::parse(&input.attrs)?; let zv = zvariant_path(); let mut entries = quote! {}; let mut num_entries: usize = 0; for f in &data.fields { let FieldAttributes { rename } = FieldAttributes::parse(&f.attrs)?; let name = &f.ident; let dict_name = dict_name_for_field(f, rename, rename_all.as_deref())?; let is_option = macros::ty_is_option(&f.ty); let e = if is_option { quote! { if self.#name.is_some() { map.serialize_entry(#dict_name, &#zv::SerializeValue(self.#name.as_ref().unwrap()))?; } } } else { quote! { map.serialize_entry(#dict_name, &#zv::SerializeValue(&self.#name))?; } }; entries.extend(e); num_entries += 1; } let generics = input.generics; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let num_entries = num_entries.to_token_stream(); Ok(quote! { #[allow(deprecated)] impl #impl_generics #zv::export::serde::ser::Serialize for #name #ty_generics #where_clause { fn serialize(&self, serializer: S) -> ::std::result::Result where S: #zv::export::serde::ser::Serializer, { use #zv::export::serde::ser::SerializeMap; // zbus doesn't care about number of entries (it would need bytes instead) let mut map = serializer.serialize_map(::std::option::Option::Some(#num_entries))?; #entries map.end() } } }) } pub fn expand_deserialize_derive(input: DeriveInput) -> Result { let (name, data) = match input.data { Data::Struct(data) => (input.ident, data), _ => return Err(Error::new(input.span(), "only structs supported")), }; let StructAttributes { rename_all, deny_unknown_fields, .. } = StructAttributes::parse(&input.attrs)?; let visitor = format_ident!("{}Visitor", name); let zv = zvariant_path(); let mut fields = Vec::new(); let mut req_fields = Vec::new(); let mut dict_names = Vec::new(); let mut entries = Vec::new(); for f in &data.fields { let FieldAttributes { rename } = FieldAttributes::parse(&f.attrs)?; let name = &f.ident; let dict_name = dict_name_for_field(f, rename, rename_all.as_deref())?; let is_option = macros::ty_is_option(&f.ty); entries.push(quote! { #dict_name => { // FIXME: add an option about strict parsing (instead of silently skipping the field) #name = access.next_value::<#zv::DeserializeValue<_>>().map(|v| v.0).ok(); } }); dict_names.push(dict_name); fields.push(name); if !is_option { req_fields.push(name); } } let fallback = if deny_unknown_fields { quote! { field => { return ::std::result::Result::Err( ::unknown_field( field, &[#(#dict_names),*], ), ); } } } else { quote! { unknown => { let _ = access.next_value::<#zv::Value>(); } } }; entries.push(fallback); let (_, ty_generics, _) = input.generics.split_for_impl(); let mut generics = input.generics.clone(); let def = syn::LifetimeParam { attrs: Vec::new(), lifetime: syn::Lifetime::new("'de", Span::call_site()), colon_token: None, bounds: Punctuated::new(), }; generics.params = Some(syn::GenericParam::Lifetime(def)) .into_iter() .chain(generics.params) .collect(); let (impl_generics, _, where_clause) = generics.split_for_impl(); Ok(quote! { #[allow(deprecated)] impl #impl_generics #zv::export::serde::de::Deserialize<'de> for #name #ty_generics #where_clause { fn deserialize(deserializer: D) -> ::std::result::Result where D: #zv::export::serde::de::Deserializer<'de>, { struct #visitor #ty_generics(::std::marker::PhantomData<#name #ty_generics>); impl #impl_generics #zv::export::serde::de::Visitor<'de> for #visitor #ty_generics { type Value = #name #ty_generics; fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { formatter.write_str("a dictionary") } fn visit_map( self, mut access: M, ) -> ::std::result::Result where M: #zv::export::serde::de::MapAccess<'de>, { #( let mut #fields = ::std::default::Default::default(); )* // does not check duplicated fields, since those shouldn't exist in stream while let ::std::option::Option::Some(key) = access.next_key::<&str>()? { match key { #(#entries)* } } #(let #req_fields = if let ::std::option::Option::Some(val) = #req_fields { val } else { return ::std::result::Result::Err( ::missing_field( ::std::stringify!(#req_fields), ), ); };)* ::std::result::Result::Ok(#name { #(#fields),* }) } } deserializer.deserialize_map(#visitor(::std::marker::PhantomData)) } } }) } zvariant_derive-4.1.2/src/lib.rs000064400000000000000000000331451046102023000147350ustar 00000000000000#![deny(rust_2018_idioms)] #![doc( html_logo_url = "https://raw.githubusercontent.com/dbus2/zbus/9f7a90d2b594ddc48b7a5f39fda5e00cd56a7dfb/logo.png" )] #![doc = include_str!("../README.md")] #![doc(test(attr( warn(unused), deny(warnings), allow(dead_code), // W/o this, we seem to get some bogus warning about `extern crate zbus`. allow(unused_extern_crates), )))] use proc_macro::TokenStream; use syn::DeriveInput; mod dict; mod r#type; mod utils; mod value; /// 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::{serialized::Context, to_bytes, Type, LE}; /// use serde::{Deserialize, Serialize}; /// /// #[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 = Context::new_dbus(LE, 0); /// let encoded = to_bytes(ctxt, &s).unwrap(); /// let decoded: Struct = encoded.deserialize().unwrap().0; /// assert_eq!(decoded, s); /// ``` /// /// Same with enum, except that all variants of the enum must have the same number and types of /// fields (if any). If you want the encoding size of the (unit-type) enum to be dictated by /// `repr` attribute (like in the example below), you'll also need [serde_repr] crate. /// /// ``` /// use zvariant::{serialized::Context, to_bytes, Type, LE}; /// use serde::{Deserialize, Serialize}; /// use serde_repr::{Deserialize_repr, Serialize_repr}; /// /// #[repr(u8)] /// #[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq)] /// enum Enum { /// Variant1, /// Variant2, /// } /// assert_eq!(Enum::signature(), u8::signature()); /// let ctxt = Context::new_dbus(LE, 0); /// let encoded = to_bytes(ctxt, &Enum::Variant2).unwrap(); /// let decoded: Enum = encoded.deserialize().unwrap().0; /// 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()); /// /// // Not-unit enums are represented as a structure, with the first field being a u32 denoting the /// // variant and the second as the actual value. /// #[derive(Deserialize, Serialize, Type)] /// enum NewType { /// Variant1(f64), /// Variant2(f64), /// } /// assert_eq!(NewType::signature(), "(ud)"); /// /// #[derive(Deserialize, Serialize, Type)] /// enum StructFields { /// Variant1(u16, i64, &'static str), /// Variant2 { field1: u16, field2: i64, field3: &'static str }, /// } /// assert_eq!(StructFields::signature(), "(u(qxs))"); /// ``` /// /// # Custom signatures /// /// There are times when you'd find yourself wanting to specify a hardcoded signature yourself for /// the type. The `signature` attribute exists for this purpose. A typical use case is when you'd /// need to encode your type as a dictionary (signature `a{sv}`) type. For convenience, `dict` is /// an alias for `a{sv}`. Here is an example: /// /// ``` /// use zvariant::{SerializeDict, DeserializeDict, serialized::Context, to_bytes, Type, LE}; /// /// #[derive(DeserializeDict, SerializeDict, Type, PartialEq, Debug)] /// // `#[zvariant(signature = "a{sv}")]` would be the same. /// #[zvariant(signature = "dict")] /// struct Struct { /// field1: u16, /// field2: i64, /// field3: String, /// } /// /// assert_eq!(Struct::signature(), "a{sv}"); /// let s = Struct { /// field1: 42, /// field2: i64::max_value(), /// field3: "hello".to_string(), /// }; /// let ctxt = Context::new_dbus(LE, 0); /// let encoded = to_bytes(ctxt, &s).unwrap(); /// let decoded: Struct = encoded.deserialize().unwrap().0; /// assert_eq!(decoded, s); /// ``` /// /// Another common use for custom signatures is (de)serialization of unit enums as strings: /// /// ``` /// use zvariant::{serialized::Context, to_bytes, Type, LE}; /// use serde::{Deserialize, Serialize}; /// /// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)] /// #[zvariant(signature = "s")] /// enum StrEnum { /// Variant1, /// Variant2, /// Variant3, /// } /// /// assert_eq!(StrEnum::signature(), "s"); /// let ctxt = Context::new_dbus(LE, 0); /// let encoded = to_bytes(ctxt, &StrEnum::Variant2).unwrap(); /// assert_eq!(encoded.len(), 13); /// let decoded: StrEnum = encoded.deserialize().unwrap().0; /// assert_eq!(decoded, StrEnum::Variant2); /// ``` /// /// [`Type`]: https://docs.rs/zvariant/latest/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, attributes(zvariant))] pub fn type_macro_derive(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); r#type::expand_derive(ast) .unwrap_or_else(|err| err.to_compile_error()) .into() } /// 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::{SerializeDict, Type}; /// /// #[derive(SerializeDict, Type)] /// #[zvariant(signature = "a{sv}")] /// struct Struct { /// field1: u16, /// #[zvariant(rename = "another-name")] /// field2: i64, /// optional_field: Option, /// } /// ``` /// /// The serialized D-Bus version of `Struct {42, 77, None}` /// will be `{"field1": Value::U16(42), "another-name": Value::I64(77)}`. /// /// # Auto renaming fields /// /// The macro supports specifying a Serde-like `#[zvariant(rename_all = "case")]` attribute on /// structures. The attribute allows to rename all the fields from snake case to another case /// automatically: /// /// ``` /// use zvariant::{SerializeDict, Type}; /// /// #[derive(SerializeDict, Type)] /// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")] /// struct Struct { /// field1: u16, /// #[zvariant(rename = "another-name")] /// field2: i64, /// optional_field: Option, /// } /// ``` /// /// It's still possible to specify custom names for individual fields using the /// `#[zvariant(rename = "another-name")]` attribute even when the `rename_all` attribute is /// present. /// /// Currently the macro supports the following values for `case`: /// /// * `"lowercase"` /// * `"UPPERCASE"` /// * `"PascalCase"` /// * `"camelCase"` /// * `"snake_case"` /// /// [`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) .unwrap_or_else(|err| err.to_compile_error()) .into() } /// 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::{DeserializeDict, Type}; /// /// #[derive(DeserializeDict, Type)] /// #[zvariant(signature = "a{sv}")] /// ##[allow(unused)] /// struct Struct { /// field1: u16, /// #[zvariant(rename = "another-name")] /// field2: i64, /// optional_field: Option, /// } /// ``` /// /// The deserialized D-Bus dictionary `{"field1": Value::U16(42), "another-name": Value::I64(77)}` /// will be `Struct {42, 77, None}`. /// /// # Auto renaming fields /// /// The macro supports specifying a Serde-like `#[zvariant(rename_all = "case")]` attribute on /// structures. The attribute allows to rename all the fields from snake case to another case /// automatically: /// /// ``` /// use zvariant::{SerializeDict, Type}; /// /// #[derive(SerializeDict, Type)] /// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")] /// struct Struct { /// field1: u16, /// #[zvariant(rename = "another-name")] /// field2: i64, /// optional_field: Option, /// } /// ``` /// /// It's still possible to specify custom names for individual fields using the /// `#[zvariant(rename = "another-name")]` attribute even when the `rename_all` attribute is /// present. /// /// Currently the macro supports the following values for `case`: /// /// * `"lowercase"` /// * `"UPPERCASE"` /// * `"PascalCase"` /// * `"camelCase"` /// * `"snake_case"` /// /// [`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) .unwrap_or_else(|err| err.to_compile_error()) .into() } /// Implements conversions for your type to/from [`Value`]. /// /// Implements `TryFrom` and `Into` for your type. /// /// # Examples /// /// Simple owned strutures: /// /// ``` /// use zvariant::{OwnedObjectPath, OwnedValue, Value}; /// /// #[derive(Clone, Value, OwnedValue)] /// struct OwnedStruct { /// owned_str: String, /// owned_path: OwnedObjectPath, /// } /// /// let s = OwnedStruct { /// owned_str: String::from("hi"), /// owned_path: OwnedObjectPath::try_from("/blah").unwrap(), /// }; /// let value = Value::from(s.clone()); /// let _ = OwnedStruct::try_from(value).unwrap(); /// let value = OwnedValue::try_from(s).unwrap(); /// let s = OwnedStruct::try_from(value).unwrap(); /// assert_eq!(s.owned_str, "hi"); /// assert_eq!(s.owned_path.as_str(), "/blah"); /// ``` /// /// Now for the more exciting case of unowned structures: /// /// ``` /// use zvariant::{ObjectPath, Str}; /// # use zvariant::{OwnedValue, Value}; /// # /// #[derive(Clone, Value, OwnedValue)] /// struct UnownedStruct<'a> { /// s: Str<'a>, /// path: ObjectPath<'a>, /// } /// /// let hi = String::from("hi"); /// let s = UnownedStruct { /// s: Str::from(&hi), /// path: ObjectPath::try_from("/blah").unwrap(), /// }; /// let value = Value::from(s.clone()); /// let s = UnownedStruct::try_from(value).unwrap(); /// /// let value = OwnedValue::try_from(s).unwrap(); /// let s = UnownedStruct::try_from(value).unwrap(); /// assert_eq!(s.s, "hi"); /// assert_eq!(s.path, "/blah"); /// ``` /// /// Generic structures also supported: /// /// ``` /// # use zvariant::{OwnedObjectPath, OwnedValue, Value}; /// # /// #[derive(Clone, Value, OwnedValue)] /// struct GenericStruct { /// field1: S, /// field2: O, /// } /// /// let s = GenericStruct { /// field1: String::from("hi"), /// field2: OwnedObjectPath::try_from("/blah").unwrap(), /// }; /// let value = Value::from(s.clone()); /// let _ = GenericStruct::::try_from(value).unwrap(); /// let value = OwnedValue::try_from(s).unwrap(); /// let s = GenericStruct::::try_from(value).unwrap(); /// assert_eq!(s.field1, "hi"); /// assert_eq!(s.field2.as_str(), "/blah"); /// ``` /// /// Enums also supported but currently only simple ones w/ an integer representation: /// /// ``` /// # use zvariant::{OwnedValue, Value}; /// # /// #[derive(Debug, PartialEq, Value, OwnedValue)] /// #[repr(u8)] /// enum Enum { /// Variant1 = 1, /// Variant2 = 2, /// } /// /// let value = Value::from(Enum::Variant1); /// let e = Enum::try_from(value).unwrap(); /// assert_eq!(e, Enum::Variant1); /// let value = OwnedValue::try_from(Enum::Variant2).unwrap(); /// let e = Enum::try_from(value).unwrap(); /// assert_eq!(e, Enum::Variant2); /// ``` /// /// # Dictionary encoding /// /// For treating your type as a dictionary, you can use the `signature = "dict"` attribute. See /// [`Type`] for more details and an example use. Please note that this macro can only handle /// `dict` or `a{sv}` values. All other values will be ignored. /// /// [`Value`]: https://docs.rs/zvariant/latest/zvariant/enum.Value.html /// [`Type`]: derive.Type.html#custom-types #[proc_macro_derive(Value)] pub fn value_macro_derive(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); value::expand_derive(ast, value::ValueType::Value) .unwrap_or_else(|err| err.to_compile_error()) .into() } /// Implements conversions for your type to/from [`OwnedValue`]. /// /// Implements `TryFrom` and `TryInto` for your type. /// /// See [`Value`] documentation for examples. /// /// [`OwnedValue`]: https://docs.rs/zvariant/latest/zvariant/struct.OwnedValue.html #[proc_macro_derive(OwnedValue)] pub fn owned_value_macro_derive(input: TokenStream) -> TokenStream { let ast: DeriveInput = syn::parse(input).unwrap(); value::expand_derive(ast, value::ValueType::OwnedValue) .unwrap_or_else(|err| err.to_compile_error()) .into() } zvariant_derive-4.1.2/src/type.rs000064400000000000000000000152111046102023000151420ustar 00000000000000use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{ spanned::Spanned, Attribute, Data, DataEnum, DeriveInput, Error, Fields, Generics, Ident, }; use crate::utils::*; pub fn expand_derive(ast: DeriveInput) -> Result { let StructAttributes { signature, .. } = StructAttributes::parse(&ast.attrs)?; let zv = zvariant_path(); if let Some(signature) = signature { let signature = match signature.as_str() { "dict" => "a{sv}".to_string(), _ => signature, }; // Signature already provided, easy then! let name = ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); return Ok(quote! { impl #impl_generics #zv::Type for #name #ty_generics #where_clause { #[inline] fn signature() -> #zv::Signature<'static> { // FIXME: Would be nice if we had a parsed `Signature` in the macro code already so // it's checked at the build time but currently that's not easily possible w/o // zvariant_derive requiring zvaraint and we don't want it as it creates a cyclic // dep. Maybe we can find a way to share the `Signature` type between the two // crates? #zv::Signature::from_static_str(#signature).unwrap() } } }); } match ast.data { Data::Struct(ds) => match ds.fields { Fields::Named(_) if ds.fields.is_empty() => { impl_empty_struct(ast.ident, ast.generics, &zv) } Fields::Named(_) | Fields::Unnamed(_) => { impl_struct(ast.ident, ast.generics, ds.fields, &zv) } Fields::Unit => impl_unit_struct(ast.ident, ast.generics, &zv), }, Data::Enum(data) => impl_enum(ast.ident, ast.generics, ast.attrs, data, &zv), _ => Err(Error::new( ast.span(), "only structs and enums supported at the moment", )), } .map(|implementation| { quote! { #[allow(deprecated)] #implementation } }) } fn impl_struct( name: Ident, generics: Generics, fields: Fields, zv: &TokenStream, ) -> Result { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let signature = signature_for_struct(&fields, zv, false); Ok(quote! { impl #impl_generics #zv::Type for #name #ty_generics #where_clause { #[inline] fn signature() -> #zv::Signature<'static> { #signature } } }) } fn signature_for_struct( fields: &Fields, zv: &TokenStream, insert_enum_variant: bool, ) -> TokenStream { let field_types = fields.iter().map(|field| field.ty.to_token_stream()); let new_type = match fields { Fields::Named(_) => false, Fields::Unnamed(_) if field_types.len() == 1 => true, Fields::Unnamed(_) => false, Fields::Unit => panic!("signature_for_struct must not be called for unit fields"), }; let inner_impl = if new_type { quote! { #( <#field_types as #zv::Type>::signature() )* } } else { quote! { let mut s = <::std::string::String as ::std::convert::From<_>>::from("("); #( s.push_str(<#field_types as #zv::Type>::signature().as_str()); )* s.push_str(")"); #zv::Signature::from_string_unchecked(s) } }; if insert_enum_variant { quote! { let inner_signature = { #inner_impl }; let mut s = <::std::string::String as ::std::convert::From<_>>::from("("); s.push_str(::signature().as_str()); s.push_str(inner_signature.as_str()); s.push_str(")"); #zv::Signature::from_string_unchecked(s) } } else { inner_impl } } fn impl_unit_struct( name: Ident, generics: Generics, zv: &TokenStream, ) -> Result { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); Ok(quote! { impl #impl_generics #zv::Type for #name #ty_generics #where_clause { #[inline] fn signature() -> #zv::Signature<'static> { #zv::Signature::from_static_str_unchecked("") } } }) } fn impl_empty_struct( name: Ident, generics: Generics, zv: &TokenStream, ) -> Result { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); Ok(quote! { impl #impl_generics #zv::Type for #name #ty_generics #where_clause { #[inline] fn signature() -> #zv::Signature<'static> { #zv::Signature::from_static_str_unchecked("y") } } }) } fn impl_enum( name: Ident, generics: Generics, attrs: Vec, data: DataEnum, zv: &TokenStream, ) -> Result { let mut all_signatures: Vec> = data .variants .iter() .map(|variant| signature_for_variant(variant, &attrs, zv)) .collect(); let signature = all_signatures.pop().unwrap()?; // Ensure all variants of the enum have the same number and type of fields. for sig in all_signatures { if sig?.to_string() != signature.to_string() { return Err(Error::new( name.span(), "all variants must have the same number and type of fields", )); } } let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); Ok(quote! { impl #impl_generics #zv::Type for #name #ty_generics #where_clause { #[inline] fn signature() -> #zv::Signature<'static> { #signature } } }) } fn signature_for_variant( variant: &syn::Variant, attrs: &[Attribute], zv: &TokenStream, ) -> Result { let repr = attrs.iter().find(|attr| attr.path().is_ident("repr")); match &variant.fields { Fields::Unit => { let repr = match repr { Some(repr_attr) => repr_attr.parse_args()?, None => quote! { u32 }, }; Ok(quote! { <#repr as #zv::Type>::signature() }) } Fields::Named(_) => Ok(signature_for_struct(&variant.fields, zv, true)), Fields::Unnamed(_) => Ok(signature_for_struct(&variant.fields, zv, true)), } } zvariant_derive-4.1.2/src/utils.rs000064400000000000000000000014261046102023000153240ustar 00000000000000use proc_macro2::TokenStream; use proc_macro_crate::{crate_name, FoundCrate}; use quote::{format_ident, quote}; use zvariant_utils::def_attrs; pub fn zvariant_path() -> TokenStream { if let Ok(FoundCrate::Name(name)) = crate_name("zvariant") { let ident = format_ident!("{}", name); quote! { ::#ident } } else if let Ok(FoundCrate::Name(name)) = crate_name("zbus") { let ident = format_ident!("{}", name); quote! { ::#ident::zvariant } } else { quote! { ::zvariant } } } def_attrs! { crate zvariant; /// Attributes defined on structures. pub StructAttributes("struct") { signature str, rename_all str, deny_unknown_fields none }; /// Attributes defined on fields. pub FieldAttributes("field") { rename str }; } zvariant_derive-4.1.2/src/value.rs000064400000000000000000000264351046102023000153070ustar 00000000000000use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ spanned::Spanned, Attribute, Data, DataEnum, DeriveInput, Error, Expr, Fields, Generics, Ident, Lifetime, LifetimeParam, }; use crate::utils::*; pub enum ValueType { Value, OwnedValue, } pub fn expand_derive(ast: DeriveInput, value_type: ValueType) -> Result { let zv = zvariant_path(); match &ast.data { Data::Struct(ds) => match &ds.fields { Fields::Named(_) | Fields::Unnamed(_) => { let StructAttributes { signature, .. } = StructAttributes::parse(&ast.attrs)?; let signature = signature.map(|signature| match signature.as_str() { "dict" => "a{sv}".to_string(), _ => signature, }); impl_struct( value_type, ast.ident, ast.generics, &ds.fields, signature, &zv, ) } Fields::Unit => Err(Error::new(ast.span(), "Unit structures not supported")), }, Data::Enum(data) => impl_enum(value_type, ast.ident, ast.generics, ast.attrs, data, &zv), _ => Err(Error::new( ast.span(), "only structs and enums are supported", )), } } fn impl_struct( value_type: ValueType, name: Ident, generics: Generics, fields: &Fields, signature: Option, zv: &TokenStream, ) -> Result { let statc_lifetime = LifetimeParam::new(Lifetime::new("'static", Span::call_site())); let ( value_type, value_lifetime, into_value_trait, into_value_method, into_value_error_decl, into_value_ret, into_value_error_transform, ) = match value_type { ValueType::Value => { let mut lifetimes = generics.lifetimes(); let value_lifetime = lifetimes .next() .cloned() .unwrap_or_else(|| statc_lifetime.clone()); if lifetimes.next().is_some() { return Err(Error::new( name.span(), "Type with more than 1 lifetime not supported", )); } ( quote! { #zv::Value<#value_lifetime> }, value_lifetime, quote! { From }, quote! { from }, quote! {}, quote! { Self }, quote! {}, ) } ValueType::OwnedValue => ( quote! { #zv::OwnedValue }, statc_lifetime, quote! { TryFrom }, quote! { try_from }, quote! { type Error = #zv::Error; }, quote! { #zv::Result }, quote! { .map_err(::std::convert::Into::into) }, ), }; let type_params = generics.type_params().cloned().collect::>(); let (from_value_where_clause, into_value_where_clause) = if !type_params.is_empty() { ( Some(quote! { where #( #type_params: ::std::convert::TryFrom<#zv::Value<#value_lifetime>> + #zv::Type, <#type_params as ::std::convert::TryFrom<#zv::Value<#value_lifetime>>>::Error: ::std::convert::Into<#zv::Error> ),* }), Some(quote! { where #( #type_params: ::std::convert::Into<#zv::Value<#value_lifetime>> + #zv::Type ),* }), ) } else { (None, None) }; let (impl_generics, ty_generics, _) = generics.split_for_impl(); match fields { Fields::Named(_) => { let field_names: Vec<_> = fields .iter() .map(|field| field.ident.to_token_stream()) .collect(); let (from_value_impl, into_value_impl) = match signature { Some(signature) if signature == "a{sv}" => ( // User wants the type to be encoded as a dict. // FIXME: Not the most efficient implementation. quote! { let mut fields = <::std::collections::HashMap::<::std::string::String, #zv::Value>>::try_from(value)?; ::std::result::Result::Ok(Self { #( #field_names: fields .remove(stringify!(#field_names)) .ok_or_else(|| #zv::Error::IncorrectType)? .downcast()? ),* }) }, quote! { let mut fields = ::std::collections::HashMap::new(); #( fields.insert(stringify!(#field_names), #zv::Value::from(s.#field_names)); )* <#value_type>::#into_value_method(#zv::Value::from(fields)) #into_value_error_transform }, ), Some(_) | None => ( quote! { let mut fields = #zv::Structure::try_from(value)?.into_fields(); ::std::result::Result::Ok(Self { #( #field_names: fields.remove(0).downcast()? ),* }) }, quote! { <#value_type>::#into_value_method(#zv::StructureBuilder::new() #( .add_field(s.#field_names) )* .build()) #into_value_error_transform }, ), }; Ok(quote! { impl #impl_generics ::std::convert::TryFrom<#value_type> for #name #ty_generics #from_value_where_clause { type Error = #zv::Error; #[inline] fn try_from(value: #value_type) -> #zv::Result { #from_value_impl } } impl #impl_generics #into_value_trait<#name #ty_generics> for #value_type #into_value_where_clause { #into_value_error_decl #[inline] fn #into_value_method(s: #name #ty_generics) -> #into_value_ret { #into_value_impl } } }) } Fields::Unnamed(_) if fields.iter().next().is_some() => { // Newtype struct. Ok(quote! { impl #impl_generics ::std::convert::TryFrom<#value_type> for #name #ty_generics #from_value_where_clause { type Error = #zv::Error; #[inline] fn try_from(value: #value_type) -> #zv::Result { ::std::convert::TryInto::try_into(value).map(Self) } } impl #impl_generics #into_value_trait<#name #ty_generics> for #value_type #into_value_where_clause { #into_value_error_decl #[inline] fn #into_value_method(s: #name #ty_generics) -> #into_value_ret { <#value_type>::#into_value_method(s.0) #into_value_error_transform } } }) } Fields::Unnamed(_) => panic!("impl_struct must not be called for tuples"), Fields::Unit => panic!("impl_struct must not be called for unit structures"), } } fn impl_enum( value_type: ValueType, name: Ident, _generics: Generics, attrs: Vec, data: &DataEnum, zv: &TokenStream, ) -> Result { let repr: TokenStream = match attrs.iter().find(|attr| attr.path().is_ident("repr")) { Some(repr_attr) => repr_attr.parse_args()?, None => quote! { u32 }, }; let mut variant_names = vec![]; let mut variant_values = vec![]; for variant in &data.variants { // Ensure all variants of the enum are unit type match variant.fields { Fields::Unit => { variant_names.push(&variant.ident); let value = match &variant .discriminant .as_ref() .ok_or_else(|| Error::new(variant.span(), "expected `Name = Value` variants"))? .1 { Expr::Lit(lit_exp) => &lit_exp.lit, _ => { return Err(Error::new( variant.span(), "expected `Name = Value` variants", )) } }; variant_values.push(value); } _ => return Err(Error::new(variant.span(), "must be a unit variant")), } } let (value_type, into_value) = match value_type { ValueType::Value => ( quote! { #zv::Value<'_> }, quote! { impl ::std::convert::From<#name> for #zv::Value<'_> { #[inline] fn from(e: #name) -> Self { let u: #repr = match e { #( #name::#variant_names => #variant_values ),* }; <#zv::Value as ::std::convert::From<_>>::from(u).into() } } }, ), ValueType::OwnedValue => ( quote! { #zv::OwnedValue }, quote! { impl ::std::convert::TryFrom<#name> for #zv::OwnedValue { type Error = #zv::Error; #[inline] fn try_from(e: #name) -> #zv::Result { let u: #repr = match e { #( #name::#variant_names => #variant_values ),* }; <#zv::OwnedValue as ::std::convert::TryFrom<_>>::try_from( <#zv::Value as ::std::convert::From<_>>::from(u) ) } } }, ), }; Ok(quote! { impl ::std::convert::TryFrom<#value_type> for #name { type Error = #zv::Error; #[inline] fn try_from(value: #value_type) -> #zv::Result { let v: #repr = ::std::convert::TryInto::try_into(value)?; ::std::result::Result::Ok(match v { #( #variant_values => #name::#variant_names ),*, _ => return ::std::result::Result::Err(#zv::Error::IncorrectType), }) } } #into_value }) } zvariant_derive-4.1.2/tests/no_prelude.rs000064400000000000000000000012261046102023000166710ustar 00000000000000#![no_implicit_prelude] #![allow(dead_code)] use ::zvariant_derive::{DeserializeDict, SerializeDict, Type}; #[derive(Type)] struct FooF(f64); #[derive(Type)] struct TestStruct { name: ::std::string::String, age: u8, blob: ::std::vec::Vec, } #[repr(u32)] #[derive(Type)] enum RequestNameFlags { AllowReplacement = 0x01, ReplaceExisting = 0x02, DoNotQueue = 0x04, } #[derive(SerializeDict, DeserializeDict, Type)] #[zvariant(deny_unknown_fields, signature = "a{sv}")] struct Test { field_a: ::std::option::Option, #[zvariant(rename = "field-b")] field_b: ::std::string::String, field_c: ::std::vec::Vec, } zvariant_derive-4.1.2/tests/tests.rs000064400000000000000000000037711046102023000157060ustar 00000000000000#![allow(dead_code)] use std::collections::HashMap; use zvariant::{ serialized::{Context, Format}, DeserializeDict, OwnedValue, SerializeDict, Type, Value, LE, }; #[test] fn derive_unit_struct() { #[derive(Type)] struct FooF(f64); assert_eq!(FooF::signature(), "d") } #[test] fn derive_struct() { #[derive(Type)] struct TestStruct { name: String, age: u8, blob: Vec, } assert_eq!(TestStruct::signature(), "(syay)") } #[test] fn derive_enum() { #[repr(u32)] #[derive(Type)] enum RequestNameFlags { AllowReplacement = 0x01, ReplaceExisting = 0x02, DoNotQueue = 0x04, } assert_eq!(RequestNameFlags::signature(), "u") } #[test] fn derive_dict() { #[derive(SerializeDict, DeserializeDict, Type)] #[zvariant(deny_unknown_fields, signature = "a{sv}", rename_all = "camelCase")] struct Test { field_a: Option, #[zvariant(rename = "field-b")] field_b: String, field_c: Vec, } let test = Test { field_a: Some(1), field_b: "foo".to_string(), field_c: vec![1, 2, 3], }; let ctxt = Context::new(Format::DBus, LE, 0); let serialized = zvariant::to_bytes(ctxt, &test).unwrap(); let deserialized: HashMap = serialized.deserialize().unwrap().0; assert_eq!( deserialized["fieldA"], Value::from(1u32).try_into().unwrap() ); assert_eq!( deserialized["field-b"], Value::from("foo").try_into().unwrap() ); assert_eq!( deserialized["fieldC"], Value::from(&[1u8, 2, 3][..]).try_into().unwrap() ); let serialized = zvariant::to_bytes(ctxt, &deserialized).unwrap(); let deserialized: Test = serialized.deserialize().unwrap().0; assert_eq!(deserialized.field_a, Some(1u32)); assert_eq!(deserialized.field_b.as_str(), "foo"); assert_eq!(deserialized.field_c.as_slice(), &[1u8, 2, 3][..]); assert_eq!(Test::signature(), "a{sv}") }