gather_all_code_from_crates/
process_crate_directory.rs1crate::ix!();
2
3pub fn process_crate_directory(dir: &PathBuf, criteria: &AstFilterCriteria) -> AppResult<String> {
4 if !dir.exists() {
5 return Err(AppError::InvalidInput{reason:ErrorReason::MissingData});
6 }
7
8 let mut combined = String::new();
9 let mut dirs_to_visit = vec![dir.clone()];
10 while let Some(d) = dirs_to_visit.pop() {
11 let entries = fs::read_dir(&d).map_err(|e|AppError::Io{code:e.kind()})?;
12 for entry in entries {
13 let entry = entry.map_err(|e|AppError::Io{code:e.kind()})?;
14 let p = entry.path();
15
16 if !p.starts_with(dir.join("src")) {
17 continue; }
19
20 if p.is_dir() {
21 dirs_to_visit.push(p);
22 } else {
23 if p.extension().and_then(|s|s.to_str()) == Some("rs") {
24 let relative = match p.strip_prefix(dir) {
25 Ok(rel) => rel.display().to_string(),
26 Err(_) => p.display().to_string(),
27 };
28 if criteria.excluded_files().iter().any(|ex| ex == &relative) {
29 continue;
30 }
31
32 if *criteria.exclude_main_file() && relative == "src/main.rs" {
34 continue;
35 }
36
37 let snippet = process_file(&p, criteria)?;
38 if !snippet.trim().is_empty() {
39 combined.push_str(&snippet);
40 combined.push('\n');
41 }
42 }
43 }
44 }
45 }
46
47 Ok(combined)
48}
49
50#[cfg(test)]
51mod process_crate_directory_tests {
52 use super::*;
53
54 #[test]
55 fn test_process_crate_directory_basic() {
56 let tmp_dir = TempDir::new().unwrap();
57 let src_dir = tmp_dir.path().join("src");
58 fs::create_dir(&src_dir).unwrap();
59
60 let lib_path = src_dir.join("lib.rs");
61 let code_lib = r#"pub fn lib_func() {} fn priv_func() {}"#;
62 {
63 let mut f = File::create(&lib_path).unwrap();
64 f.write_all(code_lib.as_bytes()).unwrap();
65 }
66
67 let mod_path = src_dir.join("mod.rs");
68 let code_mod = r#"#[test] fn test_in_mod() {} fn normal_in_mod() {}"#;
69 {
70 let mut f = File::create(&mod_path).unwrap();
71 f.write_all(code_mod.as_bytes()).unwrap();
72 }
73
74 let criteria = AstFilterCriteriaBuilder::default()
75 .include_tests(false)
76 .omit_private(true)
77 .build().unwrap();
78
79 let result = process_crate_directory(&tmp_dir.path().to_path_buf(), &criteria).unwrap();
80 assert!(result.contains("pub fn lib_func()"));
83 assert!(!result.contains("priv_func"));
84 assert!(!result.contains("test_in_mod"));
85 assert!(!result.contains("normal_in_mod"));
86 }
87
88 #[test]
89 fn test_process_crate_directory_excluded_file() {
90 let tmp_dir = TempDir::new().unwrap();
91 let src_dir = tmp_dir.path().join("src");
92 fs::create_dir(&src_dir).unwrap();
93
94 let keep_path = src_dir.join("keep.rs");
95 let exclude_path = src_dir.join("exclude.rs");
96
97 let code_keep = r#"pub fn keep_me() {}"#;
98 {
99 let mut f = File::create(&keep_path).unwrap();
100 f.write_all(code_keep.as_bytes()).unwrap();
101 }
102
103 let code_exclude = r#"pub fn exclude_me() {}"#;
104 {
105 let mut f = File::create(&exclude_path).unwrap();
106 f.write_all(code_exclude.as_bytes()).unwrap();
107 }
108
109 let criteria = AstFilterCriteriaBuilder::default()
110 .excluded_files(vec!["src/exclude.rs".to_string()])
111 .build().unwrap();
112
113 let result = process_crate_directory(&tmp_dir.path().to_path_buf(), &criteria).unwrap();
114 assert!(result.contains("keep_me"));
115 assert!(!result.contains("exclude_me"));
116 }
117
118 #[test]
119 fn test_process_crate_directory_exclude_main_file() {
120 let tmp_dir = TempDir::new().unwrap();
121 let src_dir = tmp_dir.path().join("src");
122 fs::create_dir(&src_dir).unwrap();
123
124 let main_path = src_dir.join("main.rs");
125 let lib_path = src_dir.join("lib.rs");
126
127 let code_main = r#"fn main() {}"#;
128 {
129 let mut f = File::create(&main_path).unwrap();
130 f.write_all(code_main.as_bytes()).unwrap();
131 }
132
133 let code_lib = r#"pub fn visible() {}"#;
134 {
135 let mut f = File::create(&lib_path).unwrap();
136 f.write_all(code_lib.as_bytes()).unwrap();
137 }
138
139 let criteria = AstFilterCriteriaBuilder::default()
140 .exclude_main_file(true)
141 .build().unwrap();
142
143 info!("criteria: {:#?}", criteria);
144
145 let result = process_crate_directory(&tmp_dir.path().to_path_buf(), &criteria).unwrap();
146
147 info!("result: {:#?}", result);
148
149 assert!(result.contains("visible"));
151 assert!(!result.contains("main()"));
152 }
153
154 #[test]
155 fn test_error_handling_invalid_input() {
156 let path = PathBuf::from("non_existent_dir");
157 let criteria = AstFilterCriteriaBuilder::default().build().unwrap();
158 let result = process_crate_directory(&path, &criteria);
159 match result {
160 Err(AppError::InvalidInput { reason: ErrorReason::MissingData }) => (),
161 _ => panic!("Expected MissingData error"),
162 }
163 }
164}