vika_cli/templates/
resolver.rs

1use crate::error::Result;
2use crate::templates::loader::TemplateLoader;
3use crate::templates::registry::TemplateId;
4use std::path::Path;
5
6/// Resolves templates with priority: user override > built-in.
7pub struct TemplateResolver {
8    project_root: Option<std::path::PathBuf>,
9}
10
11impl TemplateResolver {
12    /// Create a new template resolver.
13    pub fn new(project_root: Option<&Path>) -> Self {
14        Self {
15            project_root: project_root.map(|p| p.to_path_buf()),
16        }
17    }
18
19    /// Resolve a template by ID, checking user override first, then built-in.
20    pub fn resolve(&self, template_id: TemplateId) -> Result<String> {
21        let template_name = template_id.name();
22
23        // First check for user override
24        if let Some(ref project_root) = self.project_root {
25            if let Some(user_template) = TemplateLoader::load_user(template_name, project_root)? {
26                return Ok(user_template);
27            }
28        }
29
30        // Fallback to built-in template
31        TemplateLoader::load_builtin(template_name)
32    }
33
34    /// Check if a template is overridden by user.
35    pub fn is_overridden(&self, template_id: TemplateId) -> bool {
36        if let Some(ref project_root) = self.project_root {
37            let template_name = template_id.name();
38            let user_template_dir = project_root.join(".vika").join("templates");
39            let template_path = user_template_dir.join(format!("{}.tera", template_name));
40            template_path.exists()
41        } else {
42            false
43        }
44    }
45
46    /// Get list of all templates with their override status.
47    pub fn list_templates(&self) -> Result<Vec<(String, bool)>> {
48        let builtin = TemplateLoader::list_builtin();
49        let user = if let Some(ref project_root) = self.project_root {
50            TemplateLoader::list_user(project_root)?
51        } else {
52            Vec::new()
53        };
54
55        let mut result: Vec<(String, bool)> = builtin
56            .iter()
57            .map(|name| {
58                let overridden = user.contains(name);
59                (name.clone(), overridden)
60            })
61            .collect();
62
63        // Add user-only templates (if any)
64        for name in user {
65            if !result.iter().any(|(n, _)| n == &name) {
66                result.push((name, true));
67            }
68        }
69
70        Ok(result)
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    use tempfile::TempDir;
78
79    #[test]
80    fn test_resolve_builtin() {
81        let resolver = TemplateResolver::new(None);
82        let result = resolver.resolve(TemplateId::TypeInterface);
83        assert!(result.is_ok());
84    }
85
86    #[test]
87    fn test_is_overridden_false() {
88        let temp_dir = TempDir::new().unwrap();
89        let resolver = TemplateResolver::new(Some(temp_dir.path()));
90        assert!(!resolver.is_overridden(TemplateId::TypeInterface));
91    }
92
93    #[test]
94    fn test_list_templates() {
95        let resolver = TemplateResolver::new(None);
96        let templates = resolver.list_templates().unwrap();
97        assert!(!templates.is_empty());
98    }
99}