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>>; } /// 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>>; } impl DynSystemCard for T { fn name(&self) -> &'static str { T::Ctor::NAME } fn atoms(&'_ self) -> BoxedIter<'_, Option>> { Box::new(Self::atoms().into_iter()) } } impl DynSystemCardExt for T {} pub(crate) trait DynSystemCardExt: DynSystemCard { fn ops_by_tid(&self, tid: TypeId) -> Option<(AtomTypeId, Box)> { (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> { 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(&self) -> (AtomTypeId, Box) { self .ops_by_tid(TypeId::of::()) .unwrap_or_else(|| panic!("{} is not an atom in {}", type_name::(), 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>> { [ Some(Fun::ops()), Some(Lambda::ops()), Some(Replier::ops()), Some(CmdAtom::ops()), Some(WriterAtom::ops()), Some(ReaderAtom::ops()), ] .into_iter() }