/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{spanned::Spanned, visit_mut::VisitMut, Item, Type}; use uniffi_meta::Metadata; #[cfg(not(feature = "nightly"))] pub fn mod_path() -> syn::Result> { // Without the nightly feature and TokenStream::expand_expr, just return the crate name use std::path::Path; use fs_err as fs; use once_cell::sync::Lazy; use serde::Deserialize; #[derive(Deserialize)] struct CargoToml { package: Package, #[serde(default)] lib: Lib, } #[derive(Deserialize)] struct Package { name: String, } #[derive(Default, Deserialize)] struct Lib { name: Option, } static LIB_CRATE_MOD_PATH: Lazy, String>> = Lazy::new(|| { let manifest_dir = std::env::var_os("CARGO_MANIFEST_DIR").ok_or("`CARGO_MANIFEST_DIR` is not set")?; let cargo_toml_bytes = fs::read(Path::new(&manifest_dir).join("Cargo.toml")).map_err(|e| e.to_string())?; let cargo_toml = toml::from_slice::(&cargo_toml_bytes) .map_err(|e| format!("Failed to parse `Cargo.toml`: {e}"))?; let lib_crate_name = cargo_toml .lib .name .unwrap_or_else(|| cargo_toml.package.name.replace('-', "_")); Ok(vec![lib_crate_name]) }); LIB_CRATE_MOD_PATH .clone() .map_err(|e| syn::Error::new(Span::call_site(), e)) } #[cfg(feature = "nightly")] pub fn mod_path() -> syn::Result> { use proc_macro::TokenStream; use quote::quote; let module_path_invoc = TokenStream::from(quote! { ::core::module_path!() }); // We ask the compiler what `module_path!()` expands to here. // This is a nightly feature, tracked at https://github.com/rust-lang/rust/issues/90765 let expanded_module_path = TokenStream::expand_expr(&module_path_invoc) .map_err(|e| syn::Error::new(Span::call_site(), e))?; Ok(syn::parse::(expanded_module_path)? .value() .split("::") .collect()) } /// Rewrite Self type alias usage in an impl block to the type itself. /// /// For example, /// /// ```ignore /// impl some::module::Foo { /// fn method( /// self: Arc, /// arg: Option>, /// ) -> Result { /// todo!() /// } /// } /// ``` /// /// will be rewritten to /// /// ```ignore /// impl some::module::Foo { /// fn method( /// self: Arc, /// arg: Option>, /// ) -> Result { /// todo!() /// } /// } /// ``` pub fn rewrite_self_type(item: &mut Item) { let item = match item { Item::Impl(i) => i, _ => return, }; struct RewriteSelfVisitor<'a>(&'a Type); impl<'a> VisitMut for RewriteSelfVisitor<'a> { fn visit_type_mut(&mut self, i: &mut Type) { match i { Type::Path(p) if p.qself.is_none() && p.path.is_ident("Self") => { *i = self.0.clone(); } _ => syn::visit_mut::visit_type_mut(self, i), } } } let mut visitor = RewriteSelfVisitor(&item.self_ty); for item in &mut item.items { visitor.visit_impl_item_mut(item); } } pub fn create_metadata_static_var(name: &Ident, val: Metadata) -> TokenStream { let data: Vec = bincode::serialize(&val).expect("Error serializing metadata item"); let count = data.len(); let var_name = format_ident!("UNIFFI_META_{}", name); quote! { #[no_mangle] #[doc(hidden)] pub static #var_name: [u8; #count] = [#(#data),*]; } } pub fn assert_type_eq(a: impl ToTokens + Spanned, b: impl ToTokens) -> TokenStream { quote_spanned! {a.span()=> #[allow(unused_qualifications)] const _: () = { ::uniffi::deps::static_assertions::assert_type_eq_all!(#a, #b); }; } }