Skip to main content

rumdl_lib/utils/
jinja_utils.rs

1use regex::Regex;
2use std::sync::LazyLock;
3
4// Jinja2 template delimiters
5static JINJA_EXPRESSION_REGEX: LazyLock<Regex> =
6    LazyLock::new(|| Regex::new(r"\{\{.*?\}\}").expect("Failed to compile Jinja expression regex"));
7
8static JINJA_STATEMENT_REGEX: LazyLock<Regex> =
9    LazyLock::new(|| Regex::new(r"\{%.*?%\}").expect("Failed to compile Jinja statement regex"));
10
11/// Pre-compute all Jinja template ranges in the content
12pub fn find_jinja_ranges(content: &str) -> Vec<(usize, usize)> {
13    let mut ranges = Vec::new();
14
15    // Collect Jinja expressions {{ ... }}
16    for mat in JINJA_EXPRESSION_REGEX.find_iter(content) {
17        ranges.push((mat.start(), mat.end()));
18    }
19
20    // Collect Jinja statements {% ... %}
21    for mat in JINJA_STATEMENT_REGEX.find_iter(content) {
22        ranges.push((mat.start(), mat.end()));
23    }
24
25    // Sort by start position for efficient binary search later
26    ranges.sort_by_key(|r| r.0);
27    ranges
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33
34    #[test]
35    fn test_find_jinja_ranges_expressions() {
36        let content = "Some text {{ variable }} more text";
37        let ranges = find_jinja_ranges(content);
38        assert_eq!(ranges.len(), 1);
39        assert_eq!(&content[ranges[0].0..ranges[0].1], "{{ variable }}");
40    }
41
42    #[test]
43    fn test_find_jinja_ranges_statements() {
44        let content = "{% if condition %} text {% endif %}";
45        let ranges = find_jinja_ranges(content);
46        assert_eq!(ranges.len(), 2);
47        assert_eq!(&content[ranges[0].0..ranges[0].1], "{% if condition %}");
48        assert_eq!(&content[ranges[1].0..ranges[1].1], "{% endif %}");
49    }
50
51    #[test]
52    fn test_find_jinja_ranges_complex_expression() {
53        let content = "{{ pd_read_csv()[index] | filter }}";
54        let ranges = find_jinja_ranges(content);
55        assert_eq!(ranges.len(), 1);
56        assert_eq!(ranges[0], (0, content.len()));
57    }
58
59    #[test]
60    fn test_find_jinja_ranges_sorted() {
61        let content = "{% if x %} foo {{ bar }} baz {% endif %}";
62        let ranges = find_jinja_ranges(content);
63        assert_eq!(ranges.len(), 3);
64        assert!(ranges[0].0 < ranges[1].0);
65        assert!(ranges[1].0 < ranges[2].0);
66    }
67}