Skip to main content

driven/templates/
project.rs

1//! Project structure templates
2
3use super::{Template, TemplateCategory};
4use crate::{Result, parser::UnifiedRule};
5
6/// Project template definition
7#[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    /// Rust workspace project
19    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    /// TypeScript monorepo
46    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    /// Full-stack application
78    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    /// CLI tool project
110    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    /// Library crate
132    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}