use crate::e2e::field_access::FieldResolver;
use heck::ToLowerCamelCase;
struct AccessorWalk {
current_type: Option<String>,
via_rust_vec: bool,
via_opaque: bool,
out: String,
has_optional: bool,
already_unwrapped: bool,
path_so_far: String,
}
pub(super) fn swift_build_accessor(field: &str, result_var: &str, field_resolver: &FieldResolver) -> (String, bool) {
let resolved = field_resolver.resolve(field);
let parts: Vec<&str> = resolved.split('.').collect();
let mut walk = AccessorWalk {
current_type: field_resolver.swift_root_type().cloned(),
via_rust_vec: false,
via_opaque: false,
out: result_var.to_string(),
has_optional: false,
already_unwrapped: false,
path_so_far: String::new(),
};
let total = parts.len();
for (i, part) in parts.iter().enumerate() {
render_accessor_segment(&mut walk, part, i == total - 1, field_resolver);
}
(walk.out, walk.has_optional)
}
fn render_accessor_segment(walk: &mut AccessorWalk, part: &str, is_leaf: bool, field_resolver: &FieldResolver) {
let (field_name, subscript): (&str, Option<&str>) = if let Some(bracket_pos) = part.find('[') {
(&part[..bracket_pos], Some(&part[bracket_pos..]))
} else {
(part, None)
};
if !walk.path_so_far.is_empty() {
walk.path_so_far.push('.');
}
let base_path = {
let mut p = walk.path_so_far.clone();
p.push_str(field_name);
p
};
walk.path_so_far.push_str(part);
let is_first_class = walk
.current_type
.as_ref()
.is_some_and(|t| field_resolver.swift_is_first_class(Some(t)));
let property_syntax = !walk.via_rust_vec && !walk.via_opaque && is_first_class;
if !property_syntax {
walk.via_opaque = true;
}
walk.out.push('.');
walk.out.push_str(&field_name.to_lower_camel_case());
if let Some(sub) = subscript {
render_indexed_segment(walk, field_name, sub, &base_path, property_syntax, field_resolver);
} else {
render_plain_segment(walk, field_name, is_leaf, &base_path, property_syntax, field_resolver);
}
}
fn render_indexed_segment(
walk: &mut AccessorWalk,
field_name: &str,
sub: &str,
base_path: &str,
property_syntax: bool,
field_resolver: &FieldResolver,
) {
let field_is_optional = field_resolver.is_optional(base_path);
let access = if property_syntax { "" } else { "()" };
if field_is_optional && !walk.already_unwrapped {
walk.out.push_str(&format!("{access}?"));
walk.has_optional = true;
walk.already_unwrapped = true;
} else {
walk.out.push_str(access);
}
walk.out.push_str(sub);
walk.current_type = field_resolver.swift_advance(walk.current_type.as_deref(), field_name);
if !property_syntax {
walk.via_rust_vec = true;
}
}
fn render_plain_segment(
walk: &mut AccessorWalk,
field_name: &str,
is_leaf: bool,
base_path: &str,
property_syntax: bool,
field_resolver: &FieldResolver,
) {
if !property_syntax {
walk.out.push_str("()");
}
if !is_leaf && field_resolver.is_optional(base_path) && !walk.already_unwrapped {
walk.out.push('?');
walk.has_optional = true;
walk.already_unwrapped = true;
}
walk.current_type = field_resolver.swift_advance(walk.current_type.as_deref(), field_name);
}