pub(super) fn match_uri_template(
template: &str,
uri: &str,
) -> Option<std::collections::HashMap<String, String>> {
let mut vars = std::collections::HashMap::new();
let mut t_pos = 0;
let mut u_pos = 0;
let t_bytes = template.as_bytes();
let u_bytes = uri.as_bytes();
while t_pos < t_bytes.len() {
if t_bytes[t_pos] == b'{' {
let close = template[t_pos..].find('}')? + t_pos;
let var_name = &template[t_pos + 1..close];
t_pos = close + 1;
let next_literal = if t_pos < t_bytes.len() {
let lit_start = t_pos;
let lit_end = template[t_pos..]
.find('{')
.map(|i| t_pos + i)
.unwrap_or(t_bytes.len());
Some(&template[lit_start..lit_end])
} else {
None
};
let value_end = match next_literal {
Some(lit) if !lit.is_empty() => uri[u_pos..].find(lit).map(|i| u_pos + i)?,
_ => u_bytes.len(),
};
vars.insert(var_name.to_string(), uri[u_pos..value_end].to_string());
u_pos = value_end;
} else {
if u_pos >= u_bytes.len() || t_bytes[t_pos] != u_bytes[u_pos] {
return None;
}
t_pos += 1;
u_pos += 1;
}
}
if u_pos == u_bytes.len() {
Some(vars)
} else {
None
}
}
pub(super) fn uri_template_variables(template: &str) -> Vec<String> {
let mut variables = Vec::new();
let mut cursor = template;
while let Some(open) = cursor.find('{') {
cursor = &cursor[open + 1..];
let Some(close) = cursor.find('}') else {
break;
};
let name = cursor[..close].trim();
if !name.is_empty() && !variables.iter().any(|variable| variable == name) {
variables.push(name.to_string());
}
cursor = &cursor[close + 1..];
}
variables
}