use super::{DefaultValue, EvalScope, FieldMemberExt, expr_to_default_value, extract_cfg_condition, unreadable};
use ahash::AHashMap;
use quote::ToTokens;
const WASM32_POSITIVE: &str = "target_arch = \"wasm32\"";
pub(super) fn struct_expr_defaults(
struct_expr: &syn::ExprStruct,
scope: &EvalScope<'_>,
) -> AHashMap<String, DefaultValue> {
let refuse_cfg_gated = struct_expr.rest.is_some();
let mut grouped: Vec<(String, Vec<&syn::FieldValue>)> = Vec::new();
for field in &struct_expr.fields {
let Some(ident) = field.member_named() else {
continue;
};
let name = ident.to_string();
match grouped.iter_mut().find(|(existing, _)| existing == &name) {
Some((_, entries)) => entries.push(field),
None => grouped.push((name, vec![field])),
}
}
let mut defaults = AHashMap::new();
for (name, entries) in grouped {
let value = match entries.as_slice() {
[field] => resolve_initializer(field, scope, &name, refuse_cfg_gated),
arms => resolve_duplicate_cfg_arms(arms, scope, &name),
};
if let DefaultValue::Unresolved(source) = &value {
tracing::debug!(
target: "alef::extract::defaults",
rust_type = scope.self_type,
field = %name,
initializer = %source,
"field initializer is not constant-foldable; its default is unresolved"
);
}
defaults.insert(name, value);
}
defaults
}
fn resolve_initializer(
field: &syn::FieldValue,
scope: &EvalScope<'_>,
name: &str,
refuse_cfg_gated: bool,
) -> DefaultValue {
let readable =
field.attrs.is_empty() || (!refuse_cfg_gated && field.attrs.iter().all(|attr| attr.path().is_ident("cfg")));
if !readable {
return unreadable(&field.expr);
}
expr_to_default_value(&field.expr, scope, scope.field_types.get(name))
}
fn resolve_duplicate_cfg_arms(arms: &[&syn::FieldValue], scope: &EvalScope<'_>, name: &str) -> DefaultValue {
if let [first, second] = arms {
let first_is_wasm32 = extract_cfg_condition(&first.attrs).as_deref() == Some(WASM32_POSITIVE);
let second_is_wasm32 = extract_cfg_condition(&second.attrs).as_deref() == Some(WASM32_POSITIVE);
if first_is_wasm32 && !second_is_wasm32 {
return resolve_initializer(second, scope, name, false);
}
if second_is_wasm32 && !first_is_wasm32 {
return resolve_initializer(first, scope, name, false);
}
}
let joined = arms
.iter()
.map(|field| field.expr.to_token_stream().to_string())
.collect::<Vec<_>>()
.join(" | ");
DefaultValue::Unresolved(joined)
}