use std::collections::HashMap;
pub fn interpolate_path_params(template: &str, params: &HashMap<String, String>) -> String {
let mut result = String::with_capacity(template.len());
let mut chars = template.chars().peekable();
while let Some(c) = chars.next() {
if c == '{' {
let mut name = String::new();
let mut closed = false;
for next in chars.by_ref() {
if next == '}' {
closed = true;
break;
}
name.push(next);
}
if closed {
if let Some(value) = params.get(&name) {
result.push_str(value);
} else {
result.push('{');
result.push_str(&name);
result.push('}');
}
} else {
result.push('{');
result.push_str(&name);
}
} else {
result.push(c);
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
fn params(pairs: &[(&str, &str)]) -> HashMap<String, String> {
pairs
.iter()
.map(|(k, v)| ((*k).to_string(), (*v).to_string()))
.collect()
}
#[test]
fn substitutes_single_placeholder() {
let p = params(&[("id", "abc-123")]);
assert_eq!(interpolate_path_params("Ledger:{id}", &p), "Ledger:abc-123");
}
#[test]
fn substitutes_multiple_placeholders() {
let p = params(&[("tenant", "t1"), ("id", "x")]);
assert_eq!(interpolate_path_params("Doc:{tenant}/{id}", &p), "Doc:t1/x");
}
#[test]
fn leaves_unknown_placeholder_literal() {
let p = params(&[]);
assert_eq!(
interpolate_path_params("Ledger:{id}", &p),
"Ledger:{id}",
"missing key should leave placeholder literal so Cedar parse fails loudly"
);
}
#[test]
fn handles_no_placeholders() {
let p = params(&[]);
assert_eq!(interpolate_path_params("Platform", &p), "Platform");
}
#[test]
fn handles_unmatched_open_brace() {
let p = params(&[]);
assert_eq!(
interpolate_path_params("Bad:{unterm", &p),
"Bad:{unterm",
"unmatched '{{' preserved literally"
);
}
}