1use super::types::ModuleNode;
6use super::types_enhanced::ArchitectureLayer;
7
8struct ClassificationRule {
10 patterns: Vec<&'static str>,
11 layer: ArchitectureLayer,
12 sub_layer: Option<&'static str>,
13 priority: u8,
14}
15
16fn get_classification_rules() -> Vec<ClassificationRule> {
18 vec![
19 ClassificationRule {
21 patterns: vec![
22 "/ui/",
23 "/components/",
24 "/pages/",
25 "/views/",
26 "/screens/",
27 ".tsx",
28 ],
29 layer: ArchitectureLayer::Presentation,
30 sub_layer: Some("components"),
31 priority: 10,
32 },
33 ClassificationRule {
35 patterns: vec!["/styles/", "/css/", "/themes/", ".css", ".scss"],
36 layer: ArchitectureLayer::Presentation,
37 sub_layer: Some("styles"),
38 priority: 10,
39 },
40 ClassificationRule {
42 patterns: vec![
43 "/core/",
44 "/domain/",
45 "/business/",
46 "/services/",
47 "/usecases/",
48 ],
49 layer: ArchitectureLayer::Business,
50 sub_layer: Some("core"),
51 priority: 20,
52 },
53 ClassificationRule {
55 patterns: vec!["/tools/"],
56 layer: ArchitectureLayer::Business,
57 sub_layer: Some("tools"),
58 priority: 15,
59 },
60 ClassificationRule {
62 patterns: vec!["/api/", "/client/", "/http/", "/fetch/"],
63 layer: ArchitectureLayer::Data,
64 sub_layer: Some("api"),
65 priority: 20,
66 },
67 ClassificationRule {
69 patterns: vec![
70 "/db/",
71 "/database/",
72 "/repositories/",
73 "/storage/",
74 "/cache/",
75 ],
76 layer: ArchitectureLayer::Data,
77 sub_layer: Some("storage"),
78 priority: 20,
79 },
80 ClassificationRule {
82 patterns: vec!["/config/", "/settings/", "/env/"],
83 layer: ArchitectureLayer::Infrastructure,
84 sub_layer: Some("config"),
85 priority: 5,
86 },
87 ClassificationRule {
89 patterns: vec!["/utils/", "/helpers/", "/lib/", "/common/", "/shared/"],
90 layer: ArchitectureLayer::Infrastructure,
91 sub_layer: Some("utils"),
92 priority: 5,
93 },
94 ClassificationRule {
96 patterns: vec!["/types/", "/interfaces/", "/models/", ".d.ts"],
97 layer: ArchitectureLayer::Infrastructure,
98 sub_layer: Some("types"),
99 priority: 5,
100 },
101 ClassificationRule {
103 patterns: vec!["/hooks/"],
104 layer: ArchitectureLayer::CrossCutting,
105 sub_layer: Some("hooks"),
106 priority: 15,
107 },
108 ClassificationRule {
110 patterns: vec!["/middleware/", "/interceptors/"],
111 layer: ArchitectureLayer::CrossCutting,
112 sub_layer: Some("middleware"),
113 priority: 15,
114 },
115 ClassificationRule {
117 patterns: vec!["/log/", "/logging/", "/monitor/", "/telemetry/"],
118 layer: ArchitectureLayer::CrossCutting,
119 sub_layer: Some("logging"),
120 priority: 15,
121 },
122 ClassificationRule {
124 patterns: vec!["/auth/", "/permission/", "/security/", "/oauth/"],
125 layer: ArchitectureLayer::CrossCutting,
126 sub_layer: Some("auth"),
127 priority: 15,
128 },
129 ]
130}
131
132#[derive(Debug, Clone)]
134pub struct ClassificationResult {
135 pub layer: ArchitectureLayer,
136 pub sub_layer: Option<String>,
137 pub confidence: f64,
138 pub matched_rules: Vec<String>,
139}
140
141pub struct LayerClassifier {
143 rules: Vec<ClassificationRule>,
144}
145
146impl LayerClassifier {
147 pub fn new() -> Self {
148 Self {
149 rules: get_classification_rules(),
150 }
151 }
152
153 pub fn classify(&self, module: &ModuleNode) -> ClassificationResult {
155 let path = &module.id;
156 let path_lower = path.to_lowercase();
157 let mut matched: Vec<(&ClassificationRule, Vec<&str>)> = Vec::new();
158
159 for rule in &self.rules {
160 let matches: Vec<&str> = rule
161 .patterns
162 .iter()
163 .filter(|p| path_lower.contains(&p.to_lowercase()))
164 .copied()
165 .collect();
166 if !matches.is_empty() {
167 matched.push((rule, matches));
168 }
169 }
170
171 if !matched.is_empty() {
172 matched.sort_by(|a, b| {
173 b.0.priority
174 .cmp(&a.0.priority)
175 .then_with(|| b.1.len().cmp(&a.1.len()))
176 });
177
178 let best = &matched[0];
179 return ClassificationResult {
180 layer: best.0.layer,
181 sub_layer: best.0.sub_layer.map(String::from),
182 confidence: (0.5 + best.1.len() as f64 * 0.1).min(0.9),
183 matched_rules: best.1.iter().map(|s| s.to_string()).collect(),
184 };
185 }
186
187 if let Some(result) = self.classify_by_content(module) {
189 return result;
190 }
191
192 ClassificationResult {
194 layer: ArchitectureLayer::Infrastructure,
195 sub_layer: None,
196 confidence: 0.3,
197 matched_rules: vec!["default".to_string()],
198 }
199 }
200
201 fn classify_by_content(&self, module: &ModuleNode) -> Option<ClassificationResult> {
202 let mut has_react = false;
203 let mut has_db = false;
204 let mut has_api = false;
205
206 for imp in &module.imports {
207 let src = imp.source.to_lowercase();
208 if src.contains("react") || src.contains("ink") {
209 has_react = true;
210 }
211 if src.contains("mongo") || src.contains("mysql") || src.contains("postgres") {
212 has_db = true;
213 }
214 if src.contains("axios") || src.contains("fetch") || src.contains("http") {
215 has_api = true;
216 }
217 }
218
219 if has_react {
220 return Some(ClassificationResult {
221 layer: ArchitectureLayer::Presentation,
222 sub_layer: None,
223 confidence: 0.7,
224 matched_rules: vec!["content:react".to_string()],
225 });
226 }
227
228 if has_db {
229 return Some(ClassificationResult {
230 layer: ArchitectureLayer::Data,
231 sub_layer: Some("storage".to_string()),
232 confidence: 0.7,
233 matched_rules: vec!["content:database".to_string()],
234 });
235 }
236
237 if has_api {
238 return Some(ClassificationResult {
239 layer: ArchitectureLayer::Data,
240 sub_layer: Some("api".to_string()),
241 confidence: 0.6,
242 matched_rules: vec!["content:api".to_string()],
243 });
244 }
245
246 None
247 }
248
249 pub fn classify_all(
251 &self,
252 modules: &[ModuleNode],
253 ) -> std::collections::HashMap<String, ClassificationResult> {
254 modules
255 .iter()
256 .map(|m| (m.id.clone(), self.classify(m)))
257 .collect()
258 }
259
260 pub fn get_layer_description(layer: ArchitectureLayer) -> &'static str {
262 match layer {
263 ArchitectureLayer::Presentation => "表现层:用户界面、组件、页面、视图渲染",
264 ArchitectureLayer::Business => "业务层:核心业务逻辑、领域模型、服务实现",
265 ArchitectureLayer::Data => "数据层:API 调用、数据库访问、存储管理",
266 ArchitectureLayer::Infrastructure => "基础设施层:工具函数、配置管理、类型定义",
267 ArchitectureLayer::CrossCutting => "横切关注点:认证、日志、中间件、插件系统",
268 }
269 }
270}
271
272impl Default for LayerClassifier {
273 fn default() -> Self {
274 Self::new()
275 }
276}
277
278pub fn classify_module(module: &ModuleNode) -> ClassificationResult {
280 LayerClassifier::new().classify(module)
281}
282
283pub fn classify_modules(
285 modules: &[ModuleNode],
286) -> std::collections::HashMap<String, ClassificationResult> {
287 LayerClassifier::new().classify_all(modules)
288}