use crate::e2e::codegen::field_skip::{FieldSkip, nested_wildcard_skip_line};
use crate::e2e::escape::go_string_literal;
use crate::e2e::fixture::Assertion;
use std::fmt::Write as FmtWrite;
use super::super::json_values::json_to_go;
use super::AssertionRenderContext;
pub(super) fn render_wildcard_or_unavailable_field(
out: &mut String,
assertion: &Assertion,
context: &AssertionRenderContext<'_>,
) -> bool {
if context.result_is_simple {
return false;
}
let Some(f) = assertion.field.as_deref() else {
return false;
};
if f.is_empty() {
return false;
}
let field_resolver = context.field_resolver;
if !field_resolver.is_valid_for_result(f) {
let _ = writeln!(out, "\t// skipped: {}", FieldSkip::NotAvailableOnResultType.message(f));
return true;
}
if let Some((array_part, elem_part)) = field_resolver.wildcard_split(f) {
render_wildcard_assertion(out, assertion, context, f, &array_part, &elem_part);
return true;
}
false
}
pub(super) fn emit_non_empty_precondition(out: &mut String, array_expr: &str) {
let fatal_line = format!(
"\t\tt.Fatalf(\"expected non-empty %s\", {})",
go_string_literal(array_expr)
);
if out.lines().any(|line| line == fatal_line) {
return;
}
let _ = writeln!(out, "\tif len({array_expr}) == 0 {{");
let _ = writeln!(out, "{fatal_line}");
let _ = writeln!(out, "\t}}");
}
fn wildcard_local_suffix(assertion: &Assertion) -> String {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
assertion.assertion_type.hash(&mut hasher);
assertion.field.hash(&mut hasher);
assertion
.value
.as_ref()
.map(std::string::ToString::to_string)
.unwrap_or_default()
.hash(&mut hasher);
assertion
.values
.as_ref()
.map(|vs| {
vs.iter()
.map(std::string::ToString::to_string)
.collect::<Vec<_>>()
.join(",")
})
.unwrap_or_default()
.hash(&mut hasher);
format!("{:x}", hasher.finish() & 0xffff_ffff)
}
fn emit_wildcard_scan(out: &mut String, array_accessor: &str, local: &str, cond: &str) {
let _ = writeln!(out, "\t{local} := false");
let _ = writeln!(out, "\tfor _, e := range {array_accessor} {{");
let _ = writeln!(out, "\t\tif {cond} {{");
let _ = writeln!(out, "\t\t\t{local} = true");
let _ = writeln!(out, "\t\t\tbreak");
let _ = writeln!(out, "\t\t}}");
let _ = writeln!(out, "\t}}");
}
fn render_wildcard_single_value(
out: &mut String,
assertion: &Assertion,
field: &str,
array_accessor: &str,
elem_accessor: &str,
suffix: &str,
) {
let expected = assertion.value.as_ref().expect("guarded by the caller's match arm");
let go_val = json_to_go(expected);
let local = format!("found{suffix}");
let cond = format!("strings.Contains(fmt.Sprintf(\"%v\", {elem_accessor}), {go_val})");
emit_wildcard_scan(out, array_accessor, &local, &cond);
if assertion.assertion_type == "contains" {
let _ = writeln!(out, "\tif !{local} {{");
let _ = writeln!(
out,
"\t\tt.Errorf(\"expected some element of '{field}' to contain %v\", {go_val})"
);
} else {
let _ = writeln!(out, "\tif {local} {{");
let _ = writeln!(
out,
"\t\tt.Errorf(\"expected no element of '{field}' to contain %v\", {go_val})"
);
}
let _ = writeln!(out, "\t}}");
}
fn render_wildcard_multi_value(
out: &mut String,
assertion: &Assertion,
field: &str,
array_accessor: &str,
elem_accessor: &str,
suffix: &str,
) {
let Some(values) = &assertion.values else {
let _ = writeln!(out, "\t// skipped: '{field}' traversal assertion has no values");
return;
};
let negated = assertion.assertion_type == "not_contains";
for (i, val) in values.iter().enumerate() {
let go_val = json_to_go(val);
let local = format!("found{suffix}v{i}");
let cond = format!("strings.Contains(fmt.Sprintf(\"%v\", {elem_accessor}), {go_val})");
emit_wildcard_scan(out, array_accessor, &local, &cond);
if negated {
let _ = writeln!(out, "\tif {local} {{");
let _ = writeln!(
out,
"\t\tt.Errorf(\"expected no element of '{field}' to contain %v\", {go_val})"
);
} else {
let _ = writeln!(out, "\tif !{local} {{");
let _ = writeln!(
out,
"\t\tt.Errorf(\"expected some element of '{field}' to contain %v\", {go_val})"
);
}
let _ = writeln!(out, "\t}}");
}
}
fn render_wildcard_non_empty(out: &mut String, field: &str, array_accessor: &str, elem_accessor: &str, suffix: &str) {
let local = format!("found{suffix}");
let cond = format!("fmt.Sprintf(\"%v\", {elem_accessor}) != \"\"");
emit_wildcard_scan(out, array_accessor, &local, &cond);
let _ = writeln!(out, "\tif !{local} {{");
let _ = writeln!(
out,
"\t\tt.Errorf(\"expected some element of '{field}' to be non-empty\")"
);
let _ = writeln!(out, "\t}}");
}
pub(super) fn render_wildcard_assertion(
out: &mut String,
assertion: &Assertion,
context: &AssertionRenderContext<'_>,
field: &str,
array_part: &str,
elem_part: &str,
) {
if let Some(line) = nested_wildcard_skip_line("\t", "//", field, elem_part) {
let _ = writeln!(out, "{line}");
return;
}
let field_resolver = context.field_resolver;
let array_accessor = if array_part.is_empty() {
context.effective_result_var.to_string()
} else {
field_resolver.accessor(array_part, "go", context.effective_result_var)
};
let elem_accessor = field_resolver.element_accessor(elem_part, "go", "e");
let suffix = wildcard_local_suffix(assertion);
match assertion.assertion_type.as_str() {
"contains" | "not_contains" if assertion.value.is_some() => {
render_wildcard_single_value(out, assertion, field, &array_accessor, &elem_accessor, &suffix);
}
"contains" | "contains_all" | "not_contains" => {
render_wildcard_multi_value(out, assertion, field, &array_accessor, &elem_accessor, &suffix);
}
"not_empty" => {
render_wildcard_non_empty(out, field, &array_accessor, &elem_accessor, &suffix);
}
other => {
let _ = writeln!(
out,
"\t// skipped: unsupported traversal assertion '{other}' on '{field}'"
);
}
}
}