zvariant_derive-2.10.0/.cargo_vcs_info.json0000644000000001120000000000100143020ustar { "git": { "sha1": "98e4fa1d424e40ca925d6117edc191b30b170048" } } zvariant_derive-2.10.0/Cargo.toml0000644000000025540000000000100123140ustar # 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 = "2018" name = "zvariant_derive" version = "2.10.0" authors = ["Zeeshan Ali "] description = "D-Bus & GVariant encoding & decoding" readme = "../README.md" keywords = ["D-Bus", "DBus", "IPC", "GVariant"] categories = ["data-structures", "encoding", "parsing"] license = "MIT" repository = "https://gitlab.freedesktop.org/dbus/zbus/" resolver = "2" [lib] proc-macro = true [dependencies.proc-macro-crate] version = "1.0.0" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0.3" [dependencies.syn] version = "1.0.38" features = ["extra-traits", "full"] [dev-dependencies.byteorder] version = "1.3.1" [dev-dependencies.enumflags2] version = "0.6.4" features = ["serde"] [dev-dependencies.serde] version = "1.0" features = ["derive"] [dev-dependencies.serde_repr] version = "0.1.5" [dev-dependencies.zvariant] version = "2" features = ["enumflags2"] zvariant_derive-2.10.0/Cargo.toml.orig000064400000000000000000000015220072674642500160170ustar 00000000000000[package] name = "zvariant_derive" # Keep major and minor version in sync with zvariant crate version = "2.10.0" authors = ["Zeeshan Ali "] edition = "2018" description = "D-Bus & GVariant encoding & decoding" repository = "https://gitlab.freedesktop.org/dbus/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" syn = { version = "1.0.38", features = ["extra-traits", "full"] } quote = "1.0.3" proc-macro-crate = "1.0.0" [dev-dependencies] byteorder = "1.3.1" zvariant = { path = "../zvariant", version = "2", features = ["enumflags2"] } enumflags2 = { version = "0.6.4", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } serde_repr = "0.1.5" zvariant_derive-2.10.0/LICENSE000064400000000000000000000017770072674642500141510ustar 00000000000000Permission 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-2.10.0/src/dict.rs000064400000000000000000000172750072674642500152240ustar 00000000000000use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{ punctuated::Punctuated, Data, DeriveInput, Meta::Path, NestedMeta::Meta, Type, TypePath, }; use crate::utils::*; pub fn expand_type_derive(input: DeriveInput) -> TokenStream { let name = match input.data { Data::Struct(_) => input.ident, _ => panic!("Only works with structure"), }; let zv = zvariant_path(); let generics = input.generics; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { impl #impl_generics #zv::Type for #name #ty_generics #where_clause { fn signature() -> #zv::Signature<'static> { #zv::Signature::from_static_str_unchecked("a{sv}") } } } } pub fn expand_serialize_derive(input: DeriveInput) -> TokenStream { let (name, data) = match input.data { Data::Struct(data) => (input.ident, data), _ => panic!("Only works with structure"), }; let zv = zvariant_path(); let mut entries = quote! {}; for f in &data.fields { let attrs = parse_item_attributes(&f.attrs).unwrap(); let name = &f.ident; let dict_name = attrs .iter() .find_map(|x| match x { ItemAttribute::Rename(n) => Some(n.to_string()), }) .unwrap_or_else(|| f.ident.as_ref().unwrap().to_string()); let is_option = match &f.ty { Type::Path(TypePath { path: syn::Path { segments, .. }, .. }) => segments.last().unwrap().ident == "Option", _ => false, }; 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); } let generics = input.generics; let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { 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::None)?; #entries map.end() } } } } pub fn expand_deserialize_derive(input: DeriveInput) -> TokenStream { let (name, data) = match input.data { Data::Struct(data) => (input.ident, data), _ => panic!("Only works with structure"), }; let mut deny_unknown_fields = false; for meta_item in input.attrs.iter().flat_map(get_meta_items).flatten() { match &meta_item { Meta(Path(p)) if p.is_ident("deny_unknown_fields") => { deny_unknown_fields = true; } _ => panic!("unsupported attribute"), } } 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 attrs = parse_item_attributes(&f.attrs).unwrap(); let name = &f.ident; let dict_name = attrs .iter() .find_map(|x| match x { ItemAttribute::Rename(n) => Some(n.to_string()), }) .unwrap_or_else(|| f.ident.as_ref().unwrap().to_string()); let is_option = match &f.ty { Type::Path(TypePath { path: syn::Path { segments, .. }, .. }) => segments.last().unwrap().ident == "Option", _ => false, }; 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::LifetimeDef { 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(); quote! { 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-2.10.0/src/lib.rs000064400000000000000000000232710072674642500150400ustar 00000000000000#![deny(rust_2018_idioms)] #![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. use proc_macro::TokenStream; use syn::{self, 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::{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::::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::::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).into() } /// 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).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::{EncodingContext, to_bytes}; /// use zvariant_derive::{SerializeDict, TypeDict}; /// /// #[derive(SerializeDict, TypeDict)] /// 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)}`. /// /// [`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).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::{EncodingContext, to_bytes}; /// use zvariant_derive::{DeserializeDict, TypeDict}; /// /// #[derive(DeserializeDict, TypeDict)] /// 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}`. /// /// [`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).into() } /// Implements conversions for your type to/from [`Value`]. /// /// Implements `TryFrom` and `Into` for your type. /// /// # Examples /// /// Simple owned strutures: /// /// ``` /// use std::convert::TryFrom; /// use zvariant::{OwnedObjectPath, OwnedValue, Value}; /// use zvariant_derive::{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::from(s); /// 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 std::convert::TryFrom; /// use zvariant::{ObjectPath, Str}; ///# use zvariant::{OwnedValue, Value}; ///# use zvariant_derive::{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::from(s); /// let s = UnownedStruct::try_from(value).unwrap(); /// assert_eq!(s.s, "hi"); /// assert_eq!(s.path, "/blah"); /// ``` /// /// Generic structures also supported: /// /// ``` ///# use std::convert::TryFrom; ///# use zvariant::{OwnedObjectPath, OwnedValue, Value}; ///# use zvariant_derive::{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::from(s); /// 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 std::convert::TryFrom; ///# use zvariant::{OwnedObjectPath, OwnedValue, Value}; ///# use zvariant_derive::{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::from(Enum::Variant2); /// let e = Enum::try_from(value).unwrap(); /// assert_eq!(e, Enum::Variant2); /// ``` /// /// [`Value`]: https://docs.rs/zvariant/2.0.0/zvariant/enum.Value.html #[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).into() } /// Implements conversions for your type to/from [`OwnedValue`]. /// /// Implements `TryFrom` and `Into` for your type. /// /// See [`Value`] documentation for examples. /// /// [`OwnedValue`]: https://docs.rs/zvariant/2.0.0/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).into() } zvariant_derive-2.10.0/src/type.rs000064400000000000000000000066200072674642500152520ustar 00000000000000use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::{self, Attribute, Data, DataEnum, DeriveInput, Fields, Generics, Ident}; use crate::utils::zvariant_path; pub fn expand_derive(ast: DeriveInput) -> TokenStream { let zv = zvariant_path(); match ast.data { Data::Struct(ds) => match ds.fields { 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), _ => panic!("Only structures and enums supported at the moment"), } } fn impl_struct(name: Ident, generics: Generics, fields: Fields, zv: &TokenStream) -> TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let signature = signature_for_struct(fields, zv); 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) -> 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"), }; 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) } } } fn impl_unit_struct(name: Ident, generics: Generics, zv: &TokenStream) -> TokenStream { let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 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_enum( name: Ident, generics: Generics, attrs: Vec, data: DataEnum, zv: &TokenStream, ) -> TokenStream { let repr: TokenStream = match attrs.iter().find(|attr| attr.path.is_ident("repr")) { Some(repr_attr) => repr_attr .parse_args() .expect("Failed to parse `#[repr(...)]` attribute"), None => quote! { u32 }, }; for variant in data.variants { // Ensure all variants of the enum are unit type match variant.fields { Fields::Unit => (), _ => panic!("`{}` must be a unit variant", variant.ident), } } let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); quote! { impl #impl_generics #zv::Type for #name #ty_generics #where_clause { #[inline] fn signature() -> #zv::Signature<'static> { <#repr as #zv::Type>::signature() } } } } zvariant_derive-2.10.0/src/utils.rs000064400000000000000000000047650072674642500154410ustar 00000000000000use proc_macro2::TokenStream; use proc_macro_crate::{crate_name, FoundCrate}; use quote::{format_ident, quote}; use syn::{Attribute, Lit, Meta, Meta::List, MetaList, NestedMeta, Result}; pub fn zvariant_path() -> TokenStream { if let Ok(FoundCrate::Name(name)) = crate_name("zvariant") { let ident = format_ident!("{}", name); quote! { ::#ident } } else { quote! { ::zvariant } } } // find the #[@attr_name] attribute in @attrs fn find_attribute_meta(attrs: &[Attribute], attr_name: &str) -> Result> { let meta = match attrs.iter().find(|a| a.path.is_ident(attr_name)) { Some(a) => a.parse_meta(), _ => return Ok(None), }; match meta? { Meta::List(n) => Ok(Some(n)), _ => panic!("wrong meta type"), } } // parse a single meta like: ident = "value" fn parse_attribute(meta: &NestedMeta) -> (String, String) { let meta = match &meta { NestedMeta::Meta(m) => m, _ => panic!("wrong meta type"), }; let meta = match meta { Meta::Path(p) => return (p.get_ident().unwrap().to_string(), "".to_string()), Meta::NameValue(n) => n, _ => panic!("wrong meta type"), }; let value = match &meta.lit { Lit::Str(s) => s.value(), _ => panic!("wrong meta type"), }; let ident = match meta.path.get_ident() { None => panic!("missing ident"), Some(ident) => ident, }; (ident.to_string(), value) } #[derive(Debug, PartialEq)] pub enum ItemAttribute { Rename(String), } fn parse_item_attribute(meta: &NestedMeta) -> Result { let (ident, v) = parse_attribute(meta); match ident.as_ref() { "rename" => Ok(ItemAttribute::Rename(v)), s => panic!("Unknown item meta {}", s), } } // Parse optional item attributes such as: // #[zvariant(rename = "MyName")] pub fn parse_item_attributes(attrs: &[Attribute]) -> Result> { let meta = find_attribute_meta(attrs, "zvariant")?; let v = match meta { Some(meta) => meta .nested .iter() .map(|m| parse_item_attribute(m).unwrap()) .collect(), None => Vec::new(), }; Ok(v) } pub fn get_meta_items(attr: &Attribute) -> Result> { if !attr.path.is_ident("zvariant") { return Ok(Vec::new()); } match attr.parse_meta() { Ok(List(meta)) => Ok(meta.nested.into_iter().collect()), _ => panic!("unsupported attribute"), } } zvariant_derive-2.10.0/src/value.rs000064400000000000000000000163040072674642500154050ustar 00000000000000use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ self, Attribute, Data, DataEnum, DeriveInput, Expr, Fields, Generics, Ident, Lifetime, LifetimeDef, }; use crate::utils::zvariant_path; pub enum ValueType { Value, OwnedValue, } pub fn expand_derive(ast: DeriveInput, value_type: ValueType) -> TokenStream { let zv = zvariant_path(); match ast.data { Data::Struct(ds) => match ds.fields { Fields::Named(_) | Fields::Unnamed(_) => { impl_struct(value_type, ast.ident, ast.generics, ds.fields, &zv) } Fields::Unit => panic!("Unit structures not supported"), }, Data::Enum(data) => impl_enum(value_type, ast.ident, ast.generics, ast.attrs, data, &zv), _ => panic!("Only structures and enums supported at the moment"), } } fn impl_struct( value_type: ValueType, name: Ident, generics: Generics, fields: Fields, zv: &TokenStream, ) -> TokenStream { let statc_lifetime = LifetimeDef::new(Lifetime::new("'static", Span::call_site())); let (value_type, value_lifetime) = match value_type { ValueType::Value => { let mut lifetimes = generics.lifetimes(); let value_lifetime = lifetimes .next() .cloned() .unwrap_or_else(|| statc_lifetime.clone()); assert!( lifetimes.next().is_none(), "Type with more than 1 lifetime not supported" ); (quote! { #zv::Value<#value_lifetime> }, value_lifetime) } ValueType::OwnedValue => (quote! { #zv::OwnedValue }, statc_lifetime), }; 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 ),* }), 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(); 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 { let mut fields = #zv::Structure::try_from(value)?.into_fields(); ::std::result::Result::Ok(Self { #( #field_names: fields .remove(0) .downcast() .ok_or_else(|| #zv::Error::IncorrectType)? ),* }) } } impl #impl_generics From<#name #ty_generics> for #value_type #into_value_where_clause { #[inline] fn from(s: #name #ty_generics) -> Self { #zv::StructureBuilder::new() #( .add_field(s.#field_names) )* .build() .into() } } } } Fields::Unnamed(_) if fields.iter().next().is_some() => { // Newtype struct. 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 From<#name #ty_generics> for #value_type #into_value_where_clause { #[inline] fn from(s: #name #ty_generics) -> Self { s.0.into() } } } } 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, ) -> TokenStream { let repr: TokenStream = match attrs.iter().find(|attr| attr.path.is_ident("repr")) { Some(repr_attr) => repr_attr .parse_args() .expect("Failed to parse `#[repr(...)]` attribute"), 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 .expect("expected `Name = Value` variants") .1 { Expr::Lit(lit_exp) => lit_exp.lit, _ => panic!("expected `Name = Value` variants"), }; variant_values.push(value); } _ => panic!("`{}` must be a unit variant", variant.ident), } } let value_type = match value_type { ValueType::Value => quote! { #zv::Value<'_> }, ValueType::OwnedValue => quote! { #zv::OwnedValue }, }; 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), }) } } impl ::std::convert::From<#name> for #value_type { #[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() } } } } zvariant_derive-2.10.0/tests/no_prelude.rs000064400000000000000000000012170072674642500167750ustar 00000000000000#![no_implicit_prelude] #![allow(dead_code)] use ::zvariant_derive::{DeserializeDict, SerializeDict, Type, TypeDict}; #[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, TypeDict)] #[zvariant(deny_unknown_fields)] struct Test { field_a: ::std::option::Option, #[zvariant(rename = "field-b")] field_b: ::std::string::String, field_c: ::std::vec::Vec, } zvariant_derive-2.10.0/tests/tests.rs000064400000000000000000000017300072674642500160030ustar 00000000000000#![allow(dead_code)] use zvariant::Type; use zvariant_derive::{DeserializeDict, SerializeDict, Type, TypeDict}; #[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, TypeDict)] #[zvariant(deny_unknown_fields)] struct Test { field_a: Option, #[zvariant(rename = "field-b")] field_b: String, field_c: Vec, } assert_eq!(Test::signature(), "a{sv}") }