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/. */
5 use proc_macro2::{Ident, Span, TokenStream};
6 use quote::{format_ident, quote, ToTokens};
7 use std::path::{Path as StdPath, PathBuf};
10 parse::{Parse, ParseStream},
14 pub fn manifest_path() -> Result<PathBuf, String> {
16 std::env::var_os("CARGO_MANIFEST_DIR").ok_or("`CARGO_MANIFEST_DIR` is not set")?;
18 Ok(StdPath::new(&manifest_dir).join("Cargo.toml"))
21 #[cfg(not(feature = "nightly"))]
22 pub fn mod_path() -> syn::Result<String> {
23 // Without the nightly feature and TokenStream::expand_expr, just return the crate name
26 use once_cell::sync::Lazy;
27 use serde::Deserialize;
29 #[derive(Deserialize)]
36 #[derive(Deserialize)]
41 #[derive(Default, Deserialize)]
46 static LIB_CRATE_MOD_PATH: Lazy<Result<String, String>> = Lazy::new(|| {
47 let file = manifest_path()?;
48 let cargo_toml_bytes = fs::read(file).map_err(|e| e.to_string())?;
50 let cargo_toml = toml::from_slice::<CargoToml>(&cargo_toml_bytes)
51 .map_err(|e| format!("Failed to parse `Cargo.toml`: {e}"))?;
53 let lib_crate_name = cargo_toml
56 .unwrap_or_else(|| cargo_toml.package.name.replace('-', "_"));
63 .map_err(|e| syn::Error::new(Span::call_site(), e))
66 #[cfg(feature = "nightly")]
67 pub fn mod_path() -> syn::Result<String> {
68 use proc_macro::TokenStream;
70 let module_path_invoc = TokenStream::from(quote! { ::core::module_path!() });
71 // We ask the compiler what `module_path!()` expands to here.
72 // This is a nightly feature, tracked at https://github.com/rust-lang/rust/issues/90765
73 let expanded_module_path = TokenStream::expand_expr(&module_path_invoc)
74 .map_err(|e| syn::Error::new(Span::call_site(), e))?;
75 Ok(syn::parse::<syn::LitStr>(expanded_module_path)?.value())
78 pub fn try_read_field(f: &syn::Field) -> TokenStream {
83 #ident: <#ty as ::uniffi::Lift<crate::UniFfiTag>>::try_read(buf)?,
87 pub fn ident_to_string(ident: &Ident) -> String {
88 ident.unraw().to_string()
91 pub fn crate_name() -> String {
92 std::env::var("CARGO_CRATE_NAME").unwrap().replace('-', "_")
95 pub fn create_metadata_items(
98 metadata_expr: TokenStream,
99 checksum_fn_name: Option<String>,
101 let crate_name = crate_name();
102 let crate_name_upper = crate_name.to_uppercase();
103 let kind_upper = kind.to_uppercase();
104 let name_upper = name.to_uppercase();
106 format_ident!("UNIFFI_META_CONST_{crate_name_upper}_{kind_upper}_{name_upper}");
107 let static_ident = format_ident!("UNIFFI_META_{crate_name_upper}_{kind_upper}_{name_upper}");
109 let checksum_fn = checksum_fn_name.map(|name| {
110 let ident = Ident::new(&name, Span::call_site());
114 pub extern "C" fn #ident() -> u16 {
115 #const_ident.checksum()
121 const #const_ident: ::uniffi::MetadataBuffer = #metadata_expr;
124 pub static #static_ident: [u8; #const_ident.size] = #const_ident.into_array();
130 pub fn try_metadata_value_from_usize(value: usize, error_message: &str) -> syn::Result<u8> {
133 .map_err(|_| syn::Error::new(Span::call_site(), error_message))
137 a: impl IntoIterator<Item = T>,
138 b: impl IntoIterator<Item = T>,
139 ) -> impl Iterator<Item = T> {
140 a.into_iter().chain(b)
143 pub trait UniffiAttributeArgs: Default {
144 fn parse_one(input: ParseStream<'_>) -> syn::Result<Self>;
145 fn merge(self, other: Self) -> syn::Result<Self>;
148 pub fn parse_comma_separated<T: UniffiAttributeArgs>(input: ParseStream<'_>) -> syn::Result<T> {
149 let punctuated = input.parse_terminated(T::parse_one, Token![,])?;
150 punctuated.into_iter().try_fold(T::default(), T::merge)
154 pub struct ArgumentNotAllowedHere;
156 impl Parse for ArgumentNotAllowedHere {
157 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
158 parse_comma_separated(input)
162 impl UniffiAttributeArgs for ArgumentNotAllowedHere {
163 fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
166 "attribute arguments are not currently recognized in this position",
170 fn merge(self, _other: Self) -> syn::Result<Self> {
175 pub trait AttributeSliceExt {
176 fn parse_uniffi_attr_args<T: UniffiAttributeArgs>(&self) -> syn::Result<T>;
177 fn uniffi_attr_args_not_allowed_here(&self) -> Option<syn::Error> {
178 self.parse_uniffi_attr_args::<ArgumentNotAllowedHere>()
183 impl AttributeSliceExt for [Attribute] {
184 fn parse_uniffi_attr_args<T: UniffiAttributeArgs>(&self) -> syn::Result<T> {
186 .filter(|attr| attr.path().is_ident("uniffi"))
187 .try_fold(T::default(), |res, attr| {
188 let parsed = attr.parse_args_with(parse_comma_separated)?;
194 pub fn either_attribute_arg<T: ToTokens>(a: Option<T>, b: Option<T>) -> syn::Result<Option<T>> {
196 (None, None) => Ok(None),
197 (Some(val), None) | (None, Some(val)) => Ok(Some(val)),
198 (Some(a), Some(b)) => {
199 let mut error = syn::Error::new_spanned(a, "redundant attribute argument");
200 error.combine(syn::Error::new_spanned(b, "note: first one here"));
206 pub(crate) fn tagged_impl_header(
208 ident: &impl ToTokens,
211 let trait_name = Ident::new(trait_name, Span::call_site());
213 quote! { impl ::uniffi::#trait_name<crate::UniFfiTag> for #ident }
215 quote! { impl<T> ::uniffi::#trait_name<T> for #ident }
219 pub(crate) fn derive_all_ffi_traits(ty: &Ident, udl_mode: bool) -> TokenStream {
221 quote! { ::uniffi::derive_ffi_traits!(local #ty); }
223 quote! { ::uniffi::derive_ffi_traits!(blanket #ty); }
227 pub(crate) fn derive_ffi_traits(ty: &Ident, udl_mode: bool, trait_names: &[&str]) -> TokenStream {
228 let trait_idents = trait_names
230 .map(|name| Ident::new(name, Span::call_site()));
234 ::uniffi::derive_ffi_traits!(impl #trait_idents<crate::UniFfiTag> for #ty);
240 ::uniffi::derive_ffi_traits!(impl<UT> #trait_idents<UT> for #ty);
248 syn::custom_keyword!(async_runtime);
249 syn::custom_keyword!(callback_interface);
250 syn::custom_keyword!(constructor);
251 syn::custom_keyword!(default);
252 syn::custom_keyword!(flat_error);
253 syn::custom_keyword!(None);
254 syn::custom_keyword!(with_try_read);
255 syn::custom_keyword!(Debug);
256 syn::custom_keyword!(Display);
257 syn::custom_keyword!(Eq);
258 syn::custom_keyword!(Hash);
260 syn::custom_keyword!(handle_unknown_callback_error);
263 /// Specifies a type from a dependent crate
264 pub struct ExternalTypeItem {
265 pub crate_ident: Ident,
267 pub type_ident: Ident,
270 impl Parse for ExternalTypeItem {
271 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
273 crate_ident: input.parse()?,
275 type_ident: input.parse()?,