ricecoder_storage/industry/
adapter.rs1use crate::config::Config;
8use crate::error::StorageResult;
9use std::path::Path;
10
11pub trait IndustryFileAdapter: Send + Sync {
13 fn name(&self) -> &'static str;
15
16 fn can_handle(&self, project_root: &Path) -> bool;
18
19 fn read_config(&self, project_root: &Path) -> StorageResult<Config>;
21
22 fn priority(&self) -> u32 {
25 0
26 }
27}
28
29#[derive(Debug, Clone)]
31pub struct FileDetectionResult {
32 pub adapter_name: String,
34 pub priority: u32,
36 pub file_path: std::path::PathBuf,
38}
39
40pub struct IndustryFileDetector {
42 adapters: Vec<Box<dyn IndustryFileAdapter>>,
43}
44
45impl IndustryFileDetector {
46 pub fn new(adapters: Vec<Box<dyn IndustryFileAdapter>>) -> Self {
48 IndustryFileDetector { adapters }
49 }
50
51 pub fn detect_files(&self, project_root: &Path) -> Vec<FileDetectionResult> {
53 let mut results = Vec::new();
54
55 for adapter in &self.adapters {
56 if adapter.can_handle(project_root) {
57 results.push(FileDetectionResult {
60 adapter_name: adapter.name().to_string(),
61 priority: adapter.priority(),
62 file_path: project_root.to_path_buf(),
63 });
64 }
65 }
66
67 results.sort_by(|a, b| b.priority.cmp(&a.priority));
69 results
70 }
71
72 pub fn get_best_adapter(&self, project_root: &Path) -> Option<&dyn IndustryFileAdapter> {
74 self.adapters
75 .iter()
76 .filter(|adapter| adapter.can_handle(project_root))
77 .max_by_key(|adapter| adapter.priority())
78 .map(|adapter| adapter.as_ref())
79 }
80
81 pub fn register_adapter(&mut self, adapter: Box<dyn IndustryFileAdapter>) {
83 self.adapters.push(adapter);
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 struct MockAdapter {
92 name: &'static str,
93 priority: u32,
94 can_handle: bool,
95 }
96
97 impl IndustryFileAdapter for MockAdapter {
98 fn name(&self) -> &'static str {
99 self.name
100 }
101
102 fn can_handle(&self, _project_root: &Path) -> bool {
103 self.can_handle
104 }
105
106 fn read_config(&self, _project_root: &Path) -> StorageResult<Config> {
107 Ok(Config::default())
108 }
109
110 fn priority(&self) -> u32 {
111 self.priority
112 }
113 }
114
115 #[test]
116 fn test_detector_sorts_by_priority() {
117 let adapters: Vec<Box<dyn IndustryFileAdapter>> = vec![
118 Box::new(MockAdapter {
119 name: "low",
120 priority: 1,
121 can_handle: true,
122 }),
123 Box::new(MockAdapter {
124 name: "high",
125 priority: 10,
126 can_handle: true,
127 }),
128 Box::new(MockAdapter {
129 name: "medium",
130 priority: 5,
131 can_handle: true,
132 }),
133 ];
134
135 let detector = IndustryFileDetector::new(adapters);
136 let results = detector.detect_files(Path::new("."));
137
138 assert_eq!(results.len(), 3);
139 assert_eq!(results[0].adapter_name, "high");
140 assert_eq!(results[1].adapter_name, "medium");
141 assert_eq!(results[2].adapter_name, "low");
142 }
143
144 #[test]
145 fn test_detector_filters_by_can_handle() {
146 let adapters: Vec<Box<dyn IndustryFileAdapter>> = vec![
147 Box::new(MockAdapter {
148 name: "yes",
149 priority: 1,
150 can_handle: true,
151 }),
152 Box::new(MockAdapter {
153 name: "no",
154 priority: 10,
155 can_handle: false,
156 }),
157 ];
158
159 let detector = IndustryFileDetector::new(adapters);
160 let results = detector.detect_files(Path::new("."));
161
162 assert_eq!(results.len(), 1);
163 assert_eq!(results[0].adapter_name, "yes");
164 }
165
166 #[test]
167 fn test_get_best_adapter() {
168 let adapters: Vec<Box<dyn IndustryFileAdapter>> = vec![
169 Box::new(MockAdapter {
170 name: "low",
171 priority: 1,
172 can_handle: true,
173 }),
174 Box::new(MockAdapter {
175 name: "high",
176 priority: 10,
177 can_handle: true,
178 }),
179 ];
180
181 let detector = IndustryFileDetector::new(adapters);
182 let best = detector.get_best_adapter(Path::new("."));
183
184 assert!(best.is_some());
185 assert_eq!(best.unwrap().name(), "high");
186 }
187
188 #[test]
189 fn test_get_best_adapter_respects_can_handle() {
190 let adapters: Vec<Box<dyn IndustryFileAdapter>> = vec![
191 Box::new(MockAdapter {
192 name: "low",
193 priority: 1,
194 can_handle: true,
195 }),
196 Box::new(MockAdapter {
197 name: "high",
198 priority: 10,
199 can_handle: false,
200 }),
201 ];
202
203 let detector = IndustryFileDetector::new(adapters);
204 let best = detector.get_best_adapter(Path::new("."));
205
206 assert!(best.is_some());
207 assert_eq!(best.unwrap().name(), "low");
208 }
209}