zvariant_derive-5.9.2/.cargo_vcs_info.json0000644000000001550000000000100142460ustar { "git": { "sha1": "f473c1dd77904ea32213f07a4fee400c3a9247bd" }, "path_in_vcs": "zvariant_derive" }zvariant_derive-5.9.2/CHANGELOG.md000064400000000000000000000014631046102023000146520ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 5.9.1 - 2026-01-10 ### Fixed - 🐛 Fix hard-coded zvariant path in signature generation. ### Other - 🤖 release-plz: Fix formatting of CHANGELOG files. - 🤖 release-plz: Use the default header in changelog. ### Testing - ✅ Add tests for `signature_to_tokens_with_crate`. ## 5.9.0 - 2026-01-09 ### Added - ✨ zvariant_derive: Add crate attribute for custom crate paths. - ✨ Add signature! macro for compile-time validation. #984 ### Changed - 🎨 Format all files (rust 1.85). ### Fixed - 🐛 zvariant_derive shouldn't set features on zvariant. zvariant_derive-5.9.2/Cargo.lock0000644000000115650000000000100122300ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "enumflags2" version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", ] [[package]] name = "enumflags2_derive" version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "indexmap" version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "proc-macro-crate" version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_repr" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", "toml_datetime", "toml_parser", "winnow", ] [[package]] name = "toml_parser" version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "winnow" version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] [[package]] name = "zvariant_derive" version = "5.9.2" dependencies = [ "enumflags2", "proc-macro-crate", "proc-macro2", "quote", "serde", "serde_repr", "syn", "zvariant_utils", ] [[package]] name = "zvariant_utils" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" dependencies = [ "proc-macro2", "quote", "serde", "syn", "winnow", ] zvariant_derive-5.9.2/Cargo.toml0000644000000034200000000000100122420ustar # 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 = "2024" rust-version = "1.85" name = "zvariant_derive" version = "5.9.2" authors = ["Zeeshan Ali Khan "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false 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/z-galaxy/zbus/" resolver = "2" [features] default = [] gvariant = ["zvariant_utils/gvariant"] [lib] name = "zvariant_derive" path = "src/lib.rs" proc-macro = true [[test]] name = "no_prelude" path = "tests/no_prelude.rs" [[test]] name = "tests" path = "tests/tests.rs" [dependencies.proc-macro-crate] version = "3.2.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 = "3.3.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" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = ["cfg(tokio_unstable)"] zvariant_derive-5.9.2/Cargo.toml.orig000064400000000000000000000016531046102023000157310ustar 00000000000000[package] name = "zvariant_derive" # Keep major and minor version in sync with zvariant crate version = "5.9.2" authors = ["Zeeshan Ali Khan "] edition = { workspace = true } rust-version = { workspace = true } description = "D-Bus & GVariant encoding & decoding" repository = { workspace = true } keywords = ["D-Bus", "DBus", "IPC", "GVariant"] license = { workspace = true } categories = ["data-structures", "encoding", "parsing"] readme = "README.md" [lib] proc-macro = true [features] default = [] gvariant = ["zvariant_utils/gvariant"] [dependencies] proc-macro2.workspace = true syn.workspace = true quote.workspace = true proc-macro-crate.workspace = true zvariant_utils = { path = "../zvariant_utils", version = "3.3.0" } [dev-dependencies] zvariant = { workspace = true, features = ["enumflags2"] } enumflags2.workspace = true serde.workspace = true serde_repr.workspace = true [lints] workspace = true zvariant_derive-5.9.2/LICENSE000064400000000000000000000020701046102023000140410ustar 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-5.9.2/README.md000064400000000000000000000017371046102023000143240ustar 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-5.9.2/src/dict.rs000064400000000000000000000167441046102023000151310ustar 00000000000000use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::{Data, DeriveInput, Error, Field, punctuated::Punctuated, spanned::Spanned}; use zvariant_utils::macros; use crate::utils::*; fn dict_name_for_field( f: &Field, rename_attr: Option, rename_all_attr: Option<&str>, ) -> Result { let ident = f.ident.as_ref().unwrap().to_string(); rename_identifier(ident, f.span(), rename_attr, rename_all_attr) } /// Implements `Serialize` for structs as D-Bus dictionaries via a serde helper. pub fn expand_serialize_derive(input: DeriveInput) -> Result { let StructAttributes { rename_all, crate_path: crate_attr, .. } = StructAttributes::parse(&input.attrs)?; let crate_path = parse_crate_path(crate_attr.as_deref())?; let rename_all_str = rename_all.as_deref().unwrap_or("snake_case"); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let name = &input.ident; let helper = format_ident!("__SerializeDict{}", name); let zv = zvariant_path(crate_path.as_ref()); let mut field_defs = Vec::new(); let mut field_inits = Vec::new(); if let Data::Struct(data) = &input.data { for field in &data.fields { let ident = field.ident.as_ref().unwrap(); let ty = &field.ty; let FieldAttributes { rename, .. } = FieldAttributes::parse(&field.attrs)?; let dict_name = dict_name_for_field(field, rename, rename_all.as_deref())?; let is_opt = macros::ty_is_option(ty); if is_opt { let as_value_opt_path = quote! { #zv::as_value::optional }; let as_value_opt_str = format!("{as_value_opt_path}"); field_defs.push(quote! { #[serde( rename = #dict_name, with = #as_value_opt_str, skip_serializing_if = "Option::is_none", )] #ident: &'a #ty }); } else { let as_value_path = quote! { #zv::as_value }; let as_value_str = format!("{as_value_path}"); field_defs.push(quote! { #[serde(rename = #dict_name, with = #as_value_str)] #ident: &'a #ty }); } field_inits.push(quote! { #ident: &self.#ident }); } } else { return Err(Error::new(input.span(), "only structs supported")); } 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::Serialize; #[derive(Serialize)] #[serde(rename_all = #rename_all_str)] struct #helper<'a> { #[serde(skip)] phantom: ::std::marker::PhantomData<&'a ()>, #(#field_defs,)* } let helper = #helper { phantom: ::std::marker::PhantomData, #(#field_inits,)* }; helper.serialize(serializer) } } }) } /// Implements `Deserialize` for structs from D-Bus dictionaries via a serde helper. pub fn expand_deserialize_derive(input: DeriveInput) -> Result { let StructAttributes { rename_all, deny_unknown_fields, crate_path: crate_attr, .. } = StructAttributes::parse(&input.attrs)?; let crate_path = parse_crate_path(crate_attr.as_deref())?; let rename_all_str = rename_all.as_deref().unwrap_or("snake_case"); let zv = zvariant_path(crate_path.as_ref()); // Create a new generics with a 'de lifetime let mut generics = input.generics.clone(); let lifetime_param = syn::LifetimeParam { attrs: Vec::new(), lifetime: syn::Lifetime::new("'de", Span::call_site()), colon_token: None, bounds: Punctuated::new(), }; generics .params .insert(0, syn::GenericParam::Lifetime(lifetime_param)); let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl(); let (_, orig_ty_generics, _) = input.generics.split_for_impl(); let name = &input.ident; let helper = format_ident!("__DeserializeDict{}", name); let mut field_defs = Vec::new(); let mut field_assignments = Vec::new(); let mut non_optional_field_checks = Vec::new(); if let Data::Struct(data) = &input.data { for field in &data.fields { let ident = field.ident.as_ref().unwrap(); let ty = &field.ty; let FieldAttributes { rename, .. } = FieldAttributes::parse(&field.attrs)?; let dict_name = dict_name_for_field(field, rename, rename_all.as_deref())?; let is_opt = macros::ty_is_option(ty); if is_opt { let as_value_opt_path = quote! { #zv::as_value::optional }; let as_value_opt_str = format!("{as_value_opt_path}"); field_defs.push(quote! { #[serde(rename = #dict_name, with = #as_value_opt_str, default)] #ident: #ty }); field_assignments.push(quote! { #ident: helper.#ident }); } else { // For non-optional fields, use Option in helper for default support let as_value_opt_path = quote! { #zv::as_value::optional }; let as_value_opt_str = format!("{as_value_opt_path}"); field_defs.push(quote! { #[serde(rename = #dict_name, with = #as_value_opt_str, default)] #ident: Option<#ty> }); // Add a check to make sure this field was provided non_optional_field_checks.push(quote! { if helper.#ident.is_none() { return Err(::missing_field(#dict_name)); } }); // Unwrap the option for field assignment field_assignments.push(quote! { #ident: helper.#ident.unwrap() }); } } } else { return Err(Error::new(input.span(), "only structs supported")); } let deny_attr = if deny_unknown_fields { quote! { , deny_unknown_fields } } else { quote! {} }; Ok(quote! { #[allow(deprecated)] impl #impl_generics #zv::export::serde::de::Deserialize<'de> for #name #orig_ty_generics #where_clause { fn deserialize(deserializer: D) -> ::std::result::Result where D: #zv::export::serde::de::Deserializer<'de>, { use #zv::export::serde::Deserialize; #[derive(Deserialize, Default)] #[serde(default, rename_all = #rename_all_str #deny_attr)] struct #helper { #(#field_defs,)* } let helper = #helper::deserialize(deserializer)?; // Check for missing non-optional fields #(#non_optional_field_checks)* Ok(Self { #(#field_assignments,)* }) } } }) } zvariant_derive-5.9.2/src/lib.rs000064400000000000000000000456221046102023000147510ustar 00000000000000#![deny(rust_2018_idioms)] #![doc( html_logo_url = "https://raw.githubusercontent.com/z-galaxy/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 signature; 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::{ /// serialized::Context, as_value, to_bytes, Type, LE, /// }; /// use serde::{Deserialize, Serialize}; /// /// #[derive(Deserialize, Serialize, Type, PartialEq, Debug)] /// // `#[zvariant(signature = "a{sv}")]` would be the same. /// #[zvariant(signature = "dict")] /// struct Struct { /// #[serde(with = "as_value")] /// field1: u16, /// #[serde(with = "as_value")] /// field2: i64, /// #[serde(with = "as_value")] /// 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); /// ``` /// /// # Custom crate path /// /// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export, /// you can specify the crate path using the `crate` attribute: /// /// ``` /// use zvariant::Type; /// /// #[derive(Type)] /// #[zvariant(crate = "zvariant")] /// struct MyStruct { /// field: String, /// } /// ``` /// /// [`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(zbus, 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. /// /// # Alternative Approaches /// /// There are two approaches to serializing structs as dictionaries: /// /// 1. Using this macro (simpler, but less control). /// 2. Using the `Serialize` derive with `zvariant::as_value` (more verbose, but more control). /// /// See the example below and the relevant [FAQ entry] in our book for more details on the /// alternative approach. /// /// # Example /// /// ## Approach #1 /// /// ``` /// use zvariant::{SerializeDict, Type}; /// /// #[derive(Debug, Default, SerializeDict, Type)] /// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")] /// pub struct MyStruct { /// field1: Option, /// field2: String, /// } /// ``` /// /// ## Approach #2 /// /// ``` /// use serde::Serialize; /// use zvariant::{Type, as_value}; /// /// #[derive(Debug, Default, Serialize, Type)] /// #[zvariant(signature = "a{sv}")] /// #[serde(default, rename_all = "PascalCase")] /// pub struct MyStruct { /// #[serde(with = "as_value::optional", skip_serializing_if = "Option::is_none")] /// field1: Option, /// #[serde(with = "as_value")] /// field2: String, /// } /// ``` /// /// # Custom crate path /// /// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export, /// you can specify the crate path using the `crate` attribute: /// /// ``` /// use zvariant::{SerializeDict, Type}; /// /// #[derive(SerializeDict, Type)] /// #[zvariant(signature = "a{sv}", crate = "zvariant")] /// struct MyStruct { /// field: String, /// } /// ``` /// /// [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html /// [FAQ entry]: https://z-galaxy.github.io/zbus/faq.html#how-to-use-a-struct-as-a-dictionary #[proc_macro_derive(SerializeDict, attributes(zbus, 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. /// /// # Alternative Approaches /// /// There are two approaches to deserializing dictionaries as structs: /// /// 1. Using this macro (simpler, but less control). /// 2. Using the `Deserialize` derive with `zvariant::as_value` (more verbose, but more control). /// /// See the example below and the relevant [FAQ entry] in our book for more details on the /// alternative approach. /// /// # Example /// /// ## Approach #1 /// /// ``` /// use zvariant::{DeserializeDict, Type}; /// /// #[derive(Debug, Default, DeserializeDict, Type)] /// #[zvariant(signature = "a{sv}", rename_all = "PascalCase")] /// pub struct MyStruct { /// field1: Option, /// field2: String, /// } /// ``` /// /// ## Approach #2 /// /// ``` /// use serde::Deserialize; /// use zvariant::{Type, as_value}; /// /// #[derive(Debug, Default, Deserialize, Type)] /// #[zvariant(signature = "a{sv}")] /// #[serde(default, rename_all = "PascalCase")] /// pub struct MyStruct { /// #[serde(with = "as_value::optional")] /// field1: Option, /// #[serde(with = "as_value")] /// field2: String, /// } /// ``` /// /// # Custom crate path /// /// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export, /// you can specify the crate path using the `crate` attribute: /// /// ``` /// use zvariant::{DeserializeDict, Type}; /// /// #[derive(DeserializeDict, Type)] /// #[zvariant(signature = "a{sv}", crate = "zvariant")] /// struct MyStruct { /// field: String, /// } /// ``` /// /// [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html /// [FAQ entry]: https://z-galaxy.github.io/zbus/faq.html#how-to-use-a-struct-as-a-dictionary #[proc_macro_derive(DeserializeDict, attributes(zbus, 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 with unit variants: /// /// ``` /// # use zvariant::{OwnedValue, Value}; /// # /// #[derive(Debug, PartialEq, Value, OwnedValue)] /// // Default representation is `u32`. /// #[repr(u8)] /// enum Enum { /// Variant1 = 0, /// Variant2, /// } /// /// let value = Value::from(Enum::Variant1); /// let e = Enum::try_from(value).unwrap(); /// assert_eq!(e, Enum::Variant1); /// assert_eq!(e as u8, 0); /// let value = OwnedValue::try_from(Enum::Variant2).unwrap(); /// let e = Enum::try_from(value).unwrap(); /// assert_eq!(e, Enum::Variant2); /// ``` /// /// String-encoded enums are also supported: /// /// ``` /// # use zvariant::{OwnedValue, Value}; /// # /// #[derive(Debug, PartialEq, Value, OwnedValue)] /// #[zvariant(signature = "s")] /// enum StrEnum { /// Variant1, /// Variant2, /// } /// /// let value = Value::from(StrEnum::Variant1); /// let e = StrEnum::try_from(value).unwrap(); /// assert_eq!(e, StrEnum::Variant1); /// let value = OwnedValue::try_from(StrEnum::Variant2).unwrap(); /// let e = StrEnum::try_from(value).unwrap(); /// assert_eq!(e, StrEnum::Variant2); /// ``` /// /// # Renaming fields /// /// ## Auto Renaming /// /// 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. /// /// Currently the macro supports the following values for `case`: /// /// * `"lowercase"` /// * `"UPPERCASE"` /// * `"PascalCase"` /// * `"camelCase"` /// * `"snake_case"` /// * `"kebab-case"` /// /// ## Individual Fields /// /// 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. /// /// Here is an example using both `rename` and `rename_all`: /// /// ``` /// # use zvariant::{OwnedValue, Value, Dict}; /// # use std::collections::HashMap; /// # /// #[derive(Clone, Value, OwnedValue)] /// #[zvariant(signature = "dict", rename_all = "PascalCase")] /// struct RenamedStruct { /// #[zvariant(rename = "MyValue")] /// field1: String, /// field2: String, /// } /// /// let s = RenamedStruct { /// field1: String::from("hello"), /// field2: String::from("world") /// }; /// let v = Value::from(s); /// let d = Dict::try_from(v).unwrap(); /// let hm: HashMap = HashMap::try_from(d).unwrap(); /// assert_eq!(hm.get("MyValue").unwrap().as_str(), "hello"); /// assert_eq!(hm.get("Field2").unwrap().as_str(), "world"); /// ``` /// /// # 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. /// /// # Custom crate path /// /// If you've renamed `zvariant` in your `Cargo.toml` or are using it through a re-export, /// you can specify the crate path using the `crate` attribute: /// /// ``` /// use zvariant::Value; /// /// #[derive(Clone, Value)] /// #[zvariant(crate = "zvariant")] /// struct MyStruct { /// field: String, /// } /// ``` /// /// [`Value`]: https://docs.rs/zvariant/latest/zvariant/enum.Value.html /// [`Type`]: crate::Type#custom-signatures #[proc_macro_derive(Value, attributes(zbus, zvariant))] 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, attributes(zbus, zvariant))] 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() } /// Constructs a const [`Signature`] with compile-time validation. /// /// This macro creates a `Signature` from a string literal at compile time, validating /// that the signature string is valid D-Bus signature. Invalid signatures will cause /// a compilation error. /// /// # Examples /// /// ## Basic usage /// /// ``` /// use zvariant::signature; /// /// // Create signatures for basic types /// let sig = signature!("s"); // String signature /// assert_eq!(sig.to_string(), "s"); /// /// let sig = signature!("i"); // 32-bit integer signature /// assert_eq!(sig.to_string(), "i"); /// ``` /// /// ## Container types /// /// ``` /// use zvariant::signature; /// /// // Array of strings /// let sig = signature!("as"); /// assert_eq!(sig.to_string(), "as"); /// /// // Dictionary mapping strings to variants /// let sig = signature!("a{sv}"); /// assert_eq!(sig.to_string(), "a{sv}"); /// /// // Structures /// let sig = signature!("(isx)"); /// assert_eq!(sig.to_string(), "(isx)"); /// ``` /// /// ## Const signatures /// /// The macro can be used to create const signatures, which is especially useful /// for defining signatures at compile time: /// /// ``` /// use zvariant::{signature, Signature}; /// /// const MY_SIGNATURE: Signature = signature!("a{sv}"); /// /// fn process_data(_data: &str) { /// assert_eq!(MY_SIGNATURE.to_string(), "a{sv}"); /// } /// ``` /// /// ## Using the `dict` alias /// /// For convenience, `dict` is an alias for `a{sv}` (string-to-variant dictionary): /// /// ``` /// use zvariant::signature; /// /// let sig = signature!("dict"); /// assert_eq!(sig.to_string(), "a{sv}"); /// ``` /// /// ## Compile-time validation /// /// Invalid signatures will be caught at compile time: /// /// ```compile_fail /// use zvariant::signature; /// /// // This will fail to compile because 'z' is not a valid D-Bus type /// let sig = signature!("z"); /// ``` /// /// [`Signature`]: https://docs.rs/zvariant/latest/zvariant/enum.Signature.html #[proc_macro] pub fn signature(input: TokenStream) -> TokenStream { signature::expand_signature_macro(input.into()) .unwrap_or_else(|err| err.to_compile_error()) .into() } zvariant_derive-5.9.2/src/signature.rs000064400000000000000000000135371046102023000162040ustar 00000000000000use std::str::FromStr; use proc_macro2::{Literal, TokenStream}; use quote::quote; use syn::{Error, parse::Parse}; use zvariant_utils::signature::Signature; /// Expand the `signature!` macro implementation. /// /// Takes a string literal signature and converts it to compile-time tokens /// representing a const `Signature`. pub fn expand_signature_macro(input: TokenStream) -> Result { let SignatureInput { literal: signature_str, } = syn::parse2(input)?; let signature_string = signature_str.to_string(); let signature_string = signature_string.trim_matches('"'); let signature = match signature_string { "dict" => Signature::dict(Signature::Str, Signature::Variant), s => Signature::from_str(s).map_err(|e| Error::new(signature_str.span(), e))?, }; let signature_tokens = signature_to_tokens(&signature); Ok(signature_tokens) } /// Input type for the signature macro. struct SignatureInput { literal: Literal, } impl Parse for SignatureInput { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { Ok(SignatureInput { literal: input.parse()?, }) } } /// Converts a parsed `Signature` to compile-time token representation. /// /// This function generates the Rust tokens that will construct the signature /// at compile time. Used by both the signature! macro and the Type derive macro. pub fn signature_to_tokens(signature: &Signature) -> TokenStream { signature_to_tokens_with_crate(signature, "e! { ::zvariant }) } /// Converts a parsed `Signature` to compile-time token representation with a custom crate path. /// /// This function generates the Rust tokens that will construct the signature /// at compile time, using the provided crate path for zvariant. pub fn signature_to_tokens_with_crate(signature: &Signature, zv: &TokenStream) -> TokenStream { match signature { Signature::Unit => quote! { #zv::Signature::Unit }, Signature::Bool => quote! { #zv::Signature::Bool }, Signature::U8 => quote! { #zv::Signature::U8 }, Signature::I16 => quote! { #zv::Signature::I16 }, Signature::U16 => quote! { #zv::Signature::U16 }, Signature::I32 => quote! { #zv::Signature::I32 }, Signature::U32 => quote! { #zv::Signature::U32 }, Signature::I64 => quote! { #zv::Signature::I64 }, Signature::U64 => quote! { #zv::Signature::U64 }, Signature::F64 => quote! { #zv::Signature::F64 }, Signature::Str => quote! { #zv::Signature::Str }, Signature::Signature => quote! { #zv::Signature::Signature }, Signature::ObjectPath => quote! { #zv::Signature::ObjectPath }, Signature::Variant => quote! { #zv::Signature::Variant }, #[cfg(unix)] Signature::Fd => quote! { #zv::Signature::Fd }, Signature::Array(child) => { let signature = signature_to_tokens_with_crate(child.signature(), zv); quote! { #zv::Signature::Array(#zv::signature::Child::Static { child: &#signature, }) } } Signature::Dict { key, value } => { let key_sig = signature_to_tokens_with_crate(key.signature(), zv); let value_sig = signature_to_tokens_with_crate(value.signature(), zv); quote! { #zv::Signature::Dict { key: #zv::signature::Child::Static { child: &#key_sig, }, value: #zv::signature::Child::Static { child: &#value_sig, }, } } } Signature::Structure(fields) => { let fields = fields.iter().map(|f| signature_to_tokens_with_crate(f, zv)); quote! { #zv::Signature::Structure(#zv::signature::Fields::Static { fields: &[#(&#fields),*], }) } } #[cfg(feature = "gvariant")] Signature::Maybe(child) => { let signature = signature_to_tokens_with_crate(child.signature(), zv); quote! { #zv::Signature::Maybe(#zv::signature::Child::Static { child: &#signature, }) } } } } #[cfg(test)] mod tests { use super::*; #[test] fn signature_to_tokens_with_crate_uses_custom_path() { let custom_path = quote! { ::zbus::zvariant }; let sig = Signature::Str; let tokens = signature_to_tokens_with_crate(&sig, &custom_path).to_string(); assert!( tokens.contains("zbus"), "Expected custom path in output: {}", tokens ); } #[test] fn signature_to_tokens_with_crate_uses_custom_path_for_complex_types() { let custom_path = quote! { ::zbus::zvariant }; // Dict signature - has multiple path references let dict_sig = Signature::from_str("a{sv}").unwrap(); let tokens = signature_to_tokens_with_crate(&dict_sig, &custom_path).to_string(); // All occurrences should use the custom path assert!( !tokens.contains(":: zvariant ::") || tokens.contains(":: zbus :: zvariant ::"), "Found bare ::zvariant without ::zbus prefix: {}", tokens ); assert!( tokens.contains(":: zbus :: zvariant ::"), "Expected custom path in struct output: {}", tokens ); // Structure signature - has multiple path references let struct_sig = Signature::from_str("(su)").unwrap(); let tokens = signature_to_tokens_with_crate(&struct_sig, &custom_path).to_string(); // All occurrences should use the custom path assert!( tokens.contains("zbus"), "Expected custom path in struct output: {}", tokens ); } } zvariant_derive-5.9.2/src/type.rs000064400000000000000000000141051046102023000151540ustar 00000000000000use std::str::FromStr; use proc_macro2::TokenStream; use quote::{ToTokens, quote}; use syn::{ Attribute, Data, DataEnum, DeriveInput, Error, Fields, Generics, Ident, spanned::Spanned, }; use zvariant_utils::signature::Signature; use crate::{signature::signature_to_tokens_with_crate, utils::*}; pub fn expand_derive(ast: DeriveInput) -> Result { let StructAttributes { signature, crate_path: crate_attr, .. } = StructAttributes::parse(&ast.attrs)?; let crate_path = parse_crate_path(crate_attr.as_deref())?; let zv = zvariant_path(crate_path.as_ref()); if let Some(signature_str) = signature { // Signature already provided, easy then! let signature = match signature_str.as_str() { "dict" => Signature::dict(Signature::Str, Signature::Variant), s => Signature::from_str(s).map_err(|e| Error::new(ast.span(), e))?, }; let signature_tokens = signature_to_tokens_with_crate(&signature, &zv); 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 { const SIGNATURE: &'static #zv::Signature = &#signature_tokens; } }); } 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 { const SIGNATURE: &'static #zv::Signature = #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 field_types_clone = field_types.clone(); let signature = if new_type { quote! {#( <#field_types_clone as #zv::Type>::SIGNATURE )*} } else { quote! { &#zv::Signature::Structure(#zv::signature::Fields::Static { fields: &[#( <#field_types_clone as #zv::Type>::SIGNATURE ),*], }) } }; if insert_enum_variant { quote! { &#zv::Signature::Structure(#zv::signature::Fields::Static { fields: &[ ::SIGNATURE, #signature ], }) } } else { signature } } 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 { const SIGNATURE: &'static #zv::Signature = &#zv::Signature::Unit; } }) } 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 { const SIGNATURE: &'static #zv::Signature = &#zv::Signature::U8; } }) } 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 { const SIGNATURE: &'static #zv::Signature = #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-5.9.2/src/utils.rs000064400000000000000000000046411046102023000153370ustar 00000000000000use proc_macro_crate::{FoundCrate, crate_name}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use zvariant_utils::{case, def_attrs}; /// Parses the `crate` attribute value into a path. pub fn parse_crate_path(crate_attr: Option<&str>) -> Result, syn::Error> { crate_attr.map(syn::parse_str).transpose() } /// Returns the path to the zvariant crate. /// /// If a custom crate path is provided via the `crate` attribute, it will be used. /// Otherwise, uses `proc-macro-crate` to detect the crate name. pub fn zvariant_path(crate_path: Option<&syn::Path>) -> TokenStream { if let Some(path) = crate_path { quote! { ::#path } } else 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 } } } pub fn rename_identifier( ident: String, span: proc_macro2::Span, rename_attr: Option, rename_all_attr: Option<&str>, ) -> Result { if let Some(name) = rename_attr { Ok(name) } else { 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_or_kebab_case(&ident, true)), Some("kebab-case") => Ok(case::snake_or_kebab_case(&ident, false)), None => Ok(ident), Some(other) => Err(syn::Error::new( span, format!("invalid `rename_all` attribute value {other}"), )), } } } def_attrs! { crate zbus, zvariant; /// Attributes defined on structures. pub StructAttributes("struct") { signature str, rename_all str, deny_unknown_fields none, crate_path str }; /// Attributes defined on fields. pub FieldAttributes("field") { rename str }; /// Attributes defined on enumerations. pub EnumAttributes("enum") { signature str, rename_all str, crate_path str }; /// Attributes defined on variants. pub VariantAttributes("variant") { rename str }; } zvariant_derive-5.9.2/src/value.rs000064400000000000000000000340401046102023000153070ustar 00000000000000use proc_macro2::{Span, TokenStream}; use quote::{ToTokens, quote}; use syn::{ Attribute, Data, DataEnum, DeriveInput, Error, Fields, Generics, Ident, Lifetime, LifetimeParam, Variant, spanned::Spanned, }; use zvariant_utils::macros; use crate::utils::*; pub enum ValueType { Value, OwnedValue, } pub fn expand_derive(ast: DeriveInput, value_type: ValueType) -> Result { let StructAttributes { signature, rename_all, crate_path: crate_attr, .. } = StructAttributes::parse(&ast.attrs)?; let crate_path = parse_crate_path(crate_attr.as_deref())?; let zv = zvariant_path(crate_path.as_ref()); let signature = signature.map(|signature| match signature.as_str() { "dict" => "a{sv}".to_string(), _ => signature, }); match &ast.data { Data::Struct(ds) => match &ds.fields { Fields::Named(_) | Fields::Unnamed(_) => impl_struct( value_type, ast.ident, ast.generics, &ds.fields, signature, &zv, rename_all, ), 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, rename_all: Option, ) -> 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. let (fields_init, entries_init): (TokenStream, TokenStream) = fields .iter() .map(|field| { let FieldAttributes { rename, .. } = FieldAttributes::parse(&field.attrs).unwrap_or_default(); let field_name = field.ident.to_token_stream(); let key_name = rename_identifier( field.ident.as_ref().unwrap().to_string(), field.span(), rename, rename_all.as_deref(), ) .unwrap_or(field_name.to_string()); let convert = if macros::ty_is_option(&field.ty) { quote! { .map(#zv::Value::downcast) .transpose()? } } else { quote! { .ok_or_else(|| #zv::Error::IncorrectType)? .downcast()? } }; let fields_init = quote! { #field_name: fields .remove(#key_name) #convert, }; let entries_init = if macros::ty_is_option(&field.ty) { quote! { if let Some(v) = s.#field_name { fields.insert( #key_name, #zv::Value::from(v), ); } } } else { quote! { fields.insert( #key_name, #zv::Value::from(s.#field_name), ); } }; (fields_init, entries_init) }) .unzip(); ( quote! { let mut fields = <::std::collections::HashMap::< ::std::string::String, #zv::Value, >>::try_from(value)?; ::std::result::Result::Ok(Self { #fields_init }) }, quote! { let mut fields = ::std::collections::HashMap::new(); #entries_init <#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().unwrap()) #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 enum_attrs = EnumAttributes::parse(&attrs)?; let str_enum = enum_attrs .signature .map(|sig| sig == "s") .unwrap_or_default(); let mut variant_names = vec![]; let mut str_values = vec![]; for variant in &data.variants { let variant_attrs = VariantAttributes::parse(&variant.attrs)?; // Ensure all variants of the enum are unit type match variant.fields { Fields::Unit => { variant_names.push(&variant.ident); if str_enum { let str_value = enum_name_for_variant( variant, variant_attrs.rename, enum_attrs.rename_all.as_ref().map(AsRef::as_ref), )?; str_values.push(str_value); } } _ => return Err(Error::new(variant.span(), "must be a unit variant")), } } let into_val = if str_enum { quote! { match e { #( #name::#variant_names => #str_values, )* } } } else { quote! { e as #repr } }; 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 { <#zv::Value as ::std::convert::From<_>>::from(#into_val) } } }, ), 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 { <#zv::OwnedValue as ::std::convert::TryFrom<_>>::try_from( <#zv::Value as ::std::convert::From<_>>::from(#into_val) ) } } }, ), }; let from_val = if str_enum { quote! { let v: #zv::Str = ::std::convert::TryInto::try_into(value)?; ::std::result::Result::Ok(match v.as_str() { #( #str_values => #name::#variant_names, )* _ => return ::std::result::Result::Err(#zv::Error::IncorrectType), }) } } else { quote! { let v: #repr = ::std::convert::TryInto::try_into(value)?; ::std::result::Result::Ok(match v { #( x if x == #name::#variant_names as #repr => #name::#variant_names ),*, _ => return ::std::result::Result::Err(#zv::Error::IncorrectType), }) } }; Ok(quote! { impl ::std::convert::TryFrom<#value_type> for #name { type Error = #zv::Error; #[inline] fn try_from(value: #value_type) -> #zv::Result { #from_val } } #into_value }) } fn enum_name_for_variant( v: &Variant, rename_attr: Option, rename_all_attr: Option<&str>, ) -> Result { let ident = v.ident.to_string(); rename_identifier(ident, v.span(), rename_attr, rename_all_attr) } zvariant_derive-5.9.2/tests/no_prelude.rs000064400000000000000000000016051046102023000167030ustar 00000000000000#![no_implicit_prelude] #![allow(dead_code)] use ::serde::{Deserialize, Serialize}; use ::zvariant_derive::Type; use ::zvariant::as_value::{self, optional}; #[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(Serialize, Deserialize, Type)] #[zvariant(signature = "a{sv}")] #[serde(deny_unknown_fields)] struct Test { #[serde( with = "optional", skip_serializing_if = "::std::option::Option::is_none", default )] field_a: ::std::option::Option, #[serde(rename = "field-b")] #[serde(with = "as_value")] field_b: ::std::string::String, #[serde(with = "as_value")] field_c: ::std::vec::Vec, } zvariant_derive-5.9.2/tests/tests.rs000064400000000000000000000075241046102023000157170ustar 00000000000000#![allow(dead_code)] use serde::{Deserialize, Serialize}; use std::collections::HashMap; use zvariant::{ LE, OwnedValue, Type, Value, as_value::{self, optional}, serialized::{Context, Format}, }; #[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(Serialize, Deserialize, Type, Default)] #[zvariant(signature = "a{sv}")] #[serde(deny_unknown_fields, rename_all = "camelCase", default)] struct Test { #[serde(with = "optional", skip_serializing_if = "Option::is_none")] field_a: Option, #[serde(with = "as_value", rename = "field-b")] field_b: String, #[serde(with = "as_value")] 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}") } #[test] #[ignore] fn issues_311() { // Issue 311: Value macro not able to handle Option in Dict. // // org.freedesktop.ModemManager1.Modem.Signal props are a dict with optional values depending on // the property you read. #[derive(Debug, Type, Deserialize, OwnedValue, Value, Default)] #[zbus(signature = "dict")] #[serde(deny_unknown_fields, default)] pub struct SignalInfo { #[serde(with = "optional")] pub rssi: Option, #[serde(with = "optional")] pub ecio: Option, #[serde(with = "optional")] pub io: Option, #[serde(with = "optional")] pub sinr: Option, } } #[test] #[ignore] fn issues_1252() { // Issue 1252: Naming a field `key` in a dict struct causes a conflict with variables created by // `DeserializeDict` macro, ending up with a strange error. #[derive(Type, Deserialize)] #[zvariant(signature = "a{sv}")] pub struct OwnedProperties { #[serde(with = "as_value")] key: String, #[serde(with = "as_value")] val: OwnedValue, } } #[test] fn derive_with_crate_attr() { // Test that the `crate` attribute works for custom crate paths. // This is useful when zvariant is re-exported or renamed in Cargo.toml. #[derive(Type)] #[zvariant(crate = "zvariant")] struct TestCrateAttr { name: String, value: u32, } assert_eq!(TestCrateAttr::SIGNATURE, "(su)"); // Also test on enums #[repr(u8)] #[derive(Type)] #[zvariant(crate = "zvariant")] enum TestCrateAttrEnum { A = 1, B = 2, } assert_eq!(TestCrateAttrEnum::SIGNATURE, "y"); }