use crate::core::ir::{EnumDef, EnumVariant, FieldDef, TypeDef, TypeRef};
use crate::e2e::field_access::{FieldResolver, IrEnumMap, JsonNavStep, SwiftFirstClassMap};
use std::collections::{HashMap, HashSet};
fn resolver_with_json_bridged_field(field_name: &str) -> FieldResolver {
let swift_first_class_map = SwiftFirstClassMap {
json_bridged_field_names: HashSet::from([field_name.to_string()]),
..SwiftFirstClassMap::default()
};
FieldResolver::new_with_swift_first_class(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashMap::new(),
swift_first_class_map,
)
}
fn ir_field(name: &str, ty: TypeRef) -> FieldDef {
FieldDef {
name: name.to_string(),
ty,
..FieldDef::default()
}
}
fn ir_struct(name: &str, fields: Vec<FieldDef>) -> TypeDef {
TypeDef {
name: name.to_string(),
fields,
has_serde: true,
..TypeDef::default()
}
}
fn format_metadata_ir_enum_map() -> IrEnumMap {
let type_defs = vec![
ir_struct(
"ExtractionResult",
vec![ir_field(
"results",
TypeRef::Vec(Box::new(TypeRef::Named("ExtractedDocument".to_string()))),
)],
),
ir_struct(
"ExtractedDocument",
vec![ir_field("metadata", TypeRef::Named("Metadata".to_string()))],
),
ir_struct(
"Metadata",
vec![ir_field(
"format",
TypeRef::Optional(Box::new(TypeRef::Named("FormatMetadata".to_string()))),
)],
),
ir_struct("ExcelMetadata", vec![ir_field("sheet_count", TypeRef::String)]),
ir_struct("HtmlMetadata", vec![ir_field("title", TypeRef::String)]),
];
let enums = vec![EnumDef {
name: "FormatMetadata".to_string(),
has_serde: true,
serde_tag: Some("format_type".to_string()),
serde_rename_all: Some("snake_case".to_string()),
variants: vec![
newtype_variant("Excel", "ExcelMetadata"),
newtype_variant("Html", "HtmlMetadata"),
],
..EnumDef::default()
}];
FieldResolver::ir_enum_fields(&type_defs, &enums)
}
fn newtype_variant(name: &str, payload: &str) -> EnumVariant {
EnumVariant {
name: name.to_string(),
is_tuple: true,
fields: vec![ir_field("_0", TypeRef::Named(payload.to_string()))],
..EnumVariant::default()
}
}
fn resolver_with_format_metadata_ir(field_name: &str) -> FieldResolver {
resolver_with_json_bridged_field(field_name)
.with_ir_enum_map(format_metadata_ir_enum_map(), Some("ExtractionResult".to_string()))
}
#[test]
fn indexed_element_directly_after_the_bridged_leaf_yields_one_index_step() {
let resolver = resolver_with_json_bridged_field("detected_languages");
let (leaf_field, steps) = resolver
.swift_json_bridged_navigation("results[0].detected_languages[0]")
.expect("an indexed element after a JSON-bridged leaf must be navigable");
assert_eq!(leaf_field, "results[0].detected_languages");
assert_eq!(steps, vec![JsonNavStep::Index(0)]);
}
#[test]
fn dotted_key_after_the_bridged_leaf_yields_one_key_step() {
let resolver = resolver_with_json_bridged_field("metadata");
let (leaf_field, steps) = resolver
.swift_json_bridged_navigation("results[0].metadata.output_format")
.expect("a dotted key after a JSON-bridged leaf must be navigable");
assert_eq!(leaf_field, "results[0].metadata");
assert_eq!(steps, vec![JsonNavStep::Key("output_format".to_string())]);
}
#[test]
fn multiple_dotted_keys_chain_into_multiple_key_steps() {
let resolver = resolver_with_format_metadata_ir("metadata");
let (leaf_field, steps) = resolver
.swift_json_bridged_navigation("results[0].metadata.format.html.title")
.expect("a multi-segment dotted path after a JSON-bridged leaf must be navigable");
assert_eq!(leaf_field, "results[0].metadata");
assert_eq!(
steps,
vec![
JsonNavStep::Key("format".to_string()),
JsonNavStep::Key("title".to_string()),
]
);
}
#[test]
fn a_non_variant_key_after_format_is_still_kept() {
let resolver = resolver_with_format_metadata_ir("metadata");
let (leaf_field, steps) = resolver
.swift_json_bridged_navigation("results[0].metadata.format.revision")
.expect("a dotted key after a JSON-bridged leaf must be navigable");
assert_eq!(leaf_field, "results[0].metadata");
assert_eq!(
steps,
vec![
JsonNavStep::Key("format".to_string()),
JsonNavStep::Key("revision".to_string()),
]
);
}
#[test]
fn excel_variant_segment_is_skipped_not_turned_into_a_key_step() {
let resolver = resolver_with_format_metadata_ir("metadata");
let (leaf_field, steps) = resolver
.swift_json_bridged_navigation("results[0].metadata.format.excel.sheet_count")
.expect("a variant-crossing dotted path after a JSON-bridged leaf must be navigable");
assert_eq!(leaf_field, "results[0].metadata");
assert_eq!(
steps,
vec![
JsonNavStep::Key("format".to_string()),
JsonNavStep::Key("sheet_count".to_string()),
]
);
assert!(
!steps.contains(&JsonNavStep::Key("excel".to_string())),
"must not emit a Key step for the variant name, got: {steps:?}"
);
}
#[test]
fn html_variant_segment_is_skipped_not_turned_into_a_key_step() {
let resolver = resolver_with_format_metadata_ir("metadata");
let (leaf_field, steps) = resolver
.swift_json_bridged_navigation("results[0].metadata.format.html.title")
.expect("a variant-crossing dotted path after a JSON-bridged leaf must be navigable");
assert_eq!(leaf_field, "results[0].metadata");
assert_eq!(
steps,
vec![
JsonNavStep::Key("format".to_string()),
JsonNavStep::Key("title".to_string()),
]
);
assert!(
!steps.contains(&JsonNavStep::Key("html".to_string())),
"must not emit a Key step for the variant name, got: {steps:?}"
);
}
#[test]
fn index_then_keys_mixes_step_kinds_in_order() {
let resolver = resolver_with_json_bridged_field("chunks");
let (leaf_field, steps) = resolver
.swift_json_bridged_navigation("results[0].chunks[0].metadata.total_chunks")
.expect("an index followed by dotted keys must be navigable");
assert_eq!(leaf_field, "results[0].chunks");
assert_eq!(
steps,
vec![
JsonNavStep::Index(0),
JsonNavStep::Key("metadata".to_string()),
JsonNavStep::Key("total_chunks".to_string()),
]
);
}
#[test]
fn wildcard_bracket_is_not_navigable() {
let resolver = resolver_with_json_bridged_field("headings");
assert_eq!(resolver.swift_json_bridged_navigation("metadata.headings[].text"), None);
}
#[test]
fn string_key_bracket_is_not_navigable() {
let resolver = resolver_with_json_bridged_field("labels");
assert_eq!(resolver.swift_json_bridged_navigation("labels[key].items"), None);
}
#[test]
fn trailing_count_suffix_is_left_to_the_existing_count_suffix_mechanism() {
let resolver = resolver_with_json_bridged_field("og_locale_alternates");
assert_eq!(
resolver.swift_json_bridged_navigation("metadata.og_locale_alternates.length"),
None
);
}
#[test]
fn a_path_with_no_bridged_leaf_yields_no_navigation() {
let empty = HashSet::new();
let resolver = FieldResolver::new(&HashMap::new(), &empty, &empty, &empty, &empty);
assert_eq!(resolver.swift_json_bridged_navigation("results[0].mime_type"), None);
}
fn resolver_anchored_on_extraction_result(root_type: Option<&str>) -> FieldResolver {
let field_types = HashMap::from([
(
"ExtractionResult".to_string(),
HashMap::from([("results".to_string(), "ExtractedDocument".to_string())]),
),
(
"ExtractedDocument".to_string(),
HashMap::from([("metadata".to_string(), "Metadata".to_string())]),
),
(
"Metadata".to_string(),
HashMap::from([("format".to_string(), "FormatMetadata".to_string())]),
),
]);
let json_bridged_by_type = HashMap::from([
("Metadata".to_string(), HashMap::from([("format".to_string(), true)])),
(
"ExtractedDocument".to_string(),
HashMap::from([("metadata".to_string(), false)]),
),
]);
let swift_first_class_map = SwiftFirstClassMap {
field_types,
json_bridged_field_names: HashSet::from(["metadata".to_string()]),
json_bridged_by_type,
root_type: root_type.map(str::to_string),
..SwiftFirstClassMap::default()
};
FieldResolver::new_with_swift_first_class(
&HashMap::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashSet::new(),
&HashMap::new(),
swift_first_class_map,
)
.with_ir_enum_map(format_metadata_ir_enum_map(), root_type.map(str::to_string))
}
#[test]
fn should_anchor_json_bridge_on_owner_type_not_bare_field_name() {
let resolver = resolver_anchored_on_extraction_result(Some("ExtractionResult"));
let (leaf_field, steps) = resolver
.swift_json_bridged_navigation("results[0].metadata.format.excel.sheet_count")
.expect("format is bridged on Metadata and must be found");
assert_eq!(leaf_field, "results[0].metadata.format");
assert_eq!(steps, vec![JsonNavStep::Key("sheet_count".to_string())]);
}
#[test]
fn should_fall_back_to_the_flat_set_when_the_cursor_cannot_be_anchored() {
let resolver = resolver_anchored_on_extraction_result(None);
let (leaf_field, steps) = resolver
.swift_json_bridged_navigation("results[0].metadata.format.excel.sheet_count")
.expect("the flat set still marks metadata bridged when unanchored");
assert_eq!(leaf_field, "results[0].metadata");
assert_eq!(
steps,
vec![
JsonNavStep::Key("format".to_string()),
JsonNavStep::Key("excel".to_string()),
JsonNavStep::Key("sheet_count".to_string()),
]
);
}