1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fs;
5use std::path::{Path, PathBuf};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct CodebaseInfo {
9 pub project_type: ProjectType,
10 pub languages: Vec<Language>,
11 pub frameworks: Vec<Framework>,
12 pub build_tools: Vec<BuildTool>,
13 pub main_language: Option<Language>,
14 pub description: String,
15}
16
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18pub enum ProjectType {
19 WebApplication,
20 Library,
21 CLI,
22 MobileApp,
23 API,
24 Documentation,
25 Monorepo,
26 Unknown,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
30pub enum Language {
31 Rust,
32 JavaScript,
33 TypeScript,
34 Python,
35 Go,
36 Java,
37 CSharp,
38 CPlusPlus,
39 C,
40 Ruby,
41 PHP,
42 Swift,
43 Kotlin,
44 Scala,
45 Elixir,
46 Haskell,
47 Shell,
48 HTML,
49 CSS,
50 Other(String),
51}
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
54pub enum Framework {
55 NextJS,
56 React,
57 Vue,
58 Angular,
59 Svelte,
60 Django,
61 Flask,
62 FastAPI,
63 Rails,
64 Laravel,
65 Spring,
66 Express,
67 NestJS,
68 Actix,
69 Rocket,
70 Gin,
71 Echo,
72 DotNet,
73 Flutter,
74 ReactNative,
75 Other(String),
76}
77
78#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub enum BuildTool {
80 Npm,
81 Yarn,
82 Pnpm,
83 Cargo,
84 Maven,
85 Gradle,
86 Pip,
87 Poetry,
88 Composer,
89 Bundler,
90 Go,
91 DotNet,
92 Make,
93 CMake,
94 Other(String),
95}
96
97pub struct CodebaseDetector {
98 root_path: PathBuf,
99}
100
101impl CodebaseDetector {
102 pub fn new(root_path: PathBuf) -> Self {
103 Self { root_path }
104 }
105
106 pub fn detect(&self) -> Result<CodebaseInfo> {
107 let mut info = CodebaseInfo {
108 project_type: ProjectType::Unknown,
109 languages: Vec::new(),
110 frameworks: Vec::new(),
111 build_tools: Vec::new(),
112 main_language: None,
113 description: String::new(),
114 };
115
116 self.detect_by_config_files(&mut info)?;
117 self.detect_by_file_extensions(&mut info)?;
118 self.determine_project_type(&mut info);
119 self.determine_main_language(&mut info);
120 self.generate_description(&mut info);
121
122 Ok(info)
123 }
124
125 fn detect_by_config_files(&self, info: &mut CodebaseInfo) -> Result<()> {
126 let config_checks: Vec<(&str, fn(&Path, &mut CodebaseInfo) -> Result<()>)> = vec![
127 ("package.json", Self::check_nodejs_project),
128 ("Cargo.toml", Self::check_rust_project),
129 ("go.mod", Self::check_go_project),
130 ("requirements.txt", Self::check_python_requirements),
131 ("pyproject.toml", Self::check_python_pyproject),
132 ("Gemfile", Self::check_ruby_project),
133 ("composer.json", Self::check_php_project),
134 ("pom.xml", Self::check_maven_project),
135 ("build.gradle", Self::check_gradle_project),
136 (".csproj", Self::check_dotnet_project),
137 ("CMakeLists.txt", Self::check_cmake_project),
138 ("Makefile", Self::check_makefile_project),
139 ];
140
141 for (file, checker) in config_checks {
142 let path = self.root_path.join(file);
143 if path.exists() {
144 checker(&path, info)?;
145 } else if file.contains('.') {
146 for entry in fs::read_dir(&self.root_path)? {
147 let entry = entry?;
148 let filename = entry.file_name();
149 let filename_str = filename.to_string_lossy();
150 if filename_str.ends_with(file) {
151 checker(&entry.path(), info)?;
152 }
153 }
154 }
155 }
156
157 Ok(())
158 }
159
160 fn check_nodejs_project(path: &Path, info: &mut CodebaseInfo) -> Result<()> {
161 let content = fs::read_to_string(path)?;
162 let json: serde_json::Value = serde_json::from_str(&content)?;
163
164 if !info.languages.contains(&Language::JavaScript) {
165 info.languages.push(Language::JavaScript);
166 }
167
168 if json.get("dependencies").is_some() || json.get("devDependencies").is_some() {
169 if !info.build_tools.contains(&BuildTool::Npm) {
170 info.build_tools.push(BuildTool::Npm);
171 }
172 }
173
174 let deps = json.get("dependencies").and_then(|d| d.as_object());
175 let dev_deps = json.get("devDependencies").and_then(|d| d.as_object());
176
177 let check_dep = |name: &str| -> bool {
178 deps.map_or(false, |d| d.contains_key(name))
179 || dev_deps.map_or(false, |d| d.contains_key(name))
180 };
181
182 if check_dep("next") {
183 info.frameworks.push(Framework::NextJS);
184 info.languages.push(Language::TypeScript);
185 } else if check_dep("react") {
186 info.frameworks.push(Framework::React);
187 } else if check_dep("vue") {
188 info.frameworks.push(Framework::Vue);
189 } else if check_dep("@angular/core") {
190 info.frameworks.push(Framework::Angular);
191 info.languages.push(Language::TypeScript);
192 } else if check_dep("svelte") {
193 info.frameworks.push(Framework::Svelte);
194 } else if check_dep("express") {
195 info.frameworks.push(Framework::Express);
196 } else if check_dep("@nestjs/core") {
197 info.frameworks.push(Framework::NestJS);
198 info.languages.push(Language::TypeScript);
199 } else if check_dep("react-native") {
200 info.frameworks.push(Framework::ReactNative);
201 }
202
203 if path.parent().and_then(|p| p.join("yarn.lock").exists().then_some(())).is_some() {
204 info.build_tools.push(BuildTool::Yarn);
205 }
206 if path.parent().and_then(|p| p.join("pnpm-lock.yaml").exists().then_some(())).is_some() {
207 info.build_tools.push(BuildTool::Pnpm);
208 }
209
210 Ok(())
211 }
212
213 fn check_rust_project(path: &Path, info: &mut CodebaseInfo) -> Result<()> {
214 let content = fs::read_to_string(path)?;
215 let toml: toml::Value = toml::from_str(&content)?;
216
217 info.languages.push(Language::Rust);
218 info.build_tools.push(BuildTool::Cargo);
219
220 if let Some(deps) = toml.get("dependencies").and_then(|d| d.as_table()) {
221 if deps.contains_key("actix-web") {
222 info.frameworks.push(Framework::Actix);
223 } else if deps.contains_key("rocket") {
224 info.frameworks.push(Framework::Rocket);
225 }
226 }
227
228 Ok(())
229 }
230
231 fn check_go_project(_path: &Path, info: &mut CodebaseInfo) -> Result<()> {
232 info.languages.push(Language::Go);
233 info.build_tools.push(BuildTool::Go);
234 Ok(())
235 }
236
237 fn check_python_requirements(_path: &Path, info: &mut CodebaseInfo) -> Result<()> {
238 info.languages.push(Language::Python);
239 info.build_tools.push(BuildTool::Pip);
240 Ok(())
241 }
242
243 fn check_python_pyproject(path: &Path, info: &mut CodebaseInfo) -> Result<()> {
244 let content = fs::read_to_string(path)?;
245 let toml: toml::Value = toml::from_str(&content)?;
246
247 info.languages.push(Language::Python);
248
249 if toml.get("tool").and_then(|t| t.get("poetry")).is_some() {
250 info.build_tools.push(BuildTool::Poetry);
251 } else {
252 info.build_tools.push(BuildTool::Pip);
253 }
254
255 if let Some(deps) = toml
256 .get("tool")
257 .and_then(|t| t.get("poetry"))
258 .and_then(|p| p.get("dependencies"))
259 .and_then(|d| d.as_table())
260 {
261 if deps.contains_key("django") {
262 info.frameworks.push(Framework::Django);
263 } else if deps.contains_key("flask") {
264 info.frameworks.push(Framework::Flask);
265 } else if deps.contains_key("fastapi") {
266 info.frameworks.push(Framework::FastAPI);
267 }
268 }
269
270 Ok(())
271 }
272
273 fn check_ruby_project(path: &Path, info: &mut CodebaseInfo) -> Result<()> {
274 info.languages.push(Language::Ruby);
275 info.build_tools.push(BuildTool::Bundler);
276
277 if let Some(parent) = path.parent() {
279 if parent.join("config").join("application.rb").exists() {
280 info.frameworks.push(Framework::Rails);
281 }
282 }
283
284 Ok(())
285 }
286
287 fn check_php_project(path: &Path, info: &mut CodebaseInfo) -> Result<()> {
288 let content = fs::read_to_string(path)?;
289 let json: serde_json::Value = serde_json::from_str(&content)?;
290
291 info.languages.push(Language::PHP);
292 info.build_tools.push(BuildTool::Composer);
293
294 if let Some(require) = json.get("require").and_then(|r| r.as_object()) {
295 if require.contains_key("laravel/framework") {
296 info.frameworks.push(Framework::Laravel);
297 }
298 }
299
300 Ok(())
301 }
302
303 fn check_maven_project(_path: &Path, info: &mut CodebaseInfo) -> Result<()> {
304 info.languages.push(Language::Java);
305 info.build_tools.push(BuildTool::Maven);
306 Ok(())
307 }
308
309 fn check_gradle_project(_path: &Path, info: &mut CodebaseInfo) -> Result<()> {
310 info.languages.push(Language::Java);
311 info.build_tools.push(BuildTool::Gradle);
312 Ok(())
313 }
314
315 fn check_dotnet_project(_path: &Path, info: &mut CodebaseInfo) -> Result<()> {
316 info.languages.push(Language::CSharp);
317 info.build_tools.push(BuildTool::DotNet);
318 info.frameworks.push(Framework::DotNet);
319 Ok(())
320 }
321
322 fn check_cmake_project(_path: &Path, info: &mut CodebaseInfo) -> Result<()> {
323 info.languages.push(Language::CPlusPlus);
324 info.build_tools.push(BuildTool::CMake);
325 Ok(())
326 }
327
328 fn check_makefile_project(_path: &Path, info: &mut CodebaseInfo) -> Result<()> {
329 if !info.build_tools.contains(&BuildTool::Make) {
330 info.build_tools.push(BuildTool::Make);
331 }
332 Ok(())
333 }
334
335 fn detect_by_file_extensions(&self, info: &mut CodebaseInfo) -> Result<()> {
336 let mut language_counts: HashMap<Language, usize> = HashMap::new();
337
338 for entry in walkdir::WalkDir::new(&self.root_path)
339 .max_depth(3)
340 .into_iter()
341 .filter_map(|e| e.ok())
342 .filter(|e| e.file_type().is_file())
343 {
344 if let Some(ext) = entry.path().extension().and_then(|e| e.to_str()) {
345 let lang = match ext {
346 "rs" => Some(Language::Rust),
347 "js" | "mjs" | "cjs" => Some(Language::JavaScript),
348 "ts" | "tsx" => Some(Language::TypeScript),
349 "py" => Some(Language::Python),
350 "go" => Some(Language::Go),
351 "java" => Some(Language::Java),
352 "cs" => Some(Language::CSharp),
353 "cpp" | "cc" | "cxx" => Some(Language::CPlusPlus),
354 "c" | "h" => Some(Language::C),
355 "rb" => Some(Language::Ruby),
356 "php" => Some(Language::PHP),
357 "swift" => Some(Language::Swift),
358 "kt" | "kts" => Some(Language::Kotlin),
359 "scala" => Some(Language::Scala),
360 "ex" | "exs" => Some(Language::Elixir),
361 "hs" => Some(Language::Haskell),
362 "sh" | "bash" | "zsh" => Some(Language::Shell),
363 "html" | "htm" => Some(Language::HTML),
364 "css" | "scss" | "sass" | "less" => Some(Language::CSS),
365 _ => None,
366 };
367
368 if let Some(lang) = lang {
369 *language_counts.entry(lang.clone()).or_insert(0) += 1;
370 if !info.languages.contains(&lang) {
371 info.languages.push(lang);
372 }
373 }
374 }
375 }
376
377 if let Some((main_lang, _)) = language_counts.iter().max_by_key(|(_, count)| *count) {
378 info.main_language = Some(main_lang.clone());
379 }
380
381 Ok(())
382 }
383
384 fn determine_project_type(&self, info: &mut CodebaseInfo) {
385 if !info.frameworks.is_empty() {
386 if matches!(
387 info.frameworks.first(),
388 Some(Framework::NextJS | Framework::React | Framework::Vue | Framework::Angular | Framework::Svelte)
389 ) {
390 info.project_type = ProjectType::WebApplication;
391 } else if matches!(
392 info.frameworks.first(),
393 Some(Framework::Express | Framework::FastAPI | Framework::Django | Framework::Flask | Framework::Actix | Framework::Rocket)
394 ) {
395 info.project_type = ProjectType::API;
396 } else if matches!(
397 info.frameworks.first(),
398 Some(Framework::ReactNative | Framework::Flutter)
399 ) {
400 info.project_type = ProjectType::MobileApp;
401 }
402 } else if self.root_path.join("src").join("main.rs").exists()
403 || self.root_path.join("cmd").exists()
404 || self.root_path.join("cli.py").exists()
405 {
406 info.project_type = ProjectType::CLI;
407 } else if self.root_path.join("lib.rs").exists()
408 || self.root_path.join("index.js").exists()
409 || self.root_path.join("__init__.py").exists()
410 {
411 info.project_type = ProjectType::Library;
412 } else if self.root_path.join("docs").exists()
413 || self.root_path.join("README.md").exists()
414 {
415 info.project_type = ProjectType::Documentation;
416 } else if self.root_path.join("packages").exists()
417 || self.root_path.join("apps").exists()
418 {
419 info.project_type = ProjectType::Monorepo;
420 }
421 }
422
423 fn determine_main_language(&self, info: &mut CodebaseInfo) {
424 if info.main_language.is_none() && !info.languages.is_empty() {
425 info.main_language = Some(info.languages[0].clone());
426 }
427 }
428
429 fn generate_description(&self, info: &mut CodebaseInfo) {
430 let project_type = match &info.project_type {
431 ProjectType::WebApplication => "web application",
432 ProjectType::Library => "library",
433 ProjectType::CLI => "command-line tool",
434 ProjectType::MobileApp => "mobile application",
435 ProjectType::API => "API service",
436 ProjectType::Documentation => "documentation project",
437 ProjectType::Monorepo => "monorepo",
438 ProjectType::Unknown => "project",
439 };
440
441 let main_lang = info
442 .main_language
443 .as_ref()
444 .map(|l| format!("{:?}", l))
445 .unwrap_or_else(|| "Unknown".to_string());
446
447 let framework_str = if !info.frameworks.is_empty() {
448 format!(" using {:?}", info.frameworks[0])
449 } else {
450 String::new()
451 };
452
453 info.description = format!(
454 "{} {} written in {}{}",
455 if matches!(info.project_type, ProjectType::API | ProjectType::Unknown) {
456 "A"
457 } else {
458 "An"
459 },
460 project_type,
461 main_lang,
462 framework_str
463 );
464 }
465}
466
467pub fn get_default_include_patterns(info: &CodebaseInfo) -> Vec<String> {
468 let mut patterns = Vec::new();
469
470 for lang in &info.languages {
471 match lang {
472 Language::Rust => {
473 patterns.extend(vec![
474 "**/*.rs".to_string(),
475 "**/Cargo.toml".to_string(),
476 "**/Cargo.lock".to_string(),
477 ]);
478 }
479 Language::JavaScript | Language::TypeScript => {
480 patterns.extend(vec![
481 "**/*.js".to_string(),
482 "**/*.jsx".to_string(),
483 "**/*.ts".to_string(),
484 "**/*.tsx".to_string(),
485 "**/*.mjs".to_string(),
486 "**/*.cjs".to_string(),
487 "**/package.json".to_string(),
488 "**/tsconfig.json".to_string(),
489 "**/.eslintrc.json".to_string(),
490 "**/.babelrc".to_string(),
491 "**/webpack.config.js".to_string(),
492 ]);
493 }
494 Language::Python => {
495 patterns.extend(vec![
496 "**/*.py".to_string(),
497 "**/requirements.txt".to_string(),
498 "**/pyproject.toml".to_string(),
499 "**/setup.py".to_string(),
500 ]);
501 }
502 Language::Go => {
503 patterns.extend(vec![
504 "**/*.go".to_string(),
505 "**/go.mod".to_string(),
506 "**/go.sum".to_string(),
507 ]);
508 }
509 Language::Java => {
510 patterns.extend(vec![
511 "**/*.java".to_string(),
512 "**/pom.xml".to_string(),
513 "**/build.gradle".to_string(),
514 ]);
515 }
516 Language::CSharp => {
517 patterns.extend(vec![
518 "**/*.cs".to_string(),
519 "**/*.csproj".to_string(),
520 "**/*.sln".to_string(),
521 ]);
522 }
523 Language::Ruby => {
524 patterns.extend(vec![
525 "**/*.rb".to_string(),
526 "**/Gemfile".to_string(),
527 "**/Gemfile.lock".to_string(),
528 ]);
529 }
530 Language::PHP => {
531 patterns.extend(vec![
532 "**/*.php".to_string(),
533 "**/composer.json".to_string(),
534 "**/composer.lock".to_string(),
535 ]);
536 }
537 _ => {}
538 }
539 }
540
541 patterns.extend(vec![
542 "**/README.md".to_string(),
543 "**/.env.example".to_string(),
544 "**/Dockerfile".to_string(),
545 "**/docker-compose.yml".to_string(),
546 "**/.gitignore".to_string(),
547 ]);
548
549 patterns.sort();
550 patterns.dedup();
551 patterns
552}
553
554pub fn get_smart_exclude_patterns(info: &CodebaseInfo) -> Vec<String> {
555 let mut patterns = vec![
556 "**/node_modules/**".to_string(),
558 "**/bower_components/**".to_string(),
559 "**/jspm_packages/**".to_string(),
560 "**/vendor/**".to_string(),
561 "**/.pnp/**".to_string(),
562 "**/.yarn/**".to_string(),
563
564 "**/target/**".to_string(),
566 "**/dist/**".to_string(),
567 "**/build/**".to_string(),
568 "**/out/**".to_string(),
569 "**/output/**".to_string(),
570 "**/.next/**".to_string(),
571 "**/.nuxt/**".to_string(),
572 "**/.output/**".to_string(),
573 "**/.svelte-kit/**".to_string(),
574 "**/public/build/**".to_string(),
575
576 "**/.git/**".to_string(),
578 "**/.svn/**".to_string(),
579 "**/.hg/**".to_string(),
580
581 "**/__pycache__/**".to_string(),
583 "**/venv/**".to_string(),
584 "**/.venv/**".to_string(),
585 "**/env/**".to_string(),
586 "**/.env/**".to_string(),
587 "**/site-packages/**".to_string(),
588 "**/.tox/**".to_string(),
589 "**/*.egg-info/**".to_string(),
590 "**/pip-wheel-metadata/**".to_string(),
591
592 "**/coverage/**".to_string(),
594 "**/.coverage/**".to_string(),
595 "**/htmlcov/**".to_string(),
596 "**/.nyc_output/**".to_string(),
597 "**/test-results/**".to_string(),
598 "**/.pytest_cache/**".to_string(),
599
600 "**/.idea/**".to_string(),
602 "**/.vscode/**".to_string(),
603 "**/.vs/**".to_string(),
604 "**/*.swp".to_string(),
605 "**/*.swo".to_string(),
606 "**/*~".to_string(),
607 "**/.DS_Store".to_string(),
608 "**/Thumbs.db".to_string(),
609
610 "**/logs/**".to_string(),
612 "**/*.log".to_string(),
613 "**/tmp/**".to_string(),
614 "**/temp/**".to_string(),
615 "**/.tmp/**".to_string(),
616 "**/.temp/**".to_string(),
617 "**/.cache/**".to_string(),
618
619 "**/*.min.js".to_string(),
621 "**/*.min.css".to_string(),
622 "**/*.map".to_string(),
623 "**/bundle.js".to_string(),
624 "**/chunk.*.js".to_string(),
625 "**/*.bundle.js".to_string(),
626
627 "**/package-lock.json".to_string(),
629 "**/yarn.lock".to_string(),
630 "**/pnpm-lock.yaml".to_string(),
631 "**/composer.lock".to_string(),
632 "**/Gemfile.lock".to_string(),
633 "**/poetry.lock".to_string(),
634 "**/Pipfile.lock".to_string(),
635
636 "**/docs/_build/**".to_string(),
638 "**/site/**".to_string(),
639 "**/_site/**".to_string(),
640
641 "**/*.sqlite".to_string(),
643 "**/*.sqlite3".to_string(),
644 "**/*.db".to_string(),
645 "**/*.mdb".to_string(),
646 "**/*.accdb".to_string(),
647
648 "**/*.zip".to_string(),
650 "**/*.tar".to_string(),
651 "**/*.tar.gz".to_string(),
652 "**/*.tgz".to_string(),
653 "**/*.rar".to_string(),
654 "**/*.7z".to_string(),
655 "**/*.gz".to_string(),
656 "**/*.bz2".to_string(),
657 "**/*.xz".to_string(),
658 "**/*.jar".to_string(),
659 "**/*.war".to_string(),
660 "**/*.ear".to_string(),
661 "**/*.deb".to_string(),
662 "**/*.rpm".to_string(),
663 "**/*.dmg".to_string(),
664 "**/*.pkg".to_string(),
665 "**/*.iso".to_string(),
666
667 "**/*.pt".to_string(),
669 "**/*.pth".to_string(),
670 "**/*.pkl".to_string(),
671 "**/*.pickle".to_string(),
672 "**/*.h5".to_string(),
673 "**/*.hdf5".to_string(),
674 "**/*.pb".to_string(),
675 "**/*.onnx".to_string(),
676 "**/*.tflite".to_string(),
677 "**/*.caffemodel".to_string(),
678 "**/*.weights".to_string(),
679 "**/*.model".to_string(),
680 "**/*.ckpt".to_string(),
681 "**/*.safetensors".to_string(),
682
683 "**/*.csv".to_string(),
685 "**/*.tsv".to_string(),
686 "**/*.parquet".to_string(),
687 "**/*.feather".to_string(),
688 "**/*.msgpack".to_string(),
689 "**/*.npy".to_string(),
690 "**/*.npz".to_string(),
691
692 "**/*.doc".to_string(),
694 "**/*.docx".to_string(),
695 "**/*.xls".to_string(),
696 "**/*.xlsx".to_string(),
697 "**/*.ppt".to_string(),
698 "**/*.pptx".to_string(),
699 "**/*.pdf".to_string(),
700 "**/*.odt".to_string(),
701 "**/*.ods".to_string(),
702 "**/*.odp".to_string(),
703
704 "**/*.jpg".to_string(),
706 "**/*.jpeg".to_string(),
707 "**/*.png".to_string(),
708 "**/*.gif".to_string(),
709 "**/*.bmp".to_string(),
710 "**/*.tiff".to_string(),
711 "**/*.tif".to_string(),
712 "**/*.svg".to_string(),
713 "**/*.webp".to_string(),
714 "**/*.ico".to_string(),
715 "**/*.mp4".to_string(),
716 "**/*.avi".to_string(),
717 "**/*.mov".to_string(),
718 "**/*.wmv".to_string(),
719 "**/*.flv".to_string(),
720 "**/*.webm".to_string(),
721 "**/*.mkv".to_string(),
722 "**/*.mp3".to_string(),
723 "**/*.wav".to_string(),
724 "**/*.flac".to_string(),
725 "**/*.aac".to_string(),
726 "**/*.ogg".to_string(),
727 "**/*.wma".to_string(),
728
729 "**/*.woff".to_string(),
731 "**/*.woff2".to_string(),
732 "**/*.ttf".to_string(),
733 "**/*.otf".to_string(),
734 "**/*.eot".to_string(),
735
736 "**/*.exe".to_string(),
738 "**/*.dll".to_string(),
739 "**/*.so".to_string(),
740 "**/*.dylib".to_string(),
741 "**/*.a".to_string(),
742 "**/*.lib".to_string(),
743 "**/*.o".to_string(),
744 "**/*.obj".to_string(),
745 "**/*.pyc".to_string(),
746 "**/*.pyo".to_string(),
747 "**/*.class".to_string(),
748 "**/*.elc".to_string(),
749 "**/*.beam".to_string(),
750 ];
751
752 for framework in &info.frameworks {
754 match framework {
755 Framework::NextJS => {
756 patterns.extend(vec![
757 "**/.next/**".to_string(),
758 "**/next-env.d.ts".to_string(),
759 ]);
760 }
761 Framework::Django => {
762 patterns.extend(vec![
763 "**/migrations/**".to_string(),
764 "**/staticfiles/**".to_string(),
765 "**/media/**".to_string(),
766 ]);
767 }
768 Framework::Rails => {
769 patterns.extend(vec![
770 "**/log/**".to_string(),
771 "**/tmp/**".to_string(),
772 "**/storage/**".to_string(),
773 "**/public/assets/**".to_string(),
774 ]);
775 }
776 _ => {}
777 }
778 }
779
780 for language in &info.languages {
782 match language {
783 Language::Java => {
784 patterns.extend(vec![
785 "**/target/**".to_string(),
786 "**/*.class".to_string(),
787 "**/bin/**".to_string(),
788 ]);
789 }
790 Language::CSharp => {
791 patterns.extend(vec![
792 "**/bin/**".to_string(),
793 "**/obj/**".to_string(),
794 "**/packages/**".to_string(),
795 ]);
796 }
797 Language::Go => {
798 patterns.extend(vec![
799 "**/vendor/**".to_string(),
800 "**/*.exe".to_string(),
801 "**/*.test".to_string(),
802 ]);
803 }
804 Language::PHP => {
805 patterns.extend(vec![
806 "**/vendor/**".to_string(),
807 "**/storage/**".to_string(),
808 "**/bootstrap/cache/**".to_string(),
809 ]);
810 }
811 _ => {}
812 }
813 }
814
815 patterns.sort();
816 patterns.dedup();
817 patterns
818}