#![forbid(unsafe_code)]
use std::path::{Path, PathBuf};
use super::{FrameworkResolver, ResolveContext};
pub struct RustResolver;
impl FrameworkResolver for RustResolver {
fn name(&self) -> &'static str {
"rust"
}
fn detect(&self, project_root: &Path) -> bool {
project_root.join("Cargo.toml").exists()
}
fn resolve_to_paths(&self, ctx: &ResolveContext<'_>) -> Vec<PathBuf> {
let name = ctx.reference_name;
if let Some(path_part) = name.strip_prefix("crate::") {
return resolve_crate_path(ctx.project_root, path_part);
}
if let Some(path_part) = name.strip_prefix("super::") {
return resolve_relative_path(ctx.from_file, path_part, 1);
}
if let Some(path_part) = name.strip_prefix("self::") {
return resolve_relative_path(ctx.from_file, path_part, 0);
}
if is_valid_module_name(name) {
return resolve_adjacent_module(ctx.from_file, name);
}
Vec::new()
}
}
fn resolve_crate_path(project_root: &Path, path_part: &str) -> Vec<PathBuf> {
let segments: Vec<&str> = path_part.split("::").collect();
if segments.is_empty() {
return Vec::new();
}
let module_segs = if segments.len() > 1 {
segments.get(..segments.len() - 1).unwrap_or(&[])
} else {
&segments[..]
};
let rel: PathBuf = module_segs.iter().collect();
let src = project_root.join("src");
let mut out = Vec::new();
let rs = src.join(&rel).with_extension("rs");
if rs.exists() {
out.push(rs);
}
let mod_rs = src.join(&rel).join("mod.rs");
if mod_rs.exists() {
out.push(mod_rs);
}
out
}
fn resolve_relative_path(from_file: &str, path_part: &str, up_levels: usize) -> Vec<PathBuf> {
let from = Path::new(from_file);
let mut base = from.parent().unwrap_or_else(|| Path::new(""));
for _ in 0..up_levels {
base = base.parent().unwrap_or(base);
}
let segments: Vec<&str> = path_part.split("::").collect();
if segments.is_empty() {
return Vec::new();
}
let module_segs = if segments.len() > 1 {
segments.get(..segments.len() - 1).unwrap_or(&[])
} else {
&segments[..]
};
let rel: PathBuf = module_segs.iter().collect();
let mut out = Vec::new();
let rs = base.join(&rel).with_extension("rs");
if rs.exists() {
out.push(rs);
}
let mod_rs = base.join(&rel).join("mod.rs");
if mod_rs.exists() {
out.push(mod_rs);
}
out
}
fn resolve_adjacent_module(from_file: &str, module_name: &str) -> Vec<PathBuf> {
let Some(parent) = Path::new(from_file).parent() else {
return Vec::new();
};
let mut out = Vec::new();
let rs = parent.join(format!("{module_name}.rs"));
if rs.exists() {
out.push(rs);
}
let mod_rs = parent.join(module_name).join("mod.rs");
if mod_rs.exists() {
out.push(mod_rs);
}
out
}
fn is_valid_module_name(name: &str) -> bool {
!name.is_empty()
&& !name.contains("::")
&& !name.contains('.')
&& name
.chars()
.next()
.is_some_and(|c| c.is_ascii_lowercase() || c == '_')
&& name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
}