use std::fmt::Write as FmtWrite;
use crate::e2e::field_access::FieldResolver;
use crate::e2e::fixture::Assertion;
#[allow(clippy::too_many_arguments)]
pub(super) fn render_scalar_pipeline(
out: &mut String,
assertion: &Assertion,
field_resolver: &FieldResolver,
result_var: &str,
result_is_simple: bool,
result_is_option: bool,
enum_fields: &std::collections::HashSet<String>,
json_scalar_fields: &std::collections::HashSet<String>,
fields_c_types: &std::collections::HashMap<String, String>,
kotlin_android_style: bool,
is_streaming: bool,
not_error_may_assert_presence: bool,
) {
let (
field_expr,
string_field_expr,
nonnull_field_expr,
string_expr,
field_is_optional,
field_is_collection,
field_is_long,
) = super::assertion_scalar_context::compute_scalar_context(
assertion,
field_resolver,
result_var,
result_is_simple,
result_is_option,
enum_fields,
json_scalar_fields,
fields_c_types,
kotlin_android_style,
);
render_scalar_assertion(
out,
assertion,
&field_expr,
&string_field_expr,
&nonnull_field_expr,
&string_expr,
field_is_optional,
field_is_collection,
field_is_long,
result_var,
result_is_simple,
result_is_option,
kotlin_android_style,
is_streaming,
not_error_may_assert_presence,
);
}
const VALUE_ASSERTION_TYPES: &[&str] = &[
"equals",
"contains",
"contains_all",
"not_contains",
"contains_any",
"greater_than",
"less_than",
"greater_than_or_equal",
"less_than_or_equal",
"starts_with",
"ends_with",
"min_length",
"max_length",
"count_min",
"count_equals",
"is_true",
"is_false",
"matches_regex",
];
#[allow(clippy::too_many_arguments)]
pub(super) fn render_scalar_assertion(
out: &mut String,
assertion: &Assertion,
field_expr: &str,
string_field_expr: &str,
nonnull_field_expr: &str,
string_expr: &str,
field_is_optional: bool,
field_is_collection: bool,
field_is_long: bool,
result_var: &str,
result_is_simple: bool,
result_is_option: bool,
kotlin_android_style: bool,
is_streaming: bool,
not_error_may_assert_presence: bool,
) {
if VALUE_ASSERTION_TYPES.contains(&assertion.assertion_type.as_str()) {
render_value_arm(
out,
assertion,
field_expr,
string_field_expr,
nonnull_field_expr,
string_expr,
field_is_optional,
field_is_collection,
field_is_long,
result_var,
result_is_simple,
);
} else {
render_presence_arm(
out,
assertion,
field_expr,
string_field_expr,
field_is_collection,
field_is_optional,
result_var,
result_is_option,
kotlin_android_style,
is_streaming,
not_error_may_assert_presence,
);
}
}
#[allow(clippy::too_many_arguments)]
fn render_value_arm(
out: &mut String,
assertion: &Assertion,
field_expr: &str,
string_field_expr: &str,
nonnull_field_expr: &str,
string_expr: &str,
field_is_optional: bool,
field_is_collection: bool,
field_is_long: bool,
result_var: &str,
result_is_simple: bool,
) {
match assertion.assertion_type.as_str() {
"equals" => render_equals_arm(out, assertion, field_is_long, string_expr, nonnull_field_expr),
"contains" => render_contains_arm(out, assertion, field_is_collection, string_expr),
"contains_all" => render_contains_all_arm(out, assertion, field_is_collection, string_expr),
"not_contains" => render_not_contains_arm(out, assertion, field_is_collection, string_expr),
"contains_any" => render_contains_any_arm(out, assertion, string_expr),
"greater_than" => render_greater_than_arm(out, assertion, nonnull_field_expr),
"less_than" => render_less_than_arm(out, assertion, nonnull_field_expr),
"greater_than_or_equal" => render_greater_than_or_equal_arm(out, assertion, nonnull_field_expr),
"less_than_or_equal" => render_less_than_or_equal_arm(out, assertion, nonnull_field_expr),
"starts_with" => render_starts_with_arm(out, assertion, string_expr),
"ends_with" => render_ends_with_arm(out, assertion, string_expr),
"min_length" => {
render_min_length_arm(
out,
assertion,
result_is_simple,
field_expr,
result_var,
string_field_expr,
);
}
"max_length" => {
render_max_length_arm(
out,
assertion,
result_is_simple,
field_expr,
result_var,
string_field_expr,
);
}
"count_min" => render_count_min_arm(out, assertion, nonnull_field_expr),
"count_equals" => render_count_equals_arm(out, assertion, nonnull_field_expr),
"is_true" => render_is_true_arm(out, field_is_optional, field_expr),
"is_false" => render_is_false_arm(out, field_is_optional, field_expr),
"matches_regex" => {
if let Some(expected) = &assertion.value {
let kotlin_val = super::values::json_to_kotlin(expected);
let _ = writeln!(
out,
" assertTrue(Regex({kotlin_val}).containsMatchIn({string_expr}), \"expected value to match regex: \" + {kotlin_val})"
);
}
}
other => unreachable!("render_value_arm called for non-value assertion type {other}"),
}
}
#[allow(clippy::too_many_arguments)]
fn render_presence_arm(
out: &mut String,
assertion: &Assertion,
field_expr: &str,
string_field_expr: &str,
field_is_collection: bool,
field_is_optional: bool,
result_var: &str,
result_is_option: bool,
kotlin_android_style: bool,
is_streaming: bool,
not_error_may_assert_presence: bool,
) {
match assertion.assertion_type.as_str() {
"not_empty" | "is_empty" => render_empty_presence_arm(
out,
assertion,
EmptyPresenceContext {
result_is_option,
kotlin_android_style,
field_is_collection,
field_is_optional,
field_expr,
string_field_expr,
},
),
"not_error" => {
super::not_error::render_not_error(out, result_var, not_error_may_assert_presence, is_streaming);
}
"error" => {
}
"method_result" => {
let _ = writeln!(
out,
" // method_result assertions not yet implemented for Kotlin"
);
}
other => {
panic!("Kotlin e2e generator: unsupported assertion type: {other}");
}
}
}
fn render_equals_arm(
out: &mut String,
assertion: &Assertion,
field_is_long: bool,
string_expr: &str,
nonnull_field_expr: &str,
) {
if let Some(expected) = &assertion.value {
let kotlin_val = if field_is_long && expected.is_number() && !expected.is_f64() {
format!("{}L", expected)
} else {
super::values::json_to_kotlin(expected)
};
if expected.is_string() {
let _ = writeln!(out, " assertEquals({kotlin_val}, {string_expr})");
} else {
let _ = writeln!(out, " assertEquals({kotlin_val}, {nonnull_field_expr})");
}
}
}
fn render_contains_arm(out: &mut String, assertion: &Assertion, field_is_collection: bool, string_expr: &str) {
if let Some(expected) = &assertion.value {
let kotlin_val = super::values::json_to_kotlin(expected);
if field_is_collection {
let _ = writeln!(
out,
" assertTrue({string_expr}.toString().lowercase().contains({kotlin_val}.toString().lowercase()), \"expected to contain: \" + {kotlin_val})"
);
} else {
let _ = writeln!(
out,
" assertTrue({string_expr}.contains({kotlin_val}), \"expected to contain: \" + {kotlin_val})"
);
}
}
}
fn render_contains_all_arm(out: &mut String, assertion: &Assertion, field_is_collection: bool, string_expr: &str) {
if let Some(values) = &assertion.values {
for val in values {
let kotlin_val = super::values::json_to_kotlin(val);
if field_is_collection {
let _ = writeln!(
out,
" assertTrue({string_expr}.toString().lowercase().contains({kotlin_val}.toString().lowercase()), \"expected to contain: \" + {kotlin_val})"
);
} else {
let _ = writeln!(
out,
" assertTrue({string_expr}.contains({kotlin_val}), \"expected to contain: \" + {kotlin_val})"
);
}
}
}
}
fn render_not_contains_arm(out: &mut String, assertion: &Assertion, field_is_collection: bool, string_expr: &str) {
for expected in assertion.expected_values() {
let kotlin_val = super::values::json_to_kotlin(expected);
if field_is_collection {
let _ = writeln!(
out,
" assertFalse({string_expr}.toString().lowercase().contains({kotlin_val}.toString().lowercase()), \"expected NOT to contain: \" + {kotlin_val})"
);
} else {
let _ = writeln!(
out,
" assertFalse({string_expr}.contains({kotlin_val}), \"expected NOT to contain: \" + {kotlin_val})"
);
}
}
}
#[derive(Clone, Copy)]
struct EmptyPresenceContext<'a> {
result_is_option: bool,
kotlin_android_style: bool,
field_is_collection: bool,
field_is_optional: bool,
field_expr: &'a str,
string_field_expr: &'a str,
}
fn render_empty_presence_arm(out: &mut String, assertion: &Assertion, context: EmptyPresenceContext<'_>) {
if assertion.assertion_type == "not_empty" {
render_not_empty_arm(out, assertion, context);
} else {
render_is_empty_arm(out, assertion, context);
}
}
fn render_not_empty_arm(out: &mut String, assertion: &Assertion, context: EmptyPresenceContext<'_>) {
let bare_result_is_option =
context.result_is_option && assertion.field.as_deref().filter(|f| !f.is_empty()).is_none();
if bare_result_is_option && !context.kotlin_android_style {
out.push_str(&crate::e2e::template_env::render(
"kotlin/not_empty_assertion.kt.jinja",
minijinja::context! { predicate => format!("{}.isPresent", context.field_expr) },
));
} else if context.field_is_collection && (bare_result_is_option || context.field_is_optional) {
out.push_str(&crate::e2e::template_env::render(
"kotlin/not_empty_assertion.kt.jinja",
minijinja::context! { predicate => format!("{}?.isNotEmpty() == true", context.field_expr) },
));
} else if bare_result_is_option || context.field_is_optional {
out.push_str(&crate::e2e::template_env::render(
"kotlin/not_empty_assertion.kt.jinja",
minijinja::context! { predicate => format!("{} != null", context.field_expr) },
));
} else {
let _ = writeln!(
out,
" assertFalse({}.isEmpty(), \"expected non-empty value\")",
context.string_field_expr
);
}
}
fn render_is_empty_arm(out: &mut String, assertion: &Assertion, context: EmptyPresenceContext<'_>) {
let bare_result_is_option =
context.result_is_option && assertion.field.as_deref().filter(|f| !f.is_empty()).is_none();
if bare_result_is_option && !context.kotlin_android_style {
out.push_str(&crate::e2e::template_env::render(
"kotlin/is_empty_assertion.kt.jinja",
minijinja::context! { predicate => format!("{}.isEmpty", context.field_expr) },
));
} else if context.field_is_collection && (bare_result_is_option || context.field_is_optional) {
out.push_str(&crate::e2e::template_env::render(
"kotlin/is_empty_assertion.kt.jinja",
minijinja::context! { predicate => format!("({}?.isEmpty() ?: true)", context.field_expr) },
));
} else if bare_result_is_option || context.field_is_optional {
out.push_str(&crate::e2e::template_env::render(
"kotlin/is_empty_assertion.kt.jinja",
minijinja::context! { predicate => format!("{} == null", context.field_expr) },
));
} else {
out.push_str(&crate::e2e::template_env::render(
"kotlin/is_empty_assertion.kt.jinja",
minijinja::context! { predicate => format!("{}.isEmpty()", context.string_field_expr) },
));
}
}
fn render_contains_any_arm(out: &mut String, assertion: &Assertion, string_expr: &str) {
if let Some(values) = &assertion.values {
let checks: Vec<String> = values
.iter()
.map(|v| {
let kotlin_val = super::values::json_to_kotlin(v);
format!("{string_expr}.contains({kotlin_val})")
})
.collect();
let joined = checks.join(" || ");
let _ = writeln!(
out,
" assertTrue({joined}, \"expected to contain at least one of the specified values\")"
);
}
}
fn render_greater_than_arm(out: &mut String, assertion: &Assertion, nonnull_field_expr: &str) {
if let Some(val) = &assertion.value {
let kotlin_val = super::values::json_to_kotlin(val);
let _ = writeln!(
out,
" assertTrue({nonnull_field_expr} > {kotlin_val}, \"expected > {kotlin_val}\")"
);
}
}
fn render_less_than_arm(out: &mut String, assertion: &Assertion, nonnull_field_expr: &str) {
if let Some(val) = &assertion.value {
let kotlin_val = super::values::json_to_kotlin(val);
let _ = writeln!(
out,
" assertTrue({nonnull_field_expr} < {kotlin_val}, \"expected < {kotlin_val}\")"
);
}
}
fn render_greater_than_or_equal_arm(out: &mut String, assertion: &Assertion, nonnull_field_expr: &str) {
if let Some(val) = &assertion.value {
let kotlin_val = super::values::json_to_kotlin(val);
let _ = writeln!(
out,
" assertTrue({nonnull_field_expr} >= {kotlin_val}, \"expected >= {kotlin_val}\")"
);
}
}
fn render_less_than_or_equal_arm(out: &mut String, assertion: &Assertion, nonnull_field_expr: &str) {
if let Some(val) = &assertion.value {
let kotlin_val = super::values::json_to_kotlin(val);
let _ = writeln!(
out,
" assertTrue({nonnull_field_expr} <= {kotlin_val}, \"expected <= {kotlin_val}\")"
);
}
}
fn render_starts_with_arm(out: &mut String, assertion: &Assertion, string_expr: &str) {
if let Some(expected) = &assertion.value {
let kotlin_val = super::values::json_to_kotlin(expected);
let _ = writeln!(
out,
" assertTrue({string_expr}.startsWith({kotlin_val}), \"expected to start with: \" + {kotlin_val})"
);
}
}
fn render_ends_with_arm(out: &mut String, assertion: &Assertion, string_expr: &str) {
if let Some(expected) = &assertion.value {
let kotlin_val = super::values::json_to_kotlin(expected);
let _ = writeln!(
out,
" assertTrue({string_expr}.endsWith({kotlin_val}), \"expected to end with: \" + {kotlin_val})"
);
}
}
fn render_min_length_arm(
out: &mut String,
assertion: &Assertion,
result_is_simple: bool,
field_expr: &str,
result_var: &str,
string_field_expr: &str,
) {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let length_accessor = if result_is_simple && field_expr == result_var {
"size"
} else {
"length"
};
let _ = writeln!(
out,
" assertTrue({string_field_expr}.{length_accessor} >= {n}, \"expected {length_accessor} >= {n}\")"
);
}
}
fn render_max_length_arm(
out: &mut String,
assertion: &Assertion,
result_is_simple: bool,
field_expr: &str,
result_var: &str,
string_field_expr: &str,
) {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let length_accessor = if result_is_simple && field_expr == result_var {
"size"
} else {
"length"
};
let _ = writeln!(
out,
" assertTrue({string_field_expr}.{length_accessor} <= {n}, \"expected {length_accessor} <= {n}\")"
);
}
}
fn render_count_min_arm(out: &mut String, assertion: &Assertion, nonnull_field_expr: &str) {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let _ = writeln!(
out,
" assertTrue({nonnull_field_expr}.size >= {n}, \"expected at least {n} elements\")"
);
}
}
fn render_count_equals_arm(out: &mut String, assertion: &Assertion, nonnull_field_expr: &str) {
if let Some(val) = &assertion.value
&& let Some(n) = val.as_u64()
{
let _ = writeln!(
out,
" assertEquals({n}, {nonnull_field_expr}.size, \"expected exactly {n} elements\")"
);
}
}
fn render_is_true_arm(out: &mut String, field_is_optional: bool, field_expr: &str) {
if field_is_optional {
let _ = writeln!(
out,
" assertTrue({field_expr} != null, \"expected true (non-null)\")"
);
} else {
let _ = writeln!(out, " assertTrue({field_expr} == true, \"expected true\")");
}
}
fn render_is_false_arm(out: &mut String, field_is_optional: bool, field_expr: &str) {
if field_is_optional {
let _ = writeln!(
out,
" assertTrue({field_expr} == null, \"expected false (null)\")"
);
} else {
let _ = writeln!(out, " assertTrue({field_expr} == false, \"expected false\")");
}
}