use super::gen_from_core_to_binding_cfg;
use crate::codegen::conversions::ConversionConfig;
use crate::core::ir::{FieldDef, TypeDef, TypeRef};
use ahash::AHashSet;
use std::collections::HashSet;
fn type_with_fields(fields: Vec<FieldDef>) -> TypeDef {
TypeDef {
name: "ExtractionConfig".to_string(),
rust_path: "test_lib::ExtractionConfig".to_string(),
fields,
..TypeDef::default()
}
}
fn crawl_field() -> FieldDef {
FieldDef {
name: "crawl".to_string(),
ty: TypeRef::String,
cfg: Some(r#"any(feature = "url-ingestion", feature = "url-config-types")"#.to_string()),
..FieldDef::default()
}
}
#[test]
fn bare_field_default_on_non_default_owner_wraps_only_that_binding_field() {
let typ = type_with_fields(vec![
FieldDef {
name: "llm".to_string(),
ty: TypeRef::Named("LlmConfig".to_string()),
..Default::default()
},
FieldDef {
name: "alt_text".to_string(),
ty: TypeRef::Named("CaptionAltTextMode".to_string()),
default: Some("/* serde(default) */".to_string()),
typed_default: Some(crate::core::ir::DefaultValue::EnumVariant("Preserve".to_string())),
..Default::default()
},
]);
let config = ConversionConfig {
type_name_prefix: "Js",
optionalize_bare_field_defaults: true,
..Default::default()
};
let out = gen_from_core_to_binding_cfg(&typ, "test_lib", &AHashSet::new(), &config);
assert!(out.contains("llm: val.llm.into()"), "{out}");
assert!(out.contains("alt_text: Some(val.alt_text.into())"), "{out}");
assert!(!out.contains("llm: Some("), "{out}");
}
#[test]
fn gate_with_every_feature_declared_is_emitted_unchanged() {
let field = crawl_field();
let never_skip = vec!["crawl".to_string()];
let declared: HashSet<&str> = ["url-ingestion", "url-config-types"].into_iter().collect();
let config = ConversionConfig {
never_skip_cfg_field_names: &never_skip,
strip_cfg_fields_from_binding_struct: true,
declared_features: Some(&declared),
..ConversionConfig::default()
};
let out = gen_from_core_to_binding_cfg(&type_with_fields(vec![field]), "test_lib", &AHashSet::new(), &config);
assert!(
out.contains(r#"#[cfg(any(feature = "url-ingestion", feature = "url-config-types"))]"#),
"a gate whose every feature is declared must be copied verbatim, got:\n{out}"
);
}
#[test]
fn gate_with_one_undeclared_feature_narrows_to_the_declared_term_alone() {
let field = crawl_field();
let never_skip = vec!["crawl".to_string()];
let declared: HashSet<&str> = ["url-config-types"].into_iter().collect();
let config = ConversionConfig {
never_skip_cfg_field_names: &never_skip,
strip_cfg_fields_from_binding_struct: true,
declared_features: Some(&declared),
..ConversionConfig::default()
};
let out = gen_from_core_to_binding_cfg(&type_with_fields(vec![field]), "test_lib", &AHashSet::new(), &config);
let crawl_line = out
.lines()
.find(|line| line.contains("crawl:"))
.expect("crawl field initialiser present");
let cfg_line = out
.lines()
.take_while(|line| !line.contains("crawl:"))
.last()
.expect("a preceding cfg attribute line exists");
assert!(
cfg_line.contains(r#"#[cfg(feature = "url-config-types")]"#),
"gate must narrow to the single declared term, got cfg line:\n{cfg_line}\nfull output:\n{out}"
);
assert!(
!cfg_line.contains("url-ingestion"),
"the undeclared feature must not appear in the emitted cfg attribute, got:\n{cfg_line}"
);
assert!(
crawl_line.contains("val.crawl"),
"field initialiser missing, got:\n{out}"
);
}