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