use crate::Load;
#[derive(Clone, Copy, Debug)]
pub(crate) struct Values<T: 'static, const N: usize>([(T, Option<u64>); N]);
impl<T: Copy + PartialEq, const N: usize> Values<T, N> {
#[cfg(test)]
pub(crate) const fn new(values: [(T, Option<u64>); N]) -> Self {
Self(values)
}
pub(crate) fn resolve(load: &Load<'_>, ids: [(T, &str); N]) -> Self {
Self(ids.map(|(variant, id)| (variant, load.constant(id).ok())))
}
pub(crate) fn variant(&self, raw: u64) -> Option<T> {
self.0
.iter()
.find(|&&(_, value)| value == Some(raw))
.map(|&(variant, _)| variant)
}
pub(crate) fn value(&self, variant: T) -> Option<u64> {
self.0
.iter()
.find(|&&(known, _)| known == variant)
.and_then(|&(_, value)| value)
}
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct Bits([u16; 16]);
impl Bits {
pub(crate) fn resolve<const N: usize>(load: &Load<'_>, ids: [(u16, &str); N]) -> Self {
Self::from_indices(
ids.into_iter()
.map(|(ours, id)| (ours, load.constant(id).ok())),
)
}
fn from_indices(bits: impl Iterator<Item = (u16, Option<u64>)>) -> Self {
let mut table = [0u16; 16];
for (ours, index) in bits {
if let Some(entry) = index
.and_then(|i| usize::try_from(i).ok())
.and_then(|slot| table.get_mut(slot))
{
*entry |= ours;
}
}
Self(table)
}
pub(crate) fn map(&self, raw: u16) -> u16 {
let mut rest = raw;
let mut out = 0;
while rest != 0 {
out |= self.0[rest.trailing_zeros() as usize];
rest &= rest - 1;
}
out
}
}
#[cfg(test)]
pub(crate) fn assert_named_after_engine<T: core::fmt::Debug>(ids: &[(T, &str)]) {
let plain = |name: &str| -> String {
name.chars()
.filter(|&c| c != '_')
.map(|c| c.to_ascii_lowercase())
.collect()
};
for (variant, id) in ids {
let (_, constant) = id.rsplit_once('.').unwrap_or(("", id));
assert_eq!(plain(&format!("{variant:?}")), plain(constant), "{id}");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Copy, Debug, PartialEq)]
enum Side {
Left,
Right,
Up,
}
#[test]
fn a_variant_decodes_from_its_engine_value_and_back() {
let values = Values([
(Side::Left, Some(7)),
(Side::Right, Some(2)),
(Side::Up, None),
]);
assert_eq!(values.variant(2), Some(Side::Right));
assert_eq!(values.variant(9), None);
assert_eq!(values.variant(7), Some(Side::Left));
assert_eq!(values.value(Side::Right), Some(2));
assert_eq!(values.value(Side::Up), None, "the build lacks its constant");
}
#[test]
fn engine_bits_map_to_the_api_s_bits_and_unnamed_bits_drop() {
let bits =
Bits::from_indices([(1, Some(3)), (2, Some(0)), (4, None), (8, Some(99))].into_iter());
assert_eq!(bits.map(0b1001), 0b11);
assert_eq!(bits.map(0b0100), 0, "bit 2 has no name");
assert_eq!(bits.map(0x8000), 0);
assert_eq!(bits.map(0), 0);
}
}