proplate_core/template/
interpolation.rs

1use regex::Regex;
2
3use crate::gen::bootstrap::Context;
4
5const INTERPOLATION_PATTERN: &str = r"(\\*)\$([a-zA-Z_][a-zA-Z0-9_]*)\b";
6
7fn create_regex() -> Regex {
8  Regex::new(INTERPOLATION_PATTERN).unwrap()
9}
10
11/// Regex-based var binding replacement inside of a string slice
12///
13/// # Example
14/// ```
15/// use proplate_core::{gen::bootstrap::Context, template::interpolation::interpolate};
16///
17/// let mut ctx = Context::new();
18/// ctx.insert("name".to_string(), "proplate".to_string());
19/// println!("{}", interpolate("Hello $name", &ctx)); // "Hello proplate"
20/// ````
21pub fn interpolate(source: &str, ctx: &Context) -> String {
22  let re = create_regex();
23
24  let mut result = String::new();
25  let mut last_end = 0;
26
27  for caps in re.captures_iter(source) {
28    let escape = caps.get(1).unwrap().as_str();
29    let name = caps.get(2).unwrap().as_str();
30
31    let escape_len = escape.len();
32    let value = ctx.get(name).map(|v| v.as_str()).unwrap_or("");
33
34    let unescaped_match = &source[last_end..caps.get(0).unwrap().start()];
35    result.push_str(unescaped_match);
36
37    if escape_len % 2 != 0 {
38      result.push_str(&format!(
39        "{}${}",
40        escape[..escape_len - 1].to_string(),
41        name
42      ));
43    } else {
44      result.push_str(&format!("{}{}", escape, value));
45    }
46
47    last_end = caps.get(0).unwrap().end();
48  }
49
50  result.push_str(&source[last_end..]);
51  result
52}
53
54pub trait Interpolate {
55  fn interpolate(&self, ctx: &Context) -> Self;
56}
57
58/// Allow to use
59impl Interpolate for String {
60  fn interpolate(&self, ctx: &Context) -> Self {
61    interpolate(self, ctx)
62  }
63}