use std::collections::HashSet;
use std::path::Path;
use serde_json::Value;
use crate::resolve::{
BareFallback, ExternRenameMap, ReexportMap, UseMap, apply_bare_alias_rename,
apply_crate_root_rename, canonicalize_through_reexports, extern_verbatim_renamed,
renames_shadowed, resolve_path, strip_raw,
};
use crate::scan::scan_crate;
const SYSROOT_CRATES: [&str; 5] = ["std", "core", "alloc", "proc_macro", "test"];
pub(crate) fn dependency_names(package: &Value) -> Vec<String> {
package["dependencies"]
.as_array()
.map(|deps| {
deps.iter()
.filter_map(|dep| {
dep["rename"]
.as_str()
.or_else(|| dep["name"].as_str())
.map(|name| name.replace('-', "_"))
})
.collect()
})
.unwrap_or_default()
}
pub(crate) fn external_crate_set(dep_names: &[String]) -> HashSet<String> {
dep_names
.iter()
.cloned()
.chain(SYSROOT_CRATES.iter().map(|s| s.to_string()))
.collect()
}
pub(crate) fn local_type_namespace_names(items: &[syn::Item]) -> HashSet<String> {
items
.iter()
.filter_map(|item| match item {
syn::Item::Mod(m) => Some(strip_raw(&m.ident.to_string())),
syn::Item::Struct(s) => Some(strip_raw(&s.ident.to_string())),
syn::Item::Enum(e) => Some(strip_raw(&e.ident.to_string())),
syn::Item::Union(u) => Some(strip_raw(&u.ident.to_string())),
syn::Item::Trait(t) => Some(strip_raw(&t.ident.to_string())),
syn::Item::Type(t) => Some(strip_raw(&t.ident.to_string())),
_ => None,
})
.collect()
}
pub(crate) fn child_module_names(items: &[syn::Item]) -> HashSet<String> {
items
.iter()
.filter_map(|item| match item {
syn::Item::Mod(m) => Some(strip_raw(&m.ident.to_string())),
_ => None,
})
.collect()
}
pub(crate) struct ExternResolution {
pub(crate) externs: HashSet<String>,
pub(crate) externs_type: HashSet<String>,
pub(crate) reexports: ReexportMap,
pub(crate) extern_renames: ExternRenameMap,
pub(crate) renames_bare: ExternRenameMap,
}
pub(crate) fn extern_resolution(
src_dir: &Path,
root_file: &Path,
crate_package: &str,
dep_names: &[String],
items: &[syn::Item],
) -> Result<ExternResolution, String> {
let externs = external_crate_set(dep_names);
let externs_type: HashSet<String> = externs
.difference(&local_type_namespace_names(items))
.cloned()
.collect();
let scan = scan_crate(src_dir, root_file, crate_package, &externs)?;
let renames_bare = renames_shadowed(&scan.extern_renames, &child_module_names(items));
Ok(ExternResolution {
externs,
externs_type,
reexports: scan.reexports,
extern_renames: scan.extern_renames,
renames_bare,
})
}
pub(crate) fn resolve_principal(
path: &syn::Path,
uses: &UseMap,
module: &str,
res: &ExternResolution,
) -> Option<String> {
let resolved = if path.leading_colon.is_some() {
extern_verbatim_renamed(path, &res.externs, &res.extern_renames)
} else {
resolve_path(path, uses, module, BareFallback::Ignore)
.or_else(|| extern_verbatim_renamed(path, &res.externs_type, &res.renames_bare))
};
resolved.map(|canonical| {
let canonical = canonicalize_through_reexports(&canonical, &res.reexports);
let canonical = apply_crate_root_rename(canonical, &res.extern_renames);
apply_bare_alias_rename(canonical, &res.renames_bare)
})
}