use std::any::{Any, TypeId, type_name}; use std::future::Future; use std::pin::Pin; use async_once_cell::OnceCell; use futures::future::LocalBoxFuture; use futures::{AsyncWrite, FutureExt}; use orchid_api_traits::{Coding, enc_vec}; use orchid_base::{FmtUnit, Sym, log}; use crate::atom::{ AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet, MethodSetBuilder, err_not_callable, err_not_command, }; use crate::expr::Expr; use crate::gen_expr::{GExpr, bot}; use crate::system::{DynSystemCardExt, cted}; use crate::{CmdResult, api}; /// Value of [Atomic::Variant] for a type that implements [ThinAtom] pub struct ThinVariant; impl AtomicVariant for ThinVariant {} impl> AtomicFeaturesImpl for A { fn _factory(self) -> AtomFactory { AtomFactory::new(async move || { let (id, _) = cted().inst().card().ops::(); let mut buf = enc_vec(&id); self.encode_vec(&mut buf); api::LocalAtom { drop: None, data: api::AtomData(buf) } }) } fn _info() -> Self::_Info { ThinAtomOps { msbuild: Self::reg_methods(), ms: OnceCell::new() } } type _Info = ThinAtomOps; } pub(crate) struct ThinAtomOps { msbuild: MethodSetBuilder, ms: OnceCell>, } impl AtomOps for ThinAtomOps { fn print<'a>(&self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit> { Box::pin(async move { T::decode_slice(&mut &buf[..]).print().await }) } fn tid(&self) -> TypeId { TypeId::of::() } fn name(&self) -> &'static str { type_name::() } fn decode<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box> { Box::pin(async { Box::new(T::decode_slice(&mut &buf[..])) as Box }) } fn call<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> { Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await }) } fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> { Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await }) } fn handle_req<'a>( &'a self, AtomCtx(buf, ..): AtomCtx<'a>, key: Sym, req: Box + 'a>, ) -> LocalBoxFuture<'a, bool> { Box::pin(async move { let ms = self.ms.get_or_init(self.msbuild.pack()).await; ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req).await }) } fn command<'a>(&'a self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, CmdResult> { async move { T::decode_slice(&mut &buf[..]).command().await }.boxed_local() } fn serialize<'a, 'b: 'a>( &'a self, ctx: AtomCtx<'a>, write: Pin<&'b mut dyn AsyncWrite>, ) -> LocalBoxFuture<'a, Option>> { Box::pin(async { T::decode_slice(&mut &ctx.0[..]).encode(write).await.unwrap(); Some(Vec::new()) }) } fn deserialize<'a>( &'a self, data: &'a [u8], refs: &'a [Expr], ) -> LocalBoxFuture<'a, api::LocalAtom> { assert!(refs.is_empty(), "Refs found when deserializing thin atom"); Box::pin(async { T::decode_slice(&mut &data[..])._factory().build().await }) } fn drop<'a>(&'a self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> { Box::pin(async move { let string_self = T::decode_slice(&mut &buf[..]).print().await; writeln!(log("warn"), "Received drop signal for non-drop atom {string_self:?}").await; }) } } /// A simple value that is serializable and does not reference any other values pub trait ThinAtom: Atomic + Atomic + Coding + 'static { #[allow(unused_variables)] fn call(&self, arg: Expr) -> impl Future { async move { bot(err_not_callable(&self.print().await).await) } } #[allow(unused_variables)] fn command(&self) -> impl Future { async move { Err(err_not_command(&self.print().await).await.into()) } } #[allow(unused_variables)] fn print(&self) -> impl Future { async { format!("ThinAtom({})", type_name::()).into() } } }