forked from Orchid/orchid
113 lines
3.4 KiB
Rust
113 lines
3.4 KiB
Rust
use std::fmt;
|
|
use std::rc::Rc;
|
|
|
|
use async_once_cell::OnceCell;
|
|
use derive_destructure::destructure;
|
|
use orchid_api::ExtAtomPrint;
|
|
use orchid_base::error::OrcErrv;
|
|
use orchid_base::format::{FmtCtx, FmtUnit, Format};
|
|
use orchid_base::location::Pos;
|
|
use orchid_base::reqnot::Requester;
|
|
|
|
use crate::api;
|
|
use crate::atom::ForeignAtom;
|
|
use crate::gen_expr::{GExpr, GExprKind};
|
|
use crate::system::SysCtx;
|
|
|
|
#[derive(destructure)]
|
|
pub struct ExprHandle {
|
|
pub tk: api::ExprTicket,
|
|
pub ctx: SysCtx,
|
|
}
|
|
impl ExprHandle {
|
|
/// # Safety
|
|
///
|
|
/// This function does not signal to take ownership of the expr. It must only
|
|
/// be called on tickets that are already implicitly owned.
|
|
pub unsafe fn from_args(ctx: SysCtx, tk: api::ExprTicket) -> Self { Self { ctx, tk } }
|
|
pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() }
|
|
pub async fn clone(&self) -> Self {
|
|
self.ctx.reqnot().notify(api::Acquire(self.ctx.sys_id(), self.tk)).await;
|
|
Self { ctx: self.ctx.clone(), tk: self.tk }
|
|
}
|
|
/// Drop the handle and get the ticket without a release notification.
|
|
/// Use this with messages that imply ownership transfer. This function is
|
|
/// safe because abusing it is a memory leak.
|
|
pub fn into_tk(self) -> api::ExprTicket { self.destructure().0 }
|
|
}
|
|
impl fmt::Debug for ExprHandle {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "ExprHandle({})", self.tk.0)
|
|
}
|
|
}
|
|
impl Drop for ExprHandle {
|
|
fn drop(&mut self) {
|
|
let notif = api::Release(self.ctx.sys_id(), self.tk);
|
|
let reqnot = self.ctx.reqnot().clone();
|
|
self.ctx.spawner()(Box::pin(async move { reqnot.notify(notif).await }))
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, destructure)]
|
|
pub struct Expr {
|
|
handle: Rc<ExprHandle>,
|
|
data: Rc<OnceCell<ExprData>>,
|
|
}
|
|
impl Expr {
|
|
pub fn from_handle(handle: Rc<ExprHandle>) -> Self { Self { handle, data: Rc::default() } }
|
|
pub fn new(handle: Rc<ExprHandle>, d: ExprData) -> Self {
|
|
Self { handle, data: Rc::new(OnceCell::from(d)) }
|
|
}
|
|
|
|
pub async fn data(&self) -> &ExprData {
|
|
(self.data.get_or_init(async {
|
|
let details = self.handle.ctx.reqnot().request(api::Inspect { target: self.handle.tk }).await;
|
|
let pos = Pos::from_api(&details.location, self.handle.ctx.i()).await;
|
|
let kind = match details.kind {
|
|
api::InspectedKind::Atom(a) =>
|
|
ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())),
|
|
api::InspectedKind::Bottom(b) =>
|
|
ExprKind::Bottom(OrcErrv::from_api(&b, self.handle.ctx.i()).await),
|
|
api::InspectedKind::Opaque => ExprKind::Opaque,
|
|
};
|
|
ExprData { pos, kind }
|
|
}))
|
|
.await
|
|
}
|
|
pub async fn atom(self) -> Result<ForeignAtom, Self> {
|
|
match self.data().await {
|
|
ExprData { kind: ExprKind::Atom(atom), .. } => Ok(atom.clone()),
|
|
_ => Err(self),
|
|
}
|
|
}
|
|
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
|
|
pub fn ctx(&self) -> SysCtx { self.handle.ctx.clone() }
|
|
|
|
pub fn slot(&self) -> GExpr {
|
|
GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(self.clone()) }
|
|
}
|
|
}
|
|
impl Format for Expr {
|
|
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
|
match &self.data().await.kind {
|
|
ExprKind::Opaque => "OPAQUE".to_string().into(),
|
|
ExprKind::Bottom(b) => format!("Bottom({b})").into(),
|
|
ExprKind::Atom(a) =>
|
|
FmtUnit::from_api(&self.handle.ctx.reqnot().request(ExtAtomPrint(a.atom.clone())).await),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ExprData {
|
|
pub pos: Pos,
|
|
pub kind: ExprKind,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum ExprKind {
|
|
Atom(ForeignAtom),
|
|
Bottom(OrcErrv),
|
|
Opaque,
|
|
}
|