ralph_workflow/prompts/
template_macros.rs

1//! Template enforcement macros for ensuring template usage conventions.
2//!
3//! This module provides compile-time and runtime tools to enforce that all
4//! AI communication prompts come from template files, not inline strings.
5
6#![deny(unsafe_code)]
7
8/// Macro to verify that a string comes from a template file.
9///
10/// This macro provides compile-time assurance by using `include_str!` which
11/// only works with files at compile time. This prevents inline prompt strings
12/// from being accidentally used.
13///
14/// # Example
15///
16/// ```ignore
17/// use crate::prompts::template_macros::include_template;
18///
19/// // This works - loads from template file
20/// let template = include_template!("templates/my_prompt.txt");
21///
22/// // This would NOT work with include_template! macro - prevents inline templates
23/// // let inline = "Hello {{NAME}}";  // Cannot be passed to include_template!
24/// ```
25///
26/// # Enforcement
27///
28/// - The macro uses `concat!` with `include_str!` to ensure the template
29///   path is known at compile time
30/// - Returns a `&'static str` which makes it clear this is compiled content
31#[macro_export]
32macro_rules! include_template {
33    ($path:expr) => {
34        include_str!(concat!("../prompts/templates/", $path))
35    };
36}
37
38/// Macro to verify a template file exists and contains expected content.
39///
40/// This is primarily used in tests to verify template structure.
41///
42/// # Example
43///
44/// ```ignore
45/// assert_template_exists!("templates/my_prompt.txt");
46/// assert_template_has_variable!("templates/my_prompt.txt", "CONTEXT");
47/// ```
48#[macro_export]
49macro_rules! assert_template_exists {
50    ($path:expr) => {
51        let content = include_str!(concat!("../prompts/templates/", $path));
52        assert!(!content.is_empty(), "Template file {} is empty", $path);
53    };
54}
55
56#[macro_export]
57macro_rules! assert_template_has_variable {
58    ($path:expr, $var:expr) => {
59        let content = include_str!(concat!("../prompts/templates/", $path));
60        let var_pattern = concat!("{{", $var, "}}");
61        assert!(
62            content.contains(var_pattern) || content.contains(concat!("{{ ", $var, " }}")),
63            "Template {} does not contain variable {{{}}}",
64            $path,
65            $var
66        );
67    };
68}
69
70#[cfg(test)]
71mod tests {
72    #[test]
73    fn test_include_template_macro() {
74        // Test that we can include a template using the macro
75        let _ = include_template!("conflict_resolution.txt");
76    }
77
78    #[test]
79    fn test_assert_template_exists() {
80        assert_template_exists!("conflict_resolution.txt");
81    }
82
83    #[test]
84    fn test_assert_template_has_variable() {
85        assert_template_has_variable!("conflict_resolution.txt", "CONTEXT");
86        assert_template_has_variable!("conflict_resolution.txt", "CONFLICTS");
87    }
88
89    #[test]
90    fn test_inline_template_detection() {
91        // Test that we can detect potential inline templates in strings
92        // These patterns suggest inline prompt content that should be in templates
93
94        let suspicious_patterns = [
95            // Multi-line raw string literals with prompt-like content
96            r"You are a",
97            r"Please review",
98            r"Generate a",
99            // Long format strings that look like prompts
100            "## Instructions",
101            "### Task",
102            "# PROMPT",
103            // JSON/structured prompt patterns
104            r#"{"role": "developer""#,
105        ];
106
107        // This test documents what patterns to look for
108        // In a real scenario, you'd use a build script or clippy lint to detect these
109        for pattern in suspicious_patterns {
110            assert!(!pattern.is_empty(), "Pattern should not be empty");
111        }
112    }
113}