use crate::utils::aliases::JsonMap;
use crate::utils::aliases::TResult;
use tera::Value;
pub fn form_filter(value: &Value, args: &JsonMap) -> TResult {
if let Some(field_name) = args.get("field").and_then(|v| v.as_str()) {
let field_html = render_field(value, field_name)?;
if is_last_field_by_index(value, field_name) {
let scripts = render_scripts(value).unwrap_or_default();
if !scripts.is_empty() {
let combined = format!(
"{}\n<!-- Form scripts (auto-injected after last field) -->\n{}",
field_html.as_str().unwrap_or(""),
scripts
);
return Ok(Value::String(combined));
}
}
return Ok(field_html);
}
render_form_html(value)
}
fn is_last_field_by_index(value: &Value, field_name: &str) -> bool {
let fields = value
.get("fields")
.or_else(|| value.get("form").and_then(|f| f.get("fields")));
let fields_obj = match fields.and_then(|f| f.as_object()) {
Some(obj) => obj,
None => return false,
};
let max_index = fields_obj
.values()
.filter_map(|field| field.get("index"))
.filter_map(|idx| idx.as_u64())
.max();
let current_index = fields_obj
.get(field_name)
.and_then(|field| field.get("index"))
.and_then(|idx| idx.as_u64());
match (max_index, current_index) {
(Some(max), Some(current)) => max == current,
_ => false,
}
}
fn render_scripts(value: &Value) -> Option<String> {
let js_files = value
.get("js_files")
.or_else(|| value.get("form").and_then(|f| f.get("js_files")))?;
let files = js_files.as_array()?;
if files.is_empty() {
return None;
}
let scripts: Vec<String> = files
.iter()
.filter_map(|f| f.as_str())
.map(|file| format!(r#"<script src="/static/{}" defer></script>"#, file))
.collect();
if scripts.is_empty() {
None
} else {
Some(scripts.join("\n"))
}
}
fn render_field(value: &Value, field_name: &str) -> TResult {
let rendered_fields = find_rendered_fields(value);
if let Some(fields) = rendered_fields {
if let Some(html) = fields.get(field_name).and_then(|v| v.as_str()) {
return Ok(Value::String(html.to_string()));
}
}
Err(tera::Error::msg(format!(
"Field '{}' not found in form",
field_name
)))
}
fn find_rendered_fields(value: &Value) -> Option<&Value> {
value
.get("rendered_fields")
.or_else(|| value.get("form").and_then(|f| f.get("rendered_fields")))
}
fn render_form_html(value: &Value) -> TResult {
let html = find_html(value);
if let Some(html_str) = html {
return Ok(Value::String(html_str.to_string()));
}
Err(tera::Error::msg(
"Cannot render form: no 'html' field found",
))
}
fn find_html(value: &Value) -> Option<String> {
if let Some(html) = value.get("html").and_then(|v| v.as_str()) {
return Some(html.to_string());
}
if let Some(form) = value.get("form") {
if let Some(html) = form.get("html").and_then(|v| v.as_str()) {
return Some(html.to_string());
}
}
if let Some(fields) = find_rendered_fields(value) {
if let Some(obj) = fields.as_object() {
let html: Vec<&str> = obj.values().filter_map(|v| v.as_str()).collect();
if !html.is_empty() {
return Some(html.join("\n"));
}
}
}
None
}