tauri_typegen/analysis/
ast_cache.rs

1use std::collections::HashMap;
2use std::path::PathBuf;
3use syn::File as SynFile;
4use walkdir::WalkDir;
5
6/// Cache entry for a parsed Rust file
7#[derive(Debug, Clone)]
8pub struct ParsedFile {
9    /// The parsed AST
10    pub ast: SynFile,
11    /// File path for reference
12    pub path: PathBuf,
13    // Last modified time for cache invalidation (if needed later)
14    // modified: std::time::SystemTime,
15}
16
17impl ParsedFile {
18    pub fn new(ast: SynFile, path: PathBuf) -> Self {
19        Self { ast, path }
20    }
21}
22
23/// AST cache for parsed Rust files
24#[derive(Debug, Default)]
25pub struct AstCache {
26    cache: HashMap<PathBuf, ParsedFile>,
27}
28
29impl AstCache {
30    pub fn new() -> Self {
31        Self {
32            cache: HashMap::new(),
33        }
34    }
35
36    /// Parse and cache all Rust files in the given project path
37    pub fn parse_and_cache_all_files(
38        &mut self,
39        project_path: &str,
40        verbose: bool,
41    ) -> Result<(), Box<dyn std::error::Error>> {
42        if verbose {
43            println!("🔄 Parsing and caching all Rust files in: {}", project_path);
44        }
45
46        for entry in WalkDir::new(project_path) {
47            let entry = entry?;
48            let path = entry.path();
49
50            if path.is_file() && path.extension().is_some_and(|ext| ext == "rs") {
51                // Skip target directory and other build artifacts
52                if path.to_string_lossy().contains("/target/")
53                    || path.to_string_lossy().contains("/.git/")
54                {
55                    continue;
56                }
57
58                if verbose {
59                    println!("📄 Parsing file: {}", path.display());
60                }
61
62                let content = std::fs::read_to_string(path)?;
63                match syn::parse_file(&content) {
64                    Ok(ast) => {
65                        let parsed_file = ParsedFile::new(ast, path.to_path_buf());
66                        self.cache.insert(path.to_path_buf(), parsed_file);
67                        if verbose {
68                            println!("✅ Successfully parsed: {}", path.display());
69                        }
70                    }
71                    Err(e) => {
72                        eprintln!("❌ Failed to parse {}: {}", path.display(), e);
73                        // Continue processing other files even if one fails
74                    }
75                }
76            }
77        }
78
79        if verbose {
80            println!("📊 Cached {} Rust files", self.cache.len());
81        }
82        Ok(())
83    }
84
85    /// Get a parsed file from the cache
86    pub fn get(&self, path: &PathBuf) -> Option<&ParsedFile> {
87        self.cache.get(path)
88    }
89
90    /// Get a cloned parsed file from the cache
91    pub fn get_cloned(&self, path: &PathBuf) -> Option<ParsedFile> {
92        self.cache.get(path).cloned()
93    }
94
95    /// Get all cached file paths
96    pub fn keys(&self) -> std::collections::hash_map::Keys<'_, PathBuf, ParsedFile> {
97        self.cache.keys()
98    }
99
100    /// Get all cached files as an iterator
101    pub fn iter(&self) -> std::collections::hash_map::Iter<'_, PathBuf, ParsedFile> {
102        self.cache.iter()
103    }
104
105    /// Check if a file is cached
106    pub fn contains(&self, path: &PathBuf) -> bool {
107        self.cache.contains_key(path)
108    }
109
110    /// Get the number of cached files
111    pub fn len(&self) -> usize {
112        self.cache.len()
113    }
114
115    /// Check if the cache is empty
116    pub fn is_empty(&self) -> bool {
117        self.cache.is_empty()
118    }
119
120    /// Clear the cache
121    pub fn clear(&mut self) {
122        self.cache.clear();
123    }
124
125    /// Insert a parsed file into the cache
126    pub fn insert(&mut self, path: PathBuf, parsed_file: ParsedFile) -> Option<ParsedFile> {
127        self.cache.insert(path, parsed_file)
128    }
129
130    /// Parse a single file and add it to the cache
131    pub fn parse_and_cache_file(
132        &mut self,
133        file_path: &std::path::Path,
134    ) -> Result<(), Box<dyn std::error::Error>> {
135        let content = std::fs::read_to_string(file_path)?;
136        let ast = syn::parse_file(&content)?;
137        let parsed_file = ParsedFile::new(ast, file_path.to_path_buf());
138        self.cache.insert(file_path.to_path_buf(), parsed_file);
139        Ok(())
140    }
141}