Files
orchid/orchid-host/src/atom.rs

136 lines
4.0 KiB
Rust

use std::fmt;
use std::rc::{Rc, Weak};
use async_once_cell::OnceCell;
use derive_destructure::destructure;
#[cfg(feature = "orchid-extension")]
use orchid_api_traits::{Request, UnderRoot};
use orchid_base::{AtomRepr, ClientExt, FmtCtx, FmtUnit, Format, Pos, take_first_fmt};
#[cfg(feature = "orchid-extension")]
use orchid_extension::AtomMethod;
use crate::api;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprFromApiCtx, 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]
#[cfg(feature = "orchid-extension")]
pub async fn ipc<M: Request + UnderRoot<Root: AtomMethod>>(
&self,
method: M,
) -> Option<M::Response> {
use orchid_api_traits::{Decode, Encode};
use orchid_base::Sym;
let name = Sym::parse(<M as UnderRoot>::Root::NAME).await.unwrap();
let mut buf = Vec::new();
method.into_root().encode_vec(&mut buf);
let reply_buf = self.req(name.to_api(), buf).await?;
Some(M::Response::decode_slice(&mut &reply_buf[..]))
}
#[must_use]
pub async fn call(self, arg: Expr) -> Expr {
let owner = self.0.owner.clone();
let ctx = owner.ctx();
let client = owner.client();
ctx.exprs.give_expr(arg.clone());
let ret = match Rc::try_unwrap(self.0) {
Ok(data) => client.request(api::FinalCall(data.api(), arg.id())).await.unwrap(),
Err(hand) => client.request(api::CallRef(hand.api_ref(), arg.id())).await.unwrap(),
};
let val = Expr::from_api(ret, PathSetBuilder::new(), ExprFromApiCtx {
sys: owner.id(),
ctx: ctx.clone(),
})
.await;
ctx.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.client().request(api::Fwded(self.0.api_ref(), key, req)).await.unwrap()
}
#[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).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.client().request(api::AtomPrint(self.0.api_ref())).await.unwrap(),
)
}))
.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) }
}