codeprism_analysis/semantic/
concepts.rs1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct CodeConcept {
12 pub name: String,
14 pub description: String,
16 pub keywords: Vec<String>,
18 pub category: ConceptCategory,
20}
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
24pub enum ConceptCategory {
25 Architecture,
27 DesignPattern,
29 DataProcessing,
31 Security,
33 UserInterface,
35 Integration,
37 Performance,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct ConceptRelationship {
44 pub from: String,
46 pub to: String,
48 pub relationship_type: RelationshipType,
50 pub strength: f64,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub enum RelationshipType {
57 PartOf,
59 Similar,
61 DependsOn,
63 ComplementaryTo,
65 ImplementationOf,
67}
68
69pub struct ConceptMapper {
71 concepts: HashMap<String, CodeConcept>,
73 relationships: Vec<ConceptRelationship>,
75}
76
77impl ConceptMapper {
78 pub fn new() -> Self {
80 let mut concepts = HashMap::new();
81
82 concepts.insert(
84 "authentication".to_string(),
85 CodeConcept {
86 name: "authentication".to_string(),
87 description: "User identity verification and access control".to_string(),
88 keywords: vec![
89 "login".to_string(),
90 "auth".to_string(),
91 "authenticate".to_string(),
92 "verify".to_string(),
93 "credential".to_string(),
94 "token".to_string(),
95 "session".to_string(),
96 "password".to_string(),
97 "oauth".to_string(),
98 "jwt".to_string(),
99 ],
100 category: ConceptCategory::Security,
101 },
102 );
103
104 concepts.insert(
106 "database".to_string(),
107 CodeConcept {
108 name: "database".to_string(),
109 description: "Data storage and retrieval operations".to_string(),
110 keywords: vec![
111 "query".to_string(),
112 "sql".to_string(),
113 "database".to_string(),
114 "db".to_string(),
115 "connection".to_string(),
116 "transaction".to_string(),
117 "repository".to_string(),
118 "model".to_string(),
119 "entity".to_string(),
120 "orm".to_string(),
121 ],
122 category: ConceptCategory::DataProcessing,
123 },
124 );
125
126 concepts.insert(
128 "api".to_string(),
129 CodeConcept {
130 name: "api".to_string(),
131 description: "Application programming interface and web services".to_string(),
132 keywords: vec![
133 "endpoint".to_string(),
134 "route".to_string(),
135 "controller".to_string(),
136 "handler".to_string(),
137 "request".to_string(),
138 "response".to_string(),
139 "middleware".to_string(),
140 "validation".to_string(),
141 "rest".to_string(),
142 "graphql".to_string(),
143 ],
144 category: ConceptCategory::Integration,
145 },
146 );
147
148 concepts.insert(
150 "message_processing".to_string(),
151 CodeConcept {
152 name: "message_processing".to_string(),
153 description: "Event-driven messaging and queue processing".to_string(),
154 keywords: vec![
155 "message".to_string(),
156 "queue".to_string(),
157 "event".to_string(),
158 "handler".to_string(),
159 "processor".to_string(),
160 "publish".to_string(),
161 "subscribe".to_string(),
162 "broker".to_string(),
163 "dispatch".to_string(),
164 "stream".to_string(),
165 ],
166 category: ConceptCategory::Architecture,
167 },
168 );
169
170 concepts.insert(
172 "error_handling".to_string(),
173 CodeConcept {
174 name: "error_handling".to_string(),
175 description: "Exception management and error recovery".to_string(),
176 keywords: vec![
177 "error".to_string(),
178 "exception".to_string(),
179 "try".to_string(),
180 "catch".to_string(),
181 "handle".to_string(),
182 "raise".to_string(),
183 "throw".to_string(),
184 "fail".to_string(),
185 "recover".to_string(),
186 "retry".to_string(),
187 ],
188 category: ConceptCategory::Architecture,
189 },
190 );
191
192 let relationships = vec![
193 ConceptRelationship {
194 from: "authentication".to_string(),
195 to: "api".to_string(),
196 relationship_type: RelationshipType::ComplementaryTo,
197 strength: 0.8,
198 },
199 ConceptRelationship {
200 from: "database".to_string(),
201 to: "api".to_string(),
202 relationship_type: RelationshipType::ComplementaryTo,
203 strength: 0.7,
204 },
205 ConceptRelationship {
206 from: "error_handling".to_string(),
207 to: "api".to_string(),
208 relationship_type: RelationshipType::PartOf,
209 strength: 0.6,
210 },
211 ];
212
213 Self {
214 concepts,
215 relationships,
216 }
217 }
218
219 pub fn map_text_to_concepts(&self, text: &str) -> Vec<String> {
221 let mut matched_concepts = Vec::new();
222 let text_lower = text.to_lowercase();
223
224 for (concept_name, concept) in &self.concepts {
225 let mut score = 0.0;
226
227 if text_lower.contains(concept_name) {
229 score += 1.0;
230 }
231
232 for keyword in &concept.keywords {
234 if text_lower.contains(keyword) {
235 score += 0.5;
236 }
237 }
238
239 if score >= 0.5 {
241 matched_concepts.push(concept_name.clone());
242 }
243 }
244
245 matched_concepts
246 }
247
248 pub fn get_concept(&self, name: &str) -> Option<&CodeConcept> {
250 self.concepts.get(name)
251 }
252
253 pub fn get_concepts_by_category(&self, category: &ConceptCategory) -> Vec<&CodeConcept> {
255 self.concepts
256 .values()
257 .filter(|concept| &concept.category == category)
258 .collect()
259 }
260
261 pub fn find_related_concepts(&self, concept_name: &str) -> Vec<String> {
263 let mut related = Vec::new();
264
265 for relationship in &self.relationships {
266 if relationship.from == concept_name && relationship.strength > 0.5 {
267 related.push(relationship.to.clone());
268 } else if relationship.to == concept_name && relationship.strength > 0.5 {
269 related.push(relationship.from.clone());
270 }
271 }
272
273 related
274 }
275
276 pub fn add_concept(&mut self, concept: CodeConcept) {
278 self.concepts.insert(concept.name.clone(), concept);
279 }
280
281 pub fn add_relationship(&mut self, relationship: ConceptRelationship) {
283 self.relationships.push(relationship);
284 }
285
286 pub fn get_all_concept_names(&self) -> Vec<String> {
288 self.concepts.keys().cloned().collect()
289 }
290
291 pub fn calculate_similarity(&self, concept1: &str, concept2: &str) -> f64 {
293 if concept1 == concept2 {
294 return 1.0;
295 }
296
297 for relationship in &self.relationships {
299 if (relationship.from == concept1 && relationship.to == concept2)
300 || (relationship.from == concept2 && relationship.to == concept1)
301 {
302 return relationship.strength;
303 }
304 }
305
306 if let (Some(c1), Some(c2)) = (self.concepts.get(concept1), self.concepts.get(concept2)) {
308 let common_keywords: Vec<_> = c1
309 .keywords
310 .iter()
311 .filter(|k| c2.keywords.contains(k))
312 .collect();
313
314 let total_keywords = c1.keywords.len() + c2.keywords.len();
315 if total_keywords > 0 {
316 return (common_keywords.len() * 2) as f64 / total_keywords as f64;
317 }
318 }
319
320 0.0
321 }
322}
323
324impl Default for ConceptMapper {
325 fn default() -> Self {
326 Self::new()
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
335 fn test_concept_creation() {
336 let concept = CodeConcept {
337 name: "test".to_string(),
338 description: "Test concept".to_string(),
339 keywords: vec!["test".to_string(), "testing".to_string()],
340 category: ConceptCategory::DataProcessing,
341 };
342
343 assert_eq!(concept.name, "test");
344 assert_eq!(concept.keywords.len(), 2, "Should have 2 items");
345 }
346
347 #[test]
348 fn test_concept_mapper() {
349 let mapper = ConceptMapper::new();
350
351 let concepts = mapper.map_text_to_concepts("user authentication system");
353 assert!(concepts.contains(&"authentication".to_string()));
354
355 let auth_concept = mapper.get_concept("authentication");
357 assert!(auth_concept.is_some(), "Should have value");
358
359 let related = mapper.find_related_concepts("authentication");
361 assert!(!related.is_empty(), "Should not be empty");
362 }
363
364 #[test]
365 fn test_concept_similarity() {
366 let mapper = ConceptMapper::new();
367
368 let sim = mapper.calculate_similarity("authentication", "authentication");
370 assert_eq!(sim, 1.0);
371
372 let sim = mapper.calculate_similarity("authentication", "api");
374 assert!(sim > 0.0);
375 }
376
377 #[test]
378 fn test_category_filtering() {
379 let mapper = ConceptMapper::new();
380 let security_concepts = mapper.get_concepts_by_category(&ConceptCategory::Security);
381
382 assert!(!security_concepts.is_empty(), "Should not be empty");
383 assert!(security_concepts.iter().any(|c| c.name == "authentication"));
384 }
385}