use std::collections::BTreeSet;
const FACADE_MANIFEST: &str = include_str!("../Cargo.toml");
const CORE_MANIFEST: &str = include_str!("../../fuel-tensor/Cargo.toml");
const TRANSFORMERS_MANIFEST: &str = include_str!("../../fuel-transformers/Cargo.toml");
const DISPATCH_MANIFEST: &str = include_str!("../../fuel-dispatch/Cargo.toml");
fn parse_features(manifest: &str) -> Vec<(String, Vec<String>)> {
let mut out = Vec::new();
let mut in_features = false;
let mut lines = manifest.lines();
while let Some(line) = lines.next() {
let trimmed = line.trim();
if trimmed.starts_with('[') && trimmed.ends_with(']') {
in_features = trimmed == "[features]";
continue;
}
if !in_features || trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
let Some(eq) = line.find('=') else { continue };
let name = line[..eq].trim().to_string();
if name.is_empty()
|| !name
.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '-')
{
continue;
}
let mut value = line[eq + 1..].to_string();
let mut depth = value.matches('[').count() as i64 - value.matches(']').count() as i64;
while depth > 0 {
let cont = lines
.next()
.expect("unterminated feature array in [features]");
depth += cont.matches('[').count() as i64 - cont.matches(']').count() as i64;
value.push('\n');
value.push_str(cont);
}
out.push((name, extract_quoted(&value)));
}
out
}
fn extract_quoted(s: &str) -> Vec<String> {
let mut out = Vec::new();
let bytes = s.as_bytes();
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'"' {
let start = i + 1;
let mut j = start;
while j < bytes.len() && bytes[j] != b'"' {
j += 1;
}
out.push(s[start..j].to_string());
i = j + 1;
} else {
i += 1;
}
}
out
}
fn feature_names(parsed: &[(String, Vec<String>)]) -> BTreeSet<String> {
parsed
.iter()
.map(|(n, _)| n.clone())
.filter(|n| n != "default")
.collect()
}
#[test]
fn facade_forwards_each_feature_to_every_dep_that_declares_it() {
let facade = parse_features(FACADE_MANIFEST);
let core = parse_features(CORE_MANIFEST);
let transformers = parse_features(TRANSFORMERS_MANIFEST);
let dispatch = parse_features(DISPATCH_MANIFEST);
assert!(
facade.len() >= 5,
"parsed only {} facade feature(s) — parser or include path broken",
facade.len()
);
assert!(
core.len() >= 5,
"parsed only {} fuel-tensor feature(s) — parser or include path broken",
core.len()
);
assert!(
transformers.len() >= 3,
"parsed only {} fuel-transformers feature(s) — parser or include path broken",
transformers.len()
);
assert!(
!dispatch.is_empty(),
"parsed only {} fuel-dispatch feature(s) — parser or include path broken",
dispatch.len()
);
let core_names = feature_names(&core);
let tf_names = feature_names(&transformers);
let dispatch_names = feature_names(&dispatch);
let facade_names = feature_names(&facade);
let deps: [(&str, &BTreeSet<String>); 2] = [
("fuel-tensor", &core_names),
("fuel-transformers", &tf_names),
];
let union: BTreeSet<String> = core_names.union(&tf_names).cloned().collect();
assert_eq!(
facade_names,
union,
"facade feature set must equal the UNION of its deps' features. \
Missing (a dep declares it, facade doesn't): {:?}; \
Extra (facade has it, no dep declares it): {:?}",
union.difference(&facade_names).collect::<Vec<_>>(),
facade_names.difference(&union).collect::<Vec<_>>(),
);
for (name, elems) in &facade {
if name == "default" {
assert!(
elems.is_empty(),
"facade `default` must stay empty (enable nothing), got {elems:?}"
);
continue;
}
if name == "telemetry" {
let expected: BTreeSet<String> = deps
.iter()
.filter(|(_, feats)| feats.contains(name))
.map(|(dep, _)| format!("{dep}/{name}"))
.chain(
dispatch_names
.contains("telemetry")
.then(|| "fuel-dispatch/telemetry".to_string()),
)
.collect();
let got: BTreeSet<String> = elems.iter().cloned().collect();
assert_eq!(
got, expected,
"facade feature `telemetry` must forward to fuel-tensor/telemetry \
(if declared) AND fuel-dispatch/telemetry (the GAP-347 exception) \
— expected {expected:?}, got {got:?}"
);
continue;
}
let expected: BTreeSet<String> = deps
.iter()
.filter(|(_, feats)| feats.contains(name))
.map(|(dep, _)| format!("{dep}/{name}"))
.collect();
let got: BTreeSet<String> = elems.iter().cloned().collect();
assert_eq!(
got, expected,
"facade feature `{name}` must forward to EXACTLY the deps that declare it \
— expected {expected:?}, got {got:?}"
);
}
}
#[cfg(feature = "telemetry")]
#[test]
fn telemetry_forward_reaches_gated_module() {
#[allow(unused_imports)]
use fuel::telemetry;
}
#[cfg(feature = "vulkan")]
#[test]
fn vulkan_forward_reaches_gated_module() {
#[allow(unused_imports)]
use fuel::vulkan_backend;
}