use crate::core::backend::Backend;
use crate::core::config::{NewAlefConfig, ResolvedCrateConfig};
use crate::core::ir::{ApiSurface, FunctionDef, ParamDef, PrimitiveType, TypeRef};
fn empty_api() -> ApiSurface {
ApiSurface {
crate_name: "test-lib".to_string(),
version: "0.1.0".to_string(),
types: vec![],
functions: vec![],
enums: vec![],
errors: vec![],
excluded_type_paths: ::std::collections::HashMap::new(),
excluded_trait_names: ::std::collections::HashSet::new(),
services: vec![],
handler_contracts: vec![],
unsupported_public_items: Vec::new(),
}
}
fn config_with_features(features_line: &str) -> ResolvedCrateConfig {
let toml_src = format!(
"\n[workspace]\nlanguages = [\"wasm\"]\n[[crates]]\nname = \"test-lib\"\nsources = \
[\"src/lib.rs\"]\n[crates.wasm]\n{features_line}\n"
);
let cfg: NewAlefConfig = toml::from_str(&toml_src).unwrap();
cfg.resolve().unwrap().remove(0)
}
fn real_and_stub_variants() -> (FunctionDef, FunctionDef) {
let real = FunctionDef {
name: "classify".to_string(),
rust_path: "test_lib::deep::classify".to_string(),
params: vec![ParamDef {
name: "value".to_string(),
ty: TypeRef::Primitive(PrimitiveType::I32),
..Default::default()
}],
return_type: TypeRef::Primitive(PrimitiveType::I32),
cfg: Some(r#"feature = "gated""#.to_string()),
..Default::default()
};
let stub = FunctionDef {
name: "classify".to_string(),
rust_path: "test_lib::classify".to_string(),
params: vec![ParamDef {
name: "_value".to_string(),
ty: TypeRef::Primitive(PrimitiveType::I32),
..Default::default()
}],
return_type: TypeRef::Primitive(PrimitiveType::I32),
cfg: Some(r#"not(feature = "gated")"#.to_string()),
..Default::default()
};
(real, stub)
}
fn lib_rs_of(files: &[crate::core::backend::GeneratedFile]) -> &str {
&files
.iter()
.find(|f| f.path.to_string_lossy().ends_with("lib.rs"))
.expect("generate_bindings must emit lib.rs")
.content
}
#[test]
fn disabled_real_variant_rust_path_is_never_called() {
let (real, stub) = real_and_stub_variants();
let api = ApiSurface {
functions: vec![real, stub],
..empty_api()
};
let config = config_with_features("");
let files = crate::backends::wasm::WasmBackend
.generate_bindings(&api, &config)
.expect("generate_bindings must succeed");
let lib_rs = lib_rs_of(&files);
assert!(
!lib_rs.contains("test_lib::deep::classify"),
"the disabled real variant's rust_path must never be called unconditionally:\n{lib_rs}"
);
assert!(
lib_rs.contains("test_lib::classify"),
"the surviving stub variant must still be emitted:\n{lib_rs}"
);
}
#[test]
fn enabled_real_variant_rust_path_is_called_not_the_stub() {
let (real, stub) = real_and_stub_variants();
let api = ApiSurface {
functions: vec![real, stub],
..empty_api()
};
let config = config_with_features("features = [\"gated\"]");
let files = crate::backends::wasm::WasmBackend
.generate_bindings(&api, &config)
.expect("generate_bindings must succeed");
let lib_rs = lib_rs_of(&files);
assert!(
lib_rs.contains("test_lib::deep::classify"),
"the enabled real variant must be called through its own rust_path:\n{lib_rs}"
);
}
#[test]
fn ungated_function_is_emitted_with_no_cfg_gate() {
let plain = FunctionDef {
name: "always_on".to_string(),
rust_path: "test_lib::always_on".to_string(),
params: vec![],
return_type: TypeRef::Primitive(PrimitiveType::I32),
cfg: None,
..Default::default()
};
let api = ApiSurface {
functions: vec![plain],
..empty_api()
};
let config = config_with_features("");
let files = crate::backends::wasm::WasmBackend
.generate_bindings(&api, &config)
.expect("generate_bindings must succeed");
let lib_rs = lib_rs_of(&files);
assert!(
lib_rs.contains("test_lib::always_on"),
"an ungated function must still be emitted:\n{lib_rs}"
);
assert!(
!lib_rs.contains("#[cfg("),
"an ungated function must never gain a synthetic #[cfg(...)] gate:\n{lib_rs}"
);
}