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_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.registry().has_user_template("developer_iteration"));
69    }
70
71    #[test]
72    fn test_template_context_default() {
73        let context = TemplateContext::default();
74        // Default should work and access templates
75        assert!(context.registry().template_exists("developer_iteration"));
76    }
77
78    #[test]
79    fn test_template_context_from_user_templates_dir() {
80        let custom_dir = PathBuf::from("/custom/templates");
81        let context = TemplateContext::from_user_templates_dir(Some(custom_dir));
82        // Context should be created successfully
83        assert!(!context.registry().has_user_template("developer_iteration"));
84    }
85
86    #[test]
87    fn test_template_context_from_user_templates_dir_none() {
88        let context = TemplateContext::from_user_templates_dir(None);
89        // Should not have user templates
90        assert!(!context.registry().has_user_template("developer_iteration"));
91    }
92
93    #[test]
94    fn test_template_context_registry_access() {
95        let context = TemplateContext::default();
96        let _registry = context.registry();
97        // Should be able to access registry methods
98        assert!(!TemplateRegistry::all_template_names().is_empty());
99    }
100
101    #[test]
102    fn test_template_context_clone() {
103        let context = TemplateContext::default();
104        let _cloned = context.clone();
105        // Verify clone compiles and original still works
106        assert!(context.registry().template_exists("developer_iteration"));
107    }
108
109    #[test]
110    fn test_template_context_get_template() {
111        let context = TemplateContext::default();
112        // Should be able to get templates
113        let result = context.registry().get_template("developer_iteration");
114        assert!(result.is_ok());
115    }
116
117    #[test]
118    fn test_template_context_template_source() {
119        let context = TemplateContext::default();
120        // Should report embedded source for templates that don't have user overrides
121        assert_eq!(
122            context.registry().template_source("developer_iteration"),
123            "embedded"
124        );
125    }
126
127    #[test]
128    fn test_template_context_all_templates() {
129        let _context = TemplateContext::default();
130        // Should be able to list all templates
131        let names = TemplateRegistry::all_template_names();
132        assert!(names.len() > 10);
133        assert!(names.contains(&"developer_iteration".to_string()));
134    }
135
136    #[test]
137    fn test_template_context_all_templates_not_empty() {
138        let _context = TemplateContext::default();
139        let names = TemplateRegistry::all_template_names();
140        assert!(!names.is_empty());
141    }
142}