Backed out 5 changesets (bug 1890092, bug 1888683) for causing build bustages & crash...
[gecko.git] / third_party / rust / uniffi_macros / src / util.rs
blob9f213ea1d7fe902099a61256cfcf8621fcc13c35
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};
8 use syn::{
9     ext::IdentExt,
10     parse::{Parse, ParseStream},
11     Attribute, Token,
14 pub fn manifest_path() -> Result<PathBuf, String> {
15     let manifest_dir =
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
25     use fs_err as fs;
26     use once_cell::sync::Lazy;
27     use serde::Deserialize;
29     #[derive(Deserialize)]
30     struct CargoToml {
31         package: Package,
32         #[serde(default)]
33         lib: Lib,
34     }
36     #[derive(Deserialize)]
37     struct Package {
38         name: String,
39     }
41     #[derive(Default, Deserialize)]
42     struct Lib {
43         name: Option<String>,
44     }
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
54             .lib
55             .name
56             .unwrap_or_else(|| cargo_toml.package.name.replace('-', "_"));
58         Ok(lib_crate_name)
59     });
61     LIB_CRATE_MOD_PATH
62         .clone()
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 {
79     let ident = &f.ident;
80     let ty = &f.ty;
82     quote! {
83         #ident: <#ty as ::uniffi::Lift<crate::UniFfiTag>>::try_read(buf)?,
84     }
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(
96     kind: &str,
97     name: &str,
98     metadata_expr: TokenStream,
99     checksum_fn_name: Option<String>,
100 ) -> TokenStream {
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();
105     let const_ident =
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());
111         quote! {
112             #[doc(hidden)]
113             #[no_mangle]
114             pub extern "C" fn #ident() -> u16 {
115                 #const_ident.checksum()
116             }
117         }
118     });
120     quote! {
121         const #const_ident: ::uniffi::MetadataBuffer = #metadata_expr;
122         #[no_mangle]
123         #[doc(hidden)]
124         pub static #static_ident: [u8; #const_ident.size] = #const_ident.into_array();
126         #checksum_fn
127     }
130 pub fn try_metadata_value_from_usize(value: usize, error_message: &str) -> syn::Result<u8> {
131     value
132         .try_into()
133         .map_err(|_| syn::Error::new(Span::call_site(), error_message))
136 pub fn chain<T>(
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)
153 #[derive(Default)]
154 pub struct ArgumentNotAllowedHere;
156 impl Parse for ArgumentNotAllowedHere {
157     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
158         parse_comma_separated(input)
159     }
162 impl UniffiAttributeArgs for ArgumentNotAllowedHere {
163     fn parse_one(input: ParseStream<'_>) -> syn::Result<Self> {
164         Err(syn::Error::new(
165             input.span(),
166             "attribute arguments are not currently recognized in this position",
167         ))
168     }
170     fn merge(self, _other: Self) -> syn::Result<Self> {
171         Ok(Self)
172     }
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>()
179             .err()
180     }
183 impl AttributeSliceExt for [Attribute] {
184     fn parse_uniffi_attr_args<T: UniffiAttributeArgs>(&self) -> syn::Result<T> {
185         self.iter()
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)?;
189                 res.merge(parsed)
190             })
191     }
194 pub fn either_attribute_arg<T: ToTokens>(a: Option<T>, b: Option<T>) -> syn::Result<Option<T>> {
195     match (a, b) {
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"));
201             Err(error)
202         }
203     }
206 pub(crate) fn tagged_impl_header(
207     trait_name: &str,
208     ident: &impl ToTokens,
209     udl_mode: bool,
210 ) -> TokenStream {
211     let trait_name = Ident::new(trait_name, Span::call_site());
212     if udl_mode {
213         quote! { impl ::uniffi::#trait_name<crate::UniFfiTag> for #ident }
214     } else {
215         quote! { impl<T> ::uniffi::#trait_name<T> for #ident }
216     }
219 pub(crate) fn derive_all_ffi_traits(ty: &Ident, udl_mode: bool) -> TokenStream {
220     if udl_mode {
221         quote! { ::uniffi::derive_ffi_traits!(local #ty); }
222     } else {
223         quote! { ::uniffi::derive_ffi_traits!(blanket #ty); }
224     }
227 pub(crate) fn derive_ffi_traits(ty: &Ident, udl_mode: bool, trait_names: &[&str]) -> TokenStream {
228     let trait_idents = trait_names
229         .iter()
230         .map(|name| Ident::new(name, Span::call_site()));
231     if udl_mode {
232         quote! {
233             #(
234                 ::uniffi::derive_ffi_traits!(impl #trait_idents<crate::UniFfiTag> for #ty);
235             )*
236         }
237     } else {
238         quote! {
239             #(
240                 ::uniffi::derive_ffi_traits!(impl<UT> #trait_idents<UT> for #ty);
241             )*
242         }
243     }
246 /// Custom keywords
247 pub mod kw {
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);
259     // Not used anymore
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,
266     pub sep: Token![,],
267     pub type_ident: Ident,
270 impl Parse for ExternalTypeItem {
271     fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
272         Ok(Self {
273             crate_ident: input.parse()?,
274             sep: input.parse()?,
275             type_ident: input.parse()?,
276         })
277     }