1use super::{Template, TemplateCategory};
4use crate::{Result, parser::UnifiedRule};
5
6#[derive(Debug, Clone)]
8pub struct ProjectTemplate {
9 name: String,
10 description: String,
11 includes: Vec<String>,
12 excludes: Vec<String>,
13 focus: Vec<String>,
14 tags: Vec<String>,
15}
16
17impl ProjectTemplate {
18 pub fn rust_workspace() -> Self {
20 Self {
21 name: "rust-workspace".to_string(),
22 description: "Rust Cargo workspace with multiple crates".to_string(),
23 includes: vec![
24 "crates/**".to_string(),
25 "src/**".to_string(),
26 "Cargo.toml".to_string(),
27 "Cargo.lock".to_string(),
28 ],
29 excludes: vec!["target/**".to_string(), "**/*.log".to_string()],
30 focus: vec![
31 "This is a Rust workspace with multiple crates".to_string(),
32 "Use idiomatic Rust patterns".to_string(),
33 "Follow Rust API guidelines".to_string(),
34 "Run cargo fmt and cargo clippy before commits".to_string(),
35 "Write documentation for public APIs".to_string(),
36 ],
37 tags: vec![
38 "rust".to_string(),
39 "cargo".to_string(),
40 "workspace".to_string(),
41 ],
42 }
43 }
44
45 pub fn typescript_monorepo() -> Self {
47 Self {
48 name: "typescript-monorepo".to_string(),
49 description: "TypeScript monorepo with multiple packages".to_string(),
50 includes: vec![
51 "packages/**".to_string(),
52 "apps/**".to_string(),
53 "package.json".to_string(),
54 "tsconfig.json".to_string(),
55 ],
56 excludes: vec![
57 "node_modules/**".to_string(),
58 "dist/**".to_string(),
59 "build/**".to_string(),
60 ".next/**".to_string(),
61 ],
62 focus: vec![
63 "This is a TypeScript monorepo".to_string(),
64 "Use strict TypeScript configuration".to_string(),
65 "Prefer functional patterns".to_string(),
66 "Use ESLint and Prettier for formatting".to_string(),
67 "Write tests with Vitest or Jest".to_string(),
68 ],
69 tags: vec![
70 "typescript".to_string(),
71 "monorepo".to_string(),
72 "node".to_string(),
73 ],
74 }
75 }
76
77 pub fn fullstack() -> Self {
79 Self {
80 name: "fullstack".to_string(),
81 description: "Full-stack web application".to_string(),
82 includes: vec![
83 "src/**".to_string(),
84 "frontend/**".to_string(),
85 "backend/**".to_string(),
86 "api/**".to_string(),
87 ],
88 excludes: vec![
89 "node_modules/**".to_string(),
90 "dist/**".to_string(),
91 "target/**".to_string(),
92 ".next/**".to_string(),
93 ],
94 focus: vec![
95 "This is a full-stack web application".to_string(),
96 "Consider both frontend and backend impacts".to_string(),
97 "Maintain API consistency".to_string(),
98 "Consider security at every layer".to_string(),
99 "Write integration tests for critical paths".to_string(),
100 ],
101 tags: vec![
102 "fullstack".to_string(),
103 "web".to_string(),
104 "api".to_string(),
105 ],
106 }
107 }
108
109 pub fn cli_tool() -> Self {
111 Self {
112 name: "cli-tool".to_string(),
113 description: "Command-line interface tool".to_string(),
114 includes: vec!["src/**".to_string(), "Cargo.toml".to_string()],
115 excludes: vec!["target/**".to_string()],
116 focus: vec![
117 "This is a CLI tool".to_string(),
118 "Focus on user experience and helpful error messages".to_string(),
119 "Support common CLI conventions (--help, --version, etc.)".to_string(),
120 "Consider exit codes and scripting use cases".to_string(),
121 "Write comprehensive help text".to_string(),
122 ],
123 tags: vec![
124 "cli".to_string(),
125 "tool".to_string(),
126 "command-line".to_string(),
127 ],
128 }
129 }
130
131 pub fn library() -> Self {
133 Self {
134 name: "library".to_string(),
135 description: "Reusable library crate".to_string(),
136 includes: vec!["src/**".to_string(), "Cargo.toml".to_string()],
137 excludes: vec!["target/**".to_string(), "examples/**".to_string()],
138 focus: vec![
139 "This is a library crate for reuse".to_string(),
140 "Design clean and intuitive public APIs".to_string(),
141 "Document all public items with examples".to_string(),
142 "Minimize dependencies".to_string(),
143 "Consider semver compatibility".to_string(),
144 "Write comprehensive tests".to_string(),
145 ],
146 tags: vec![
147 "library".to_string(),
148 "crate".to_string(),
149 "reusable".to_string(),
150 ],
151 }
152 }
153}
154
155impl Template for ProjectTemplate {
156 fn name(&self) -> &str {
157 &self.name
158 }
159
160 fn description(&self) -> &str {
161 &self.description
162 }
163
164 fn category(&self) -> TemplateCategory {
165 TemplateCategory::Project
166 }
167
168 fn expand(&self) -> Result<Vec<UnifiedRule>> {
169 Ok(vec![UnifiedRule::Context {
170 includes: self.includes.clone(),
171 excludes: self.excludes.clone(),
172 focus: self.focus.clone(),
173 }])
174 }
175
176 fn tags(&self) -> Vec<&str> {
177 self.tags.iter().map(|s| s.as_str()).collect()
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_rust_workspace() {
187 let template = ProjectTemplate::rust_workspace();
188 assert_eq!(template.name(), "rust-workspace");
189 assert_eq!(template.category(), TemplateCategory::Project);
190
191 let rules = template.expand().unwrap();
192 assert_eq!(rules.len(), 1);
193
194 if let UnifiedRule::Context { includes, .. } = &rules[0] {
195 assert!(includes.contains(&"crates/**".to_string()));
196 } else {
197 panic!("Expected Context rule");
198 }
199 }
200}