#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Phase {
GroupA,
GroupB,
ArenaOnly,
}
pub type NoteFn<M, A, N, E> = fn(&M, &FamilyRow<M, A, N, E>, &mut A);
pub type EmitFn<M, A, N, E> =
fn(&M, &FamilyRow<M, A, N, E>, &mut N) -> Result<(), NativeConvertError>;
pub struct FamilyRow<M, A, N, E> {
pub arena: &'static str,
pub tag: Option<&'static str>,
pub exactness: E,
pub phase: Phase,
pub note: Option<NoteFn<M, A, N, E>>,
pub emit: EmitFn<M, A, N, E>,
pub len: fn(&M) -> usize,
pub counts_toward_emptiness: bool,
}
pub struct Catalogue<'a, M, A, N, E> {
rows: &'a [FamilyRow<M, A, N, E>],
version: VersionContract,
}
impl<'a, M, A, N, E> Catalogue<'a, M, A, N, E> {
pub const fn new(rows: &'a [FamilyRow<M, A, N, E>], version: VersionContract) -> Self {
Self { rows, version }
}
pub const fn rows(&self) -> &'a [FamilyRow<M, A, N, E>] {
self.rows
}
pub const fn check_version(&self, version: u32) -> Result<(), NativeVersionError> {
self.version.check_version(version)
}
pub fn emit_all(&self, model: &M, namespace: &mut N) -> Result<(), NativeConvertError> {
for row in self.rows {
(row.emit)(model, row, namespace)?;
}
Ok(())
}
pub fn note_phase(&self, phase: Phase, model: &M, annotations: &mut A) {
for row in self.rows.iter().filter(|row| row.phase == phase) {
if let Some(note) = row.note {
note(model, row, annotations);
}
}
}
pub fn is_empty(&self, model: &M) -> bool {
self.rows
.iter()
.filter(|row| row.counts_toward_emptiness)
.all(|row| (row.len)(model) == 0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VersionContract {
pub minimum: u32,
pub maximum: u32,
}
impl VersionContract {
pub const fn check_version(self, version: u32) -> Result<(), NativeVersionError> {
if version < self.minimum || version > self.maximum {
Err(NativeVersionError::Unsupported {
version,
minimum: self.minimum,
maximum: self.maximum,
})
} else {
Ok(())
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum NativeVersionError {
#[error("unsupported native version {version}; accepted range is {minimum}..={maximum}")]
Unsupported {
version: u32,
minimum: u32,
maximum: u32,
},
}
use super::NativeConvertError;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_contract_accepts_only_its_inclusive_range() {
let contract = VersionContract {
minimum: 4,
maximum: 13,
};
assert!(contract.check_version(4).is_ok());
assert!(contract.check_version(13).is_ok());
assert!(matches!(
contract.check_version(3),
Err(NativeVersionError::Unsupported {
version: 3,
minimum: 4,
maximum: 13
})
));
assert!(matches!(
contract.check_version(14),
Err(NativeVersionError::Unsupported {
version: 14,
minimum: 4,
maximum: 13
})
));
}
}