use super::FieldResolver;
use crate::core::ir::{FieldDef, TypeDef, TypeRef};
use std::collections::{HashMap, HashSet};
fn set(entries: &[&str]) -> HashSet<String> {
entries.iter().map(|s| (*s).to_string()).collect()
}
fn scalar_field(name: &str) -> FieldDef {
FieldDef {
name: name.to_string(),
ty: TypeRef::String,
..FieldDef::default()
}
}
fn struct_field(name: &str, ty: TypeRef) -> FieldDef {
FieldDef {
name: name.to_string(),
ty,
..FieldDef::default()
}
}
fn envelope_type_defs() -> Vec<TypeDef> {
vec![
TypeDef {
name: "Envelope".to_string(),
fields: vec![struct_field(
"results",
TypeRef::Vec(Box::new(TypeRef::Named("Document".to_string()))),
)],
..TypeDef::default()
},
TypeDef {
name: "Document".to_string(),
fields: vec![struct_field("metadata", TypeRef::Named("Metadata".to_string()))],
..TypeDef::default()
},
TypeDef {
name: "Metadata".to_string(),
fields: vec![scalar_field("output_format")],
..TypeDef::default()
},
]
}
fn flat_type_defs() -> Vec<TypeDef> {
vec![
TypeDef {
name: "Report".to_string(),
fields: vec![
struct_field("metrics", TypeRef::Named("Metrics".to_string())),
scalar_field("final_url"),
],
..TypeDef::default()
},
TypeDef {
name: "Metrics".to_string(),
fields: vec![scalar_field("total_lines")],
..TypeDef::default()
},
]
}
fn resolver_for(type_defs: &[TypeDef], root: &str, result_fields: &[&str]) -> FieldResolver {
let result_field_map = FieldResolver::ir_result_field_facts(type_defs, "rust");
let collection_map = FieldResolver::ir_collection_fields(type_defs);
let (reachable, excluded, optional) = FieldResolver::ir_field_sets(type_defs);
FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&set(result_fields),
&HashSet::new(),
&HashSet::new(),
)
.with_ir_result_fields(result_field_map, Some(root.to_string()))
.with_ir_collection_map(collection_map, Some(root.to_string()))
.with_ir_fields(reachable, excluded, optional)
}
#[test]
fn envelope_rooted_nested_path_keeps_every_segment() {
let resolver = resolver_for(&envelope_type_defs(), "Envelope", &["results"]);
assert_eq!(
resolver.result_relative_path("metadata.output_format"),
"results[0].metadata.output_format",
"the shared answer must place the value behind the envelope projection, hop intact"
);
assert_eq!(
resolver.accessor("metadata.output_format", "rust", "result"),
"result.results[0].metadata.output_format"
);
}
#[test]
fn an_already_projected_path_is_not_prefixed_twice() {
let resolver = resolver_for(&envelope_type_defs(), "Envelope", &["results"]);
assert_eq!(
resolver.accessor("results[0].metadata.output_format", "rust", "result"),
"result.results[0].metadata.output_format"
);
}
#[test]
fn a_genuinely_virtual_label_is_still_stripped() {
let mut type_defs = envelope_type_defs();
type_defs[0].fields.push(scalar_field("browser_used"));
let resolver = resolver_for(&type_defs, "Envelope", &["results", "browser_used"]);
assert_eq!(
resolver.accessor("browser.browser_used", "python", "result"),
"result.browser_used",
"a label no type declares must still lose its segment"
);
}
#[test]
fn a_real_nested_path_on_a_non_envelope_root_keeps_its_prefix() {
let resolver = resolver_for(&flat_type_defs(), "Report", &["final_url"]);
assert_eq!(
resolver.result_relative_path("metrics.total_lines"),
"metrics.total_lines"
);
assert_eq!(
resolver.accessor("metrics.total_lines", "python", "result"),
"result.metrics.total_lines"
);
}
#[test]
fn an_empty_result_fields_still_disables_both_strip_and_prefix() {
let resolver = resolver_for(&envelope_type_defs(), "Envelope", &[]);
assert_eq!(
resolver.result_relative_path("metadata.output_format"),
"metadata.output_format"
);
assert_eq!(
resolver.accessor("metadata.output_format", "python", "result"),
"result.metadata.output_format"
);
}
#[test]
fn a_first_segment_listed_in_result_fields_is_never_treated_as_a_prefix() {
let resolver = resolver_for(&envelope_type_defs(), "Envelope", &["results"]);
assert_eq!(resolver.result_relative_path("results"), "results");
assert_eq!(resolver.accessor("results", "python", "result"), "result.results");
}
#[test]
fn a_path_no_projection_reaches_is_left_alone() {
let resolver = resolver_for(&envelope_type_defs(), "Envelope", &["results"]);
assert_eq!(
resolver.result_relative_path("not_a_type.not_a_field"),
"not_a_type.not_a_field",
"unchanged from today: the strip is refused because the remainder is not a known member"
);
}
#[test]
fn a_config_only_resolver_is_never_prefixed() {
let resolver = FieldResolver::new(
&HashMap::new(),
&HashSet::new(),
&set(&["results"]),
&HashSet::new(),
&HashSet::new(),
);
assert_eq!(
resolver.result_relative_path("metadata.output_format"),
"metadata.output_format",
"unchanged from today: with no IR, nothing confirms `output_format` is where the value sits"
);
}