use std::collections::HashMap;
use std::path::PathBuf;
use syn::File as SynFile;
use walkdir::WalkDir;
#[derive(Debug, Clone)]
pub struct ParsedFile {
pub ast: SynFile,
pub path: PathBuf,
}
impl ParsedFile {
pub fn new(ast: SynFile, path: PathBuf) -> Self {
Self { ast, path }
}
}
#[derive(Debug, Default)]
pub struct AstCache {
cache: HashMap<PathBuf, ParsedFile>,
}
impl AstCache {
pub fn new() -> Self {
Self {
cache: HashMap::new(),
}
}
pub fn parse_and_cache_all_files(
&mut self,
project_path: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("🔄 Parsing and caching all Rust files in: {}", project_path);
for entry in WalkDir::new(project_path) {
let entry = entry?;
let path = entry.path();
if path.is_file() && path.extension().is_some_and(|ext| ext == "rs") {
if path.to_string_lossy().contains("/target/")
|| path.to_string_lossy().contains("/.git/")
{
continue;
}
println!("📄 Parsing file: {}", path.display());
let content = std::fs::read_to_string(path)?;
match syn::parse_file(&content) {
Ok(ast) => {
let parsed_file = ParsedFile::new(ast, path.to_path_buf());
self.cache.insert(path.to_path_buf(), parsed_file);
println!("✅ Successfully parsed: {}", path.display());
}
Err(e) => {
eprintln!("❌ Failed to parse {}: {}", path.display(), e);
}
}
}
}
println!("📊 Cached {} Rust files", self.cache.len());
Ok(())
}
pub fn get(&self, path: &PathBuf) -> Option<&ParsedFile> {
self.cache.get(path)
}
pub fn get_cloned(&self, path: &PathBuf) -> Option<ParsedFile> {
self.cache.get(path).cloned()
}
pub fn keys(&self) -> std::collections::hash_map::Keys<PathBuf, ParsedFile> {
self.cache.keys()
}
pub fn iter(&self) -> std::collections::hash_map::Iter<PathBuf, ParsedFile> {
self.cache.iter()
}
pub fn contains(&self, path: &PathBuf) -> bool {
self.cache.contains_key(path)
}
pub fn len(&self) -> usize {
self.cache.len()
}
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
pub fn clear(&mut self) {
self.cache.clear();
}
pub fn insert(&mut self, path: PathBuf, parsed_file: ParsedFile) -> Option<ParsedFile> {
self.cache.insert(path, parsed_file)
}
pub fn parse_and_cache_file(
&mut self,
file_path: &std::path::Path,
) -> Result<(), Box<dyn std::error::Error>> {
let content = std::fs::read_to_string(file_path)?;
let ast = syn::parse_file(&content)?;
let parsed_file = ParsedFile::new(ast, file_path.to_path_buf());
self.cache.insert(file_path.to_path_buf(), parsed_file);
Ok(())
}
}