use crate::encode::EncodeError;
use zerodds_ast::{Ast, CaseLabel, MemberNode, TypeKind, TypeNode};
use zerodds_ast_core::Id;
const MAX_ALIAS_DEPTH: usize = 64;
pub(crate) fn resolve_alias(ast: &Ast, id: Id<TypeNode>) -> Id<TypeNode> {
let mut current = id;
for _ in 0..MAX_ALIAS_DEPTH {
match ast.type_(current).map(|node| &node.kind) {
Ok(TypeKind::Alias { aliased }) => match aliased.resolved() {
Some(target) => current = target,
None => return current,
},
_ => return current,
}
}
current
}
pub(crate) fn discriminator_default_value(ast: &Ast, discriminator: Id<TypeNode>) -> Option<i128> {
let discriminator = resolve_alias(ast, discriminator);
match ast.type_(discriminator).map(|node| node.kind.clone()) {
Ok(TypeKind::Enumeration { .. }) => {
let mut first = None;
for enumerator in ast.enumerators_of(discriminator) {
let Ok(node) = ast.enumerator(*enumerator) else {
return None;
};
let value = ast.effective_enumerator_value(*enumerator)?;
if node
.facets
.xtypes
.as_ref()
.is_some_and(|x| x.is_default_literal)
{
return Some(value);
}
if first.is_none() {
first = Some(value);
}
}
first
}
Ok(TypeKind::Primitive(_)) => Some(0),
_ => None,
}
}
pub(crate) fn selected_case(
ast: &Ast,
id: Id<TypeNode>,
selector: i128,
) -> Result<Option<(&MemberNode, Id<TypeNode>)>, EncodeError> {
let mut fallback = None;
for case in ast.cases_of(id) {
let Ok(case_node) = ast.case(*case) else {
return Err(EncodeError::NoWriter {
member: crate::adapter::cst::handle_of(*case),
kind: "a case the model does not hold",
});
};
let Ok(member) = ast.member(case_node.member) else {
return Err(EncodeError::NoWriter {
member: crate::adapter::cst::handle_of(case_node.member),
kind: "a member the model does not hold",
});
};
let Some(member_type) = member.type_.resolved() else {
return Err(EncodeError::NoWriter {
member: member.name.clone(),
kind: "union case with an unresolved type",
});
};
for label in &case_node.labels {
match label {
CaseLabel::Default => fallback = Some((member, member_type)),
CaseLabel::Value(expr) => {
if ast
.eval_integral(*expr)
.is_ok_and(|value| value == selector)
{
return Ok(Some((member, member_type)));
}
}
}
}
}
Ok(fallback)
}