use super::super::pattern::validate_reverse_param;
use aho_corasick::AhoCorasick;
use reinhardt_core::exception::{Error, Result};
use std::collections::HashMap;
pub type ReverseError = Error;
pub type ReverseResult<T> = Result<T>;
pub fn extract_param_names(pattern: &str) -> Vec<String> {
let mut names = Vec::new();
let mut chars = pattern.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '{' {
let name: String = chars.by_ref().take_while(|&c| c != '}').collect();
if !name.is_empty() {
names.push(name);
}
}
}
names
}
pub fn try_reverse_with_aho_corasick(
pattern: &str,
params: &HashMap<String, String>,
) -> ReverseResult<String> {
let param_names = extract_param_names(pattern);
if param_names.is_empty() {
return Ok(pattern.to_string());
}
for (name, value) in params {
if !validate_reverse_param(value) {
return Err(Error::Validation(format!(
"Invalid parameter value for '{}': contains dangerous characters (path separators, query delimiters, or encoded sequences)",
name
)));
}
}
let placeholders: Vec<String> = param_names
.iter()
.map(|name| format!("{{{}}}", name))
.collect();
let ac = match AhoCorasick::new(&placeholders) {
Ok(ac) => ac,
Err(_) => {
return try_reverse_single_pass(pattern, params);
}
};
let mut replacements = Vec::new();
for mat in ac.find_iter(pattern) {
let param_name = ¶m_names[mat.pattern()];
if let Some(value) = params.get(param_name) {
replacements.push((mat.start(), mat.end(), value.clone()));
} else {
replacements.push((mat.start(), mat.end(), format!("{{{}}}", param_name)));
}
}
let mut result = pattern.to_string();
for (start, end, value) in replacements.into_iter().rev() {
result.replace_range(start..end, &value);
}
Ok(result)
}
pub fn try_reverse_single_pass(
pattern: &str,
params: &HashMap<String, String>,
) -> ReverseResult<String> {
for (name, value) in params {
if !validate_reverse_param(value) {
return Err(Error::Validation(format!(
"Invalid parameter value for '{}': contains dangerous characters (path separators, query delimiters, or encoded sequences)",
name
)));
}
}
let mut result = String::with_capacity(pattern.len());
let mut chars = pattern.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '{' {
let param_name: String = chars.by_ref().take_while(|&c| c != '}').collect();
if let Some(value) = params.get(¶m_name) {
result.push_str(value);
} else {
result.push('{');
result.push_str(¶m_name);
result.push('}');
}
} else {
result.push(ch);
}
}
Ok(result)
}