use crate::key::{Encoding, KeyHolderNesting, KeyHolderOrder, LayoutKey};
use crate::plan::{Extent, LayoutError, TypeLayout};
use crate::xcdr::{DepCollector, plan};
use alloc::vec::Vec;
use zerodds_ast::{Ast, TypeKind, TypeNode};
use zerodds_ast_core::{Id, NodeId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum KeyHashForm {
ZeroPadded,
Md5,
}
pub const KEY_HASH_LEN: u32 = 16;
pub fn key_holder_plan(
ast: &Ast,
type_: Id<TypeNode>,
encoding: Encoding,
deps: &mut DepCollector,
) -> Result<TypeLayout, LayoutError> {
plan(ast, type_, LayoutKey::key(encoding), deps)
}
pub fn key_holder_plan_with(
ast: &Ast,
type_: Id<TypeNode>,
key: LayoutKey,
deps: &mut DepCollector,
) -> Result<TypeLayout, LayoutError> {
let key = LayoutKey {
purpose: crate::key::Purpose::Key,
endianness: crate::key::Endianness::Big,
..key
};
plan(ast, type_, key, deps)
}
#[must_use]
pub fn key_hash_form(layout: &TypeLayout) -> KeyHashForm {
match layout.extent {
Extent::Sized(size) if size <= KEY_HASH_LEN => KeyHashForm::ZeroPadded,
_ => KeyHashForm::Md5,
}
}
pub fn key_hash_form_for(
ast: &Ast,
type_: Id<TypeNode>,
key: LayoutKey,
) -> Result<KeyHashForm, LayoutError> {
let key = LayoutKey {
purpose: crate::key::Purpose::Key,
endianness: crate::key::Endianness::Big,
..key
};
key_holder_plan_with(ast, type_, key, &mut DepCollector::default())?;
Ok(
if crate::key_maximum::holder(ast, type_, key)? <= KEY_HASH_LEN {
KeyHashForm::ZeroPadded
} else {
KeyHashForm::Md5
},
)
}
#[must_use]
pub fn holder_keeps_only_keys(ast: &Ast, type_: Id<TypeNode>) -> bool {
any_key_member(ast, &ast.all_members_of(type_))
}
pub(crate) fn any_key_member(ast: &Ast, members: &[Id<zerodds_ast::MemberNode>]) -> bool {
members
.iter()
.any(|member| ast.member(*member).is_ok_and(|m| m.facets.is_key()))
}
#[must_use]
pub fn member_key(ast: &Ast, type_: Id<TypeNode>, key: LayoutKey) -> LayoutKey {
let aggregate = ast.type_(type_).is_ok_and(|node| {
matches!(
node.kind,
TypeKind::Structure { .. } | TypeKind::Valuetype { .. }
)
});
member_key_for(key, aggregate && !holder_keeps_only_keys(ast, type_))
}
pub(crate) const fn member_key_for(key: LayoutKey, keyless_aggregate: bool) -> LayoutKey {
if !keyless_aggregate
|| !matches!(key.purpose, crate::key::Purpose::Key)
|| !matches!(key.key_holder_nesting, KeyHolderNesting::KeylessNestedWhole)
{
return key;
}
LayoutKey {
purpose: crate::key::Purpose::Data,
..key
}
}
#[must_use]
pub fn union_key_discriminator(ast: &Ast, type_: Id<TypeNode>) -> Option<Id<TypeNode>> {
let mut current = type_;
for _ in 0..crate::plan::MAX_DEPTH {
let node = ast.type_(current).ok()?;
match &node.kind {
TypeKind::Alias { aliased } => current = aliased.resolved()?,
TypeKind::Union { discriminator, .. } => {
if !ast.discriminator_is_key(current) {
return None;
}
return discriminator.resolved();
}
_ => return None,
}
}
None
}
pub fn key_members(ast: &Ast, type_: Id<TypeNode>) -> Result<Vec<NodeId>, LayoutError> {
key_members_with(
ast,
type_,
KeyHolderNesting::default(),
KeyHolderOrder::default(),
)
}
pub fn key_members_with(
ast: &Ast,
type_: Id<TypeNode>,
nesting: KeyHolderNesting,
order: KeyHolderOrder,
) -> Result<Vec<NodeId>, LayoutError> {
let mut out = Vec::new();
collect_key_members(ast, type_, nesting, order, &mut out, 0)?;
Ok(out)
}
fn collect_key_members(
ast: &Ast,
type_: Id<TypeNode>,
nesting: KeyHolderNesting,
order: KeyHolderOrder,
out: &mut Vec<NodeId>,
depth: usize,
) -> Result<(), LayoutError> {
let node = ast.type_(type_)?;
if depth > crate::plan::MAX_DEPTH {
return Err(LayoutError::TooDeep { node: node.node_id });
}
if let TypeKind::Alias { aliased } = &node.kind {
let target = aliased
.resolved()
.ok_or(LayoutError::Unresolved { node: node.node_id })?;
return collect_key_members(ast, target, nesting, order, out, depth + 1);
}
if !matches!(
node.kind,
TypeKind::Structure { .. } | TypeKind::Valuetype { .. }
) {
return Ok(());
}
let members = ast.all_members_of(type_);
let only_keys = depth == 0 || any_key_member(ast, &members);
let mut keyed: Vec<(u32, NodeId, Option<Id<TypeNode>>)> = Vec::new();
for member in &members {
let m = ast.member(*member)?;
if only_keys && !m.facets.is_key() {
continue;
}
let id = m
.facets
.effective_member_id()
.unwrap_or_else(|| position_of(&members, *member));
keyed.push((id, m.node_id, m.type_.resolved()));
}
if order == KeyHolderOrder::AscendingMemberId {
keyed.sort_by_key(|(id, _, _)| *id);
}
let recurse = only_keys || nesting == KeyHolderNesting::Recursive;
for (_, node_id, member_type) in keyed {
out.push(node_id);
if let (true, Some(inner)) = (recurse, member_type) {
collect_key_members(ast, inner, nesting, order, out, depth + 1)?;
}
}
Ok(())
}
fn position_of(
members: &[Id<zerodds_ast::MemberNode>],
member: Id<zerodds_ast::MemberNode>,
) -> u32 {
members
.iter()
.position(|candidate| *candidate == member)
.and_then(|index| u32::try_from(index).ok())
.unwrap_or(u32::MAX)
}