1mod composer;
6mod persona;
7mod project;
8mod registry;
9mod standards;
10mod task;
11mod workflow;
12
13pub use composer::TemplateComposer;
14pub use persona::PersonaTemplate;
15pub use project::ProjectTemplate;
16pub use registry::TemplateRegistry;
17pub use standards::StandardsTemplate;
18pub use task::TaskTemplate;
19pub use workflow::WorkflowTemplate;
20
21use crate::{Result, parser::UnifiedRule};
22
23pub trait Template: Send + Sync {
25 fn name(&self) -> &str;
27
28 fn description(&self) -> &str;
30
31 fn category(&self) -> TemplateCategory;
33
34 fn expand(&self) -> Result<Vec<UnifiedRule>>;
36
37 fn tags(&self) -> Vec<&str> {
39 Vec::new()
40 }
41
42 fn render(&self, _variables: &std::collections::HashMap<String, String>) -> Result<String> {
44 let rules = self.expand()?;
46 let mut output = format!("# {} Template\n\n", self.name());
47 output.push_str(&format!("{}\n\n", self.description()));
48
49 for rule in rules {
50 match rule {
51 UnifiedRule::Persona {
52 name,
53 role,
54 identity,
55 style,
56 traits,
57 principles,
58 } => {
59 output.push_str("## Persona\n\n");
60 output.push_str(&format!("**{}** - {}\n\n", name, role));
61 if let Some(id) = identity {
62 output.push_str(&format!("{}\n\n", id));
63 }
64 if let Some(s) = style {
65 output.push_str(&format!("Style: {}\n\n", s));
66 }
67 if !traits.is_empty() {
68 output.push_str("### Traits\n");
69 for t in traits {
70 output.push_str(&format!("- {}\n", t));
71 }
72 output.push('\n');
73 }
74 if !principles.is_empty() {
75 output.push_str("### Principles\n");
76 for p in principles {
77 output.push_str(&format!("- {}\n", p));
78 }
79 output.push('\n');
80 }
81 }
82 UnifiedRule::Standard {
83 category,
84 description,
85 ..
86 } => {
87 output.push_str(&format!("### {:?}\n", category));
88 output.push_str(&format!("- {}\n\n", description));
89 }
90 UnifiedRule::Context {
91 includes,
92 excludes,
93 focus,
94 } => {
95 output.push_str("## Context\n\n");
96 if !includes.is_empty() {
97 output.push_str("### Include\n");
98 for inc in includes {
99 output.push_str(&format!("- {}\n", inc));
100 }
101 output.push('\n');
102 }
103 if !excludes.is_empty() {
104 output.push_str("### Exclude\n");
105 for exc in excludes {
106 output.push_str(&format!("- {}\n", exc));
107 }
108 output.push('\n');
109 }
110 if !focus.is_empty() {
111 output.push_str("### Focus\n");
112 for f in focus {
113 output.push_str(&format!("- {}\n", f));
114 }
115 output.push('\n');
116 }
117 }
118 UnifiedRule::Workflow { name, steps } => {
119 output.push_str(&format!("## Workflow: {}\n\n", name));
120 for step in steps {
121 output.push_str(&format!("### Step: {}\n", step.name));
122 output.push_str(&format!("{}\n\n", step.description));
123 }
124 }
125 UnifiedRule::Raw { content } => {
126 output.push_str(&content);
127 output.push('\n');
128 }
129 }
130 }
131
132 Ok(output)
133 }
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
138pub enum TemplateCategory {
139 Persona,
141 Project,
143 Standards,
145 Workflow,
147 Task,
149}
150
151impl std::fmt::Display for TemplateCategory {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 match self {
154 TemplateCategory::Persona => write!(f, "Persona"),
155 TemplateCategory::Project => write!(f, "Project"),
156 TemplateCategory::Standards => write!(f, "Standards"),
157 TemplateCategory::Workflow => write!(f, "Workflow"),
158 TemplateCategory::Task => write!(f, "Task"),
159 }
160 }
161}
162
163pub mod builtin {
165 use super::*;
166
167 pub fn personas() -> Vec<Box<dyn Template>> {
169 vec![
170 Box::new(PersonaTemplate::architect()),
171 Box::new(PersonaTemplate::reviewer()),
172 Box::new(PersonaTemplate::documenter()),
173 Box::new(PersonaTemplate::security()),
174 Box::new(PersonaTemplate::performance()),
175 Box::new(PersonaTemplate::teacher()),
176 ]
177 }
178
179 pub fn projects() -> Vec<Box<dyn Template>> {
181 vec![
182 Box::new(ProjectTemplate::rust_workspace()),
183 Box::new(ProjectTemplate::typescript_monorepo()),
184 Box::new(ProjectTemplate::fullstack()),
185 Box::new(ProjectTemplate::cli_tool()),
186 Box::new(ProjectTemplate::library()),
187 ]
188 }
189
190 pub fn standards() -> Vec<Box<dyn Template>> {
192 vec![
193 Box::new(StandardsTemplate::rust_idioms()),
194 Box::new(StandardsTemplate::error_handling()),
195 Box::new(StandardsTemplate::testing()),
196 Box::new(StandardsTemplate::documentation()),
197 Box::new(StandardsTemplate::git_conventions()),
198 ]
199 }
200
201 pub fn workflows() -> Vec<Box<dyn Template>> {
203 vec![
204 Box::new(WorkflowTemplate::tdd()),
205 Box::new(WorkflowTemplate::feature_development()),
206 Box::new(WorkflowTemplate::bug_fixing()),
207 Box::new(WorkflowTemplate::refactoring()),
208 Box::new(WorkflowTemplate::code_review()),
209 ]
210 }
211
212 pub fn tasks() -> Vec<Box<dyn Template>> {
214 vec![
215 Box::new(TaskTemplate::implement_feature()),
216 Box::new(TaskTemplate::write_tests()),
217 Box::new(TaskTemplate::fix_bug()),
218 Box::new(TaskTemplate::optimize()),
219 Box::new(TaskTemplate::document()),
220 ]
221 }
222
223 pub fn all() -> Vec<Box<dyn Template>> {
225 let mut all = Vec::new();
226 all.extend(personas());
227 all.extend(projects());
228 all.extend(standards());
229 all.extend(workflows());
230 all.extend(tasks());
231 all
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238
239 #[test]
240 fn test_category_display() {
241 assert_eq!(format!("{}", TemplateCategory::Persona), "Persona");
242 assert_eq!(format!("{}", TemplateCategory::Workflow), "Workflow");
243 }
244
245 #[test]
246 fn test_builtin_templates() {
247 let all = builtin::all();
248 assert!(!all.is_empty());
249
250 let personas = builtin::personas();
252 assert!(!personas.is_empty());
253 }
254}