Skip to main content

ralph_workflow/prompts/
template_context.rs

1//! Template context for prompt generation.
2//!
3//! This module provides the `TemplateContext` struct which holds the
4//! template registry and is passed through the application to enable
5//! user template overrides.
6
7use super::template_registry::TemplateRegistry;
8use std::path::PathBuf;
9
10/// Context for template-based prompt generation.
11///
12/// Provides access to the template registry for loading user-customizable
13/// templates. This context is created from the application config and passed
14/// to prompt generation functions.
15///
16/// # Example
17///
18/// ```ignore
19/// let context = TemplateContext::from_user_templates_dir(Some(PathBuf::from("~/.config/ralph/templates")));
20/// let prompt = prompt_developer_iteration_xml_with_context(
21///     &context,
22///     1, 5, ContextLevel::Normal, "prompt", "plan"
23/// );
24/// ```
25#[derive(Debug, Clone)]
26pub struct TemplateContext {
27    /// Template registry for loading templates.
28    pub(crate) registry: TemplateRegistry,
29}
30
31impl TemplateContext {
32    /// Create a new template context with the given registry.
33    #[must_use]
34    pub const fn new(registry: TemplateRegistry) -> Self {
35        Self { registry }
36    }
37
38    /// Create a template context from a config's user templates directory.
39    ///
40    /// This is the recommended way to create a `TemplateContext` as it
41    /// respects the user's configured templates directory.
42    #[must_use]
43    pub const fn from_user_templates_dir(user_templates_dir: Option<PathBuf>) -> Self {
44        Self::new(TemplateRegistry::new(user_templates_dir))
45    }
46
47    /// Get a reference to the template registry.
48    #[must_use]
49    pub const fn registry(&self) -> &TemplateRegistry {
50        &self.registry
51    }
52}
53
54impl Default for TemplateContext {
55    fn default() -> Self {
56        Self::new(TemplateRegistry::default())
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_template_context_creation() {
66        let context = TemplateContext::new(TemplateRegistry::new(None));
67        // Should not have user templates since we passed None
68        assert!(!context
69            .registry()
70            .has_user_template("developer_iteration_xml"));
71    }
72
73    #[test]
74    fn test_template_context_default() {
75        let context = TemplateContext::default();
76        // Default should work and access templates
77        assert!(context
78            .registry()
79            .template_exists("developer_iteration_xml"));
80    }
81
82    #[test]
83    fn test_template_context_from_user_templates_dir() {
84        let custom_dir = PathBuf::from("/custom/templates");
85        let context = TemplateContext::from_user_templates_dir(Some(custom_dir));
86        // Context should be created successfully
87        assert!(!context
88            .registry()
89            .has_user_template("developer_iteration_xml"));
90    }
91
92    #[test]
93    fn test_template_context_from_user_templates_dir_none() {
94        let context = TemplateContext::from_user_templates_dir(None);
95        // Should not have user templates
96        assert!(!context
97            .registry()
98            .has_user_template("developer_iteration_xml"));
99    }
100
101    #[test]
102    fn test_template_context_registry_access() {
103        let context = TemplateContext::default();
104        let _registry = context.registry();
105        // Should be able to access registry methods
106        assert!(!TemplateRegistry::all_template_names().is_empty());
107    }
108
109    #[test]
110    fn test_template_context_clone() {
111        let context = TemplateContext::default();
112        let _cloned = context.clone();
113        // Verify clone compiles and original still works
114        assert!(context
115            .registry()
116            .template_exists("developer_iteration_xml"));
117    }
118
119    #[test]
120    fn test_template_context_get_template() {
121        let context = TemplateContext::default();
122        // Should be able to get templates
123        let result = context.registry().get_template("developer_iteration_xml");
124        assert!(result.is_ok());
125    }
126
127    #[test]
128    fn test_template_context_template_source() {
129        let context = TemplateContext::default();
130        // Should report embedded source for templates that don't have user overrides
131        assert_eq!(
132            context
133                .registry()
134                .template_source("developer_iteration_xml"),
135            "embedded"
136        );
137    }
138
139    #[test]
140    fn test_template_context_all_templates() {
141        let _context = TemplateContext::default();
142        // Should be able to list all templates
143        let names = TemplateRegistry::all_template_names();
144        assert!(names.len() > 10);
145        assert!(names.contains(&"developer_iteration_xml".to_string()));
146    }
147
148    #[test]
149    fn test_template_context_all_templates_not_empty() {
150        let _context = TemplateContext::default();
151        let names = TemplateRegistry::all_template_names();
152        assert!(!names.is_empty());
153    }
154}