Skip to main content

dampen_cli/commands/add/
templates.rs

1//! Template loading and rendering logic.
2
3/// Type of window template.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum TemplateKind {
6    /// Rust module template (.rs file)
7    RustModule,
8    /// Dampen XML template (.dampen file)
9    DampenXml,
10}
11
12/// A window file template with placeholder replacement.
13#[derive(Debug, Clone)]
14pub struct WindowTemplate {
15    /// Template content with placeholders
16    pub content: String,
17    /// Template type
18    pub kind: TemplateKind,
19}
20
21/// Window name variants for template rendering.
22///
23/// This is a simplified version for template rendering.
24/// The full WindowName with validation will be in validation.rs.
25#[derive(Debug, Clone)]
26pub struct WindowNameVariants {
27    /// snake_case representation (e.g., "user_profile")
28    pub snake: String,
29    /// PascalCase representation (e.g., "UserProfile")
30    pub pascal: String,
31    /// Title Case representation (e.g., "User Profile")
32    pub title: String,
33}
34
35impl WindowTemplate {
36    /// Load a template from embedded resources.
37    ///
38    /// # Examples
39    ///
40    /// ```no_run
41    /// use dampen_cli::commands::add::templates::{WindowTemplate, TemplateKind};
42    ///
43    /// let template = WindowTemplate::load(TemplateKind::RustModule);
44    /// assert!(!template.content.is_empty());
45    /// ```
46    pub fn load(kind: TemplateKind) -> Self {
47        let content = match kind {
48            TemplateKind::RustModule => {
49                include_str!("../../../templates/add/window.rs.template")
50            }
51            TemplateKind::DampenXml => {
52                include_str!("../../../templates/add/window.dampen.template")
53            }
54        };
55
56        Self {
57            content: content.to_string(),
58            kind,
59        }
60    }
61
62    /// Render the template by replacing placeholders with actual values.
63    ///
64    /// # Placeholders
65    ///
66    /// - `{{WINDOW_NAME}}` - replaced with snake_case name
67    /// - `{{WINDOW_NAME_PASCAL}}` - replaced with PascalCase name
68    /// - `{{WINDOW_NAME_TITLE}}` - replaced with Title Case name
69    ///
70    /// # Examples
71    ///
72    /// ```no_run
73    /// use dampen_cli::commands::add::templates::{WindowTemplate, TemplateKind, WindowNameVariants};
74    ///
75    /// let template = WindowTemplate::load(TemplateKind::RustModule);
76    /// let names = WindowNameVariants {
77    ///     snake: "user_profile".to_string(),
78    ///     pascal: "UserProfile".to_string(),
79    ///     title: "User Profile".to_string(),
80    /// };
81    /// let rendered = template.render(&names);
82    /// assert!(rendered.contains("user_profile"));
83    /// ```
84    pub fn render(&self, window_name: &WindowNameVariants) -> String {
85        self.content
86            .replace("{{WINDOW_NAME}}", &window_name.snake)
87            .replace("{{WINDOW_NAME_PASCAL}}", &window_name.pascal)
88            .replace("{{WINDOW_NAME_TITLE}}", &window_name.title)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_template_load_rust_module() {
98        let template = WindowTemplate::load(TemplateKind::RustModule);
99        assert!(!template.content.is_empty());
100        assert_eq!(template.kind, TemplateKind::RustModule);
101        // Verify it contains expected placeholders
102        assert!(template.content.contains("{{WINDOW_NAME}}"));
103        // Note: Current template only uses {{WINDOW_NAME}}, not separate pascal/title variants
104        assert!(template.content.contains("use dampen_core"));
105        assert!(template.content.contains("Model"));
106    }
107
108    #[test]
109    fn test_template_load_dampen_xml() {
110        let template = WindowTemplate::load(TemplateKind::DampenXml);
111        assert!(!template.content.is_empty());
112        assert_eq!(template.kind, TemplateKind::DampenXml);
113        // Verify it contains expected placeholders
114        assert!(template.content.contains("{{WINDOW_NAME_TITLE}}"));
115    }
116
117    #[test]
118    fn test_template_render_snake_case() {
119        let template = WindowTemplate {
120            content: "File: {{WINDOW_NAME}}.rs".to_string(),
121            kind: TemplateKind::RustModule,
122        };
123        let names = WindowNameVariants {
124            snake: "user_profile".to_string(),
125            pascal: "UserProfile".to_string(),
126            title: "User Profile".to_string(),
127        };
128        let rendered = template.render(&names);
129        assert_eq!(rendered, "File: user_profile.rs");
130    }
131
132    #[test]
133    fn test_template_render_pascal_case() {
134        let template = WindowTemplate {
135            content: "struct {{WINDOW_NAME_PASCAL}} {}".to_string(),
136            kind: TemplateKind::RustModule,
137        };
138        let names = WindowNameVariants {
139            snake: "user_profile".to_string(),
140            pascal: "UserProfile".to_string(),
141            title: "User Profile".to_string(),
142        };
143        let rendered = template.render(&names);
144        assert_eq!(rendered, "struct UserProfile {}");
145    }
146
147    #[test]
148    fn test_template_render_title_case() {
149        let template = WindowTemplate {
150            content: "<text>{{WINDOW_NAME_TITLE}}</text>".to_string(),
151            kind: TemplateKind::DampenXml,
152        };
153        let names = WindowNameVariants {
154            snake: "user_profile".to_string(),
155            pascal: "UserProfile".to_string(),
156            title: "User Profile".to_string(),
157        };
158        let rendered = template.render(&names);
159        assert_eq!(rendered, "<text>User Profile</text>");
160    }
161
162    #[test]
163    fn test_template_render_all_variants() {
164        let template = WindowTemplate {
165            content: "{{WINDOW_NAME}} {{WINDOW_NAME_PASCAL}} {{WINDOW_NAME_TITLE}}".to_string(),
166            kind: TemplateKind::RustModule,
167        };
168        let names = WindowNameVariants {
169            snake: "test_window".to_string(),
170            pascal: "TestWindow".to_string(),
171            title: "Test Window".to_string(),
172        };
173        let rendered = template.render(&names);
174        assert_eq!(rendered, "test_window TestWindow Test Window");
175    }
176
177    #[test]
178    fn test_template_render_multiple_occurrences() {
179        let template = WindowTemplate {
180            content: "{{WINDOW_NAME}} and {{WINDOW_NAME}} again".to_string(),
181            kind: TemplateKind::RustModule,
182        };
183        let names = WindowNameVariants {
184            snake: "settings".to_string(),
185            pascal: "Settings".to_string(),
186            title: "Settings".to_string(),
187        };
188        let rendered = template.render(&names);
189        assert_eq!(rendered, "settings and settings again");
190    }
191}