herolib_code/parser/
mod.rs1mod error;
32mod rust_parser;
33mod types;
34mod walker;
35
36pub use error::{ParseError, ParseResult};
37pub use rust_parser::RustParser;
38pub use types::{
39 CodeBase, EnumInfo, EnumVariant, FieldInfo, FileInfo, MethodInfo, ParameterInfo, Receiver,
40 StructInfo, Visibility,
41};
42pub use walker::{DirectoryWalker, WalkerConfig};
43
44use std::path::Path;
45
46pub struct CodebaseParser {
52 walker_config: WalkerConfig,
54 include_private: bool,
56 continue_on_error: bool,
58}
59
60impl Default for CodebaseParser {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl CodebaseParser {
67 pub fn new() -> Self {
69 Self {
70 walker_config: WalkerConfig::default(),
71 include_private: true,
72 continue_on_error: true,
73 }
74 }
75
76 pub fn walker_config(mut self, config: WalkerConfig) -> Self {
78 self.walker_config = config;
79 self
80 }
81
82 pub fn include_private(mut self, include: bool) -> Self {
84 self.include_private = include;
85 self
86 }
87
88 pub fn continue_on_error(mut self, continue_on_error: bool) -> Self {
90 self.continue_on_error = continue_on_error;
91 self
92 }
93
94 pub fn parse_directory<P: AsRef<Path>>(&self, path: P) -> ParseResult<CodeBase> {
109 let path = path.as_ref();
110 let walker = DirectoryWalker::new(self.walker_config.clone());
111 let rust_files = walker.discover_rust_files(path)?;
112
113 let rust_parser = RustParser::new().include_private(self.include_private);
114 let mut codebase = CodeBase::new();
115
116 for file_path in rust_files {
117 match rust_parser.parse_file(&file_path) {
118 Ok(file_codebase) => {
119 codebase.merge(file_codebase);
120 }
121 Err(e) => {
122 if !self.continue_on_error {
123 return Err(e);
124 }
125 eprintln!("Warning: Failed to parse {:?}: {}", file_path, e);
127 }
128 }
129 }
130
131 Ok(codebase)
132 }
133
134 pub fn parse_file<P: AsRef<Path>>(&self, path: P) -> ParseResult<CodeBase> {
144 let rust_parser = RustParser::new().include_private(self.include_private);
145 rust_parser.parse_file(path)
146 }
147
148 pub fn parse_source(&self, source: &str, file_path: &str) -> ParseResult<CodeBase> {
159 let rust_parser = RustParser::new().include_private(self.include_private);
160 rust_parser.parse_source(source, file_path.to_string())
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use std::fs;
168 use tempfile::tempdir;
169
170 #[test]
171 fn test_codebase_parser_directory() {
172 let dir = tempdir().unwrap();
173 let dir_path = dir.path();
174
175 fs::write(
177 dir_path.join("lib.rs"),
178 r#"
179/// A test struct.
180pub struct TestStruct {
181 /// A field.
182 pub value: i32,
183}
184
185impl TestStruct {
186 /// Creates a new instance.
187 pub fn new(value: i32) -> Self {
188 Self { value }
189 }
190}
191"#,
192 )
193 .unwrap();
194
195 fs::write(
196 dir_path.join("enums.rs"),
197 r#"
198/// A test enum.
199#[derive(Debug, Clone)]
200pub enum TestEnum {
201 /// First variant.
202 First,
203 /// Second variant.
204 Second(i32),
205}
206"#,
207 )
208 .unwrap();
209
210 let parser = CodebaseParser::new();
211 let codebase = parser.parse_directory(dir_path).unwrap();
212
213 assert_eq!(codebase.structs.len(), 1);
214 assert_eq!(codebase.enums.len(), 1);
215 assert_eq!(codebase.files.len(), 2);
216
217 let test_struct = &codebase.structs[0];
218 assert_eq!(test_struct.name, "TestStruct");
219 assert_eq!(test_struct.methods.len(), 1);
220
221 let test_enum = &codebase.enums[0];
222 assert_eq!(test_enum.name, "TestEnum");
223 assert_eq!(test_enum.variants.len(), 2);
224 }
225
226 #[test]
227 fn test_codebase_parser_source() {
228 let source = r#"
229pub struct Point {
230 pub x: f64,
231 pub y: f64,
232}
233"#;
234
235 let parser = CodebaseParser::new();
236 let codebase = parser.parse_source(source, "test.rs").unwrap();
237
238 assert_eq!(codebase.structs.len(), 1);
239 assert_eq!(codebase.structs[0].name, "Point");
240 }
241}