use std::fmt; use std::num::NonZeroU64; use itertools::Itertools; use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_traits::Request; use crate::{ ExprTicket, Expression, ExtHostReq, FormattingUnit, HostExtReq, OrcResult, SysId, TStrv, }; #[derive(Clone, Coding)] pub struct AtomData(pub Vec); impl fmt::Debug for AtomData { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut byte_strings = self.0.iter().map(|b| format!("{b:02x}")); if self.0.len() < 32 { write!(f, "AtomData({})", byte_strings.join(" ")) } else { let data_table = byte_strings.chunks(32).into_iter().map(|mut chunk| chunk.join(" ")).join("\n"); write!(f, "AtomData(\n{}\n)", data_table) } } } /// Unique ID associated with atoms that have an identity #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)] pub struct AtomId(pub NonZeroU64); /// An atom owned by an implied system. Usually used in responses from a system. /// This has the same semantics as [Atom] except in that the owner is implied. #[derive(Clone, Debug, Coding)] pub struct LocalAtom { pub drop: Option, pub data: AtomData, } impl LocalAtom { pub fn associate(self, owner: SysId) -> Atom { Atom { owner, drop: self.drop, data: self.data } } } /// An atom representation that can be serialized and sent around. Atoms /// represent the smallest increment of work. #[derive(Clone, Debug, Coding)] pub struct Atom { /// Instance ID of the system that created the atom pub owner: SysId, /// Indicates how the owner should be notified when this atom is dropped. /// Construction is always explicit and atoms are never cloned. /// /// Atoms with `drop == None` are also known as trivial, they can be /// duplicated and stored with no regard to expression lifetimes. NOTICE /// that this only applies to the atom. If it's referenced with an /// [ExprTicket], the ticket itself can still expire. /// /// Notice also that the atoms still expire when the system is dropped, and /// are not portable across instances of the same system, so this doesn't /// imply that the atom is serializable. pub drop: Option, /// Data stored in the atom. This could be a key into a map, or the raw data /// of the atom if it isn't too big. pub data: AtomData, } /// Attempt to apply an atom as a function to an expression #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(AtomReq, HostExtReq)] pub struct CallRef(pub Atom, pub ExprTicket); impl Request for CallRef { type Response = Expression; } /// Attempt to apply an atom as a function, consuming the atom and enabling the /// library to reuse its datastructures rather than duplicating them. This is an /// optimization over [CallRef] followed by [AtomDrop]. #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(AtomReq, HostExtReq)] pub struct FinalCall(pub Atom, pub ExprTicket); impl Request for FinalCall { type Response = Expression; } #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(AtomReq, HostExtReq)] pub struct SerializeAtom(pub Atom); impl Request for SerializeAtom { type Response = Option<(Vec, Vec)>; } #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[extends(HostExtReq)] pub struct DeserAtom(pub SysId, pub Vec, pub Vec); impl Request for DeserAtom { type Response = Atom; } /// A request blindly routed to the system that provides an atom. #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(AtomReq, HostExtReq)] pub struct Fwded(pub Atom, pub TStrv, pub Vec); impl Request for Fwded { type Response = Option>; } #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(ExtHostReq)] pub struct Fwd(pub Atom, pub TStrv, pub Vec); impl Request for Fwd { type Response = Option>; } #[derive(Clone, Debug, Coding)] pub enum NextStep { Continue(Expression), Halt, } #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(AtomReq, HostExtReq)] pub struct Command(pub Atom); impl Request for Command { type Response = OrcResult; } /// Notification that an atom is being dropped because its associated expression /// isn't referenced anywhere. This should have no effect if the atom's `drop` /// flag is false. #[derive(Clone, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[extends(HostExtReq)] pub struct AtomDrop(pub SysId, pub AtomId); impl Request for AtomDrop { type Response = (); } #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(AtomReq, HostExtReq)] pub struct AtomPrint(pub Atom); impl Request for AtomPrint { type Response = FormattingUnit; } #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(ExtHostReq)] pub struct ExtAtomPrint(pub Atom); impl Request for ExtAtomPrint { type Response = FormattingUnit; } /// Requests that apply to an existing atom instance #[derive(Clone, Debug, Coding, Hierarchy)] #[extends(HostExtReq)] #[extendable] pub enum AtomReq { CallRef(CallRef), FinalCall(FinalCall), Fwded(Fwded), Command(Command), AtomPrint(AtomPrint), SerializeAtom(SerializeAtom), } impl AtomReq { /// Obtain the first [Atom] argument of the request. All requests in this /// subclass have at least one atom argument. pub fn get_atom(&self) -> &Atom { match self { Self::CallRef(CallRef(a, ..)) | Self::Command(Command(a)) | Self::FinalCall(FinalCall(a, ..)) | Self::Fwded(Fwded(a, ..)) | Self::AtomPrint(AtomPrint(a)) | Self::SerializeAtom(SerializeAtom(a)) => a, } } }