Skip to main content

zvariant_derive/
signature.rs

1use std::str::FromStr;
2
3use proc_macro2::{Literal, TokenStream};
4use quote::quote;
5use syn::{Error, parse::Parse};
6use zvariant_utils::signature::Signature;
7
8/// Expand the `signature!` macro implementation.
9///
10/// Takes a string literal signature and converts it to compile-time tokens
11/// representing a const `Signature`.
12pub fn expand_signature_macro(input: TokenStream) -> Result<TokenStream, Error> {
13    let SignatureInput {
14        literal: signature_str,
15    } = syn::parse2(input)?;
16
17    let signature_string = signature_str.to_string();
18    let signature_string = signature_string.trim_matches('"');
19
20    let signature = match signature_string {
21        "dict" => Signature::dict(Signature::Str, Signature::Variant),
22        s => Signature::from_str(s).map_err(|e| Error::new(signature_str.span(), e))?,
23    };
24
25    let signature_tokens = signature_to_tokens(&signature);
26
27    Ok(signature_tokens)
28}
29
30/// Input type for the signature macro.
31struct SignatureInput {
32    literal: Literal,
33}
34
35impl Parse for SignatureInput {
36    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
37        Ok(SignatureInput {
38            literal: input.parse()?,
39        })
40    }
41}
42
43/// Converts a parsed `Signature` to compile-time token representation.
44///
45/// This function generates the Rust tokens that will construct the signature
46/// at compile time. Used by both the signature! macro and the Type derive macro.
47pub fn signature_to_tokens(signature: &Signature) -> TokenStream {
48    signature_to_tokens_with_crate(signature, &quote! { ::zvariant })
49}
50
51/// Converts a parsed `Signature` to compile-time token representation with a custom crate path.
52///
53/// This function generates the Rust tokens that will construct the signature
54/// at compile time, using the provided crate path for zvariant.
55pub fn signature_to_tokens_with_crate(signature: &Signature, zv: &TokenStream) -> TokenStream {
56    match signature {
57        Signature::Unit => quote! { #zv::Signature::Unit },
58        Signature::Bool => quote! { #zv::Signature::Bool },
59        Signature::U8 => quote! { #zv::Signature::U8 },
60        Signature::I16 => quote! { #zv::Signature::I16 },
61        Signature::U16 => quote! { #zv::Signature::U16 },
62        Signature::I32 => quote! { #zv::Signature::I32 },
63        Signature::U32 => quote! { #zv::Signature::U32 },
64        Signature::I64 => quote! { #zv::Signature::I64 },
65        Signature::U64 => quote! { #zv::Signature::U64 },
66        Signature::F64 => quote! { #zv::Signature::F64 },
67        Signature::Str => quote! { #zv::Signature::Str },
68        Signature::Signature => quote! { #zv::Signature::Signature },
69        Signature::ObjectPath => quote! { #zv::Signature::ObjectPath },
70        Signature::Variant => quote! { #zv::Signature::Variant },
71        #[cfg(unix)]
72        Signature::Fd => quote! { #zv::Signature::Fd },
73        Signature::Array(child) => {
74            let signature = signature_to_tokens_with_crate(child.signature(), zv);
75            quote! {
76                #zv::Signature::Array(#zv::signature::Child::Static {
77                    child: &#signature,
78                })
79            }
80        }
81        Signature::Dict { key, value } => {
82            let key_sig = signature_to_tokens_with_crate(key.signature(), zv);
83            let value_sig = signature_to_tokens_with_crate(value.signature(), zv);
84            quote! {
85                #zv::Signature::Dict {
86                    key: #zv::signature::Child::Static {
87                        child: &#key_sig,
88                    },
89                    value: #zv::signature::Child::Static {
90                        child: &#value_sig,
91                    },
92                }
93            }
94        }
95        Signature::Structure(fields) => {
96            let fields = fields.iter().map(|f| signature_to_tokens_with_crate(f, zv));
97            quote! {
98                #zv::Signature::Structure(#zv::signature::Fields::Static {
99                    fields: &[#(&#fields),*],
100                })
101            }
102        }
103        #[cfg(feature = "gvariant")]
104        Signature::Maybe(child) => {
105            let signature = signature_to_tokens_with_crate(child.signature(), zv);
106            quote! {
107                #zv::Signature::Maybe(#zv::signature::Child::Static {
108                    child: &#signature,
109                })
110            }
111        }
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn signature_to_tokens_with_crate_uses_custom_path() {
121        let custom_path = quote! { ::zbus::zvariant };
122        let sig = Signature::Str;
123
124        let tokens = signature_to_tokens_with_crate(&sig, &custom_path).to_string();
125
126        assert!(
127            tokens.contains("zbus"),
128            "Expected custom path in output: {}",
129            tokens
130        );
131    }
132
133    #[test]
134    fn signature_to_tokens_with_crate_uses_custom_path_for_complex_types() {
135        let custom_path = quote! { ::zbus::zvariant };
136
137        // Dict signature - has multiple path references
138        let dict_sig = Signature::from_str("a{sv}").unwrap();
139        let tokens = signature_to_tokens_with_crate(&dict_sig, &custom_path).to_string();
140
141        // All occurrences should use the custom path
142        assert!(
143            !tokens.contains(":: zvariant ::") || tokens.contains(":: zbus :: zvariant ::"),
144            "Found bare ::zvariant without ::zbus prefix: {}",
145            tokens
146        );
147        assert!(
148            tokens.contains(":: zbus :: zvariant ::"),
149            "Expected custom path in struct output: {}",
150            tokens
151        );
152
153        // Structure signature - has multiple path references
154        let struct_sig = Signature::from_str("(su)").unwrap();
155        let tokens = signature_to_tokens_with_crate(&struct_sig, &custom_path).to_string();
156
157        // All occurrences should use the custom path
158        assert!(
159            tokens.contains("zbus"),
160            "Expected custom path in struct output: {}",
161            tokens
162        );
163    }
164}