/* 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/. */ //! Initial IR, this is the Metadata from uniffi_meta with some slight changes: //! //! * The Type/Literal enums are wrapped in TypeNode/LiteralNode structs. This allows for future pipeline passes to add fields. //! * Metadata is normalized and grouped from a Rust module_path/crate_namse into namespace modules. use std::fs; mod from_uniffi_meta; mod nodes; pub use nodes::*; use anyhow::Result; use camino::Utf8Path; use crate::{crate_name_from_cargo_toml, interface, macro_metadata, BindgenPaths}; pub use from_uniffi_meta::UniffiMetaConverter; impl Root { pub fn from_library( bindgen_paths: BindgenPaths, path: &Utf8Path, crate_name: Option, ) -> Result { let mut metadata_converter = UniffiMetaConverter::default(); let mut all_metadata = macro_metadata::extract_from_library(path)?; if let Some(crate_name) = crate_name { all_metadata.retain(|meta| meta.module_path().split("::").next() == Some(&crate_name)); } let mut udl_to_load = vec![]; for meta in all_metadata { match meta { uniffi_meta::Metadata::UdlFile(udl) => { udl_to_load.push(( bindgen_paths.get_udl(&udl.module_path, &udl.file_stub)?, udl.module_path, )); } uniffi_meta::Metadata::Namespace(namespace) => { let table = bindgen_paths.get_config(&namespace.crate_name)?; if !table.is_empty() { metadata_converter.add_module_config_toml(namespace.name.clone(), table)?; } metadata_converter .add_metadata_item(uniffi_meta::Metadata::Namespace(namespace))?; } meta => metadata_converter.add_metadata_item(meta)?, } } for (udl, module_path) in udl_to_load { Self::add_metadata_from_udl( &mut metadata_converter, &bindgen_paths, &udl, &module_path, true, )?; } let mut root = metadata_converter.try_into_initial_ir()?; root.cdylib = calc_cdylib_name(path); Ok(root) } pub fn from_udl( bindgen_paths: BindgenPaths, path: &Utf8Path, crate_name: Option, ) -> Result { let mut metadata_converter = UniffiMetaConverter::default(); let crate_name = match crate_name { Some(c) => c, None => crate_name_from_cargo_toml(path)?, }; Self::add_metadata_from_udl( &mut metadata_converter, &bindgen_paths, &fs::read_to_string(path)?, &crate_name, false, )?; metadata_converter.try_into_initial_ir() } fn add_metadata_from_udl( metadata_converter: &mut UniffiMetaConverter, bindgen_paths: &BindgenPaths, udl: &str, crate_name: &str, library_mode: bool, ) -> Result<()> { let metadata_group = uniffi_udl::parse_udl(udl, crate_name)?; // parse_udl returns a metadata group, which is nice for the CI, but we actually want to // start with a raw metadata list if let Some(docstring) = metadata_group.namespace_docstring { metadata_converter .add_module_docstring(metadata_group.namespace.name.clone(), docstring)?; } if !library_mode { let table = bindgen_paths.get_config(&metadata_group.namespace.crate_name)?; if !table.is_empty() { metadata_converter .add_module_config_toml(metadata_group.namespace.name.clone(), table)?; } } metadata_converter .add_metadata_item(uniffi_meta::Metadata::Namespace(metadata_group.namespace))?; for mut meta in metadata_group.items { // Make sure metadata checksums are set match &mut meta { uniffi_meta::Metadata::Func(func) => { func.checksum = Some(uniffi_meta::checksum(&interface::Function::from( func.clone(), ))); } uniffi_meta::Metadata::Method(meth) => { // making a method is mildly tricky as we need a type for self. // for the purposes of a checksum we ignore self info from udl. let method_object = interface::Method::from_metadata(meth.clone(), uniffi_meta::Type::UInt8); meth.checksum = Some(uniffi_meta::checksum(&method_object)); } uniffi_meta::Metadata::Constructor(cons) => { cons.checksum = Some(uniffi_meta::checksum(&interface::Constructor::from( cons.clone(), ))); } // Note: UDL-based callbacks don't have checksum functions, don't set the // checksum for those. _ => (), } metadata_converter.add_metadata_item(meta)?; } Ok(()) } } // If `library_path` is a C dynamic library, return its name fn calc_cdylib_name(library_path: &Utf8Path) -> Option { let cdylib_extensions = [".so", ".dll", ".dylib"]; let filename = library_path.file_name()?; let filename = filename.strip_prefix("lib").unwrap_or(filename); for ext in cdylib_extensions { if let Some(f) = filename.strip_suffix(ext) { return Some(f.to_string()); } } None }