use crate::utils::aliases::{ARlockmap, JsonMap};
use serde_json::Value;
use tera::{Function, Kwargs, State, TeraResult};
pub struct LinkFunction {
pub url_registry: ARlockmap,
}
impl Function<TeraResult<String>> for LinkFunction {
fn call(&self, kwargs: Kwargs, _: &State) -> TeraResult<String> {
link_function(&kwargs, &self.url_registry)
}
}
fn link_function(kwargs: &Kwargs, url_registry: &ARlockmap) -> TeraResult<String> {
let link_name = kwargs.must_get::<&str>("link")?;
let map = url_registry.read().unwrap_or_else(|e| e.into_inner());
let pattern = map.get(link_name).cloned().ok_or_else(|| {
tera::Error::message(format!(
"Route '{}' not found.\n\nVerify that the route exists in your routes!",
link_name
))
})?;
drop(map);
let args: JsonMap = kwargs.deserialize()?;
let mut result = args
.iter()
.filter(|(k, _)| *k != "link" && *k != "query")
.fold(pattern, |acc, (k, v)| {
let value = match v {
Value::String(s) => urlencoding::encode(s).into_owned(),
Value::Number(n) => n.to_string(),
_ => urlencoding::encode(&v.to_string()).into_owned(),
};
acc.replace(&format!("{{{}}}", k), &value)
});
if let Some(query_val) = args.get("query") {
let query_str = match query_val {
Value::String(s) => s.clone(),
Value::Object(map) => {
map.iter()
.map(|(k, v)| {
let v_encoded: String = match v {
Value::String(s) => urlencoding::encode(s).into_owned(),
Value::Number(n) => n.to_string(),
_ => urlencoding::encode(&v.to_string()).into_owned(),
};
format!("{}={}", k, v_encoded)
})
.collect::<Vec<_>>()
.join("&")
}
_ => query_val.to_string(),
};
if !query_str.is_empty() {
result.push('?');
result.push_str(&query_str);
}
}
Ok(result)
}