Files
orchid/orchid-extension/src/system_card.rs

72 lines
2.4 KiB
Rust

use std::any::{Any, TypeId, type_name};
use std::fmt::Debug;
use std::num::NonZero;
use orchid_api_traits::Coding;
use orchid_base::BoxedIter;
use crate::{
AtomOps, AtomTypeId, Atomic, AtomicFeatures, CmdAtom, Fun, Lambda, ReaderAtom, Replier,
SystemCtor, WriterAtom,
};
/// Description of a system. This is intended to be a ZST storing the static
/// properties of a [SystemCtor] which should be known to foreign systems
pub trait SystemCard: Debug + Default + 'static {
type Ctor: SystemCtor;
type Req: Coding;
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomOps>>>;
}
/// Type-erased [SystemCard]
pub trait DynSystemCard: Any + 'static {
fn name(&self) -> &'static str;
/// Atoms explicitly defined by the system card. Do not rely on this for
/// querying atoms as it doesn't include the general atom types
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomOps>>>;
}
impl<T: SystemCard> DynSystemCard for T {
fn name(&self) -> &'static str { T::Ctor::NAME }
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomOps>>> {
Box::new(Self::atoms().into_iter())
}
}
impl<T: DynSystemCard + ?Sized> DynSystemCardExt for T {}
pub(crate) trait DynSystemCardExt: DynSystemCard {
fn ops_by_tid(&self, tid: TypeId) -> Option<(AtomTypeId, Box<dyn AtomOps>)> {
(self.atoms().enumerate().map(|(i, o)| (NonZero::new(i as u32 + 1).unwrap(), o)))
.chain(general_atoms().enumerate().map(|(i, o)| (NonZero::new(!(i as u32)).unwrap(), o)))
.filter_map(|(i, o)| o.map(|a| (AtomTypeId(i), a)))
.find(|ent| ent.1.tid() == tid)
}
fn ops_by_atid(&self, tid: AtomTypeId) -> Option<Box<dyn AtomOps>> {
if (u32::from(tid.0) >> (u32::BITS - 1)) & 1 == 1 {
general_atoms().nth(!u32::from(tid.0) as usize).unwrap()
} else {
self.atoms().nth(u32::from(tid.0) as usize - 1).unwrap()
}
}
fn ops<A: Atomic>(&self) -> (AtomTypeId, Box<dyn AtomOps>) {
self
.ops_by_tid(TypeId::of::<A>())
.unwrap_or_else(|| panic!("{} is not an atom in {}", type_name::<A>(), self.name()))
}
}
/// Atoms supported by this package which may appear in all extensions.
/// The indices of these are bitwise negated, such that the MSB of an atom index
/// marks whether it belongs to this package (0) or the importer (1)
pub(crate) fn general_atoms() -> impl Iterator<Item = Option<Box<dyn AtomOps>>> {
[
Some(Fun::ops()),
Some(Lambda::ops()),
Some(Replier::ops()),
Some(CmdAtom::ops()),
Some(WriterAtom::ops()),
Some(ReaderAtom::ops()),
]
.into_iter()
}