use std::path::Path;
use crate::core::ir::ApiSurface;
use super::helpers::{ReexportKind, collect_reexport_map, is_pub};
use super::reexports::{UseFilter, collect_use_names};
pub(super) fn apply_parent_reexport_shortening(
source: &Path,
crate_name: &str,
module_path: &str,
surface: &mut ApiSurface,
types_before: usize,
enums_before: usize,
fns_before: usize,
) {
let parent_dir = match source.parent() {
Some(p) => p,
None => return,
};
let parent_mod = parent_dir.join("mod.rs");
let parent_lib = parent_dir.join("lib.rs");
let parent_content = if parent_mod.exists() && parent_mod != source {
std::fs::read_to_string(&parent_mod).ok()
} else if parent_lib.exists() && parent_lib != source {
std::fs::read_to_string(&parent_lib).ok()
} else {
None
};
let Some(content) = parent_content else {
return;
};
let Ok(parent_file) = syn::parse_file(&content) else {
return;
};
let mod_name = source.file_stem().and_then(|s| s.to_str()).unwrap_or("");
if mod_name.is_empty() || mod_name == "mod" {
return;
}
let reexport_map = collect_reexport_map(&parent_file.items);
let mut reexported_names = std::collections::HashSet::new();
for item in &parent_file.items {
if let syn::Item::Use(item_use) = item {
if is_pub(&item_use.vis) {
if let syn::UseTree::Path(use_path) = &item_use.tree {
if use_path.ident == mod_name {
match collect_use_names(&use_path.tree) {
UseFilter::All => {
let parent_module_path = module_path.rsplit_once("::").map(|(p, _)| p).unwrap_or("");
let parent_prefix = if parent_module_path.is_empty() {
crate_name.to_string()
} else {
format!("{crate_name}::{parent_module_path}")
};
for ty in &mut surface.types[types_before..] {
ty.rust_path = format!("{parent_prefix}::{}", ty.name);
}
for en in &mut surface.enums[enums_before..] {
en.rust_path = format!("{parent_prefix}::{}", en.name);
}
for func in &mut surface.functions[fns_before..] {
func.rust_path = format!("{parent_prefix}::{}", func.name);
}
return;
}
UseFilter::Names(names) => {
reexported_names.extend(names);
}
}
}
}
}
}
}
if let Some(ReexportKind::Names(names)) = reexport_map.get(mod_name) {
reexported_names.extend(names.iter().cloned());
} else if matches!(reexport_map.get(mod_name), Some(ReexportKind::Glob)) {
let parent_module_path = module_path.rsplit_once("::").map(|(p, _)| p).unwrap_or("");
let parent_prefix = if parent_module_path.is_empty() {
crate_name.to_string()
} else {
format!("{crate_name}::{parent_module_path}")
};
for ty in &mut surface.types[types_before..] {
ty.rust_path = format!("{parent_prefix}::{}", ty.name);
}
for en in &mut surface.enums[enums_before..] {
en.rust_path = format!("{parent_prefix}::{}", en.name);
}
for func in &mut surface.functions[fns_before..] {
func.rust_path = format!("{parent_prefix}::{}", func.name);
}
return;
}
if reexported_names.is_empty() {
return;
}
let parent_module_path = module_path.rsplit_once("::").map(|(p, _)| p).unwrap_or("");
let parent_prefix = if parent_module_path.is_empty() {
crate_name.to_string()
} else {
format!("{crate_name}::{parent_module_path}")
};
for ty in &mut surface.types[types_before..] {
if reexported_names.contains(&ty.name) {
ty.rust_path = format!("{parent_prefix}::{}", ty.name);
}
}
for en in &mut surface.enums[enums_before..] {
if reexported_names.contains(&en.name) {
en.rust_path = format!("{parent_prefix}::{}", en.name);
}
}
for func in &mut surface.functions[fns_before..] {
if reexported_names.contains(&func.name) {
func.rust_path = format!("{parent_prefix}::{}", func.name);
}
}
}
pub(super) fn derive_module_path(source: &Path, crate_src_dir: Option<&Path>) -> String {
let Some(root) = crate_src_dir else {
return String::new();
};
let root_canonical = std::fs::canonicalize(root).unwrap_or_else(|_| root.to_path_buf());
let source_canonical = std::fs::canonicalize(source).unwrap_or_else(|_| source.to_path_buf());
let Ok(relative) = source_canonical.strip_prefix(&root_canonical) else {
return String::new();
};
let mut segments = Vec::new();
for component in relative.iter() {
let s = component.to_string_lossy();
if s == "lib.rs" || s == "main.rs" {
return String::new();
} else if s == "mod.rs" {
continue;
} else if let Some(stem) = s.strip_suffix(".rs") {
segments.push(stem.to_string());
} else {
segments.push(s.to_string());
}
}
segments.join("::")
}