1pub mod domain;
2pub mod signals;
3pub mod signals_backend;
4
5use crate::scan::ProjectContext;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum PrimaryType {
10 Standard,
11 Monorepo,
12 MicroRepo,
13 Mobile(MobileFramework),
14 Frontend(FrontendFramework),
15 Backend(BackendFramework),
16 IaC(IaCTool),
17 Serverless(ServerlessPlatform),
18 ML,
19 CodegenHeavy,
20 DocSite(DocFramework),
21 GameDev(GameEngine),
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum MobileFramework {
26 Flutter,
27 ReactNative,
28 IosNative,
29 AndroidNative,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub enum FrontendFramework {
34 NextJs,
35 Nuxt,
36 Angular,
37 VueVite,
38 CreateReactApp,
39 Other,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum BackendFramework {
44 Django,
45 Rails,
46 Express,
47 GoService,
48 RustService,
49 Phoenix,
50 Other,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub enum IaCTool {
55 Terraform,
56 Helm,
57 Kustomize,
58 Pulumi,
59 AwsCdk,
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub enum ServerlessPlatform {
64 ServerlessFramework,
65 AwsSam,
66 CloudflareWorkers,
67 VercelFunctions,
68 NetlifyFunctions,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
72pub enum DocFramework {
73 Docusaurus,
74 MkDocs,
75 Hugo,
76 VuePress,
77 Other,
78}
79
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub enum GameEngine {
82 Unity,
83 Godot,
84 Bevy,
85 Unreal,
86}
87
88#[derive(Debug, Clone, PartialEq, Eq)]
89pub enum Language {
90 Rust,
91 TypeScript,
92 JavaScript,
93 Python,
94 Go,
95 Dart,
96 Swift,
97 Kotlin,
98 Java,
99 Ruby,
100 CSharp,
101 Elixir,
102 Cpp,
103 Other(String),
104}
105
106#[derive(Debug, Clone, Default)]
108pub struct ProjectFlags {
109 pub is_polyglot: bool,
110 pub is_legacy: bool,
111}
112
113#[derive(Debug, Clone)]
115pub struct ProjectType {
116 pub primary: PrimaryType,
117 pub flags: ProjectFlags,
118 pub languages: Vec<Language>,
119}
120
121impl std::fmt::Display for ProjectType {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 let primary = match &self.primary {
124 PrimaryType::Standard => "Standard".to_string(),
125 PrimaryType::Monorepo => "Monorepo".to_string(),
126 PrimaryType::MicroRepo => "Micro-repo".to_string(),
127 PrimaryType::Mobile(fw) => format!("Mobile ({fw:?})"),
128 PrimaryType::Frontend(fw) => format!("Frontend ({fw:?})"),
129 PrimaryType::Backend(fw) => format!("Backend ({fw:?})"),
130 PrimaryType::IaC(tool) => format!("IaC ({tool:?})"),
131 PrimaryType::Serverless(p) => format!("Serverless ({p:?})"),
132 PrimaryType::ML => "Data Science / ML".to_string(),
133 PrimaryType::CodegenHeavy => "Code Generation Heavy".to_string(),
134 PrimaryType::DocSite(fw) => format!("Documentation Site ({fw:?})"),
135 PrimaryType::GameDev(eng) => format!("Game Development ({eng:?})"),
136 };
137
138 let mut flags = Vec::new();
139 if self.flags.is_polyglot {
140 flags.push("Polyglot");
141 }
142 if self.flags.is_legacy {
143 flags.push("Legacy");
144 }
145
146 if flags.is_empty() {
147 write!(f, "{primary}")
148 } else {
149 write!(f, "{primary} + {}", flags.join(" + "))
150 }
151 }
152}
153
154pub fn detect(ctx: &ProjectContext) -> ProjectType {
156 let languages = signals::detect_languages(ctx);
157 let flags = detect_flags(ctx, &languages);
158
159 if signals::is_monorepo(ctx) {
161 return ProjectType {
162 primary: PrimaryType::Monorepo,
163 flags,
164 languages,
165 };
166 }
167
168 if let Some(primary) = domain::detect_domain(ctx) {
171 return ProjectType {
172 primary,
173 flags,
174 languages,
175 };
176 }
177
178 if signals::is_micro_repo(ctx) {
180 return ProjectType {
181 primary: PrimaryType::MicroRepo,
182 flags,
183 languages,
184 };
185 }
186
187 ProjectType {
188 primary: PrimaryType::Standard,
189 flags,
190 languages,
191 }
192}
193
194fn detect_flags(ctx: &ProjectContext, languages: &[Language]) -> ProjectFlags {
195 let is_polyglot = languages.len() >= 2;
196
197 let is_legacy = ctx.test_files.is_empty()
198 && ctx.source_file_count() > 10
199 && ctx.average_source_file_lines() > 300.0
200 && !ctx.has_claude_md();
201
202 ProjectFlags {
203 is_polyglot,
204 is_legacy,
205 }
206}