1 use std::collections::{HashMap, HashSet};
2 
3 use crate::{
4     default::DefaultValue,
5     util::{either_attribute_arg, kw, parse_comma_separated, UniffiAttributeArgs},
6 };
7 
8 use proc_macro2::TokenStream;
9 use quote::ToTokens;
10 use syn::{
11     parenthesized,
12     parse::{Parse, ParseStream},
13     Attribute, Ident, LitStr, Meta, PathArguments, PathSegment, Token,
14 };
15 use uniffi_meta::UniffiTraitDiscriminants;
16 
17 #[derive(Default)]
18 pub struct ExportTraitArgs {
19     pub(crate) async_runtime: Option<AsyncRuntime>,
20     pub(crate) callback_interface: Option<kw::callback_interface>,
21     pub(crate) with_foreign: Option<kw::with_foreign>,
22 }
23 
24 impl Parse for ExportTraitArgs {
parse(input: ParseStream<'_>) -> syn::Result<Self>25     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
26         parse_comma_separated(input)
27     }
28 }
29 
30 impl UniffiAttributeArgs for ExportTraitArgs {
parse_one(input: ParseStream<'_>) -> syn::Result<Self>31     fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
32         let lookahead = input.lookahead1();
33         if lookahead.peek(kw::async_runtime) {
34             let _: kw::async_runtime = input.parse()?;
35             let _: Token![=] = input.parse()?;
36             Ok(Self {
37                 async_runtime: Some(input.parse()?),
38                 ..Self::default()
39             })
40         } else if lookahead.peek(kw::callback_interface) {
41             Ok(Self {
42                 callback_interface: input.parse()?,
43                 ..Self::default()
44             })
45         } else if lookahead.peek(kw::with_foreign) {
46             Ok(Self {
47                 with_foreign: input.parse()?,
48                 ..Self::default()
49             })
50         } else {
51             Ok(Self::default())
52         }
53     }
54 
merge(self, other: Self) -> syn::Result<Self>55     fn merge(self, other: Self) -> syn::Result<Self> {
56         let merged = Self {
57             async_runtime: either_attribute_arg(self.async_runtime, other.async_runtime)?,
58             callback_interface: either_attribute_arg(
59                 self.callback_interface,
60                 other.callback_interface,
61             )?,
62             with_foreign: either_attribute_arg(self.with_foreign, other.with_foreign)?,
63         };
64         if merged.callback_interface.is_some() && merged.with_foreign.is_some() {
65             return Err(syn::Error::new(
66                 merged.callback_interface.unwrap().span,
67                 "`callback_interface` and `with_foreign` are mutually exclusive",
68             ));
69         }
70         Ok(merged)
71     }
72 }
73 
74 #[derive(Clone, Default)]
75 pub struct ExportFnArgs {
76     pub(crate) async_runtime: Option<AsyncRuntime>,
77     pub(crate) name: Option<String>,
78     pub(crate) defaults: DefaultMap,
79 }
80 
81 impl Parse for ExportFnArgs {
parse(input: ParseStream<'_>) -> syn::Result<Self>82     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
83         parse_comma_separated(input)
84     }
85 }
86 
87 impl UniffiAttributeArgs for ExportFnArgs {
parse_one(input: ParseStream<'_>) -> syn::Result<Self>88     fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
89         let lookahead = input.lookahead1();
90         if lookahead.peek(kw::async_runtime) {
91             let _: kw::async_runtime = input.parse()?;
92             let _: Token![=] = input.parse()?;
93             Ok(Self {
94                 async_runtime: Some(input.parse()?),
95                 ..Self::default()
96             })
97         } else if lookahead.peek(kw::name) {
98             let _: kw::name = input.parse()?;
99             let _: Token![=] = input.parse()?;
100             let name = Some(input.parse::<LitStr>()?.value());
101             Ok(Self {
102                 name,
103                 ..Self::default()
104             })
105         } else if lookahead.peek(kw::default) {
106             Ok(Self {
107                 defaults: DefaultMap::parse(input)?,
108                 ..Self::default()
109             })
110         } else {
111             Err(syn::Error::new(
112                 input.span(),
113                 format!("uniffi::export attribute `{input}` is not supported here."),
114             ))
115         }
116     }
117 
merge(self, other: Self) -> syn::Result<Self>118     fn merge(self, other: Self) -> syn::Result<Self> {
119         Ok(Self {
120             async_runtime: either_attribute_arg(self.async_runtime, other.async_runtime)?,
121             name: either_attribute_arg(self.name, other.name)?,
122             defaults: self.defaults.merge(other.defaults),
123         })
124     }
125 }
126 
127 #[derive(Default)]
128 pub struct ExportImplArgs {
129     pub(crate) async_runtime: Option<AsyncRuntime>,
130 }
131 
132 impl Parse for ExportImplArgs {
parse(input: ParseStream<'_>) -> syn::Result<Self>133     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
134         parse_comma_separated(input)
135     }
136 }
137 
138 impl UniffiAttributeArgs for ExportImplArgs {
parse_one(input: ParseStream<'_>) -> syn::Result<Self>139     fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
140         let lookahead = input.lookahead1();
141         if lookahead.peek(kw::async_runtime) {
142             let _: kw::async_runtime = input.parse()?;
143             let _: Token![=] = input.parse()?;
144             Ok(Self {
145                 async_runtime: Some(input.parse()?),
146             })
147         } else {
148             Err(syn::Error::new(
149                 input.span(),
150                 format!("uniffi::export attribute `{input}` is not supported here."),
151             ))
152         }
153     }
154 
merge(self, other: Self) -> syn::Result<Self>155     fn merge(self, other: Self) -> syn::Result<Self> {
156         Ok(Self {
157             async_runtime: either_attribute_arg(self.async_runtime, other.async_runtime)?,
158         })
159     }
160 }
161 
162 #[derive(Default)]
163 pub struct ExportStructArgs {
164     pub(crate) traits: HashSet<UniffiTraitDiscriminants>,
165 }
166 
167 impl Parse for ExportStructArgs {
parse(input: ParseStream<'_>) -> syn::Result<Self>168     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
169         parse_comma_separated(input)
170     }
171 }
172 
173 impl UniffiAttributeArgs for ExportStructArgs {
parse_one(input: ParseStream<'_>) -> syn::Result<Self>174     fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
175         let lookahead = input.lookahead1();
176         if lookahead.peek(kw::Debug) {
177             input.parse::<Option<kw::Debug>>()?;
178             Ok(Self {
179                 traits: HashSet::from([UniffiTraitDiscriminants::Debug]),
180             })
181         } else if lookahead.peek(kw::Display) {
182             input.parse::<Option<kw::Display>>()?;
183             Ok(Self {
184                 traits: HashSet::from([UniffiTraitDiscriminants::Display]),
185             })
186         } else if lookahead.peek(kw::Hash) {
187             input.parse::<Option<kw::Hash>>()?;
188             Ok(Self {
189                 traits: HashSet::from([UniffiTraitDiscriminants::Hash]),
190             })
191         } else if lookahead.peek(kw::Eq) {
192             input.parse::<Option<kw::Eq>>()?;
193             Ok(Self {
194                 traits: HashSet::from([UniffiTraitDiscriminants::Eq]),
195             })
196         } else {
197             Err(syn::Error::new(
198                 input.span(),
199                 format!(
200                     "uniffi::export struct attributes must be builtin trait names; `{input}` is invalid"
201                 ),
202             ))
203         }
204     }
205 
merge(self, other: Self) -> syn::Result<Self>206     fn merge(self, other: Self) -> syn::Result<Self> {
207         let mut traits = self.traits;
208         traits.extend(other.traits);
209         Ok(Self { traits })
210     }
211 }
212 
213 #[derive(Clone)]
214 pub(crate) enum AsyncRuntime {
215     Tokio(LitStr),
216 }
217 
218 impl Parse for AsyncRuntime {
parse(input: ParseStream<'_>) -> syn::Result<Self>219     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
220         let lit: LitStr = input.parse()?;
221         match lit.value().as_str() {
222             "tokio" => Ok(Self::Tokio(lit)),
223             _ => Err(syn::Error::new_spanned(
224                 lit,
225                 "unknown async runtime, currently only `tokio` is supported",
226             )),
227         }
228     }
229 }
230 
231 impl ToTokens for AsyncRuntime {
to_tokens(&self, tokens: &mut TokenStream)232     fn to_tokens(&self, tokens: &mut TokenStream) {
233         match self {
234             AsyncRuntime::Tokio(lit) => lit.to_tokens(tokens),
235         }
236     }
237 }
238 
239 /// Arguments for function inside an impl block
240 ///
241 /// This stores the parsed arguments for `uniffi::constructor` and `uniffi::method`
242 #[derive(Clone, Default)]
243 pub struct ExportedImplFnArgs {
244     pub(crate) name: Option<String>,
245     pub(crate) defaults: DefaultMap,
246 }
247 
248 impl Parse for ExportedImplFnArgs {
parse(input: ParseStream<'_>) -> syn::Result<Self>249     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
250         parse_comma_separated(input)
251     }
252 }
253 
254 impl UniffiAttributeArgs for ExportedImplFnArgs {
parse_one(input: ParseStream<'_>) -> syn::Result<Self>255     fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
256         let lookahead = input.lookahead1();
257         if lookahead.peek(kw::name) {
258             let _: kw::name = input.parse()?;
259             let _: Token![=] = input.parse()?;
260             let name = Some(input.parse::<LitStr>()?.value());
261             Ok(Self {
262                 name,
263                 ..Self::default()
264             })
265         } else if lookahead.peek(kw::default) {
266             Ok(Self {
267                 defaults: DefaultMap::parse(input)?,
268                 ..Self::default()
269             })
270         } else {
271             Err(syn::Error::new(
272                 input.span(),
273                 format!("uniffi::constructor/method attribute `{input}` is not supported here."),
274             ))
275         }
276     }
277 
merge(self, other: Self) -> syn::Result<Self>278     fn merge(self, other: Self) -> syn::Result<Self> {
279         Ok(Self {
280             name: either_attribute_arg(self.name, other.name)?,
281             defaults: self.defaults.merge(other.defaults),
282         })
283     }
284 }
285 
286 #[derive(Default)]
287 pub(super) struct ExportedImplFnAttributes {
288     pub constructor: bool,
289     pub args: ExportedImplFnArgs,
290 }
291 
292 impl ExportedImplFnAttributes {
new(attrs: &[Attribute]) -> syn::Result<Self>293     pub fn new(attrs: &[Attribute]) -> syn::Result<Self> {
294         let mut this = Self::default();
295         for attr in attrs {
296             let segs = &attr.path().segments;
297 
298             let fst = segs
299                 .first()
300                 .expect("attributes have at least one path segment");
301             if fst.ident != "uniffi" {
302                 continue;
303             }
304             ensure_no_path_args(fst)?;
305 
306             let args = match &attr.meta {
307                 Meta::List(_) => attr.parse_args::<ExportedImplFnArgs>()?,
308                 _ => Default::default(),
309             };
310             this.args = args;
311 
312             if segs.len() != 2 {
313                 return Err(syn::Error::new_spanned(
314                     segs,
315                     "unsupported uniffi attribute",
316                 ));
317             }
318             let snd = &segs[1];
319             ensure_no_path_args(snd)?;
320 
321             match snd.ident.to_string().as_str() {
322                 "constructor" => {
323                     if this.constructor {
324                         return Err(syn::Error::new_spanned(
325                             attr,
326                             "duplicate constructor attribute",
327                         ));
328                     }
329                     this.constructor = true;
330                 }
331                 "method" => {
332                     if this.constructor {
333                         return Err(syn::Error::new_spanned(
334                             attr,
335                             "confused constructor/method attributes",
336                         ));
337                     }
338                 }
339                 _ => return Err(syn::Error::new_spanned(snd, "unknown uniffi attribute")),
340             }
341         }
342 
343         Ok(this)
344     }
345 }
346 
ensure_no_path_args(seg: &PathSegment) -> syn::Result<()>347 fn ensure_no_path_args(seg: &PathSegment) -> syn::Result<()> {
348     if matches!(seg.arguments, PathArguments::None) {
349         Ok(())
350     } else {
351         Err(syn::Error::new_spanned(&seg.arguments, "unexpected syntax"))
352     }
353 }
354 
355 /// Maps arguments to defaults for functions
356 #[derive(Clone, Default)]
357 pub struct DefaultMap {
358     map: HashMap<Ident, DefaultValue>,
359 }
360 
361 impl DefaultMap {
merge(self, other: Self) -> Self362     pub fn merge(self, other: Self) -> Self {
363         let mut map = self.map;
364         map.extend(other.map);
365         Self { map }
366     }
367 
remove(&mut self, ident: &Ident) -> Option<DefaultValue>368     pub fn remove(&mut self, ident: &Ident) -> Option<DefaultValue> {
369         self.map.remove(ident)
370     }
371 
idents(&self) -> Vec<&Ident>372     pub fn idents(&self) -> Vec<&Ident> {
373         self.map.keys().collect()
374     }
375 }
376 
377 impl Parse for DefaultMap {
parse(input: ParseStream<'_>) -> syn::Result<Self>378     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
379         let _: kw::default = input.parse()?;
380         let content;
381         let _ = parenthesized!(content in input);
382         let pairs = content.parse_terminated(DefaultPair::parse, Token![,])?;
383         Ok(Self {
384             map: pairs.into_iter().map(|p| (p.name, p.value)).collect(),
385         })
386     }
387 }
388 
389 pub struct DefaultPair {
390     pub name: Ident,
391     pub eq_token: Token![=],
392     pub value: DefaultValue,
393 }
394 
395 impl Parse for DefaultPair {
parse(input: ParseStream<'_>) -> syn::Result<Self>396     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
397         Ok(Self {
398             name: input.parse()?,
399             eq_token: input.parse()?,
400             value: input.parse()?,
401         })
402     }
403 }
404