Skip to main content

zvariant_utils/derive/
signature.rs

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