very elegant extension API and parts of it used in std as POC

This commit is contained in:
2024-07-01 20:11:22 +02:00
parent 93867e40c6
commit fc8441f080
63 changed files with 2040 additions and 925 deletions

View File

@@ -1,44 +1,43 @@
use std::any::{type_name, Any};
use std::borrow::Cow;
use std::fmt;
use std::io::{Read, Write};
use std::num::NonZeroU64;
use std::ops::Deref;
use std::sync::Arc;
use std::{fmt, mem};
use derive_destructure::destructure;
use orchid_api::atom::{Atom, Fwd};
use orchid_api::expr::{ExprTicket, Release};
use dyn_clone::{clone_box, DynClone};
use orchid_api::atom::{Atom, Fwd, LocalAtom};
use orchid_api::expr::ExprTicket;
use orchid_api::system::SysId;
use orchid_api_traits::{Coding, Decode, Encode, Request};
use orchid_base::id_store::{IdRecord, IdStore};
use orchid_base::reqnot::Requester as _;
use orchid_base::location::Pos;
use orchid_base::reqnot::Requester;
use trait_set::trait_set;
use typeid::ConstTypeId;
use crate::expr::GenClause;
use crate::other_system::SystemHandle;
use crate::system::{DynSystemCard, SystemCard};
use crate::expr::{bot, ExprHandle, GenClause};
use crate::system::{atom_info_for, DynSystem, DynSystemCard, SysCtx};
pub trait AtomCard: 'static + Sized {
type Owner: SystemCard;
// type Owner: SystemCard;
type Data: Clone + Coding + Sized;
type Req: Coding;
}
pub fn get_info<A: AtomCard>(sys: &(impl DynSystemCard + ?Sized)) -> (usize, &AtomInfo) {
sys.atom_info_for(ConstTypeId::of::<A>()).unwrap_or_else(|| {
pub fn get_info<A: AtomCard>(sys: &(impl DynSystemCard + ?Sized)) -> (u64, &AtomInfo) {
atom_info_for(sys, ConstTypeId::of::<A>()).unwrap_or_else(|| {
panic!("Atom {} not associated with system {}", type_name::<A>(), sys.name())
})
}
pub fn encode_atom_nodrop<A: AtomCard>(
sys_id: SysId,
sys: &(impl DynSystemCard + ?Sized),
data: &A::Data,
) -> Atom {
let (info_pos, _) = get_info::<A>(sys);
let mut buf = (info_pos as u64).enc_vec();
) -> LocalAtom {
let mut buf = get_info::<A>(sys).0.enc_vec();
data.encode(&mut buf);
Atom { owner: sys_id, drop: false, data: buf }
LocalAtom { drop: false, data: buf }
}
pub fn encode_atom_drop<A: AtomCard>(
@@ -47,8 +46,7 @@ pub fn encode_atom_drop<A: AtomCard>(
atom_id: u64,
data: &A::Data,
) -> Atom {
let (info_pos, _) = get_info::<A>(sys);
let mut buf = (info_pos as u64).enc_vec();
let mut buf = get_info::<A>(sys).0.enc_vec();
atom_id.encode(&mut buf);
data.encode(&mut buf);
Atom { owner: sys_id, drop: true, data: buf }
@@ -56,119 +54,175 @@ pub fn encode_atom_drop<A: AtomCard>(
pub fn decode_atom<A: AtomCard>(
sys: &(impl DynSystemCard + ?Sized),
Atom { data, drop, owner: _ }: &Atom,
Atom { data, drop: _, owner: _ }: &Atom,
) -> Option<A::Data> {
let (info_pos, info) = get_info::<A>(sys);
let mut data = &data[..];
if u64::decode(&mut data) != info_pos as u64 {
if u64::decode(&mut data) != info_pos {
return None;
}
let val = (info.decode)(data);
Some(*val.downcast().expect("The type-id checked out, the decode should've worked"))
}
#[derive(destructure)]
pub struct ForeignAtom<A: AtomCard> {
pub(crate) sys: SystemHandle<A::Owner>,
pub(crate) ticket: ExprTicket,
pub(crate) api: Atom,
pub(crate) value: A::Data,
#[derive(Clone)]
pub struct ForeignAtom {
pub expr: ExprHandle,
pub atom: Atom,
pub position: Pos,
}
impl<A: AtomCard> ForeignAtom<A> {
/// Unpack the object, returning the held atom and expr ticket. This is in
/// contrast to dropping the atom which releases the expr ticket.
pub fn unpack(self) -> (A::Data, ExprTicket, Atom) {
let (_, ticket, api, value) = self.destructure();
(value, ticket, api)
}
pub fn ticket(&self) -> ExprTicket { self.ticket }
impl ForeignAtom {}
#[derive(Clone)]
pub struct TypAtom<A: AtomCard> {
pub data: ForeignAtom,
pub value: A::Data,
}
impl<A: AtomCard> TypAtom<A> {
pub fn request<R: Coding + Into<A::Req> + Request>(&self, req: R) -> R::Response {
R::Response::decode(&mut &self.sys.reqnot.request(Fwd(self.api.clone(), req.enc_vec()))[..])
R::Response::decode(
&mut &self.data.expr.ctx.reqnot.request(Fwd(self.data.atom.clone(), req.enc_vec()))[..],
)
}
}
impl<A: AtomCard> Deref for ForeignAtom<A> {
impl<A: AtomCard> Deref for TypAtom<A> {
type Target = A::Data;
fn deref(&self) -> &Self::Target { &self.value }
}
impl<A: AtomCard> Drop for ForeignAtom<A> {
fn drop(&mut self) { self.sys.reqnot.notify(Release(self.sys.id(), self.ticket)) }
}
pub struct AtomInfo {
pub tid: ConstTypeId,
pub decode: fn(&[u8]) -> Box<dyn Any>,
pub call: fn(&[u8], ExprTicket) -> GenClause,
pub call_ref: fn(&[u8], ExprTicket) -> GenClause,
pub same: fn(&[u8], &[u8]) -> bool,
pub handle_req: fn(&[u8], &mut dyn Read, &mut dyn Write),
pub drop: fn(&[u8]),
pub call: fn(&[u8], SysCtx, ExprTicket) -> GenClause,
pub call_ref: fn(&[u8], SysCtx, ExprTicket) -> GenClause,
pub same: fn(&[u8], SysCtx, &[u8]) -> bool,
pub handle_req: fn(&[u8], SysCtx, &mut dyn Read, &mut dyn Write),
pub drop: fn(&[u8], SysCtx),
}
pub trait ThinAtom: AtomCard<Data = Self> + Coding + fmt::Debug {
fn call(&self, arg: ExprTicket) -> GenClause;
fn same(&self, other: &Self) -> bool;
fn handle_req(&self, req: Self::Req, rep: &mut (impl Write + ?Sized));
#[allow(unused_variables)]
fn call(&self, arg: ExprHandle) -> GenClause { bot("This atom is not callable") }
#[allow(unused_variables)]
fn same(&self, ctx: SysCtx, other: &Self) -> bool {
eprintln!(
"Override ThinAtom::same for {} if it can be generated during parsing",
type_name::<Self>()
);
false
}
fn handle_req(&self, ctx: SysCtx, req: Self::Req, rep: &mut (impl Write + ?Sized));
fn factory(self) -> AtomFactory {
AtomFactory::new(move |sys| encode_atom_nodrop::<Self>(sys.dyn_card(), &self))
}
}
pub const fn thin_atom_info<T: ThinAtom>() -> AtomInfo {
AtomInfo {
tid: ConstTypeId::of::<T>(),
decode: |mut b| Box::new(T::decode(&mut b)),
call: |mut b, extk| T::decode(&mut b).call(extk),
call_ref: |mut b, extk| T::decode(&mut b).call(extk),
handle_req: |mut b, req, rep| T::decode(&mut b).handle_req(Decode::decode(req), rep),
same: |mut b1, mut b2| T::decode(&mut b1).same(&T::decode(&mut b2)),
drop: |mut b1| eprintln!("Received drop signal for non-drop atom {:?}", T::decode(&mut b1)),
call: |mut b, ctx, extk| T::decode(&mut b).call(ExprHandle::from_args(ctx, extk)),
call_ref: |mut b, ctx, extk| T::decode(&mut b).call(ExprHandle::from_args(ctx, extk)),
handle_req: |mut b, ctx, req, rep| T::decode(&mut b).handle_req(ctx, Decode::decode(req), rep),
same: |mut b1, ctx, mut b2| T::decode(&mut b1).same(ctx, &T::decode(&mut b2)),
drop: |mut b1, _| eprintln!("Received drop signal for non-drop atom {:?}", T::decode(&mut b1)),
}
}
/// Atoms that have a [Drop]
pub trait OwnedAtom: AtomCard + Deref<Target = Self::Data> + Send + Sync + Any + 'static {
fn call_ref(&self, arg: ExprTicket) -> GenClause;
fn call(self, arg: ExprTicket) -> GenClause;
fn same(&self, other: &Self) -> bool;
fn handle_req(&self, req: Self::Req, rep: &mut (impl Write + ?Sized));
pub trait OwnedAtom: AtomCard + Send + Sync + Any + Clone + 'static {
fn val(&self) -> Cow<'_, Self::Data>;
#[allow(unused_variables)]
fn call_ref(&self, arg: ExprHandle) -> GenClause { bot("This atom is not callable") }
fn call(self, arg: ExprHandle) -> GenClause {
let ctx = arg.get_ctx();
let gcl = self.call_ref(arg);
self.free(ctx);
gcl
}
#[allow(unused_variables)]
fn same(&self, ctx: SysCtx, other: &Self) -> bool {
eprintln!(
"Override OwnedAtom::same for {} if it can be generated during parsing",
type_name::<Self>()
);
false
}
fn handle_req(&self, ctx: SysCtx, req: Self::Req, rep: &mut (impl Write + ?Sized));
#[allow(unused_variables)]
fn free(self, ctx: SysCtx) {}
#[allow(unused_variables)]
fn factory(self) -> AtomFactory {
AtomFactory::new(move |sys| {
let rec = OBJ_STORE.add(Box::new(self));
let mut data = atom_info_for(sys.dyn_card(), rec.atom_tid()).expect("obj exists").0.enc_vec();
rec.id().encode(&mut data);
rec.encode(&mut data);
LocalAtom { drop: true, data }
})
}
}
pub trait DynOwnedAtom: Send + Sync + 'static {
fn atom_tid(&self) -> ConstTypeId;
fn as_any_ref(&self) -> &dyn Any;
fn dyn_call_ref(&self, arg: ExprTicket) -> GenClause;
fn dyn_call(self: Box<Self>, arg: ExprTicket) -> GenClause;
fn dyn_same(&self, other: &dyn DynOwnedAtom) -> bool;
fn dyn_handle_req(&self, req: &mut dyn Read, rep: &mut dyn Write);
fn encode(&self, buffer: &mut dyn Write);
fn dyn_call_ref(&self, ctx: SysCtx, arg: ExprTicket) -> GenClause;
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: ExprTicket) -> GenClause;
fn dyn_same(&self, ctx: SysCtx, other: &dyn DynOwnedAtom) -> bool;
fn dyn_handle_req(&self, ctx: SysCtx, req: &mut dyn Read, rep: &mut dyn Write);
fn dyn_free(self: Box<Self>, ctx: SysCtx);
}
impl<T: OwnedAtom> DynOwnedAtom for T {
fn atom_tid(&self) -> ConstTypeId { ConstTypeId::of::<T>() }
fn as_any_ref(&self) -> &dyn Any { self }
fn dyn_call_ref(&self, arg: ExprTicket) -> GenClause { self.call_ref(arg) }
fn dyn_call(self: Box<Self>, arg: ExprTicket) -> GenClause { self.call(arg) }
fn dyn_same(&self, other: &dyn DynOwnedAtom) -> bool {
fn encode(&self, buffer: &mut dyn Write) { self.val().as_ref().encode(buffer) }
fn dyn_call_ref(&self, ctx: SysCtx, arg: ExprTicket) -> GenClause {
self.call_ref(ExprHandle::from_args(ctx, arg))
}
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: ExprTicket) -> GenClause {
self.call(ExprHandle::from_args(ctx, arg))
}
fn dyn_same(&self, ctx: SysCtx, other: &dyn DynOwnedAtom) -> bool {
if ConstTypeId::of::<Self>() != other.as_any_ref().type_id() {
return false;
}
let other_self = other.as_any_ref().downcast_ref().expect("The type_ids are the same");
self.same(other_self)
self.same(ctx, other_self)
}
fn dyn_handle_req(&self, req: &mut dyn Read, rep: &mut dyn Write) {
self.handle_req(<Self as AtomCard>::Req::decode(req), rep)
fn dyn_handle_req(&self, ctx: SysCtx, req: &mut dyn Read, rep: &mut dyn Write) {
self.handle_req(ctx, <Self as AtomCard>::Req::decode(req), rep)
}
fn dyn_free(self: Box<Self>, ctx: SysCtx) { self.free(ctx) }
}
pub(crate) static OBJ_STORE: IdStore<Box<dyn DynOwnedAtom>> = IdStore::new();
const fn owned_atom_info<T: OwnedAtom>() -> AtomInfo {
pub const fn owned_atom_info<T: OwnedAtom>() -> AtomInfo {
fn with_atom<U>(mut b: &[u8], f: impl FnOnce(IdRecord<'_, Box<dyn DynOwnedAtom>>) -> U) -> U {
f(OBJ_STORE.get(NonZeroU64::decode(&mut b)).expect("Received invalid atom ID"))
}
AtomInfo {
tid: ConstTypeId::of::<T>(),
decode: |mut b| Box::new(T::Data::decode(&mut b)),
call: |b, arg| with_atom(b, |a| a.remove().dyn_call(arg)),
call_ref: |b, arg| with_atom(b, |a| a.dyn_call_ref(arg)),
same: |b1, b2| with_atom(b1, |a1| with_atom(b2, |a2| a1.dyn_same(&**a2))),
handle_req: |b, req, rep| with_atom(b, |a| a.dyn_handle_req(req, rep)),
drop: |b| mem::drop(with_atom(b, |a| a.remove())),
call: |b, ctx, arg| with_atom(b, |a| a.remove().dyn_call(ctx, arg)),
call_ref: |b, ctx, arg| with_atom(b, |a| a.dyn_call_ref(ctx, arg)),
same: |b1, ctx, b2| with_atom(b1, |a1| with_atom(b2, |a2| a1.dyn_same(ctx, &**a2))),
handle_req: |b, ctx, req, rep| with_atom(b, |a| a.dyn_handle_req(ctx, req, rep)),
drop: |b, ctx| with_atom(b, |a| a.remove().dyn_free(ctx)),
}
}
trait_set! {
pub trait AtomFactoryFn = FnOnce(&dyn DynSystem) -> LocalAtom + DynClone;
}
pub struct AtomFactory(Box<dyn AtomFactoryFn>);
impl AtomFactory {
pub fn new(f: impl FnOnce(&dyn DynSystem) -> LocalAtom + Clone + 'static) -> Self {
Self(Box::new(f))
}
pub fn build(self, sys: &dyn DynSystem) -> LocalAtom { (self.0)(sys) }
}
impl Clone for AtomFactory {
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) }
}