1use crate::analyzer::{AnalysisConfig, DetectedTechnology, DetectedLanguage, TechnologyCategory, LibraryType};
2use crate::error::Result;
3use std::path::Path;
5use std::collections::HashMap;
6
7struct TechnologyRule {
9 name: String,
10 category: TechnologyCategory,
11 confidence: f32,
12 dependency_patterns: Vec<String>,
13 requires: Vec<String>,
15 conflicts_with: Vec<String>,
17 is_primary_indicator: bool,
19 alternative_names: Vec<String>,
21}
22
23pub fn detect_frameworks(
25 _project_root: &Path,
26 languages: &[DetectedLanguage],
27 _config: &AnalysisConfig,
28) -> Result<Vec<DetectedTechnology>> {
29 let mut all_technologies = Vec::new();
30
31 for language in languages {
32 let lang_technologies = match language.name.as_str() {
33 "Rust" => detect_rust_technologies(language),
34 "JavaScript" | "TypeScript" | "JavaScript/TypeScript" => detect_js_technologies(language),
35 "Python" => detect_python_technologies(language),
36 "Go" => detect_go_technologies(language),
37 "Java" | "Kotlin" | "Java/Kotlin" => detect_jvm_technologies(language),
38 _ => Vec::new(),
39 };
40 all_technologies.extend(lang_technologies);
41 }
42
43 let resolved_technologies = resolve_technology_conflicts(all_technologies);
45
46 let final_technologies = mark_primary_technologies(resolved_technologies);
48
49 let mut result = final_technologies;
51 result.sort_by(|a, b| b.confidence.partial_cmp(&a.confidence).unwrap_or(std::cmp::Ordering::Equal));
52 result.dedup_by(|a, b| a.name == b.name);
53
54 Ok(result)
55}
56
57fn detect_rust_technologies(language: &DetectedLanguage) -> Vec<DetectedTechnology> {
59 let rules = get_rust_technology_rules();
60
61 let all_deps: Vec<String> = language.main_dependencies.iter()
63 .chain(language.dev_dependencies.iter())
64 .cloned()
65 .collect();
66
67 detect_technologies_by_dependencies(&rules, &all_deps, language.confidence)
68}
69
70fn detect_js_technologies(language: &DetectedLanguage) -> Vec<DetectedTechnology> {
72 let rules = get_js_technology_rules();
73
74 let all_deps: Vec<String> = language.main_dependencies.iter()
76 .chain(language.dev_dependencies.iter())
77 .cloned()
78 .collect();
79
80 let mut technologies = detect_technologies_by_dependencies(&rules, &all_deps, language.confidence);
81
82 if let Some(enhanced_techs) = detect_technologies_from_source_files(language, &rules) {
84 for enhanced_tech in enhanced_techs {
86 if let Some(existing) = technologies.iter_mut().find(|t| t.name == enhanced_tech.name) {
87 if enhanced_tech.confidence > existing.confidence {
89 existing.confidence = enhanced_tech.confidence;
90 }
91 } else {
92 technologies.push(enhanced_tech);
94 }
95 }
96 }
97
98 technologies
99}
100
101fn detect_python_technologies(language: &DetectedLanguage) -> Vec<DetectedTechnology> {
103 let rules = get_python_technology_rules();
104
105 let all_deps: Vec<String> = language.main_dependencies.iter()
107 .chain(language.dev_dependencies.iter())
108 .cloned()
109 .collect();
110
111 detect_technologies_by_dependencies(&rules, &all_deps, language.confidence)
112}
113
114fn detect_go_technologies(language: &DetectedLanguage) -> Vec<DetectedTechnology> {
116 let rules = get_go_technology_rules();
117
118 let all_deps: Vec<String> = language.main_dependencies.iter()
120 .chain(language.dev_dependencies.iter())
121 .cloned()
122 .collect();
123
124 detect_technologies_by_dependencies(&rules, &all_deps, language.confidence)
125}
126
127fn detect_jvm_technologies(language: &DetectedLanguage) -> Vec<DetectedTechnology> {
129 let rules = get_jvm_technology_rules();
130
131 let all_deps: Vec<String> = language.main_dependencies.iter()
133 .chain(language.dev_dependencies.iter())
134 .cloned()
135 .collect();
136
137 detect_technologies_by_dependencies(&rules, &all_deps, language.confidence)
138}
139
140fn detect_technologies_by_dependencies(
142 rules: &[TechnologyRule],
143 dependencies: &[String],
144 base_confidence: f32,
145) -> Vec<DetectedTechnology> {
146 let mut technologies = Vec::new();
147
148 let tanstack_deps: Vec<_> = dependencies.iter()
150 .filter(|dep| dep.contains("tanstack") || dep.contains("vinxi"))
151 .collect();
152 if !tanstack_deps.is_empty() {
153 log::debug!("Found potential Tanstack dependencies: {:?}", tanstack_deps);
154 }
155
156 for rule in rules {
157 let mut matches = 0;
158 let total_patterns = rule.dependency_patterns.len();
159
160 if total_patterns == 0 {
161 continue;
162 }
163
164 for pattern in &rule.dependency_patterns {
165 let matching_deps: Vec<_> = dependencies.iter()
166 .filter(|dep| matches_pattern(dep, pattern))
167 .collect();
168
169 if !matching_deps.is_empty() {
170 matches += 1;
171
172 if rule.name.contains("Tanstack") {
174 log::debug!("Tanstack Start: Pattern '{}' matched dependencies: {:?}", pattern, matching_deps);
175 }
176 }
177 }
178
179 if matches > 0 {
181 let pattern_confidence = matches as f32 / total_patterns as f32;
182 let final_confidence = (rule.confidence * pattern_confidence * base_confidence).min(1.0);
183
184 if rule.name.contains("Tanstack") {
186 log::debug!("Tanstack Start detected with {} matches out of {} patterns, confidence: {:.2}",
187 matches, total_patterns, final_confidence);
188 }
189
190 technologies.push(DetectedTechnology {
191 name: rule.name.clone(),
192 version: None, category: rule.category.clone(),
194 confidence: final_confidence,
195 requires: rule.requires.clone(),
196 conflicts_with: rule.conflicts_with.clone(),
197 is_primary: rule.is_primary_indicator,
198 });
199 } else if rule.name.contains("Tanstack") {
200 log::debug!("Tanstack Start not detected - no patterns matched. Available dependencies: {:?}",
202 dependencies.iter().take(10).collect::<Vec<_>>());
203 }
204 }
205
206 technologies
207}
208
209fn resolve_technology_conflicts(technologies: Vec<DetectedTechnology>) -> Vec<DetectedTechnology> {
211 let mut resolved = Vec::new();
212 let mut name_to_tech: HashMap<String, DetectedTechnology> = HashMap::new();
213
214 for tech in technologies {
216 if let Some(existing) = name_to_tech.get(&tech.name) {
217 if tech.confidence > existing.confidence {
219 name_to_tech.insert(tech.name.clone(), tech);
220 }
221 } else {
222 name_to_tech.insert(tech.name.clone(), tech);
223 }
224 }
225
226 let all_techs: Vec<_> = name_to_tech.values().collect();
228 let mut excluded_names = std::collections::HashSet::new();
229
230 for tech in &all_techs {
231 if excluded_names.contains(&tech.name) {
232 continue;
233 }
234
235 for conflict in &tech.conflicts_with {
237 if let Some(conflicting_tech) = name_to_tech.get(conflict) {
238 if tech.confidence > conflicting_tech.confidence {
239 excluded_names.insert(conflict.clone());
240 log::info!("Excluding {} (confidence: {}) in favor of {} (confidence: {})",
241 conflict, conflicting_tech.confidence, tech.name, tech.confidence);
242 } else {
243 excluded_names.insert(tech.name.clone());
244 log::info!("Excluding {} (confidence: {}) in favor of {} (confidence: {})",
245 tech.name, tech.confidence, conflict, conflicting_tech.confidence);
246 break;
247 }
248 }
249 }
250 }
251
252 for tech in name_to_tech.into_values() {
254 if !excluded_names.contains(&tech.name) {
255 resolved.push(tech);
256 }
257 }
258
259 resolved
260}
261
262fn mark_primary_technologies(mut technologies: Vec<DetectedTechnology>) -> Vec<DetectedTechnology> {
264 let mut has_meta_framework = false;
266 for tech in &mut technologies {
267 if matches!(tech.category, TechnologyCategory::MetaFramework) {
268 tech.is_primary = true;
269 has_meta_framework = true;
270 }
271 }
272
273 if !has_meta_framework {
275 let mut best_framework: Option<usize> = None;
276 let mut best_confidence = 0.0;
277
278 for (i, tech) in technologies.iter().enumerate() {
279 if matches!(tech.category, TechnologyCategory::BackendFramework | TechnologyCategory::FrontendFramework) {
280 if tech.confidence > best_confidence {
281 best_confidence = tech.confidence;
282 best_framework = Some(i);
283 }
284 }
285 }
286
287 if let Some(index) = best_framework {
288 technologies[index].is_primary = true;
289 }
290 }
291
292 technologies
293}
294
295fn matches_pattern(dependency: &str, pattern: &str) -> bool {
297 if pattern.contains('*') {
298 let parts: Vec<&str> = pattern.split('*').collect();
300 if parts.len() == 2 {
301 dependency.starts_with(parts[0]) && dependency.ends_with(parts[1])
302 } else {
303 dependency.contains(&pattern.replace('*', ""))
304 }
305 } else {
306 dependency == pattern || dependency.contains(pattern)
307 }
308}
309
310fn detect_technologies_from_source_files(language: &DetectedLanguage, rules: &[TechnologyRule]) -> Option<Vec<DetectedTechnology>> {
312 use std::fs;
313
314 let mut detected = Vec::new();
315
316 for file_path in &language.files {
318 if let Ok(content) = fs::read_to_string(file_path) {
319 if let Some(drizzle_confidence) = analyze_drizzle_usage(&content, file_path) {
321 detected.push(DetectedTechnology {
322 name: "Drizzle ORM".to_string(),
323 version: None,
324 category: TechnologyCategory::Database,
325 confidence: drizzle_confidence,
326 requires: vec![],
327 conflicts_with: vec![],
328 is_primary: false,
329 });
330 }
331
332 if let Some(prisma_confidence) = analyze_prisma_usage(&content, file_path) {
334 detected.push(DetectedTechnology {
335 name: "Prisma".to_string(),
336 version: None,
337 category: TechnologyCategory::Database,
338 confidence: prisma_confidence,
339 requires: vec![],
340 conflicts_with: vec![],
341 is_primary: false,
342 });
343 }
344
345 if let Some(encore_confidence) = analyze_encore_usage(&content, file_path) {
347 detected.push(DetectedTechnology {
348 name: "Encore".to_string(),
349 version: None,
350 category: TechnologyCategory::BackendFramework,
351 confidence: encore_confidence,
352 requires: vec![],
353 conflicts_with: vec![],
354 is_primary: true,
355 });
356 }
357
358 if let Some(tanstack_confidence) = analyze_tanstack_start_usage(&content, file_path) {
360 detected.push(DetectedTechnology {
361 name: "Tanstack Start".to_string(),
362 version: None,
363 category: TechnologyCategory::MetaFramework,
364 confidence: tanstack_confidence,
365 requires: vec!["React".to_string()],
366 conflicts_with: vec!["Next.js".to_string(), "React Router v7".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string()],
367 is_primary: true,
368 });
369 }
370 }
371 }
372
373 if detected.is_empty() {
374 None
375 } else {
376 Some(detected)
377 }
378}
379
380fn analyze_drizzle_usage(content: &str, file_path: &std::path::Path) -> Option<f32> {
382 let file_name = file_path.file_name()?.to_string_lossy();
383 let mut confidence: f32 = 0.0;
384
385 if content.contains("drizzle-orm") {
387 confidence += 0.3;
388 }
389
390 if file_name.contains("schema") || file_name.contains("db.ts") || file_name.contains("database") {
392 if content.contains("pgTable") || content.contains("mysqlTable") || content.contains("sqliteTable") {
393 confidence += 0.4;
394 }
395 if content.contains("pgEnum") || content.contains("relations") {
396 confidence += 0.3;
397 }
398 }
399
400 if content.contains("from 'drizzle-orm/pg-core'") ||
402 content.contains("from 'drizzle-orm/mysql-core'") ||
403 content.contains("from 'drizzle-orm/sqlite-core'") {
404 confidence += 0.3;
405 }
406
407 if content.contains("db.select()") || content.contains("db.insert()") ||
409 content.contains("db.update()") || content.contains("db.delete()") {
410 confidence += 0.2;
411 }
412
413 if content.contains("drizzle(") && (content.contains("connectionString") || content.contains("postgres(")) {
415 confidence += 0.2;
416 }
417
418 if content.contains("drizzle.config") || file_name.contains("migrate") {
420 confidence += 0.2;
421 }
422
423 if content.contains(".prepare()") && content.contains("drizzle") {
425 confidence += 0.1;
426 }
427
428 if confidence > 0.0 {
429 Some(confidence.min(1.0_f32))
430 } else {
431 None
432 }
433}
434
435fn analyze_prisma_usage(content: &str, file_path: &std::path::Path) -> Option<f32> {
437 let file_name = file_path.file_name()?.to_string_lossy();
438 let mut confidence: f32 = 0.0;
439 let mut has_prisma_import = false;
440
441 if content.contains("@prisma/client") || content.contains("from '@prisma/client'") {
443 confidence += 0.4;
444 has_prisma_import = true;
445 }
446
447 if file_name == "schema.prisma" {
449 if content.contains("model ") || content.contains("generator ") || content.contains("datasource ") {
450 confidence += 0.6;
451 has_prisma_import = true;
452 }
453 }
454
455 if has_prisma_import {
457 if content.contains("new PrismaClient") || content.contains("PrismaClient()") {
459 confidence += 0.3;
460 }
461
462 if content.contains("prisma.") && (
464 content.contains(".findUnique(") ||
465 content.contains(".findFirst(") ||
466 content.contains(".upsert(") ||
467 content.contains(".$connect()") ||
468 content.contains(".$disconnect()")
469 ) {
470 confidence += 0.2;
471 }
472 }
473
474 if confidence > 0.0 && has_prisma_import {
476 Some(confidence.min(1.0_f32))
477 } else {
478 None
479 }
480}
481
482fn analyze_encore_usage(content: &str, file_path: &std::path::Path) -> Option<f32> {
484 let file_name = file_path.file_name()?.to_string_lossy();
485 let mut confidence: f32 = 0.0;
486
487 if content.contains("// Code generated by the Encore") || content.contains("DO NOT EDIT") {
489 return None;
490 }
491
492 if file_name.contains("client.ts") || file_name.contains("client.js") {
494 return None;
495 }
496
497 let mut has_service_patterns = false;
499
500 if file_name.contains("encore.service") || file_name.contains("service.ts") {
502 confidence += 0.4;
503 has_service_patterns = true;
504 }
505
506 if content.contains("encore.dev/api") && (content.contains("export") || content.contains("api.")) {
508 confidence += 0.4;
509 has_service_patterns = true;
510 }
511
512 if content.contains("SQLDatabase") && content.contains("encore.dev") {
514 confidence += 0.3;
515 has_service_patterns = true;
516 }
517
518 if content.contains("secret(") && content.contains("encore.dev/config") {
520 confidence += 0.3;
521 has_service_patterns = true;
522 }
523
524 if content.contains("Topic") && content.contains("encore.dev/pubsub") {
526 confidence += 0.3;
527 has_service_patterns = true;
528 }
529
530 if content.contains("cron") && content.contains("encore.dev") {
532 confidence += 0.2;
533 has_service_patterns = true;
534 }
535
536 if confidence > 0.0 && has_service_patterns {
538 Some(confidence.min(1.0_f32))
539 } else {
540 None
541 }
542}
543
544fn analyze_tanstack_start_usage(content: &str, file_path: &std::path::Path) -> Option<f32> {
546 let file_name = file_path.file_name()?.to_string_lossy();
547 let mut confidence: f32 = 0.0;
548 let mut has_start_patterns = false;
549
550 if file_name == "app.config.ts" || file_name == "app.config.js" {
552 if content.contains("@tanstack/react-start") || content.contains("tanstack") {
553 confidence += 0.5;
554 has_start_patterns = true;
555 }
556 }
557
558 if file_name.contains("router.") && (file_name.ends_with(".ts") || file_name.ends_with(".tsx")) {
560 if content.contains("createRouter") && content.contains("@tanstack/react-router") {
561 confidence += 0.4;
562 has_start_patterns = true;
563 }
564 if content.contains("routeTree") {
565 confidence += 0.2;
566 has_start_patterns = true;
567 }
568 }
569
570 if file_name == "ssr.tsx" || file_name == "ssr.ts" {
572 if content.contains("createStartHandler") || content.contains("@tanstack/react-start/server") {
573 confidence += 0.5;
574 has_start_patterns = true;
575 }
576 }
577
578 if file_name == "client.tsx" || file_name == "client.ts" {
580 if content.contains("StartClient") && content.contains("@tanstack/react-start") {
581 confidence += 0.5;
582 has_start_patterns = true;
583 }
584 if content.contains("hydrateRoot") && content.contains("createRouter") {
585 confidence += 0.3;
586 has_start_patterns = true;
587 }
588 }
589
590 if file_name == "__root.tsx" || file_name == "__root.ts" {
592 if content.contains("createRootRoute") && content.contains("@tanstack/react-router") {
593 confidence += 0.4;
594 has_start_patterns = true;
595 }
596 if content.contains("HeadContent") && content.contains("Scripts") {
597 confidence += 0.3;
598 has_start_patterns = true;
599 }
600 }
601
602 if file_path.to_string_lossy().contains("routes/") {
604 if content.contains("createFileRoute") && content.contains("@tanstack/react-router") {
605 confidence += 0.3;
606 has_start_patterns = true;
607 }
608 }
609
610 if content.contains("createServerFn") && content.contains("@tanstack/react-start") {
612 confidence += 0.4;
613 has_start_patterns = true;
614 }
615
616 if content.contains("from '@tanstack/react-start'") {
618 confidence += 0.3;
619 has_start_patterns = true;
620 }
621
622 if file_name == "vinxi.config.ts" || file_name == "vinxi.config.js" {
624 confidence += 0.2;
625 has_start_patterns = true;
626 }
627
628 if confidence > 0.0 && has_start_patterns {
630 Some(confidence.min(1.0_f32))
631 } else {
632 None
633 }
634}
635
636fn get_js_technology_rules() -> Vec<TechnologyRule> {
638 vec![
639 TechnologyRule {
641 name: "Next.js".to_string(),
642 category: TechnologyCategory::MetaFramework,
643 confidence: 0.95,
644 dependency_patterns: vec!["next".to_string()],
645 requires: vec!["React".to_string()],
646 conflicts_with: vec!["Tanstack Start".to_string(), "React Router v7".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string()],
647 is_primary_indicator: true,
648 alternative_names: vec!["nextjs".to_string()],
649 },
650 TechnologyRule {
651 name: "Tanstack Start".to_string(),
652 category: TechnologyCategory::MetaFramework,
653 confidence: 0.95,
654 dependency_patterns: vec!["@tanstack/react-start".to_string()],
655 requires: vec!["React".to_string()],
656 conflicts_with: vec!["Next.js".to_string(), "React Router v7".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string()],
657 is_primary_indicator: true,
658 alternative_names: vec!["tanstack-start".to_string(), "TanStack Start".to_string()],
659 },
660 TechnologyRule {
661 name: "React Router v7".to_string(),
662 category: TechnologyCategory::MetaFramework,
663 confidence: 0.95,
664 dependency_patterns: vec!["react-router".to_string(), "@remix-run/react".to_string()],
665 requires: vec!["React".to_string()],
666 conflicts_with: vec!["Next.js".to_string(), "Tanstack Start".to_string(), "SvelteKit".to_string(), "Nuxt.js".to_string()],
667 is_primary_indicator: true,
668 alternative_names: vec!["remix".to_string(), "react-router".to_string()],
669 },
670 TechnologyRule {
671 name: "SvelteKit".to_string(),
672 category: TechnologyCategory::MetaFramework,
673 confidence: 0.95,
674 dependency_patterns: vec!["@sveltejs/kit".to_string()],
675 requires: vec!["Svelte".to_string()],
676 conflicts_with: vec!["Next.js".to_string(), "Tanstack Start".to_string(), "React Router v7".to_string(), "Nuxt.js".to_string()],
677 is_primary_indicator: true,
678 alternative_names: vec!["svelte-kit".to_string()],
679 },
680 TechnologyRule {
681 name: "Nuxt.js".to_string(),
682 category: TechnologyCategory::MetaFramework,
683 confidence: 0.95,
684 dependency_patterns: vec!["nuxt".to_string(), "@nuxt/core".to_string()],
685 requires: vec!["Vue.js".to_string()],
686 conflicts_with: vec!["Next.js".to_string(), "Tanstack Start".to_string(), "React Router v7".to_string(), "SvelteKit".to_string()],
687 is_primary_indicator: true,
688 alternative_names: vec!["nuxtjs".to_string()],
689 },
690 TechnologyRule {
691 name: "Astro".to_string(),
692 category: TechnologyCategory::MetaFramework,
693 confidence: 0.95,
694 dependency_patterns: vec!["astro".to_string()],
695 requires: vec![],
696 conflicts_with: vec![],
697 is_primary_indicator: true,
698 alternative_names: vec![],
699 },
700 TechnologyRule {
701 name: "SolidStart".to_string(),
702 category: TechnologyCategory::MetaFramework,
703 confidence: 0.95,
704 dependency_patterns: vec!["solid-start".to_string()],
705 requires: vec!["SolidJS".to_string()],
706 conflicts_with: vec!["Next.js".to_string(), "Tanstack Start".to_string(), "React Router v7".to_string(), "SvelteKit".to_string()],
707 is_primary_indicator: true,
708 alternative_names: vec![],
709 },
710
711 TechnologyRule {
713 name: "Angular".to_string(),
714 category: TechnologyCategory::FrontendFramework,
715 confidence: 0.90,
716 dependency_patterns: vec!["@angular/core".to_string()],
717 requires: vec![],
718 conflicts_with: vec![],
719 is_primary_indicator: true,
720 alternative_names: vec!["angular".to_string()],
721 },
722 TechnologyRule {
723 name: "Svelte".to_string(),
724 category: TechnologyCategory::FrontendFramework,
725 confidence: 0.95,
726 dependency_patterns: vec!["svelte".to_string()],
727 requires: vec![],
728 conflicts_with: vec![],
729 is_primary_indicator: false, alternative_names: vec![],
731 },
732
733 TechnologyRule {
735 name: "React".to_string(),
736 category: TechnologyCategory::Library(LibraryType::UI),
737 confidence: 0.90,
738 dependency_patterns: vec!["react".to_string()],
739 requires: vec![],
740 conflicts_with: vec![],
741 is_primary_indicator: false, alternative_names: vec!["reactjs".to_string()],
743 },
744 TechnologyRule {
745 name: "Vue.js".to_string(),
746 category: TechnologyCategory::Library(LibraryType::UI),
747 confidence: 0.90,
748 dependency_patterns: vec!["vue".to_string()],
749 requires: vec![],
750 conflicts_with: vec![],
751 is_primary_indicator: false,
752 alternative_names: vec!["vuejs".to_string()],
753 },
754 TechnologyRule {
755 name: "SolidJS".to_string(),
756 category: TechnologyCategory::Library(LibraryType::UI),
757 confidence: 0.95,
758 dependency_patterns: vec!["solid-js".to_string()],
759 requires: vec![],
760 conflicts_with: vec![],
761 is_primary_indicator: false,
762 alternative_names: vec!["solid".to_string()],
763 },
764 TechnologyRule {
765 name: "HTMX".to_string(),
766 category: TechnologyCategory::Library(LibraryType::UI),
767 confidence: 0.95,
768 dependency_patterns: vec!["htmx.org".to_string()],
769 requires: vec![],
770 conflicts_with: vec![],
771 is_primary_indicator: false,
772 alternative_names: vec!["htmx".to_string()],
773 },
774
775 TechnologyRule {
780 name: "Express.js".to_string(),
781 category: TechnologyCategory::BackendFramework,
782 confidence: 0.95,
783 dependency_patterns: vec!["express".to_string()],
784 requires: vec![],
785 conflicts_with: vec![],
786 is_primary_indicator: true,
787 alternative_names: vec!["express".to_string()],
788 },
789 TechnologyRule {
790 name: "Fastify".to_string(),
791 category: TechnologyCategory::BackendFramework,
792 confidence: 0.95,
793 dependency_patterns: vec!["fastify".to_string()],
794 requires: vec![],
795 conflicts_with: vec![],
796 is_primary_indicator: true,
797 alternative_names: vec![],
798 },
799 TechnologyRule {
800 name: "Nest.js".to_string(),
801 category: TechnologyCategory::BackendFramework,
802 confidence: 0.95,
803 dependency_patterns: vec!["@nestjs/core".to_string()],
804 requires: vec![],
805 conflicts_with: vec![],
806 is_primary_indicator: true,
807 alternative_names: vec!["nestjs".to_string()],
808 },
809 TechnologyRule {
810 name: "Hono".to_string(),
811 category: TechnologyCategory::BackendFramework,
812 confidence: 0.95,
813 dependency_patterns: vec!["hono".to_string()],
814 requires: vec![],
815 conflicts_with: vec![],
816 is_primary_indicator: true,
817 alternative_names: vec![],
818 },
819 TechnologyRule {
820 name: "Elysia".to_string(),
821 category: TechnologyCategory::BackendFramework,
822 confidence: 0.95,
823 dependency_patterns: vec!["elysia".to_string()],
824 requires: vec![],
825 conflicts_with: vec![],
826 is_primary_indicator: true,
827 alternative_names: vec![],
828 },
829 TechnologyRule {
830 name: "Encore".to_string(),
831 category: TechnologyCategory::BackendFramework,
832 confidence: 0.95,
833 dependency_patterns: vec!["encore.dev".to_string(), "encore".to_string()],
834 requires: vec![],
835 conflicts_with: vec![],
836 is_primary_indicator: true,
837 alternative_names: vec!["encore-ts-starter".to_string()],
838 },
839
840 TechnologyRule {
842 name: "Vite".to_string(),
843 category: TechnologyCategory::BuildTool,
844 confidence: 0.80,
845 dependency_patterns: vec!["vite".to_string()],
846 requires: vec![],
847 conflicts_with: vec![],
848 is_primary_indicator: false,
849 alternative_names: vec![],
850 },
851 TechnologyRule {
852 name: "Webpack".to_string(),
853 category: TechnologyCategory::BuildTool,
854 confidence: 0.80,
855 dependency_patterns: vec!["webpack".to_string()],
856 requires: vec![],
857 conflicts_with: vec![],
858 is_primary_indicator: false,
859 alternative_names: vec![],
860 },
861
862 TechnologyRule {
864 name: "Prisma".to_string(),
865 category: TechnologyCategory::Database,
866 confidence: 0.90,
867 dependency_patterns: vec!["prisma".to_string(), "@prisma/client".to_string()],
868 requires: vec![],
869 conflicts_with: vec![],
870 is_primary_indicator: false,
871 alternative_names: vec![],
872 },
873 TechnologyRule {
874 name: "Drizzle ORM".to_string(),
875 category: TechnologyCategory::Database,
876 confidence: 0.90,
877 dependency_patterns: vec!["drizzle-orm".to_string(), "drizzle-kit".to_string()],
878 requires: vec![],
879 conflicts_with: vec![],
880 is_primary_indicator: false,
881 alternative_names: vec!["drizzle".to_string()],
882 },
883
884 TechnologyRule {
886 name: "Node.js".to_string(),
887 category: TechnologyCategory::Runtime,
888 confidence: 0.90,
889 dependency_patterns: vec!["node".to_string()], requires: vec![],
891 conflicts_with: vec![],
892 is_primary_indicator: false,
893 alternative_names: vec!["nodejs".to_string()],
894 },
895 TechnologyRule {
896 name: "Bun".to_string(),
897 category: TechnologyCategory::Runtime,
898 confidence: 0.95,
899 dependency_patterns: vec!["bun".to_string()], requires: vec![],
901 conflicts_with: vec![],
902 is_primary_indicator: false,
903 alternative_names: vec![],
904 },
905 TechnologyRule {
906 name: "Deno".to_string(),
907 category: TechnologyCategory::Runtime,
908 confidence: 0.95,
909 dependency_patterns: vec!["@deno/core".to_string(), "deno".to_string()],
910 requires: vec![],
911 conflicts_with: vec![],
912 is_primary_indicator: false,
913 alternative_names: vec![],
914 },
915
916 TechnologyRule {
918 name: "Jest".to_string(),
919 category: TechnologyCategory::Testing,
920 confidence: 0.85,
921 dependency_patterns: vec!["jest".to_string()],
922 requires: vec![],
923 conflicts_with: vec![],
924 is_primary_indicator: false,
925 alternative_names: vec![],
926 },
927 TechnologyRule {
928 name: "Vitest".to_string(),
929 category: TechnologyCategory::Testing,
930 confidence: 0.85,
931 dependency_patterns: vec!["vitest".to_string()],
932 requires: vec![],
933 conflicts_with: vec![],
934 is_primary_indicator: false,
935 alternative_names: vec![],
936 },
937 ]
938}
939
940fn get_rust_technology_rules() -> Vec<TechnologyRule> {
942 vec![
943 TechnologyRule {
945 name: "Actix Web".to_string(),
946 category: TechnologyCategory::BackendFramework,
947 confidence: 0.95,
948 dependency_patterns: vec!["actix-web".to_string()],
949 requires: vec![],
950 conflicts_with: vec![],
951 is_primary_indicator: true,
952 alternative_names: vec!["actix".to_string()],
953 },
954 TechnologyRule {
955 name: "Axum".to_string(),
956 category: TechnologyCategory::BackendFramework,
957 confidence: 0.95,
958 dependency_patterns: vec!["axum".to_string()],
959 requires: vec!["Tokio".to_string()],
960 conflicts_with: vec![],
961 is_primary_indicator: true,
962 alternative_names: vec![],
963 },
964 TechnologyRule {
965 name: "Rocket".to_string(),
966 category: TechnologyCategory::BackendFramework,
967 confidence: 0.95,
968 dependency_patterns: vec!["rocket".to_string()],
969 requires: vec![],
970 conflicts_with: vec![],
971 is_primary_indicator: true,
972 alternative_names: vec![],
973 },
974 TechnologyRule {
975 name: "Warp".to_string(),
976 category: TechnologyCategory::BackendFramework,
977 confidence: 0.95,
978 dependency_patterns: vec!["warp".to_string()],
979 requires: vec!["Tokio".to_string()],
980 conflicts_with: vec![],
981 is_primary_indicator: true,
982 alternative_names: vec![],
983 },
984 TechnologyRule {
985 name: "Loco".to_string(),
986 category: TechnologyCategory::BackendFramework,
987 confidence: 0.95,
988 dependency_patterns: vec!["loco-rs".to_string()],
989 requires: vec![],
990 conflicts_with: vec![],
991 is_primary_indicator: true,
992 alternative_names: vec!["loco".to_string()],
993 },
994 TechnologyRule {
995 name: "Poem".to_string(),
996 category: TechnologyCategory::BackendFramework,
997 confidence: 0.95,
998 dependency_patterns: vec!["poem".to_string()],
999 requires: vec![],
1000 conflicts_with: vec![],
1001 is_primary_indicator: true,
1002 alternative_names: vec![],
1003 },
1004 TechnologyRule {
1005 name: "Salvo".to_string(),
1006 category: TechnologyCategory::BackendFramework,
1007 confidence: 0.95,
1008 dependency_patterns: vec!["salvo".to_string()],
1009 requires: vec![],
1010 conflicts_with: vec![],
1011 is_primary_indicator: true,
1012 alternative_names: vec![],
1013 },
1014 TechnologyRule {
1015 name: "Trillium".to_string(),
1016 category: TechnologyCategory::BackendFramework,
1017 confidence: 0.95,
1018 dependency_patterns: vec!["trillium".to_string()],
1019 requires: vec!["Tokio".to_string()],
1020 conflicts_with: vec![],
1021 is_primary_indicator: true,
1022 alternative_names: vec![],
1023 },
1024
1025 TechnologyRule {
1027 name: "Tokio".to_string(),
1028 category: TechnologyCategory::Runtime,
1029 confidence: 0.90,
1030 dependency_patterns: vec!["tokio".to_string()],
1031 requires: vec![],
1032 conflicts_with: vec!["async-std".to_string()],
1033 is_primary_indicator: false,
1034 alternative_names: vec![],
1035 },
1036 TechnologyRule {
1037 name: "async-std".to_string(),
1038 category: TechnologyCategory::Runtime,
1039 confidence: 0.90,
1040 dependency_patterns: vec!["async-std".to_string()],
1041 requires: vec![],
1042 conflicts_with: vec!["Tokio".to_string()],
1043 is_primary_indicator: false,
1044 alternative_names: vec![],
1045 },
1046
1047 TechnologyRule {
1049 name: "SeaORM".to_string(),
1050 category: TechnologyCategory::Database,
1051 confidence: 0.90,
1052 dependency_patterns: vec!["sea-orm".to_string()],
1053 requires: vec![],
1054 conflicts_with: vec![],
1055 is_primary_indicator: false,
1056 alternative_names: vec!["sea_orm".to_string()],
1057 },
1058 TechnologyRule {
1059 name: "Diesel".to_string(),
1060 category: TechnologyCategory::Database,
1061 confidence: 0.90,
1062 dependency_patterns: vec!["diesel".to_string()],
1063 requires: vec![],
1064 conflicts_with: vec![],
1065 is_primary_indicator: false,
1066 alternative_names: vec![],
1067 },
1068 TechnologyRule {
1069 name: "SQLx".to_string(),
1070 category: TechnologyCategory::Database,
1071 confidence: 0.90,
1072 dependency_patterns: vec!["sqlx".to_string()],
1073 requires: vec![],
1074 conflicts_with: vec![],
1075 is_primary_indicator: false,
1076 alternative_names: vec![],
1077 },
1078
1079 TechnologyRule {
1081 name: "Serde".to_string(),
1082 category: TechnologyCategory::Library(LibraryType::Utility),
1083 confidence: 0.85,
1084 dependency_patterns: vec!["serde".to_string()],
1085 requires: vec![],
1086 conflicts_with: vec![],
1087 is_primary_indicator: false,
1088 alternative_names: vec![],
1089 },
1090
1091 TechnologyRule {
1093 name: "Criterion".to_string(),
1094 category: TechnologyCategory::Testing,
1095 confidence: 0.85,
1096 dependency_patterns: vec!["criterion".to_string()],
1097 requires: vec![],
1098 conflicts_with: vec![],
1099 is_primary_indicator: false,
1100 alternative_names: vec![],
1101 },
1102
1103 TechnologyRule {
1105 name: "Leptos".to_string(),
1106 category: TechnologyCategory::FrontendFramework,
1107 confidence: 0.95,
1108 dependency_patterns: vec!["leptos".to_string()],
1109 requires: vec![],
1110 conflicts_with: vec!["Yew".to_string(), "Dioxus".to_string()],
1111 is_primary_indicator: true,
1112 alternative_names: vec![],
1113 },
1114 TechnologyRule {
1115 name: "Yew".to_string(),
1116 category: TechnologyCategory::FrontendFramework,
1117 confidence: 0.95,
1118 dependency_patterns: vec!["yew".to_string()],
1119 requires: vec![],
1120 conflicts_with: vec!["Leptos".to_string(), "Dioxus".to_string()],
1121 is_primary_indicator: true,
1122 alternative_names: vec![],
1123 },
1124 TechnologyRule {
1125 name: "Dioxus".to_string(),
1126 category: TechnologyCategory::FrontendFramework,
1127 confidence: 0.95,
1128 dependency_patterns: vec!["dioxus".to_string()],
1129 requires: vec![],
1130 conflicts_with: vec!["Leptos".to_string(), "Yew".to_string()],
1131 is_primary_indicator: true,
1132 alternative_names: vec![],
1133 },
1134 TechnologyRule {
1135 name: "Tauri".to_string(),
1136 category: TechnologyCategory::Library(LibraryType::UI),
1137 confidence: 0.95,
1138 dependency_patterns: vec!["tauri".to_string()],
1139 requires: vec![],
1140 conflicts_with: vec![],
1141 is_primary_indicator: true,
1142 alternative_names: vec![],
1143 },
1144 TechnologyRule {
1145 name: "egui".to_string(),
1146 category: TechnologyCategory::Library(LibraryType::UI),
1147 confidence: 0.95,
1148 dependency_patterns: vec!["egui".to_string()],
1149 requires: vec![],
1150 conflicts_with: vec![],
1151 is_primary_indicator: false,
1152 alternative_names: vec![],
1153 },
1154 ]
1155}
1156
1157fn get_python_technology_rules() -> Vec<TechnologyRule> {
1159 vec![
1160 TechnologyRule {
1162 name: "Django".to_string(),
1163 category: TechnologyCategory::BackendFramework,
1164 confidence: 0.95,
1165 dependency_patterns: vec!["django".to_string(), "Django".to_string()],
1166 requires: vec![],
1167 conflicts_with: vec![],
1168 is_primary_indicator: true,
1169 alternative_names: vec![],
1170 },
1171 TechnologyRule {
1172 name: "Django REST Framework".to_string(),
1173 category: TechnologyCategory::BackendFramework,
1174 confidence: 0.90,
1175 dependency_patterns: vec!["djangorestframework".to_string(), "rest_framework".to_string()],
1176 requires: vec!["Django".to_string()],
1177 conflicts_with: vec![],
1178 is_primary_indicator: false,
1179 alternative_names: vec!["DRF".to_string()],
1180 },
1181
1182 TechnologyRule {
1184 name: "Flask".to_string(),
1185 category: TechnologyCategory::BackendFramework,
1186 confidence: 0.95,
1187 dependency_patterns: vec!["flask".to_string(), "Flask".to_string()],
1188 requires: vec![],
1189 conflicts_with: vec![],
1190 is_primary_indicator: true,
1191 alternative_names: vec![],
1192 },
1193 TechnologyRule {
1194 name: "FastAPI".to_string(),
1195 category: TechnologyCategory::BackendFramework,
1196 confidence: 0.95,
1197 dependency_patterns: vec!["fastapi".to_string()],
1198 requires: vec![],
1199 conflicts_with: vec![],
1200 is_primary_indicator: true,
1201 alternative_names: vec![],
1202 },
1203 TechnologyRule {
1204 name: "Starlette".to_string(),
1205 category: TechnologyCategory::BackendFramework,
1206 confidence: 0.90,
1207 dependency_patterns: vec!["starlette".to_string()],
1208 requires: vec![],
1209 conflicts_with: vec![],
1210 is_primary_indicator: true,
1211 alternative_names: vec![],
1212 },
1213 TechnologyRule {
1214 name: "Quart".to_string(),
1215 category: TechnologyCategory::BackendFramework,
1216 confidence: 0.90,
1217 dependency_patterns: vec!["quart".to_string()],
1218 requires: vec![],
1219 conflicts_with: vec![],
1220 is_primary_indicator: true,
1221 alternative_names: vec![],
1222 },
1223 TechnologyRule {
1224 name: "Sanic".to_string(),
1225 category: TechnologyCategory::BackendFramework,
1226 confidence: 0.90,
1227 dependency_patterns: vec!["sanic".to_string()],
1228 requires: vec![],
1229 conflicts_with: vec![],
1230 is_primary_indicator: true,
1231 alternative_names: vec![],
1232 },
1233 TechnologyRule {
1234 name: "Tornado".to_string(),
1235 category: TechnologyCategory::BackendFramework,
1236 confidence: 0.90,
1237 dependency_patterns: vec!["tornado".to_string()],
1238 requires: vec![],
1239 conflicts_with: vec![],
1240 is_primary_indicator: true,
1241 alternative_names: vec![],
1242 },
1243 TechnologyRule {
1244 name: "Falcon".to_string(),
1245 category: TechnologyCategory::BackendFramework,
1246 confidence: 0.90,
1247 dependency_patterns: vec!["falcon".to_string()],
1248 requires: vec![],
1249 conflicts_with: vec![],
1250 is_primary_indicator: true,
1251 alternative_names: vec![],
1252 },
1253 TechnologyRule {
1254 name: "Bottle".to_string(),
1255 category: TechnologyCategory::BackendFramework,
1256 confidence: 0.90,
1257 dependency_patterns: vec!["bottle".to_string()],
1258 requires: vec![],
1259 conflicts_with: vec![],
1260 is_primary_indicator: true,
1261 alternative_names: vec![],
1262 },
1263 TechnologyRule {
1264 name: "aiohttp".to_string(),
1265 category: TechnologyCategory::BackendFramework,
1266 confidence: 0.90,
1267 dependency_patterns: vec!["aiohttp".to_string()],
1268 requires: vec![],
1269 conflicts_with: vec![],
1270 is_primary_indicator: true,
1271 alternative_names: vec![],
1272 },
1273
1274 TechnologyRule {
1276 name: "SQLAlchemy".to_string(),
1277 category: TechnologyCategory::Database,
1278 confidence: 0.90,
1279 dependency_patterns: vec!["sqlalchemy".to_string(), "SQLAlchemy".to_string()],
1280 requires: vec![],
1281 conflicts_with: vec![],
1282 is_primary_indicator: false,
1283 alternative_names: vec![],
1284 },
1285 TechnologyRule {
1286 name: "Peewee".to_string(),
1287 category: TechnologyCategory::Database,
1288 confidence: 0.90,
1289 dependency_patterns: vec!["peewee".to_string()],
1290 requires: vec![],
1291 conflicts_with: vec![],
1292 is_primary_indicator: false,
1293 alternative_names: vec![],
1294 },
1295 TechnologyRule {
1296 name: "Tortoise ORM".to_string(),
1297 category: TechnologyCategory::Database,
1298 confidence: 0.90,
1299 dependency_patterns: vec!["tortoise-orm".to_string()],
1300 requires: vec![],
1301 conflicts_with: vec![],
1302 is_primary_indicator: false,
1303 alternative_names: vec!["tortoise".to_string()],
1304 },
1305 TechnologyRule {
1306 name: "Django ORM".to_string(),
1307 category: TechnologyCategory::Database,
1308 confidence: 0.95,
1309 dependency_patterns: vec!["django.db".to_string()],
1310 requires: vec!["Django".to_string()],
1311 conflicts_with: vec![],
1312 is_primary_indicator: false,
1313 alternative_names: vec![],
1314 },
1315
1316 TechnologyRule {
1318 name: "asyncio".to_string(),
1319 category: TechnologyCategory::Runtime,
1320 confidence: 0.85,
1321 dependency_patterns: vec!["asyncio".to_string()],
1322 requires: vec![],
1323 conflicts_with: vec![],
1324 is_primary_indicator: false,
1325 alternative_names: vec![],
1326 },
1327
1328 TechnologyRule {
1330 name: "NumPy".to_string(),
1331 category: TechnologyCategory::Library(LibraryType::Utility),
1332 confidence: 0.85,
1333 dependency_patterns: vec!["numpy".to_string()],
1334 requires: vec![],
1335 conflicts_with: vec![],
1336 is_primary_indicator: false,
1337 alternative_names: vec![],
1338 },
1339 TechnologyRule {
1340 name: "Pandas".to_string(),
1341 category: TechnologyCategory::Library(LibraryType::Utility),
1342 confidence: 0.85,
1343 dependency_patterns: vec!["pandas".to_string()],
1344 requires: vec![],
1345 conflicts_with: vec![],
1346 is_primary_indicator: false,
1347 alternative_names: vec![],
1348 },
1349 TechnologyRule {
1350 name: "Scikit-learn".to_string(),
1351 category: TechnologyCategory::Library(LibraryType::Utility),
1352 confidence: 0.85,
1353 dependency_patterns: vec!["scikit-learn".to_string(), "sklearn".to_string()],
1354 requires: vec![],
1355 conflicts_with: vec![],
1356 is_primary_indicator: false,
1357 alternative_names: vec!["sklearn".to_string()],
1358 },
1359 TechnologyRule {
1360 name: "TensorFlow".to_string(),
1361 category: TechnologyCategory::Library(LibraryType::Utility),
1362 confidence: 0.90,
1363 dependency_patterns: vec!["tensorflow".to_string()],
1364 requires: vec![],
1365 conflicts_with: vec![],
1366 is_primary_indicator: false,
1367 alternative_names: vec![],
1368 },
1369 TechnologyRule {
1370 name: "PyTorch".to_string(),
1371 category: TechnologyCategory::Library(LibraryType::Utility),
1372 confidence: 0.90,
1373 dependency_patterns: vec!["torch".to_string(), "pytorch".to_string()],
1374 requires: vec![],
1375 conflicts_with: vec![],
1376 is_primary_indicator: false,
1377 alternative_names: vec!["torch".to_string()],
1378 },
1379
1380 TechnologyRule {
1382 name: "Celery".to_string(),
1383 category: TechnologyCategory::Library(LibraryType::Utility),
1384 confidence: 0.90,
1385 dependency_patterns: vec!["celery".to_string()],
1386 requires: vec![],
1387 conflicts_with: vec![],
1388 is_primary_indicator: false,
1389 alternative_names: vec![],
1390 },
1391
1392 TechnologyRule {
1394 name: "pytest".to_string(),
1395 category: TechnologyCategory::Testing,
1396 confidence: 0.85,
1397 dependency_patterns: vec!["pytest".to_string()],
1398 requires: vec![],
1399 conflicts_with: vec![],
1400 is_primary_indicator: false,
1401 alternative_names: vec![],
1402 },
1403 TechnologyRule {
1404 name: "unittest".to_string(),
1405 category: TechnologyCategory::Testing,
1406 confidence: 0.80,
1407 dependency_patterns: vec!["unittest".to_string()],
1408 requires: vec![],
1409 conflicts_with: vec![],
1410 is_primary_indicator: false,
1411 alternative_names: vec![],
1412 },
1413
1414 TechnologyRule {
1416 name: "Gunicorn".to_string(),
1417 category: TechnologyCategory::Runtime,
1418 confidence: 0.85,
1419 dependency_patterns: vec!["gunicorn".to_string()],
1420 requires: vec![],
1421 conflicts_with: vec![],
1422 is_primary_indicator: false,
1423 alternative_names: vec![],
1424 },
1425 TechnologyRule {
1426 name: "Uvicorn".to_string(),
1427 category: TechnologyCategory::Runtime,
1428 confidence: 0.85,
1429 dependency_patterns: vec!["uvicorn".to_string()],
1430 requires: vec![],
1431 conflicts_with: vec![],
1432 is_primary_indicator: false,
1433 alternative_names: vec![],
1434 },
1435 ]
1436}
1437
1438fn get_go_technology_rules() -> Vec<TechnologyRule> {
1440 vec![
1441 TechnologyRule {
1443 name: "Gin".to_string(),
1444 category: TechnologyCategory::BackendFramework,
1445 confidence: 0.95,
1446 dependency_patterns: vec!["github.com/gin-gonic/gin".to_string(), "gin-gonic".to_string()],
1447 requires: vec![],
1448 conflicts_with: vec![],
1449 is_primary_indicator: true,
1450 alternative_names: vec!["gin-gonic".to_string()],
1451 },
1452 TechnologyRule {
1453 name: "Echo".to_string(),
1454 category: TechnologyCategory::BackendFramework,
1455 confidence: 0.95,
1456 dependency_patterns: vec!["github.com/labstack/echo".to_string(), "labstack/echo".to_string()],
1457 requires: vec![],
1458 conflicts_with: vec![],
1459 is_primary_indicator: true,
1460 alternative_names: vec!["labstack/echo".to_string()],
1461 },
1462 TechnologyRule {
1463 name: "Fiber".to_string(),
1464 category: TechnologyCategory::BackendFramework,
1465 confidence: 0.95,
1466 dependency_patterns: vec!["github.com/gofiber/fiber".to_string(), "gofiber".to_string()],
1467 requires: vec![],
1468 conflicts_with: vec![],
1469 is_primary_indicator: true,
1470 alternative_names: vec!["gofiber".to_string()],
1471 },
1472 TechnologyRule {
1473 name: "Beego".to_string(),
1474 category: TechnologyCategory::BackendFramework,
1475 confidence: 0.95,
1476 dependency_patterns: vec!["github.com/beego/beego".to_string(), "beego".to_string()],
1477 requires: vec![],
1478 conflicts_with: vec![],
1479 is_primary_indicator: true,
1480 alternative_names: vec![],
1481 },
1482 TechnologyRule {
1483 name: "Chi".to_string(),
1484 category: TechnologyCategory::BackendFramework,
1485 confidence: 0.90,
1486 dependency_patterns: vec!["github.com/go-chi/chi".to_string(), "go-chi".to_string()],
1487 requires: vec![],
1488 conflicts_with: vec![],
1489 is_primary_indicator: true,
1490 alternative_names: vec!["go-chi".to_string()],
1491 },
1492 TechnologyRule {
1493 name: "Gorilla Mux".to_string(),
1494 category: TechnologyCategory::BackendFramework,
1495 confidence: 0.90,
1496 dependency_patterns: vec!["github.com/gorilla/mux".to_string(), "gorilla/mux".to_string()],
1497 requires: vec![],
1498 conflicts_with: vec![],
1499 is_primary_indicator: true,
1500 alternative_names: vec!["mux".to_string(), "gorilla".to_string()],
1501 },
1502 TechnologyRule {
1503 name: "Revel".to_string(),
1504 category: TechnologyCategory::BackendFramework,
1505 confidence: 0.90,
1506 dependency_patterns: vec!["github.com/revel/revel".to_string(), "revel".to_string()],
1507 requires: vec![],
1508 conflicts_with: vec![],
1509 is_primary_indicator: true,
1510 alternative_names: vec![],
1511 },
1512 TechnologyRule {
1513 name: "Buffalo".to_string(),
1514 category: TechnologyCategory::BackendFramework,
1515 confidence: 0.90,
1516 dependency_patterns: vec!["github.com/gobuffalo/buffalo".to_string(), "gobuffalo".to_string()],
1517 requires: vec![],
1518 conflicts_with: vec![],
1519 is_primary_indicator: true,
1520 alternative_names: vec!["gobuffalo".to_string()],
1521 },
1522 TechnologyRule {
1523 name: "Iris".to_string(),
1524 category: TechnologyCategory::BackendFramework,
1525 confidence: 0.90,
1526 dependency_patterns: vec!["github.com/kataras/iris".to_string(), "kataras/iris".to_string()],
1527 requires: vec![],
1528 conflicts_with: vec![],
1529 is_primary_indicator: true,
1530 alternative_names: vec![],
1531 },
1532 TechnologyRule {
1533 name: "FastHTTP".to_string(),
1534 category: TechnologyCategory::BackendFramework,
1535 confidence: 0.95,
1536 dependency_patterns: vec!["github.com/valyala/fasthttp".to_string(), "fasthttp".to_string()],
1537 requires: vec![],
1538 conflicts_with: vec![],
1539 is_primary_indicator: true,
1540 alternative_names: vec!["valyala/fasthttp".to_string()],
1541 },
1542 TechnologyRule {
1543 name: "Hertz".to_string(),
1544 category: TechnologyCategory::BackendFramework,
1545 confidence: 0.95,
1546 dependency_patterns: vec!["github.com/cloudwego/hertz".to_string(), "cloudwego/hertz".to_string()],
1547 requires: vec![],
1548 conflicts_with: vec![],
1549 is_primary_indicator: true,
1550 alternative_names: vec!["cloudwego".to_string()],
1551 },
1552
1553 TechnologyRule {
1555 name: "GORM".to_string(),
1556 category: TechnologyCategory::Database,
1557 confidence: 0.90,
1558 dependency_patterns: vec!["gorm.io/gorm".to_string(), "gorm".to_string()],
1559 requires: vec![],
1560 conflicts_with: vec![],
1561 is_primary_indicator: false,
1562 alternative_names: vec![],
1563 },
1564 TechnologyRule {
1565 name: "Ent".to_string(),
1566 category: TechnologyCategory::Database,
1567 confidence: 0.90,
1568 dependency_patterns: vec!["entgo.io/ent".to_string(), "facebook/ent".to_string()],
1569 requires: vec![],
1570 conflicts_with: vec![],
1571 is_primary_indicator: false,
1572 alternative_names: vec!["entgo".to_string()],
1573 },
1574 TechnologyRule {
1575 name: "Xorm".to_string(),
1576 category: TechnologyCategory::Database,
1577 confidence: 0.85,
1578 dependency_patterns: vec!["xorm.io/xorm".to_string(), "xorm".to_string()],
1579 requires: vec![],
1580 conflicts_with: vec![],
1581 is_primary_indicator: false,
1582 alternative_names: vec![],
1583 },
1584
1585 TechnologyRule {
1587 name: "Go Kit".to_string(),
1588 category: TechnologyCategory::Library(LibraryType::Utility),
1589 confidence: 0.90,
1590 dependency_patterns: vec!["github.com/go-kit/kit".to_string(), "go-kit".to_string()],
1591 requires: vec![],
1592 conflicts_with: vec![],
1593 is_primary_indicator: false,
1594 alternative_names: vec!["kit".to_string()],
1595 },
1596 TechnologyRule {
1597 name: "Kratos".to_string(),
1598 category: TechnologyCategory::BackendFramework,
1599 confidence: 0.90,
1600 dependency_patterns: vec!["github.com/go-kratos/kratos".to_string(), "go-kratos".to_string()],
1601 requires: vec![],
1602 conflicts_with: vec![],
1603 is_primary_indicator: true,
1604 alternative_names: vec!["go-kratos".to_string()],
1605 },
1606
1607 TechnologyRule {
1609 name: "Sarama".to_string(),
1610 category: TechnologyCategory::Library(LibraryType::Utility),
1611 confidence: 0.85,
1612 dependency_patterns: vec!["github.com/shopify/sarama".to_string(), "sarama".to_string()],
1613 requires: vec![],
1614 conflicts_with: vec![],
1615 is_primary_indicator: false,
1616 alternative_names: vec!["shopify/sarama".to_string()],
1617 },
1618
1619 TechnologyRule {
1621 name: "Testify".to_string(),
1622 category: TechnologyCategory::Testing,
1623 confidence: 0.85,
1624 dependency_patterns: vec!["github.com/stretchr/testify".to_string(), "testify".to_string()],
1625 requires: vec![],
1626 conflicts_with: vec![],
1627 is_primary_indicator: false,
1628 alternative_names: vec!["stretchr/testify".to_string()],
1629 },
1630 TechnologyRule {
1631 name: "Ginkgo".to_string(),
1632 category: TechnologyCategory::Testing,
1633 confidence: 0.85,
1634 dependency_patterns: vec!["github.com/onsi/ginkgo".to_string(), "ginkgo".to_string()],
1635 requires: vec![],
1636 conflicts_with: vec![],
1637 is_primary_indicator: false,
1638 alternative_names: vec!["onsi/ginkgo".to_string()],
1639 },
1640
1641 TechnologyRule {
1643 name: "Cobra".to_string(),
1644 category: TechnologyCategory::Library(LibraryType::Utility),
1645 confidence: 0.85,
1646 dependency_patterns: vec!["github.com/spf13/cobra".to_string(), "cobra".to_string()],
1647 requires: vec![],
1648 conflicts_with: vec![],
1649 is_primary_indicator: false,
1650 alternative_names: vec!["spf13/cobra".to_string()],
1651 },
1652
1653 TechnologyRule {
1655 name: "Viper".to_string(),
1656 category: TechnologyCategory::Library(LibraryType::Utility),
1657 confidence: 0.80,
1658 dependency_patterns: vec!["github.com/spf13/viper".to_string(), "viper".to_string()],
1659 requires: vec![],
1660 conflicts_with: vec![],
1661 is_primary_indicator: false,
1662 alternative_names: vec!["spf13/viper".to_string()],
1663 },
1664 ]
1665}
1666
1667fn get_jvm_technology_rules() -> Vec<TechnologyRule> {
1669 vec![
1670 TechnologyRule {
1672 name: "Spring Boot".to_string(),
1673 category: TechnologyCategory::BackendFramework,
1674 confidence: 0.95,
1675 dependency_patterns: vec!["spring-boot".to_string(), "org.springframework.boot".to_string()],
1676 requires: vec![],
1677 conflicts_with: vec![],
1678 is_primary_indicator: true,
1679 alternative_names: vec!["spring".to_string()],
1680 },
1681 TechnologyRule {
1682 name: "Spring Framework".to_string(),
1683 category: TechnologyCategory::BackendFramework,
1684 confidence: 0.90,
1685 dependency_patterns: vec!["spring-context".to_string(), "org.springframework".to_string()],
1686 requires: vec![],
1687 conflicts_with: vec![],
1688 is_primary_indicator: true,
1689 alternative_names: vec!["spring".to_string()],
1690 },
1691 TechnologyRule {
1692 name: "Spring Data".to_string(),
1693 category: TechnologyCategory::Database,
1694 confidence: 0.90,
1695 dependency_patterns: vec!["spring-data".to_string(), "org.springframework.data".to_string()],
1696 requires: vec!["Spring Framework".to_string()],
1697 conflicts_with: vec![],
1698 is_primary_indicator: false,
1699 alternative_names: vec![],
1700 },
1701 TechnologyRule {
1702 name: "Spring Security".to_string(),
1703 category: TechnologyCategory::Library(LibraryType::Utility),
1704 confidence: 0.90,
1705 dependency_patterns: vec!["spring-security".to_string(), "org.springframework.security".to_string()],
1706 requires: vec!["Spring Framework".to_string()],
1707 conflicts_with: vec![],
1708 is_primary_indicator: false,
1709 alternative_names: vec![],
1710 },
1711 TechnologyRule {
1712 name: "Spring Cloud".to_string(),
1713 category: TechnologyCategory::Library(LibraryType::Utility),
1714 confidence: 0.90,
1715 dependency_patterns: vec!["spring-cloud".to_string(), "org.springframework.cloud".to_string()],
1716 requires: vec!["Spring Boot".to_string()],
1717 conflicts_with: vec![],
1718 is_primary_indicator: false,
1719 alternative_names: vec![],
1720 },
1721
1722 TechnologyRule {
1724 name: "Quarkus".to_string(),
1725 category: TechnologyCategory::BackendFramework,
1726 confidence: 0.95,
1727 dependency_patterns: vec!["quarkus".to_string(), "io.quarkus".to_string()],
1728 requires: vec![],
1729 conflicts_with: vec![],
1730 is_primary_indicator: true,
1731 alternative_names: vec![],
1732 },
1733 TechnologyRule {
1734 name: "Micronaut".to_string(),
1735 category: TechnologyCategory::BackendFramework,
1736 confidence: 0.95,
1737 dependency_patterns: vec!["micronaut".to_string(), "io.micronaut".to_string()],
1738 requires: vec![],
1739 conflicts_with: vec![],
1740 is_primary_indicator: true,
1741 alternative_names: vec![],
1742 },
1743 TechnologyRule {
1744 name: "Helidon".to_string(),
1745 category: TechnologyCategory::BackendFramework,
1746 confidence: 0.95,
1747 dependency_patterns: vec!["helidon".to_string(), "io.helidon".to_string()],
1748 requires: vec![],
1749 conflicts_with: vec![],
1750 is_primary_indicator: true,
1751 alternative_names: vec![],
1752 },
1753 TechnologyRule {
1754 name: "Vert.x".to_string(),
1755 category: TechnologyCategory::BackendFramework,
1756 confidence: 0.95,
1757 dependency_patterns: vec!["vertx".to_string(), "io.vertx".to_string()],
1758 requires: vec![],
1759 conflicts_with: vec![],
1760 is_primary_indicator: true,
1761 alternative_names: vec!["eclipse vert.x".to_string(), "vertx".to_string()],
1762 },
1763
1764 TechnologyRule {
1766 name: "Struts".to_string(),
1767 category: TechnologyCategory::BackendFramework,
1768 confidence: 0.90,
1769 dependency_patterns: vec!["struts".to_string(), "org.apache.struts".to_string()],
1770 requires: vec![],
1771 conflicts_with: vec![],
1772 is_primary_indicator: true,
1773 alternative_names: vec!["apache struts".to_string()],
1774 },
1775 TechnologyRule {
1776 name: "JSF".to_string(),
1777 category: TechnologyCategory::BackendFramework,
1778 confidence: 0.85,
1779 dependency_patterns: vec!["jsf".to_string(), "javax.faces".to_string(), "jakarta.faces".to_string()],
1780 requires: vec![],
1781 conflicts_with: vec![],
1782 is_primary_indicator: true,
1783 alternative_names: vec!["javaserver faces".to_string()],
1784 },
1785
1786 TechnologyRule {
1788 name: "Dropwizard".to_string(),
1789 category: TechnologyCategory::BackendFramework,
1790 confidence: 0.90,
1791 dependency_patterns: vec!["dropwizard".to_string(), "io.dropwizard".to_string()],
1792 requires: vec![],
1793 conflicts_with: vec![],
1794 is_primary_indicator: true,
1795 alternative_names: vec![],
1796 },
1797 TechnologyRule {
1798 name: "Spark Java".to_string(),
1799 category: TechnologyCategory::BackendFramework,
1800 confidence: 0.90,
1801 dependency_patterns: vec!["spark-core".to_string(), "com.sparkjava".to_string()],
1802 requires: vec![],
1803 conflicts_with: vec![],
1804 is_primary_indicator: true,
1805 alternative_names: vec!["spark".to_string()],
1806 },
1807 TechnologyRule {
1808 name: "Javalin".to_string(),
1809 category: TechnologyCategory::BackendFramework,
1810 confidence: 0.90,
1811 dependency_patterns: vec!["javalin".to_string(), "io.javalin".to_string()],
1812 requires: vec![],
1813 conflicts_with: vec![],
1814 is_primary_indicator: true,
1815 alternative_names: vec![],
1816 },
1817 TechnologyRule {
1818 name: "Ratpack".to_string(),
1819 category: TechnologyCategory::BackendFramework,
1820 confidence: 0.90,
1821 dependency_patterns: vec!["ratpack".to_string(), "io.ratpack".to_string()],
1822 requires: vec![],
1823 conflicts_with: vec![],
1824 is_primary_indicator: true,
1825 alternative_names: vec![],
1826 },
1827
1828 TechnologyRule {
1830 name: "Play Framework".to_string(),
1831 category: TechnologyCategory::BackendFramework,
1832 confidence: 0.95,
1833 dependency_patterns: vec!["play".to_string(), "com.typesafe.play".to_string()],
1834 requires: vec![],
1835 conflicts_with: vec![],
1836 is_primary_indicator: true,
1837 alternative_names: vec!["play".to_string()],
1838 },
1839
1840 TechnologyRule {
1842 name: "Hibernate".to_string(),
1843 category: TechnologyCategory::Database,
1844 confidence: 0.90,
1845 dependency_patterns: vec!["hibernate".to_string(), "org.hibernate".to_string()],
1846 requires: vec![],
1847 conflicts_with: vec![],
1848 is_primary_indicator: false,
1849 alternative_names: vec!["hibernate orm".to_string()],
1850 },
1851 TechnologyRule {
1852 name: "MyBatis".to_string(),
1853 category: TechnologyCategory::Database,
1854 confidence: 0.90,
1855 dependency_patterns: vec!["mybatis".to_string(), "org.mybatis".to_string()],
1856 requires: vec![],
1857 conflicts_with: vec![],
1858 is_primary_indicator: false,
1859 alternative_names: vec![],
1860 },
1861 TechnologyRule {
1862 name: "JOOQ".to_string(),
1863 category: TechnologyCategory::Database,
1864 confidence: 0.85,
1865 dependency_patterns: vec!["jooq".to_string(), "org.jooq".to_string()],
1866 requires: vec![],
1867 conflicts_with: vec![],
1868 is_primary_indicator: false,
1869 alternative_names: vec![],
1870 },
1871
1872 TechnologyRule {
1874 name: "Jakarta EE".to_string(),
1875 category: TechnologyCategory::BackendFramework,
1876 confidence: 0.90,
1877 dependency_patterns: vec!["jakarta.".to_string(), "jakarta-ee".to_string()],
1878 requires: vec![],
1879 conflicts_with: vec![],
1880 is_primary_indicator: true,
1881 alternative_names: vec!["java ee".to_string()],
1882 },
1883
1884 TechnologyRule {
1886 name: "Maven".to_string(),
1887 category: TechnologyCategory::BuildTool,
1888 confidence: 0.80,
1889 dependency_patterns: vec!["maven".to_string(), "org.apache.maven".to_string()],
1890 requires: vec![],
1891 conflicts_with: vec![],
1892 is_primary_indicator: false,
1893 alternative_names: vec!["apache maven".to_string()],
1894 },
1895 TechnologyRule {
1896 name: "Gradle".to_string(),
1897 category: TechnologyCategory::BuildTool,
1898 confidence: 0.80,
1899 dependency_patterns: vec!["gradle".to_string(), "org.gradle".to_string()],
1900 requires: vec![],
1901 conflicts_with: vec![],
1902 is_primary_indicator: false,
1903 alternative_names: vec![],
1904 },
1905
1906 TechnologyRule {
1908 name: "JUnit".to_string(),
1909 category: TechnologyCategory::Testing,
1910 confidence: 0.85,
1911 dependency_patterns: vec!["junit".to_string(), "org.junit".to_string()],
1912 requires: vec![],
1913 conflicts_with: vec![],
1914 is_primary_indicator: false,
1915 alternative_names: vec![],
1916 },
1917 TechnologyRule {
1918 name: "TestNG".to_string(),
1919 category: TechnologyCategory::Testing,
1920 confidence: 0.85,
1921 dependency_patterns: vec!["testng".to_string(), "org.testng".to_string()],
1922 requires: vec![],
1923 conflicts_with: vec![],
1924 is_primary_indicator: false,
1925 alternative_names: vec![],
1926 },
1927 TechnologyRule {
1928 name: "Mockito".to_string(),
1929 category: TechnologyCategory::Testing,
1930 confidence: 0.80,
1931 dependency_patterns: vec!["mockito".to_string(), "org.mockito".to_string()],
1932 requires: vec![],
1933 conflicts_with: vec![],
1934 is_primary_indicator: false,
1935 alternative_names: vec![],
1936 },
1937
1938 TechnologyRule {
1940 name: "Reactor".to_string(),
1941 category: TechnologyCategory::Library(LibraryType::Utility),
1942 confidence: 0.85,
1943 dependency_patterns: vec!["reactor".to_string(), "io.projectreactor".to_string()],
1944 requires: vec![],
1945 conflicts_with: vec![],
1946 is_primary_indicator: false,
1947 alternative_names: vec!["project reactor".to_string()],
1948 },
1949 TechnologyRule {
1950 name: "RxJava".to_string(),
1951 category: TechnologyCategory::Library(LibraryType::Utility),
1952 confidence: 0.85,
1953 dependency_patterns: vec!["rxjava".to_string(), "io.reactivex".to_string()],
1954 requires: vec![],
1955 conflicts_with: vec![],
1956 is_primary_indicator: false,
1957 alternative_names: vec![],
1958 },
1959
1960 TechnologyRule {
1962 name: "Ktor".to_string(),
1963 category: TechnologyCategory::BackendFramework,
1964 confidence: 0.95,
1965 dependency_patterns: vec!["ktor".to_string(), "io.ktor".to_string()],
1966 requires: vec![],
1967 conflicts_with: vec![],
1968 is_primary_indicator: true,
1969 alternative_names: vec![],
1970 },
1971 ]
1972}
1973
1974#[cfg(test)]
1975mod tests {
1976 use super::*;
1977 use std::path::PathBuf;
1978
1979 #[test]
1980 fn test_rust_actix_web_detection() {
1981 let language = DetectedLanguage {
1982 name: "Rust".to_string(),
1983 version: Some("1.70.0".to_string()),
1984 confidence: 0.9,
1985 files: vec![PathBuf::from("src/main.rs")],
1986 main_dependencies: vec!["actix-web".to_string(), "tokio".to_string()],
1987 dev_dependencies: vec!["assert_cmd".to_string()],
1988 package_manager: Some("cargo".to_string()),
1989 };
1990
1991 let technologies = detect_rust_technologies(&language);
1992
1993 assert!(technologies.is_empty() || technologies.iter().any(|f| f.name.contains("Actix")));
1995 }
1996
1997 #[test]
1998 fn test_framework_pattern_matching() {
1999 assert!(matches_pattern("express", "express"));
2000 assert!(matches_pattern("@nestjs/core", "@nestjs/*"));
2001 assert!(matches_pattern("spring-boot-starter-web", "spring-boot-starter*"));
2002 assert!(!matches_pattern("react", "vue"));
2003 }
2004
2005 #[test]
2006 fn test_framework_categories() {
2007 let rules = get_js_technology_rules();
2008
2009 let express_rule = rules.iter().find(|r| r.name == "Express.js").unwrap();
2010 assert!(matches!(express_rule.category, TechnologyCategory::BackendFramework));
2011
2012 let jest_rule = rules.iter().find(|r| r.name == "Jest").unwrap();
2013 assert!(matches!(jest_rule.category, TechnologyCategory::Testing));
2014
2015 let drizzle_rule = rules.iter().find(|r| r.name == "Drizzle ORM").unwrap();
2017 assert!(matches!(drizzle_rule.category, TechnologyCategory::Database));
2018
2019 let svelte_rule = rules.iter().find(|r| r.name == "Svelte").unwrap();
2020 assert!(matches!(svelte_rule.category, TechnologyCategory::FrontendFramework));
2021
2022 let encore_rule = rules.iter().find(|r| r.name == "Encore").unwrap();
2023 assert!(matches!(encore_rule.category, TechnologyCategory::BackendFramework));
2024
2025 let hono_rule = rules.iter().find(|r| r.name == "Hono").unwrap();
2026 assert!(matches!(hono_rule.category, TechnologyCategory::BackendFramework));
2027 }
2028
2029 #[test]
2030 fn test_modern_framework_detection() {
2031 let rules = get_js_technology_rules();
2032
2033 let framework_names: Vec<&str> = rules.iter().map(|r| r.name.as_str()).collect();
2035
2036 assert!(framework_names.contains(&"Svelte"));
2037 assert!(framework_names.contains(&"SvelteKit"));
2038 assert!(framework_names.contains(&"Astro"));
2039 assert!(framework_names.contains(&"SolidJS"));
2040 assert!(framework_names.contains(&"Encore"));
2041 assert!(framework_names.contains(&"Hono"));
2042 assert!(framework_names.contains(&"Elysia"));
2043 assert!(framework_names.contains(&"Drizzle ORM"));
2044 assert!(framework_names.contains(&"React Router v7"));
2045 assert!(framework_names.contains(&"Tanstack Start"));
2046 }
2047
2048 #[test]
2049 fn test_tanstack_start_detection() {
2050 let language = DetectedLanguage {
2051 name: "TypeScript".to_string(),
2052 version: Some("5.0.0".to_string()),
2053 confidence: 0.925,
2054 files: vec![PathBuf::from("src/routes/index.tsx")],
2055 main_dependencies: vec![
2056 "@tanstack/react-start".to_string(),
2057 "@tanstack/react-router".to_string(),
2058 "react".to_string(),
2059 "react-dom".to_string(),
2060 ],
2061 dev_dependencies: vec![
2062 "vinxi".to_string(),
2063 "typescript".to_string(),
2064 ],
2065 package_manager: Some("npm".to_string()),
2066 };
2067
2068 let technologies = detect_js_technologies(&language);
2069
2070 let tanstack_start = technologies.iter().find(|t| t.name == "Tanstack Start");
2072 assert!(tanstack_start.is_some(), "Tanstack Start should be detected");
2073
2074 let tanstack_start = tanstack_start.unwrap();
2075 assert!(matches!(tanstack_start.category, TechnologyCategory::MetaFramework));
2076 assert!(tanstack_start.is_primary, "Tanstack Start should be marked as primary");
2077 assert!(tanstack_start.confidence > 0.8, "Tanstack Start detection confidence should be high");
2078
2079 let react = technologies.iter().find(|t| t.name == "React");
2081 assert!(react.is_some(), "React should be detected as a dependency");
2082 }
2083
2084 #[test]
2085 fn test_comprehensive_python_detection() {
2086 let language = DetectedLanguage {
2087 name: "Python".to_string(),
2088 version: Some("3.11.0".to_string()),
2089 confidence: 0.95,
2090 files: vec![PathBuf::from("app.py")],
2091 main_dependencies: vec![
2092 "fastapi".to_string(),
2093 "sqlalchemy".to_string(),
2094 "pandas".to_string(),
2095 "torch".to_string(),
2096 "gunicorn".to_string(),
2097 ],
2098 dev_dependencies: vec!["pytest".to_string()],
2099 package_manager: Some("pip".to_string()),
2100 };
2101
2102 let technologies = detect_python_technologies(&language);
2103 let tech_names: Vec<&str> = technologies.iter().map(|t| t.name.as_str()).collect();
2104
2105 assert!(tech_names.contains(&"FastAPI"));
2107 assert!(tech_names.contains(&"SQLAlchemy"));
2108 assert!(tech_names.contains(&"Pandas"));
2109 assert!(tech_names.contains(&"PyTorch"));
2110 assert!(tech_names.contains(&"Gunicorn"));
2111 assert!(tech_names.contains(&"pytest"));
2112
2113 let fastapi = technologies.iter().find(|t| t.name == "FastAPI").unwrap();
2115 assert!(fastapi.is_primary);
2116 assert!(matches!(fastapi.category, TechnologyCategory::BackendFramework));
2117 }
2118
2119 #[test]
2120 fn test_comprehensive_go_detection() {
2121 let language = DetectedLanguage {
2122 name: "Go".to_string(),
2123 version: Some("1.21.0".to_string()),
2124 confidence: 0.95,
2125 files: vec![PathBuf::from("main.go")],
2126 main_dependencies: vec![
2127 "github.com/gin-gonic/gin".to_string(),
2128 "gorm.io/gorm".to_string(),
2129 "github.com/spf13/cobra".to_string(),
2130 "github.com/spf13/viper".to_string(),
2131 ],
2132 dev_dependencies: vec!["github.com/stretchr/testify".to_string()],
2133 package_manager: Some("go mod".to_string()),
2134 };
2135
2136 let technologies = detect_go_technologies(&language);
2137 let tech_names: Vec<&str> = technologies.iter().map(|t| t.name.as_str()).collect();
2138
2139 assert!(tech_names.contains(&"Gin"));
2141 assert!(tech_names.contains(&"GORM"));
2142 assert!(tech_names.contains(&"Cobra"));
2143 assert!(tech_names.contains(&"Viper"));
2144 assert!(tech_names.contains(&"Testify"));
2145
2146 let gin = technologies.iter().find(|t| t.name == "Gin").unwrap();
2148 assert!(gin.is_primary);
2149 assert!(matches!(gin.category, TechnologyCategory::BackendFramework));
2150 }
2151
2152 #[test]
2153 fn test_comprehensive_jvm_detection() {
2154 let language = DetectedLanguage {
2155 name: "Java".to_string(),
2156 version: Some("17.0.0".to_string()),
2157 confidence: 0.95,
2158 files: vec![PathBuf::from("src/main/java/Application.java")],
2159 main_dependencies: vec![
2160 "spring-boot".to_string(),
2161 "spring-data".to_string(),
2162 "hibernate".to_string(),
2163 "io.projectreactor".to_string(),
2164 ],
2165 dev_dependencies: vec!["junit".to_string(), "mockito".to_string()],
2166 package_manager: Some("maven".to_string()),
2167 };
2168
2169 let technologies = detect_jvm_technologies(&language);
2170 let tech_names: Vec<&str> = technologies.iter().map(|t| t.name.as_str()).collect();
2171
2172 assert!(tech_names.contains(&"Spring Boot"));
2174 assert!(tech_names.contains(&"Spring Data"));
2175 assert!(tech_names.contains(&"Hibernate"));
2176 assert!(tech_names.contains(&"Reactor"));
2177 assert!(tech_names.contains(&"JUnit"));
2178 assert!(tech_names.contains(&"Mockito"));
2179
2180 let spring_boot = technologies.iter().find(|t| t.name == "Spring Boot").unwrap();
2182 assert!(spring_boot.is_primary);
2183 assert!(matches!(spring_boot.category, TechnologyCategory::BackendFramework));
2184 }
2185
2186 #[test]
2187 fn test_comprehensive_rust_detection() {
2188 let language = DetectedLanguage {
2189 name: "Rust".to_string(),
2190 version: Some("1.70.0".to_string()),
2191 confidence: 0.95,
2192 files: vec![PathBuf::from("src/main.rs")],
2193 main_dependencies: vec![
2194 "axum".to_string(),
2195 "tokio".to_string(),
2196 "sqlx".to_string(),
2197 "serde".to_string(),
2198 "tauri".to_string(),
2199 ],
2200 dev_dependencies: vec!["criterion".to_string()],
2201 package_manager: Some("cargo".to_string()),
2202 };
2203
2204 let technologies = detect_rust_technologies(&language);
2205 let tech_names: Vec<&str> = technologies.iter().map(|t| t.name.as_str()).collect();
2206
2207 assert!(tech_names.contains(&"Axum"));
2209 assert!(tech_names.contains(&"Tokio"));
2210 assert!(tech_names.contains(&"SQLx"));
2211 assert!(tech_names.contains(&"Serde"));
2212 assert!(tech_names.contains(&"Tauri"));
2213 assert!(tech_names.contains(&"Criterion"));
2214
2215 let axum = technologies.iter().find(|t| t.name == "Axum").unwrap();
2217 assert!(axum.is_primary);
2218 assert!(matches!(axum.category, TechnologyCategory::BackendFramework));
2219
2220 assert!(axum.requires.contains(&"Tokio".to_string()));
2222 }
2223
2224 #[test]
2225 fn test_technology_conflicts_resolution() {
2226 use crate::analyzer::AnalysisConfig;
2227 use std::path::Path;
2228
2229 let language = DetectedLanguage {
2230 name: "Rust".to_string(),
2231 version: Some("1.70.0".to_string()),
2232 confidence: 0.95,
2233 files: vec![PathBuf::from("src/main.rs")],
2234 main_dependencies: vec![
2235 "tokio".to_string(),
2236 "async-std".to_string(), ],
2238 dev_dependencies: vec![],
2239 package_manager: Some("cargo".to_string()),
2240 };
2241
2242 let config = AnalysisConfig::default();
2243 let project_root = Path::new(".");
2244
2245 let technologies = detect_frameworks(project_root, &[language], &config).unwrap();
2247 let _tech_names: Vec<&str> = technologies.iter().map(|t| t.name.as_str()).collect();
2248
2249 let async_runtimes: Vec<_> = technologies.iter()
2251 .filter(|t| matches!(t.category, TechnologyCategory::Runtime))
2252 .collect();
2253
2254 assert!(async_runtimes.len() <= 1, "Should resolve conflicting async runtimes: found {:?}",
2255 async_runtimes.iter().map(|t| &t.name).collect::<Vec<_>>());
2256 }
2257}