use crate::{Field, Type};
pub const fn total_field_count(groups: &[&[&Field<'static>]]) -> usize {
let mut total = 0;
let mut i = 0;
while i < groups.len() {
total += groups[i].len();
i += 1;
}
total
}
pub const fn concat_fields<const N: usize>(
groups: &[&[&'static Field<'static>]],
) -> [&'static Field<'static>; N] {
const FILLER: &Field<'static> = &Field::new("", &Type::Bool, &[]);
let mut out: [&Field<'static>; N] = [FILLER; N];
let mut oi = 0;
let mut gi = 0;
while gi < groups.len() {
let group = groups[gi];
let mut fi = 0;
while fi < group.len() {
out[oi] = group[fi];
oi += 1;
fi += 1;
}
gi += 1;
}
if oi != N {
panic!("concat_fields: `N` does not match the total number of fields in `groups`");
}
out
}
pub const fn object_fields(ty: &'static Type<'static>) -> &'static [&'static Field<'static>] {
match ty.as_object() {
Some(list) => match list.as_borrowed() {
Some(fields) => fields,
None => panic!("#[zlink(flatten)]: expected a borrowed object type"),
},
None => panic!(
"#[zlink(flatten)] requires an inline object type (a `#[derive(Type)]` struct); \
named custom types and scalars are not supported"
),
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use super::*;
use crate::List;
#[test]
fn counts_and_concatenates_in_order() {
static A: Field<'static> = Field::new("a", &Type::Int, &[]);
static B: Field<'static> = Field::new("b", &Type::String, &[]);
static C: Field<'static> = Field::new("c", &Type::Bool, &[]);
static G_A: &[&Field<'static>] = &[&A];
static G_BC: &[&Field<'static>] = &[&B, &C];
static GROUPS: &[&[&Field<'static>]] = &[G_A, G_BC];
assert_eq!(total_field_count(&[]), 0);
assert_eq!(total_field_count(GROUPS), 3);
const N: usize = total_field_count(GROUPS);
static OUT: [&Field<'static>; N] = concat_fields::<N>(GROUPS);
let names: Vec<&str> = OUT.iter().map(|f| f.name()).collect();
assert_eq!(names, ["a", "b", "c"]);
}
#[test]
fn concatenates_empty_groups() {
static GROUPS: &[&[&Field<'static>]] = &[];
const N: usize = total_field_count(GROUPS);
static OUT: [&Field<'static>; N] = concat_fields::<N>(GROUPS);
assert_eq!(OUT.len(), 0);
}
#[test]
fn object_fields_returns_the_members() {
static F: Field<'static> = Field::new("x", &Type::Int, &[]);
static FIELDS: &[&Field<'static>] = &[&F];
static OBJ: Type<'static> = Type::Object(List::Borrowed(FIELDS));
let got = object_fields(&OBJ);
assert_eq!(got.len(), 1);
assert_eq!(got[0].name(), "x");
}
#[test]
#[should_panic(expected = "requires an inline object type")]
fn object_fields_rejects_a_scalar() {
static SCALAR: Type<'static> = Type::Int;
object_fields(&SCALAR);
}
}