use crate::core::config::GleamElementConstructor;
use crate::e2e::escape::escape_gleam;
use heck::ToSnakeCase;
use super::constructors::render_gleam_element_constructor;
use super::values::json_to_gleam;
#[allow(clippy::too_many_arguments)]
pub(super) fn build_args_and_setup(
input: &serde_json::Value,
args: &[crate::e2e::config::ArgMapping],
fixture_id: &str,
test_documents_path: &str,
element_constructors: &[GleamElementConstructor],
json_object_wrapper: Option<&str>,
module_path: &str,
extra_args: &[String],
options_type: Option<&str>,
options_via: &str,
) -> Option<(Vec<String>, String)> {
if args.is_empty() && extra_args.is_empty() {
return Some((Vec::new(), String::new()));
}
for arg in args {
if arg.arg_type == "json_object" {
let element_type = arg.element_type.as_deref().unwrap_or("");
let has_recipe =
!element_type.is_empty() && element_constructors.iter().any(|r| r.element_type == element_type);
let has_wrapper = json_object_wrapper.is_some();
let has_from_json = options_via == "from_json" && options_type.is_some();
let field = arg.field.strip_prefix("input.").unwrap_or(&arg.field);
let val = input.get(field);
let is_null_optional = arg.optional && matches!(val, None | Some(serde_json::Value::Null));
if !has_recipe && !has_wrapper && !has_from_json && !is_null_optional {
return None;
}
}
}
let mut setup_lines: Vec<String> = Vec::new();
let mut parts: Vec<String> = Vec::new();
let mut bytes_var_counter = 0usize;
for arg in args {
let field = arg.field.strip_prefix("input.").unwrap_or(&arg.field);
let val = input.get(field);
match arg.arg_type.as_str() {
"handle" => {
let name = &arg.name;
let constructor = format!("create_{}", name.to_snake_case());
setup_lines.push(format!(
"let assert Ok({name}) = {module_path}.{constructor}(option.None)"
));
parts.push(name.clone());
continue;
}
"mock_url" => {
let name = &arg.name;
setup_lines.push(format!(
"let {name} = case envoy.get(\"MOCK_SERVER_URL\") {{ Ok(base) -> base <> \"/fixtures/{fixture_id}\" Error(_) -> \"http://localhost:8080/fixtures/{fixture_id}\" }}"
));
parts.push(name.clone());
continue;
}
"file_path" => {
let path = val.and_then(|v| v.as_str()).unwrap_or("");
let full_path = format!("{test_documents_path}/{path}");
parts.push(format!("\"{}\"", escape_gleam(&full_path)));
}
"bytes" => {
let path = val.and_then(|v| v.as_str()).unwrap_or("");
let var_name = if bytes_var_counter == 0 {
"data_bytes__".to_string()
} else {
format!("data_bytes_{bytes_var_counter}__")
};
bytes_var_counter += 1;
let full_path = format!("{test_documents_path}/{path}");
setup_lines.push(format!(
"let assert Ok({var_name}) = e2e_gleam.read_file_bytes(\"{}\")",
escape_gleam(&full_path)
));
parts.push(var_name);
}
"string" if arg.optional => {
match val {
None | Some(serde_json::Value::Null) => {
parts.push("option.None".to_string());
}
Some(serde_json::Value::String(s)) if s.is_empty() => {
parts.push("option.None".to_string());
}
Some(serde_json::Value::String(s)) => {
parts.push(format!("option.Some(\"{}\")", escape_gleam(s)));
}
Some(v) => {
parts.push(format!("option.Some({})", json_to_gleam(v)));
}
}
}
"string" => {
match val {
None | Some(serde_json::Value::Null) => {
parts.push("\"\"".to_string());
}
Some(serde_json::Value::String(s)) => {
parts.push(format!("\"{}\"", escape_gleam(s)));
}
Some(v) => {
parts.push(json_to_gleam(v));
}
}
}
"json_object" => {
if options_via == "from_json" {
if let Some(opts_type) = options_type {
let empty_obj = serde_json::Value::Object(Default::default());
let config_val = val.unwrap_or(&empty_obj);
if !config_val.is_null() {
let snake_opts = opts_type.to_snake_case();
let json_str = serde_json::to_string(config_val).unwrap_or_default();
let escaped = escape_gleam(&json_str);
let var_name = format!("{}_json__", &arg.name);
setup_lines.push(format!(
"let assert Ok({var_name}) = {module_path}.{snake_opts}_from_json(\"{escaped}\")"
));
parts.push(var_name);
}
continue;
}
}
let element_type = arg.element_type.as_deref().unwrap_or("");
let recipe = if element_type.is_empty() {
None
} else {
element_constructors.iter().find(|r| r.element_type == element_type)
};
if let Some(recipe) = recipe {
let items_expr = match val {
Some(serde_json::Value::Array(arr)) => {
let items: Vec<String> = arr
.iter()
.map(|item| render_gleam_element_constructor(item, recipe, test_documents_path))
.collect();
format!("[{}]", items.join(", "))
}
_ => "[]".to_string(),
};
if arg.optional && (val.is_none() || val == Some(&serde_json::Value::Null)) {
parts.push("[]".to_string());
} else {
parts.push(items_expr);
}
} else if arg.optional && (val.is_none() || val == Some(&serde_json::Value::Null)) {
parts.push("option.None".to_string());
} else {
let empty_obj = serde_json::Value::Object(Default::default());
let config_val = val.unwrap_or(&empty_obj);
let json_literal = json_to_gleam(config_val);
let emitted = match json_object_wrapper {
Some(template) => template.replace("{json}", &json_literal),
None => json_literal,
};
parts.push(emitted);
}
}
"int" | "integer" => match val {
None | Some(serde_json::Value::Null) if arg.optional => {}
None | Some(serde_json::Value::Null) => parts.push("0".to_string()),
Some(v) => parts.push(json_to_gleam(v)),
},
"bool" | "boolean" => match val {
Some(serde_json::Value::Bool(true)) => parts.push("True".to_string()),
Some(serde_json::Value::Bool(false)) | None | Some(serde_json::Value::Null) => {
if !arg.optional {
parts.push("False".to_string());
}
}
Some(v) => parts.push(json_to_gleam(v)),
},
_ => {
match val {
None | Some(serde_json::Value::Null) if arg.optional => {}
None | Some(serde_json::Value::Null) => parts.push("Nil".to_string()),
Some(v) => parts.push(json_to_gleam(v)),
}
}
}
}
for extra in extra_args {
parts.push(extra.clone());
}
Some((setup_lines, parts.join(", ")))
}