use std::collections::HashMap;
use crate::e2e::field_access::FieldResolver;
use crate::e2e::fixture::Assertion;
fn leaf_is_indexed_element(path: &str) -> bool {
let Some(open) = path.rfind('[') else {
return false;
};
path.ends_with(']')
&& open + 1 < path.len() - 1
&& path[open + 1..path.len() - 1].bytes().all(|b| b.is_ascii_digit())
}
fn leaf_is_map_key_element(path: &str) -> bool {
let Some(open) = path.rfind('[') else {
return false;
};
path.ends_with(']')
&& open + 1 < path.len() - 1
&& !path[open + 1..path.len() - 1].bytes().all(|b| b.is_ascii_digit())
}
pub(super) struct AssertionFieldShape {
pub is_optional: bool,
pub is_pointer: bool,
pub is_nullable: bool,
pub is_array_for_len: bool,
pub is_slice: bool,
pub is_data_interface: bool,
}
pub(super) fn resolve_assertion_field_shape(
assertion: &Assertion,
field_resolver: &FieldResolver,
optional_locals: &HashMap<String, String>,
) -> AssertionFieldShape {
let Some(field) = assertion.field.as_deref() else {
return AssertionFieldShape {
is_optional: false,
is_pointer: false,
is_nullable: false,
is_array_for_len: false,
is_slice: false,
is_data_interface: false,
};
};
let resolved = field_resolver.resolve(field);
let check_path = resolved
.strip_suffix(".length")
.or_else(|| resolved.strip_suffix(".count"))
.or_else(|| resolved.strip_suffix(".size"))
.unwrap_or(resolved);
let uses_plain_local = optional_locals.contains_key(field);
let leaf_is_indexed_element = leaf_is_indexed_element(check_path);
let leaf_is_scalar_map_value =
leaf_is_map_key_element(check_path) && field_resolver.map_value_is_scalar(check_path).unwrap_or(false);
let container_nullability_consumed = leaf_is_indexed_element || leaf_is_scalar_map_value;
let is_optional = !container_nullability_consumed && field_resolver.is_optional(check_path) && !uses_plain_local;
let is_array_for_len = !container_nullability_consumed && field_resolver.is_array(check_path);
let is_slice = !container_nullability_consumed && field_resolver.is_array(resolved);
let is_pointer = !container_nullability_consumed
&& !uses_plain_local
&& field_resolver.target_field_is_pointer(check_path).unwrap_or(false);
AssertionFieldShape {
is_optional,
is_pointer,
is_nullable: is_optional || is_pointer,
is_array_for_len,
is_slice,
is_data_interface: field_resolver.target_field_is_data_interface(check_path),
}
}