pub const PLACEHOLDER_NAMES: [&str; 4] = ["artifacts", "report", "journal", "log"];
pub fn placeholders_in(value: &str) -> Vec<String> {
let mut names = Vec::new();
let mut rest = value;
while let Some(at) = rest.find("{{") {
let after = &rest[at + 2..];
let Some(end) = after.find("}}") else { break };
names.push(after[..end].to_string());
rest = &after[end + 2..];
}
names
}
pub(crate) fn replace_placeholders<F>(value: &str, resolve: F) -> String
where
F: Fn(&str) -> Option<String>,
{
if !value.contains("{{") {
return value.to_string();
}
let mut out = String::with_capacity(value.len());
let mut rest = value;
while let Some(at) = rest.find("{{") {
out.push_str(&rest[..at]);
let after = &rest[at + 2..];
let Some(end) = after.find("}}") else {
out.push_str("{{");
rest = after;
continue;
};
let name = &after[..end];
match resolve(name) {
Some(path) => out.push_str(&path),
None => {
out.push_str("{{");
out.push_str(name);
out.push_str("}}");
}
}
rest = &after[end + 2..];
}
out.push_str(rest);
out
}