forked from Orchid/orchid
partway towards commands
I got very confused and started mucking about with "spawn" when in fact all I needed was the "inline" extension type in orcx that allows the interpreter to expose custom constants.
This commit is contained in:
@@ -4,87 +4,82 @@ use std::fmt::{self, Debug};
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::num::NonZeroU32;
|
||||
use std::num::{NonZero, NonZeroU32, NonZeroU64};
|
||||
use std::ops::Deref;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
|
||||
use dyn_clone::{DynClone, clone_box};
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::stream::LocalBoxStream;
|
||||
use futures::{AsyncWrite, FutureExt, StreamExt, stream};
|
||||
use orchid_api_derive::Coding;
|
||||
use orchid_api_traits::{Coding, Decode, InHierarchy, Request, UnderRoot, enc_vec};
|
||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv, mk_errv_floating};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, fmt, take_first};
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::{Receipt, ReqHandle, ReqReader, ReqReaderExt};
|
||||
use orchid_base::{
|
||||
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Pos, Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym,
|
||||
fmt, is, mk_errv, mk_errv_floating, take_first,
|
||||
};
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::api;
|
||||
use crate::atom_owned::{OwnedAtom, get_obj_store};
|
||||
use crate::conv::ToExpr;
|
||||
use crate::entrypoint::request;
|
||||
// use crate::error::{ProjectError, ProjectResult};
|
||||
use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
|
||||
use crate::gen_expr::GExpr;
|
||||
use crate::system::{DynSystemCard, atom_by_idx, atom_info_for, cted, downcast_atom};
|
||||
use crate::gen_expr::{GExpr, IntoGExprStream};
|
||||
use crate::system::{DynSystemCardExt, cted, sys_id};
|
||||
|
||||
/// Every atom managed via this system starts with an ID into the type table
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
|
||||
pub struct AtomTypeId(pub NonZeroU32);
|
||||
|
||||
pub trait AtomCard: 'static + Sized {
|
||||
type Data: Clone + Coding + Sized;
|
||||
}
|
||||
|
||||
pub trait AtomicVariant {}
|
||||
|
||||
/// A value managed by Orchid. The type should also be registered in the
|
||||
/// [crate::SystemCard] through [AtomicFeatures::ops] which is provided
|
||||
/// indirectly by either [crate::OwnedAtom] or [crate::ThinAtom]
|
||||
pub trait Atomic: 'static + Sized {
|
||||
/// Either [crate::OwnedVariant] or [crate::ThinVariant] depending on whether
|
||||
/// the value implements [crate::OwnedAtom] or [crate::ThinAtom]
|
||||
type Variant: AtomicVariant;
|
||||
/// Serializable data that gets sent inside the atom to other systems that
|
||||
/// depend on this system. Methods on this value are directly accessible
|
||||
/// through [TAtom], and this data can also be used for optimized public
|
||||
/// functions. The serialized form should have a reasonable length to avoid
|
||||
/// overburdening the protocol.
|
||||
type Data: Clone + Coding + Sized + 'static;
|
||||
/// Register handlers for IPC calls. If this atom implements [Supports], you
|
||||
/// should register your implementations here. If this atom doesn't
|
||||
/// participate in IPC at all, the default implementation is fine
|
||||
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
|
||||
}
|
||||
impl<A: Atomic> AtomCard for A {
|
||||
type Data = <Self as Atomic>::Data;
|
||||
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
|
||||
}
|
||||
|
||||
/// Shared interface of all atom types created in this library for use by the
|
||||
/// library that defines them. This is provided by [Atomic] and either
|
||||
/// [crate::OwnedAtom] or [crate::ThinAtom]
|
||||
pub trait AtomicFeatures: Atomic {
|
||||
/// Convert a value of this atom inside the defining system into a function
|
||||
/// that will perform registrations and serialization
|
||||
#[allow(private_interfaces)]
|
||||
fn factory(self) -> AtomFactory;
|
||||
type Info: AtomDynfo;
|
||||
fn info() -> Self::Info;
|
||||
fn dynfo() -> Box<dyn AtomDynfo>;
|
||||
/// Expose all operations that can be performed on an instance of this type in
|
||||
/// an instanceless vtable. This vtable must be registered by the
|
||||
/// [crate::System].
|
||||
fn ops() -> Box<dyn AtomOps>;
|
||||
}
|
||||
pub trait ToAtom {
|
||||
fn to_atom_factory(self) -> AtomFactory;
|
||||
}
|
||||
impl<A: AtomicFeatures> ToAtom for A {
|
||||
fn to_atom_factory(self) -> AtomFactory { self.factory() }
|
||||
}
|
||||
impl ToAtom for AtomFactory {
|
||||
fn to_atom_factory(self) -> AtomFactory { self }
|
||||
}
|
||||
pub trait AtomicFeaturesImpl<Variant: AtomicVariant> {
|
||||
pub(crate) trait AtomicFeaturesImpl<Variant: AtomicVariant> {
|
||||
fn _factory(self) -> AtomFactory;
|
||||
type _Info: AtomDynfo;
|
||||
type _Info: AtomOps;
|
||||
fn _info() -> Self::_Info;
|
||||
}
|
||||
impl<A: Atomic + AtomicFeaturesImpl<A::Variant>> AtomicFeatures for A {
|
||||
#[allow(private_interfaces)]
|
||||
fn factory(self) -> AtomFactory { self._factory() }
|
||||
type Info = <Self as AtomicFeaturesImpl<A::Variant>>::_Info;
|
||||
fn info() -> Self::Info { Self::_info() }
|
||||
fn dynfo() -> Box<dyn AtomDynfo> { Box::new(Self::info()) }
|
||||
}
|
||||
|
||||
pub fn get_info<A: AtomCard>(
|
||||
sys: &(impl DynSystemCard + ?Sized),
|
||||
) -> (AtomTypeId, Box<dyn AtomDynfo>) {
|
||||
atom_info_for(sys, TypeId::of::<A>()).unwrap_or_else(|| {
|
||||
panic!("Atom {} not associated with system {}", type_name::<A>(), sys.name())
|
||||
})
|
||||
fn ops() -> Box<dyn AtomOps> { Box::new(Self::_info()) }
|
||||
}
|
||||
|
||||
/// A reference to a value of some [Atomic] type. This owns an [Expr]
|
||||
#[derive(Clone)]
|
||||
pub struct ForeignAtom {
|
||||
pub(crate) expr: Rc<ExprHandle>,
|
||||
@@ -92,7 +87,9 @@ pub struct ForeignAtom {
|
||||
pub(crate) pos: Pos,
|
||||
}
|
||||
impl ForeignAtom {
|
||||
/// Obtain the position in code of the expression
|
||||
pub fn pos(&self) -> Pos { self.pos.clone() }
|
||||
/// Obtain the [Expr]
|
||||
pub fn ex(self) -> Expr {
|
||||
let (handle, pos) = (self.expr.clone(), self.pos.clone());
|
||||
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { ..self }) };
|
||||
@@ -101,10 +98,9 @@ impl ForeignAtom {
|
||||
pub(crate) fn new(handle: Rc<ExprHandle>, atom: api::Atom, pos: Pos) -> Self {
|
||||
ForeignAtom { atom, expr: handle, pos }
|
||||
}
|
||||
pub async fn request<R: Request + UnderRoot<Root: AtomMethod>>(
|
||||
&self,
|
||||
r: R,
|
||||
) -> Option<R::Response> {
|
||||
/// Call an IPC method. If the type does not support the given method type,
|
||||
/// this function returns [None]
|
||||
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, r: R) -> Option<R::Response> {
|
||||
let rep = (request(api::Fwd(
|
||||
self.atom.clone(),
|
||||
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||
@@ -113,8 +109,33 @@ impl ForeignAtom {
|
||||
.await?;
|
||||
Some(R::Response::decode_slice(&mut &rep[..]))
|
||||
}
|
||||
pub async fn downcast<T: AtomicFeatures>(self) -> Result<TAtom<T>, NotTypAtom> {
|
||||
TAtom::downcast(self.ex().handle()).await
|
||||
/// Attempt to downcast this value to a concrete atom type
|
||||
pub fn downcast<A: Atomic>(self) -> Result<TAtom<A>, NotTypAtom> {
|
||||
let mut data = &self.atom.data.0[..];
|
||||
let value = AtomTypeId::decode_slice(&mut data);
|
||||
if cfg!(debug_assertions) {
|
||||
let cted = cted();
|
||||
let own_inst = cted.inst();
|
||||
let owner_id = self.atom.owner;
|
||||
let typ = type_name::<A>();
|
||||
let owner = if sys_id() == owner_id {
|
||||
own_inst.card()
|
||||
} else {
|
||||
(cted.deps().find(|s| s.id() == self.atom.owner))
|
||||
.ok_or_else(|| NotTypAtom { expr: self.clone().ex(), pos: self.pos(), typ })?
|
||||
.get_card()
|
||||
};
|
||||
let Some(ops) = owner.ops_by_atid(value) else {
|
||||
panic!("{value:?} does not refer to an atom in {owner_id:?} when downcasting {typ}");
|
||||
};
|
||||
if ops.tid() != TypeId::of::<A>() {
|
||||
panic!(
|
||||
"{value:?} of {owner_id:?} refers to a type other than {typ}. System version mismatch?"
|
||||
)
|
||||
}
|
||||
}
|
||||
let value = A::Data::decode_slice(&mut data);
|
||||
Ok(TAtom { value, untyped: self })
|
||||
}
|
||||
}
|
||||
impl fmt::Display for ForeignAtom {
|
||||
@@ -139,13 +160,14 @@ impl ToExpr for ForeignAtom {
|
||||
pub struct NotTypAtom {
|
||||
pub pos: Pos,
|
||||
pub expr: Expr,
|
||||
pub typ: Box<dyn AtomDynfo>,
|
||||
pub typ: &'static str,
|
||||
}
|
||||
impl NotTypAtom {
|
||||
/// Convert to a generic Orchid error
|
||||
pub async fn mk_err(&self) -> OrcErrv {
|
||||
mk_errv(
|
||||
is("Not the expected type").await,
|
||||
format!("The expression {} is not a {}", fmt(&self.expr).await, self.typ.name()),
|
||||
format!("The expression {} is not a {}", fmt(&self.expr).await, self.typ),
|
||||
[self.pos.clone()],
|
||||
)
|
||||
}
|
||||
@@ -155,15 +177,21 @@ impl Debug for NotTypAtom {
|
||||
f.debug_struct("NotTypAtom")
|
||||
.field("pos", &self.pos)
|
||||
.field("expr", &self.expr)
|
||||
.field("typ.name", &self.typ.name())
|
||||
.field("typ", &self.typ)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
/// An IPC request associated with an atom. This type should either implement
|
||||
/// [Request] or be the root of a [orchid_api_derive::Hierarchy] the leaves of
|
||||
/// which implement [Request].
|
||||
pub trait AtomMethod: Coding + InHierarchy {
|
||||
const NAME: &str;
|
||||
}
|
||||
pub trait Supports<M: AtomMethod>: AtomCard {
|
||||
|
||||
/// A handler for an [AtomMethod] on an [Atomic]. The [AtomMethod] must also be
|
||||
/// registered in [Atomic::reg_methods]
|
||||
pub trait Supports<M: AtomMethod>: Atomic {
|
||||
fn handle<'a>(
|
||||
&self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
@@ -192,12 +220,17 @@ impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MethodSetBuilder<A: AtomCard> {
|
||||
/// A collection of [Supports] impls for an [Atomic]. If a [Supports]
|
||||
/// impl is not added to the method set, it will not be recognized. Note that
|
||||
/// the [Supports] implementors must be registered, which are not necessarily
|
||||
/// the same as the [Request] implementors
|
||||
pub struct MethodSetBuilder<A: Atomic> {
|
||||
handlers: Vec<(&'static str, Rc<dyn HandleAtomMethod<A>>)>,
|
||||
}
|
||||
impl<A: AtomCard> MethodSetBuilder<A> {
|
||||
impl<A: Atomic> MethodSetBuilder<A> {
|
||||
pub fn new() -> Self { Self { handlers: vec![] } }
|
||||
|
||||
/// Add an [AtomMethod]
|
||||
pub fn handle<M: AtomMethod>(mut self) -> Self
|
||||
where A: Supports<M> {
|
||||
assert!(!M::NAME.is_empty(), "AtomMethod::NAME cannoot be empty");
|
||||
@@ -205,7 +238,7 @@ impl<A: AtomCard> MethodSetBuilder<A> {
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn pack(&self) -> MethodSet<A> {
|
||||
pub(crate) async fn pack(&self) -> MethodSet<A> {
|
||||
MethodSet {
|
||||
handlers: stream::iter(self.handlers.iter())
|
||||
.then(async |(k, v)| (Sym::parse(k).await.unwrap(), v.clone()))
|
||||
@@ -215,10 +248,10 @@ impl<A: AtomCard> MethodSetBuilder<A> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MethodSet<A: AtomCard> {
|
||||
pub(crate) struct MethodSet<A: Atomic> {
|
||||
handlers: HashMap<Sym, Rc<dyn HandleAtomMethod<A>>>,
|
||||
}
|
||||
impl<A: AtomCard> MethodSet<A> {
|
||||
impl<A: Atomic> MethodSet<A> {
|
||||
pub(crate) async fn dispatch<'a>(
|
||||
&self,
|
||||
atom: &'_ A,
|
||||
@@ -235,29 +268,45 @@ impl<A: AtomCard> MethodSet<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AtomCard> Default for MethodSetBuilder<A> {
|
||||
impl<A: Atomic> Default for MethodSetBuilder<A> {
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
||||
/// A handle to a value defined by this or another system. This owns an [Expr]
|
||||
#[derive(Clone)]
|
||||
pub struct TAtom<A: AtomicFeatures> {
|
||||
pub struct TAtom<A: Atomic> {
|
||||
pub untyped: ForeignAtom,
|
||||
pub value: A::Data,
|
||||
}
|
||||
impl<A: AtomicFeatures> TAtom<A> {
|
||||
impl<A: Atomic> TAtom<A> {
|
||||
/// Obtain the underlying [Expr]
|
||||
pub fn ex(&self) -> Expr { self.untyped.clone().ex() }
|
||||
/// Obtain the position in code associated with the atom
|
||||
pub fn pos(&self) -> Pos { self.untyped.pos() }
|
||||
/// Produce from an [ExprHandle] directly
|
||||
pub async fn downcast(expr: Rc<ExprHandle>) -> Result<Self, NotTypAtom> {
|
||||
match Expr::from_handle(expr).atom().await {
|
||||
Err(expr) =>
|
||||
Err(NotTypAtom { pos: expr.data().await.pos.clone(), expr, typ: Box::new(A::info()) }),
|
||||
Ok(atm) => match downcast_atom::<A>(atm).await {
|
||||
Ok(tatom) => Ok(tatom),
|
||||
Err(fa) => Err(NotTypAtom { pos: fa.pos.clone(), expr: fa.ex(), typ: Box::new(A::info()) }),
|
||||
},
|
||||
Err(NotTypAtom { pos: expr.data().await.pos.clone(), expr, typ: type_name::<A>() }),
|
||||
Ok(atm) => atm.downcast(),
|
||||
}
|
||||
}
|
||||
pub async fn request<R: Request + UnderRoot<Root: AtomMethod>>(&self, req: R) -> R::Response
|
||||
/// Find the instance associated with a [TAtom] that we own
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// if we don't actually own this atom
|
||||
pub async fn own(&self) -> A
|
||||
where A: OwnedAtom {
|
||||
let g = get_obj_store().objects.read().await;
|
||||
let atom_id = self.untyped.atom.drop.expect("Owned atoms always have a drop ID");
|
||||
let dyn_atom =
|
||||
g.get(&atom_id).expect("Atom ID invalid; atom type probably not owned by this crate");
|
||||
dyn_atom.as_any_ref().downcast_ref().cloned().expect("The ID should imply a type as well")
|
||||
}
|
||||
/// Call an IPC method on the value. Since we know the type, unlike
|
||||
/// [ForeignAtom::call], we can ensure that the callee recognizes this method
|
||||
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, req: R) -> R::Response
|
||||
where A: Supports<<R as UnderRoot>::Root> {
|
||||
R::Response::decode_slice(
|
||||
&mut &(request(api::Fwd(
|
||||
@@ -283,9 +332,82 @@ impl<A: AtomicFeatures> Format for TAtom<A> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>);
|
||||
pub(crate) struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>);
|
||||
|
||||
pub trait AtomDynfo: 'static {
|
||||
pub enum Next {
|
||||
ExitSuccess,
|
||||
Continue(Continuation),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Continuation {
|
||||
immediate: Vec<LocalBoxStream<'static, GExpr>>,
|
||||
delayed: Vec<(NonZeroU64, LocalBoxStream<'static, GExpr>)>,
|
||||
}
|
||||
impl Continuation {
|
||||
pub fn immediate<T: IntoGExprStream + 'static>(mut self, expr: T) -> Self {
|
||||
self.immediate.push(Box::pin(expr.into_gexpr_stream()));
|
||||
self
|
||||
}
|
||||
pub fn schedule<T: IntoGExprStream + 'static>(mut self, delay: Duration, expr: T) -> Self {
|
||||
let delay = delay.as_millis().try_into().unwrap();
|
||||
let Some(nzdelay) = NonZero::new(delay) else { return self.immediate(expr) };
|
||||
self.delayed.push((nzdelay, Box::pin(expr.into_gexpr_stream())));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Continuation> for Next {
|
||||
fn from(value: Continuation) -> Self { Self::Continue(value) }
|
||||
}
|
||||
|
||||
impl From<Continuation> for CmdResult {
|
||||
fn from(value: Continuation) -> Self { Ok(Next::Continue(value)) }
|
||||
}
|
||||
|
||||
pub enum CmdError {
|
||||
Orc(OrcErrv),
|
||||
FatalError,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct FatalError;
|
||||
impl From<FatalError> for CmdError {
|
||||
fn from(FatalError: FatalError) -> Self { Self::FatalError }
|
||||
}
|
||||
impl From<OrcErrv> for CmdError {
|
||||
fn from(value: OrcErrv) -> Self { Self::Orc(value) }
|
||||
}
|
||||
|
||||
pub(crate) async fn encode_command_result(
|
||||
result: Result<Next, CmdError>,
|
||||
) -> api::OrcResult<api::NextStep> {
|
||||
match result {
|
||||
Ok(Next::ExitSuccess) => Ok(api::NextStep::Exit { success: true }),
|
||||
Err(CmdError::FatalError) => Ok(api::NextStep::Exit { success: false }),
|
||||
Err(CmdError::Orc(errv)) => Err(errv.to_api()),
|
||||
Ok(Next::Continue(Continuation { immediate, delayed })) => Ok(api::NextStep::Continue {
|
||||
immediate: (stream::iter(immediate).flatten())
|
||||
.then(async |x| x.serialize().await)
|
||||
.collect()
|
||||
.await,
|
||||
delayed: stream::iter(delayed.into_iter().map(|(delay, expr)| {
|
||||
expr.then(move |expr| async move { (delay, expr.serialize().await) })
|
||||
}))
|
||||
.flatten()
|
||||
.collect()
|
||||
.await,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub type CmdResult = Result<Next, CmdError>;
|
||||
|
||||
/// A vtable-like type that collects operations defined by an [Atomic] without
|
||||
/// associating with an instance of that type. This must be registered in
|
||||
/// [crate::SystemCard]
|
||||
#[allow(private_interfaces)]
|
||||
pub trait AtomOps: 'static {
|
||||
fn tid(&self) -> TypeId;
|
||||
fn name(&self) -> &'static str;
|
||||
fn decode<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>>;
|
||||
@@ -298,25 +420,29 @@ pub trait AtomDynfo: 'static {
|
||||
key: Sym,
|
||||
req: Box<dyn ReqReader<'a> + 'a>,
|
||||
) -> LocalBoxFuture<'a, bool>;
|
||||
fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>>;
|
||||
fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, CmdResult>;
|
||||
fn serialize<'a, 'b: 'a>(
|
||||
&'a self,
|
||||
ctx: AtomCtx<'a>,
|
||||
write: Pin<&'b mut dyn AsyncWrite>,
|
||||
) -> LocalBoxFuture<'a, Option<Vec<Expr>>>;
|
||||
fn deserialize<'a>(&'a self, data: &'a [u8], refs: &'a [Expr]) -> LocalBoxFuture<'a, api::Atom>;
|
||||
fn deserialize<'a>(
|
||||
&'a self,
|
||||
data: &'a [u8],
|
||||
refs: &'a [Expr],
|
||||
) -> LocalBoxFuture<'a, api::LocalAtom>;
|
||||
fn drop<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, ()>;
|
||||
}
|
||||
|
||||
trait_set! {
|
||||
pub trait AtomFactoryFn = FnOnce() -> LocalBoxFuture<'static, api::Atom> + DynClone;
|
||||
pub trait AtomFactoryFn = FnOnce() -> LocalBoxFuture<'static, api::LocalAtom> + DynClone;
|
||||
}
|
||||
pub struct AtomFactory(Box<dyn AtomFactoryFn>);
|
||||
pub(crate) struct AtomFactory(Box<dyn AtomFactoryFn>);
|
||||
impl AtomFactory {
|
||||
pub fn new(f: impl AsyncFnOnce() -> api::Atom + Clone + 'static) -> Self {
|
||||
pub fn new(f: impl AsyncFnOnce() -> api::LocalAtom + Clone + 'static) -> Self {
|
||||
Self(Box::new(|| f().boxed_local()))
|
||||
}
|
||||
pub async fn build(self) -> api::Atom { (self.0)().await }
|
||||
pub async fn build(self) -> api::LocalAtom { (self.0)().await }
|
||||
}
|
||||
impl Clone for AtomFactory {
|
||||
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) }
|
||||
@@ -333,6 +459,7 @@ impl Format for AtomFactory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Error produced when an atom can not be applied to a value as a function
|
||||
pub async fn err_not_callable(unit: &FmtUnit) -> OrcErrv {
|
||||
mk_errv_floating(
|
||||
is("This atom is not callable").await,
|
||||
@@ -340,6 +467,7 @@ pub async fn err_not_callable(unit: &FmtUnit) -> OrcErrv {
|
||||
)
|
||||
}
|
||||
|
||||
/// Error produced when an atom can not be the final value of the program
|
||||
pub async fn err_not_command(unit: &FmtUnit) -> OrcErrv {
|
||||
mk_errv_floating(
|
||||
is("This atom is not a command").await,
|
||||
@@ -347,11 +475,35 @@ pub async fn err_not_command(unit: &FmtUnit) -> OrcErrv {
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) async fn err_exit_success_msg() -> IStr { is("Early successful exit").await }
|
||||
pub(crate) async fn err_exit_failure_msg() -> IStr { is("Early failure exit").await }
|
||||
|
||||
/// Sentinel error returnable from [crate::OwnedAtom::command] or
|
||||
/// [crate::ThinAtom::command] to indicate that the program should exit with a
|
||||
/// success
|
||||
pub async fn err_exit_success() -> OrcErrv {
|
||||
mk_errv_floating(
|
||||
err_exit_success_msg().await,
|
||||
"Sentinel error indicating that the program should exit with a success.",
|
||||
)
|
||||
}
|
||||
|
||||
/// Sentinel error returnable from [crate::OwnedAtom::command] or
|
||||
/// [crate::ThinAtom::command] to indicate that the program should exit with a
|
||||
/// failure
|
||||
pub async fn err_exit_failure() -> OrcErrv {
|
||||
mk_errv_floating(
|
||||
err_exit_failure_msg().await,
|
||||
"Sentinel error indicating that the program should exit with a failure \
|
||||
but without raising an error.",
|
||||
)
|
||||
}
|
||||
|
||||
/// Read the type ID prefix from an atom, return type information and the rest
|
||||
/// of the data
|
||||
pub(crate) fn resolve_atom_type(atom: &api::Atom) -> (Box<dyn AtomDynfo>, AtomTypeId, &[u8]) {
|
||||
pub(crate) fn resolve_atom_type(atom: &api::Atom) -> (Box<dyn AtomOps>, AtomTypeId, &[u8]) {
|
||||
let mut data = &atom.data.0[..];
|
||||
let tid = AtomTypeId::decode_slice(&mut data);
|
||||
let atom_record = atom_by_idx(cted().inst().card(), tid).expect("Unrecognized atom type ID");
|
||||
(atom_record, tid, data)
|
||||
let atid = AtomTypeId::decode_slice(&mut data);
|
||||
let atom_record = cted().inst().card().ops_by_atid(atid).expect("Unrecognized atom type ID");
|
||||
(atom_record, atid, data)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user