use crate::codegen::cfg::{
cfg_default_and_forwarding_lines, native_wrapper_default_features, native_wrapper_default_features_for_config,
};
use crate::core::config::{ComponentConfig, ComponentProvidesConfig, ResolvedCrateConfig};
use crate::core::ir::{ApiSurface, TypeDef};
use std::collections::{BTreeSet, HashSet};
fn set(names: &[&str]) -> BTreeSet<String> {
names.iter().map(|n| (*n).to_string()).collect()
}
#[test]
fn builds_default_line_then_one_forwarding_row_per_feature() {
let features = set(&["chunking-tokenizers", "ner"]);
let excluded = HashSet::new();
let lines = cfg_default_and_forwarding_lines(&features, "core-lib", &excluded);
assert_eq!(
lines,
vec![
r#"default = ["chunking-tokenizers", "ner"]"#.to_string(),
r#"chunking-tokenizers = ["core-lib/chunking-tokenizers"]"#.to_string(),
r#"ner = ["core-lib/ner"]"#.to_string(),
],
"got: {lines:?}"
);
}
#[test]
fn excluded_default_feature_is_forwarded_but_not_defaulted() {
let features = set(&["chunking-tokenizers", "heic"]);
let excluded: HashSet<&str> = ["heic"].into_iter().collect();
let lines = cfg_default_and_forwarding_lines(&features, "core-lib", &excluded);
assert_eq!(
lines[0], r#"default = ["chunking-tokenizers"]"#,
"excluded name must not appear in default: {lines:?}"
);
assert!(
lines.contains(&r#"heic = ["core-lib/heic"]"#.to_string()),
"excluded name must still get a forwarding row: {lines:?}"
);
}
#[test]
fn empty_feature_set_still_emits_a_default_line_with_no_forwarding_rows() {
let features = BTreeSet::new();
let excluded = HashSet::new();
let lines = cfg_default_and_forwarding_lines(&features, "core-lib", &excluded);
assert_eq!(lines, vec!["default = []".to_string()], "got: {lines:?}");
}
#[test]
fn component_implementation_features_are_not_forwarded_to_bindings() {
let api = ApiSurface {
crate_name: "hostlib".to_string(),
types: vec![TypeDef {
name: "OpenApiCompiler".to_string(),
rust_path: "hostlib::OpenApiCompiler".to_string(),
cfg: Some(r#"feature = "codegen-openapi""#.to_string()),
..Default::default()
}],
..Default::default()
};
let mut config = ResolvedCrateConfig {
name: "hostlib".to_string(),
wrapper_default_features: vec!["di".to_string()],
..Default::default()
};
config.components.push(ComponentConfig {
name: "openapi".to_string(),
provides: vec![ComponentProvidesConfig {
contract: "spec".to_string(),
implementation: "hostlib::OpenApiCompiler".to_string(),
}],
features: vec!["codegen-openapi".to_string()],
default_features: false,
targets: Some(vec!["aarch64-apple-darwin".to_string()]),
bundled_on: Vec::new(),
});
let unfiltered = native_wrapper_default_features(&api, &config.wrapper_default_features);
assert!(
unfiltered.contains("codegen-openapi"),
"control: raw discovery must still see the component feature, got: {unfiltered:?}"
);
let forwarded = native_wrapper_default_features_for_config(&api, &config);
assert!(
!forwarded.contains("codegen-openapi"),
"the component feature must be dropped from binding forwarding, got: {forwarded:?}"
);
assert!(
forwarded.contains("di"),
"an ordinary configured feature must still be forwarded, got: {forwarded:?}"
);
}