Skip to content

Commit 6573ffa

Browse files
committed
Reworked the derive macro code
Each derive is now based on 2 things: - The item, which is the parsed DeriveInput - The context, which specifies how we want to render the item. Do we want to generate metadata, which UniFfiTags should we implement traits on, etc. Storing all the parsed DeriveInput parts in 1 type makes it easier to change what/how we parse. We only need to update the struct not the signatures of all the functions. Adding the context will help make the UDL / proc-macro code more consistent (mozilla#1865). We can update the `udl_derive` method and have it affect all UDL-based generation. I also want to add a couple more modes for remote types and remote UDL types. Consolidated the `derive_*_for_udl` macros into a single `udl_derive` macro. I think this is simpler and I also want to use the same basic structure for the `remote_type` macro described in mozilla#2087. Made EnumItem also parse error attributes. I think this is simpler than creating a separate EnumItem vs ErrorItem, at least for now. Removed the `#[uniffi(non_exhaustive)]` attribute and parse the real `#[non_exhaustive]` attribute instead. This is easy to do now with the new system and it will be simpler for the user when we implement remote types.
1 parent a47508d commit 6573ffa

File tree

9 files changed

+455
-344
lines changed

9 files changed

+455
-344
lines changed

uniffi_bindgen/src/scaffolding/templates/EnumTemplate.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22
// Forward work to `uniffi_macros` This keeps macro-based and UDL-based generated code consistent.
33
#}
44

5-
#[::uniffi::derive_enum_for_udl(
6-
{%- if e.is_non_exhaustive() -%}
7-
non_exhaustive,
8-
{%- endif %}
9-
)]
5+
#[::uniffi::derive_enum_for_udl]
6+
{%- if e.is_non_exhaustive() %}
7+
#[non_exhaustive]
8+
{%- endif %}
109
enum r#{{ e.name() }} {
1110
{%- for variant in e.variants() %}
1211
r#{{ variant.name() }} {

uniffi_macros/src/derive.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
//! General handling for the derive and udl_derive macros
6+
7+
use crate::util::kw;
8+
use proc_macro2::{Ident, Span, TokenStream};
9+
use quote::{quote, ToTokens};
10+
use syn::parse::{Parse, ParseStream};
11+
12+
pub enum DeriveKind {
13+
Record(kw::Record),
14+
Enum(kw::Enum),
15+
Error(kw::Error),
16+
Object(kw::Object),
17+
}
18+
19+
impl Parse for DeriveKind {
20+
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
21+
let lookahead = input.lookahead1();
22+
if lookahead.peek(kw::Record) {
23+
Ok(Self::Record(input.parse()?))
24+
} else if lookahead.peek(kw::Enum) {
25+
Ok(Self::Enum(input.parse()?))
26+
} else if lookahead.peek(kw::Error) {
27+
Ok(Self::Error(input.parse()?))
28+
} else if lookahead.peek(kw::Object) {
29+
Ok(Self::Object(input.parse()?))
30+
} else {
31+
Err(lookahead.error())
32+
}
33+
}
34+
}
35+
36+
pub struct DeriveContext {
37+
/// Should we implement FFI traits for the local UniFfiTag only?
38+
pub local_tag: bool,
39+
/// Should we generate a metadata symbol?
40+
pub generate_metadata: bool,
41+
}
42+
43+
/// default() is used to construct a DeriveContext for a regular `derive` invocation
44+
impl Default for DeriveContext {
45+
fn default() -> Self {
46+
Self {
47+
local_tag: false,
48+
generate_metadata: true,
49+
}
50+
}
51+
}
52+
53+
impl DeriveContext {
54+
/// Construct the derive context for `udl_derive`
55+
pub fn udl_derive() -> Self {
56+
Self {
57+
local_tag: true,
58+
generate_metadata: false,
59+
}
60+
}
61+
62+
/// Generate the impl header for a FFI trait
63+
///
64+
/// This will output something like `impl<UT> FfiConverter<UT> for #type`. The caller is
65+
/// responsible for providing the body if the impl block.
66+
pub fn ffi_impl_header(&self, trait_name: &str, ident: &impl ToTokens) -> TokenStream {
67+
let trait_name = Ident::new(trait_name, Span::call_site());
68+
if self.local_tag {
69+
quote! { impl ::uniffi::#trait_name<crate::UniFfiTag> for #ident }
70+
} else {
71+
quote! { impl<T> ::uniffi::#trait_name<T> for #ident }
72+
}
73+
}
74+
75+
/// Generate a call to `derive_ffi_traits!` that will derive all the FFI traits
76+
pub fn derive_all_ffi_traits(&self, ty: &Ident) -> TokenStream {
77+
if self.local_tag {
78+
quote! { ::uniffi::derive_ffi_traits!(local #ty); }
79+
} else {
80+
quote! { ::uniffi::derive_ffi_traits!(blanket #ty); }
81+
}
82+
}
83+
84+
/// Generate a call to `derive_ffi_traits!` that will derive some of the FFI traits
85+
pub fn derive_ffi_traits(&self, ty: impl ToTokens, trait_names: &[&str]) -> TokenStream {
86+
let trait_idents = trait_names
87+
.iter()
88+
.map(|name| Ident::new(name, Span::call_site()));
89+
if self.local_tag {
90+
quote! {
91+
#(
92+
::uniffi::derive_ffi_traits!(impl #trait_idents<crate::UniFfiTag> for #ty);
93+
)*
94+
}
95+
} else {
96+
quote! {
97+
#(
98+
::uniffi::derive_ffi_traits!(impl<UT> #trait_idents<UT> for #ty);
99+
)*
100+
}
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)