forked from Orchid/orchid
114 lines
3.5 KiB
Rust
114 lines
3.5 KiB
Rust
use std::fmt;
|
|
use std::rc::{Rc, Weak};
|
|
|
|
use async_lock::OnceCell;
|
|
use derive_destructure::destructure;
|
|
use orchid_base::format::{FmtCtx, FmtUnit, Format, take_first_fmt};
|
|
use orchid_base::location::Pos;
|
|
use orchid_base::reqnot::Requester;
|
|
use orchid_base::tree::AtomRepr;
|
|
|
|
use crate::api;
|
|
use crate::ctx::Ctx;
|
|
use crate::expr::{Expr, ExprParseCtx, PathSetBuilder};
|
|
use crate::extension::Extension;
|
|
use crate::system::System;
|
|
|
|
#[derive(destructure)]
|
|
pub struct AtomData {
|
|
owner: System,
|
|
drop: Option<api::AtomId>,
|
|
data: Vec<u8>,
|
|
pub(crate) display: OnceCell<FmtUnit>,
|
|
}
|
|
impl AtomData {
|
|
#[must_use]
|
|
fn api(self) -> api::Atom {
|
|
let (owner, drop, data, _display) = self.destructure();
|
|
api::Atom { data: api::AtomData(data), drop, owner: owner.id() }
|
|
}
|
|
#[must_use]
|
|
fn api_ref(&self) -> api::Atom {
|
|
api::Atom { data: api::AtomData(self.data.clone()), drop: self.drop, owner: self.owner.id() }
|
|
}
|
|
}
|
|
impl Drop for AtomData {
|
|
fn drop(&mut self) {
|
|
if let Some(id) = self.drop {
|
|
self.owner.drop_atom(id);
|
|
}
|
|
}
|
|
}
|
|
impl fmt::Debug for AtomData {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("AtomData")
|
|
.field("drop", &self.drop)
|
|
.field("data", &self.data)
|
|
.field("owner", &self.owner.id())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct AtomHand(Rc<AtomData>);
|
|
impl AtomHand {
|
|
pub(crate) fn new(data: Vec<u8>, owner: System, drop: Option<api::AtomId>) -> Self {
|
|
Self(Rc::new(AtomData { owner, drop, data, display: OnceCell::new() }))
|
|
}
|
|
#[must_use]
|
|
pub async fn call(self, arg: Expr) -> Expr {
|
|
let owner_sys = self.0.owner.clone();
|
|
let reqnot = owner_sys.reqnot();
|
|
owner_sys.ext().exprs().give_expr(arg.clone());
|
|
let ret = match Rc::try_unwrap(self.0) {
|
|
Ok(data) => reqnot.request(api::FinalCall(data.api(), arg.id())).await,
|
|
Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), arg.id())).await,
|
|
};
|
|
let mut parse_ctx = ExprParseCtx { ctx: owner_sys.ctx(), exprs: owner_sys.ext().exprs() };
|
|
let val = Expr::from_api(&ret, PathSetBuilder::new(), &mut parse_ctx).await;
|
|
owner_sys.ext().exprs().take_expr(arg.id());
|
|
val
|
|
}
|
|
#[must_use]
|
|
pub fn sys(&self) -> &System { &self.0.owner }
|
|
#[must_use]
|
|
pub fn ext(&self) -> &Extension { self.sys().ext() }
|
|
pub async fn req(&self, key: api::TStrv, req: Vec<u8>) -> Option<Vec<u8>> {
|
|
self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), key, req)).await
|
|
}
|
|
#[must_use]
|
|
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }
|
|
#[must_use]
|
|
pub async fn to_string(&self) -> String { take_first_fmt(self, &self.0.owner.ctx().i).await }
|
|
#[must_use]
|
|
pub fn downgrade(&self) -> WeakAtomHand { WeakAtomHand(Rc::downgrade(&self.0)) }
|
|
}
|
|
impl Format for AtomHand {
|
|
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
|
(self.0.display.get_or_init(|| async {
|
|
FmtUnit::from_api(&self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())).await)
|
|
}))
|
|
.await
|
|
.clone()
|
|
}
|
|
}
|
|
impl AtomRepr for AtomHand {
|
|
type Ctx = Ctx;
|
|
async fn from_api(atom: &api::Atom, _: Pos, ctx: &mut Self::Ctx) -> Self {
|
|
let api::Atom { data, drop, owner } = atom.clone();
|
|
let sys = ctx.system_inst(owner).await.expect("Dropped system created atom");
|
|
if let Some(id) = drop {
|
|
sys.new_atom(data.0, id).await
|
|
} else {
|
|
AtomHand::new(data.0, sys, drop)
|
|
}
|
|
}
|
|
async fn to_api(&self) -> orchid_api::Atom { self.api_ref() }
|
|
}
|
|
|
|
pub struct WeakAtomHand(Weak<AtomData>);
|
|
impl WeakAtomHand {
|
|
#[must_use]
|
|
pub fn upgrade(&self) -> Option<AtomHand> { self.0.upgrade().map(AtomHand) }
|
|
}
|