use crate::codegen::cfg as shared_cfg;
use crate::core::ir::{ApiSurface, TypeRef};
use std::collections::BTreeSet;
pub(super) fn field_references_excluded_type(ty: &TypeRef, exclude_types: &[String]) -> bool {
match ty {
TypeRef::Named(name) => exclude_types.iter().any(|e| e == name),
TypeRef::Optional(inner) | TypeRef::Vec(inner) => field_references_excluded_type(inner, exclude_types),
TypeRef::Map(k, v) => {
field_references_excluded_type(k, exclude_types) || field_references_excluded_type(v, exclude_types)
}
_ => false,
}
}
pub(super) fn is_gated_behind_disabled_feature(cfg: &Option<String>, enabled_features: &[String]) -> bool {
let Some(cfg_str) = cfg else {
return false;
};
!cfg_condition_enabled(cfg_str, enabled_features)
}
pub(super) fn cfg_condition_enabled(cfg_str: &str, enabled_features: &[String]) -> bool {
let normalized = cfg_str.trim().replace(" (", "(");
let cfg_str = normalized.as_str();
if let Some(feature) = cfg_str.strip_prefix("feature = \"").and_then(|s| s.strip_suffix('"')) {
return enabled_features.iter().any(|ef| ef == feature);
}
if let Some(inner) = cfg_str.strip_prefix("any(").and_then(|s| s.strip_suffix(')')) {
return parse_cfg_list(inner)
.iter()
.any(|cond| cfg_condition_enabled(cond, enabled_features));
}
if let Some(inner) = cfg_str.strip_prefix("all(").and_then(|s| s.strip_suffix(')')) {
return parse_cfg_list(inner)
.iter()
.all(|cond| cfg_condition_enabled(cond, enabled_features));
}
if let Some(inner) = cfg_str.strip_prefix("not(").and_then(|s| s.strip_suffix(')')) {
return !cfg_condition_enabled(inner.trim(), enabled_features);
}
true
}
pub(super) fn collect_cfg_features(api: &ApiSurface) -> BTreeSet<String> {
shared_cfg::collect_cfg_features(api)
}
fn parse_cfg_list(s: &str) -> Vec<String> {
let mut result = Vec::new();
let mut depth = 0usize;
let mut current = String::new();
for ch in s.chars() {
match ch {
'(' => {
depth += 1;
current.push(ch);
}
')' => {
depth = depth.saturating_sub(1);
current.push(ch);
}
',' if depth == 0 => {
let trimmed = current.trim().to_string();
if !trimmed.is_empty() {
result.push(trimmed);
}
current.clear();
}
_ => current.push(ch),
}
}
let trimmed = current.trim().to_string();
if !trimmed.is_empty() {
result.push(trimmed);
}
result
}