use std::fmt; use std::rc::{Rc, Weak}; 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; use crate::extension::Extension; use crate::system::System; #[derive(destructure)] pub struct AtomData { owner: System, drop: Option, data: Vec, } impl AtomData { #[must_use] fn api(self) -> api::Atom { let (owner, drop, data) = self.destructure(); api::Atom { data, drop, owner: owner.id() } } #[must_use] fn api_ref(&self) -> api::Atom { api::Atom { data: 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); impl AtomHand { #[must_use] pub(crate) async fn new(api::Atom { data, drop, owner }: api::Atom, ctx: &Ctx) -> Self { let create = || async { let owner = ctx.system_inst(owner).await.expect("Dropped system created atom"); AtomHand(Rc::new(AtomData { data, owner, drop })) }; if let Some(id) = drop { let mut owned_g = ctx.owned_atoms.write().await; if let Some(data) = owned_g.get(&id) && let Some(atom) = data.upgrade() { return atom; } let new = create().await; owned_g.insert(id, new.downgrade()); new } else { create().await } } #[must_use] pub async fn call(self, arg: Expr) -> api::Expression { let owner_sys = self.0.owner.clone(); let reqnot = owner_sys.reqnot(); owner_sys.ext().exprs().give_expr(arg.clone()); 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, } } #[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) -> Option> { 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 { FmtUnit::from_api(&self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())).await) } } impl AtomRepr for AtomHand { type Ctx = Ctx; async fn from_api(atom: &orchid_api::Atom, _: Pos, ctx: &mut Self::Ctx) -> Self { Self::new(atom.clone(), ctx).await } async fn to_api(&self) -> orchid_api::Atom { self.api_ref() } } pub struct WeakAtomHand(Weak); impl WeakAtomHand { #[must_use] pub fn upgrade(&self) -> Option { self.0.upgrade().map(AtomHand) } }