hcl/
util.rs

1/// Scan `s` for sequences that introduce a template interpolation or directive. Returns `true`
2/// once it found one of these start markers, `false` otherwise.
3///
4/// This function only looks for start markers and does not check if the template is actually
5/// valid.
6#[inline]
7pub fn is_templated(s: &str) -> bool {
8    if s.len() < 3 {
9        return false;
10    }
11
12    let mut skip_next = false;
13
14    // Because calling `s.contains("${")` would also match escaped interpolations (`$${`) a
15    // window iterator is used here to detect and ignore these. The same applies to escaped
16    // directives.
17    for window in s.as_bytes().windows(3) {
18        if skip_next {
19            skip_next = false;
20            continue;
21        }
22
23        match window {
24            [b'$', b'$', b'{'] | [b'%', b'%', b'{'] => {
25                // The next window would incorrectly match the next arm, so it must be
26                // skipped.
27                skip_next = true;
28            }
29            [b'$' | b'%', b'{', _] => return true,
30            _ => {}
31        }
32    }
33
34    false
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn test_is_templated() {
43        assert!(is_templated("${a}"));
44        assert!(is_templated("${\"a\"}"));
45        assert!(is_templated("%{ if foo }foo%{ else }bar%{ endif }"));
46        assert!(is_templated("$${ introduces an ${\"interpolation\"}"));
47        assert!(!is_templated(
48            "escaped directive %%{ if foo }foo%%{ else }bar%%{ endif }"
49        ));
50        assert!(!is_templated("escaped interpolation $${a}"));
51    }
52}