use crate::e2e::field_access::FieldResolver;
use crate::e2e::fixture::Assertion;
pub(super) fn compute_field_shape_flags(
assertion: &Assertion,
field_resolver: &FieldResolver,
enum_fields: &std::collections::HashSet<String>,
json_scalar_fields: &std::collections::HashSet<String>,
) -> (bool, bool, bool) {
let field_is_enum = assertion.field.as_deref().is_some_and(|f| {
enum_fields.contains(f) || enum_fields.contains(field_resolver.resolve(f)) || field_resolver.is_enum(f)
});
let field_is_json_scalar = assertion
.field
.as_deref()
.is_some_and(|field| field_resolver.is_json_scalar(field, json_scalar_fields));
let field_is_display_as_text = assertion
.field
.as_deref()
.is_some_and(|f| field_resolver.is_display_as_text(f));
(field_is_enum, field_is_json_scalar, field_is_display_as_text)
}
pub(super) fn resolve_field_expr(
assertion: &Assertion,
field_resolver: &FieldResolver,
result_var: &str,
result_is_simple: bool,
accessor_lang: &str,
) -> String {
if result_is_simple {
result_var.to_string()
} else {
match &assertion.field {
Some(f) if !f.is_empty() => field_resolver.accessor(f, accessor_lang, result_var),
_ => result_var.to_string(),
}
}
}
pub(super) fn resolve_field_is_optional(
result_is_simple: bool,
field_expr: &str,
assertion: &Assertion,
field_resolver: &FieldResolver,
kotlin_android_style: bool,
) -> bool {
!result_is_simple
&& (field_expr.contains("?.")
|| assertion.field.as_deref().filter(|f| !f.is_empty()).is_some_and(|f| {
let resolved = field_resolver.resolve(f);
if field_resolver.has_map_access(f) {
return kotlin_android_style;
}
if field_resolver.is_optional(resolved) {
return true;
}
let mut prefix = String::new();
for part in resolved.split('.') {
let key = part.split('[').next().unwrap_or(part);
if !prefix.is_empty() {
prefix.push('.');
}
prefix.push_str(key);
if field_resolver.is_optional(&prefix) {
return true;
}
}
false
}))
}
pub(super) fn resolve_string_field_expr(
field_expr: &str,
field_is_display_as_text: bool,
field_is_json_scalar: bool,
bare_result_is_nullable: bool,
field_is_optional: bool,
) -> String {
if field_is_display_as_text {
if field_is_optional {
format!("{field_expr}?.text().orEmpty()")
} else {
format!("{field_expr}.text()")
}
} else if field_is_json_scalar {
format!("{field_expr}?.toString().orEmpty()")
} else if bare_result_is_nullable {
format!("{field_expr}?.toString().orEmpty()")
} else if field_is_optional {
format!("{field_expr}.orEmpty()")
} else {
field_expr.to_string()
}
}
pub(super) fn resolve_string_expr(
field_expr: &str,
field_is_enum: bool,
field_is_optional: bool,
string_field_expr: &str,
kotlin_android_style: bool,
) -> String {
if kotlin_android_style {
match (field_is_enum, field_is_optional) {
(true, true) => format!("{field_expr}?.toWire().orEmpty()"),
(true, false) => format!("{field_expr}.toWire()"),
(false, _) => string_field_expr.to_string(),
}
} else {
match (field_is_enum, field_is_optional) {
(true, true) => format!("{field_expr}?.getValue().orEmpty()"),
(true, false) => format!("{field_expr}.getValue()"),
(false, _) => string_field_expr.to_string(),
}
}
}
pub(super) fn compute_field_is_long(
assertion: &Assertion,
field_resolver: &FieldResolver,
fields_c_types: &std::collections::HashMap<String, String>,
) -> bool {
assertion.field.as_deref().filter(|f| !f.is_empty()).is_some_and(|f| {
let resolved = field_resolver.resolve(f);
matches!(
fields_c_types.get(resolved).map(String::as_str),
Some("uint64_t") | Some("int64_t")
)
})
}
pub(super) fn compute_field_is_collection(assertion: &Assertion, field_resolver: &FieldResolver) -> bool {
assertion.field.as_deref().filter(|f| !f.is_empty()).is_some_and(|f| {
let resolved = field_resolver.resolve(f);
field_resolver.is_array(f)
|| field_resolver.is_array(resolved)
|| field_resolver.is_collection_root(f)
|| field_resolver.is_collection_root(resolved)
})
}
#[allow(clippy::too_many_arguments)]
pub(super) fn compute_scalar_context(
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,
) -> (String, String, String, String, bool, bool, bool) {
let (field_is_enum, field_is_json_scalar, field_is_display_as_text) =
compute_field_shape_flags(assertion, field_resolver, enum_fields, json_scalar_fields);
let accessor_lang = if kotlin_android_style {
"kotlin_android"
} else {
"kotlin"
};
let field_expr = resolve_field_expr(assertion, field_resolver, result_var, result_is_simple, accessor_lang);
let field_is_optional = resolve_field_is_optional(
result_is_simple,
&field_expr,
assertion,
field_resolver,
kotlin_android_style,
);
let bare_result_is_nullable = result_is_option && assertion.field.as_deref().filter(|f| !f.is_empty()).is_none();
let string_field_expr = resolve_string_field_expr(
&field_expr,
field_is_display_as_text,
field_is_json_scalar,
bare_result_is_nullable,
field_is_optional,
);
let nonnull_field_expr = if field_is_optional {
format!("{field_expr}!!")
} else {
field_expr.clone()
};
let string_expr = resolve_string_expr(
&field_expr,
field_is_enum,
field_is_optional,
&string_field_expr,
kotlin_android_style,
);
let field_is_long = compute_field_is_long(assertion, field_resolver, fields_c_types);
let field_is_collection = compute_field_is_collection(assertion, field_resolver);
(
field_expr,
string_field_expr,
nonnull_field_expr,
string_expr,
field_is_optional,
field_is_collection,
field_is_long,
)
}