use std::cell::RefCell; use std::fmt; use std::hash::Hash; use std::rc::Rc; use async_once_cell::OnceCell; use derive_destructure::destructure; use hashbrown::HashSet; 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; pub struct BorrowedExprStore(RefCell>>>); impl BorrowedExprStore { pub(crate) fn new() -> Self { Self(RefCell::new(Some(HashSet::new()))) } pub async fn dispose(self) { let elements = self.0.borrow_mut().take().unwrap(); for handle in elements { handle.drop_one().await } } } impl Drop for BorrowedExprStore { fn drop(&mut self) { if self.0.borrow().is_some() { panic!("This should always be explicitly disposed") } } } #[derive(destructure)] pub struct ExprHandle { pub tk: api::ExprTicket, pub ctx: SysCtx, } impl ExprHandle { /// This function does not signal to take ownership of the expr. pub fn borrowed(ctx: SysCtx, tk: api::ExprTicket, store: &BorrowedExprStore) -> Rc { let this = Rc::new(Self { ctx, tk }); store.0.borrow_mut().as_mut().unwrap().insert(this.clone()); this } pub fn deserialize(ctx: SysCtx, tk: api::ExprTicket) -> Rc { Rc::new(Self { ctx, tk }) } pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() } /// Drop one instance of the handle silently; if it's the last one, do /// nothing, otherwise send an Acquire pub async fn drop_one(self: Rc) { match Rc::try_unwrap(self) { Err(rc) => rc.ctx.reqnot().notify(api::Acquire(rc.ctx.sys_id(), rc.tk)).await, Ok(hand) => { // avoid calling destructor hand.destructure(); }, } } /// 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 serialize(self) -> api::ExprTicket { self.destructure().0 } } impl Eq for ExprHandle {} impl PartialEq for ExprHandle { fn eq(&self, other: &Self) -> bool { self.ctx.sys_id() == other.ctx.sys_id() && self.tk == other.tk } } impl Hash for ExprHandle { fn hash(&self, state: &mut H) { self.ctx.sys_id().hash(state); self.tk.hash(state); } } 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, data: Rc>, } impl Expr { pub fn from_handle(handle: Rc) -> Self { Self { handle, data: Rc::default() } } pub fn new(handle: Rc, 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 { match self.data().await { ExprData { kind: ExprKind::Atom(atom), .. } => Ok(atom.clone()), _ => Err(self), } } pub fn handle(&self) -> Rc { 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(api::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, }