use std::future::Future; use std::pin::Pin; use dyn_clone::DynClone; use futures::future::FusedFuture; use never::Never; use orchid_base::{Format, OrcErrv, OrcRes, Pos, fmt, is, mk_errv}; use trait_set::trait_set; use crate::gen_expr::{GExpr, bot}; use crate::{AtomicFeatures, Expr, ExprKind, ForeignAtom, TAtom}; /// Values that may be converted from certain specific Orchid expressions pub trait TryFromExpr: Sized { /// Attempt to cast a generic Orchid expression reference to a concrete value. /// Note that this cannot evaluate the expression, and if it is not already /// evaluated, it will simply fail. Use [crate::ExecHandle::exec] inside /// [crate::exec] to wait for an expression to be evaluated fn try_from_expr(expr: Expr) -> impl Future>; } impl TryFromExpr for Expr { async fn try_from_expr(expr: Expr) -> OrcRes { Ok(expr) } } impl TryFromExpr for (T, U) { async fn try_from_expr(expr: Expr) -> OrcRes { Ok((T::try_from_expr(expr.clone()).await?, U::try_from_expr(expr).await?)) } } /// Error raised when a composite expression was assumed to be an /// [crate::Atomic], or if the expression was not evaluated yet async fn err_not_atom(pos: Pos, value: &impl Format) -> OrcErrv { mk_errv(is("Expected an atom").await, format!("{} is not an atom", fmt(value).await), [pos]) } impl TryFromExpr for ForeignAtom { async fn try_from_expr(expr: Expr) -> OrcRes { if let ExprKind::Bottom(err) = &expr.data().await.kind { return Err(err.clone()); } match expr.clone().atom().await { Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), &expr).await), Ok(f) => Ok(f), } } } impl TryFromExpr for TAtom { async fn try_from_expr(expr: Expr) -> OrcRes { let f = ForeignAtom::try_from_expr(expr).await?; match f.clone().downcast::() { Ok(a) => Ok(a), Err(e) => Err(e.mk_err().await), } } } /// Values that are convertible to an Orchid expression. This could mean that /// the value owns an [Expr] or it may involve more complex operations /// /// [ToExpr] is also implemented for [orchid_base::Sym] where it converts to a /// reference to the constant by that name pub trait ToExpr { /// Inline the value in an expression returned from a function or included in /// the const tree returned by [crate::System::env] fn to_gen(self) -> impl Future; /// Convert the value into a freestanding expression fn to_expr(self) -> impl Future where Self: Sized { async { self.to_gen().await.create().await } } } /// A wrapper for a future that implements [ToExpr] #[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct ToExprFuture(pub F); impl> ToExpr for ToExprFuture { async fn to_gen(self) -> GExpr { self.0.await.to_gen().await } async fn to_expr(self) -> Expr where Self: Sized { self.0.await.to_expr().await } } impl FusedFuture for ToExprFuture { fn is_terminated(&self) -> bool { self.0.is_terminated() } } impl Future for ToExprFuture { type Output = F::Output; fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll { unsafe { self.map_unchecked_mut(|this| &mut this.0) }.poll(cx) } } /// Type-erased [ToExpr] pub trait ToExprDyn { fn to_gen_dyn<'a>(self: Box) -> Pin + 'a>> where Self: 'a; fn to_expr_dyn<'a>(self: Box) -> Pin + 'a>> where Self: 'a; } impl ToExprDyn for T { fn to_gen_dyn<'a>(self: Box) -> Pin + 'a>> where Self: 'a { Box::pin(self.to_gen()) } fn to_expr_dyn<'a>(self: Box) -> Pin + 'a>> where Self: 'a { Box::pin(self.to_expr()) } } trait_set! { /// type-erased [ToExpr] and [Clone]. Needed for a value to be /// included in [crate::System::env] pub trait ClonableToExprDyn = ToExprDyn + DynClone; } impl ToExpr for Box { async fn to_gen(self) -> GExpr { self.to_gen_dyn().await } async fn to_expr(self) -> Expr { self.to_expr_dyn().await } } impl ToExpr for Box { async fn to_gen(self) -> GExpr { self.to_gen_dyn().await } async fn to_expr(self) -> Expr { self.to_expr_dyn().await } } impl Clone for Box { fn clone(&self) -> Self { dyn_clone::clone_box(&**self) } } impl ToExpr for GExpr { async fn to_gen(self) -> GExpr { self } async fn to_expr(self) -> Expr { self.create().await } } impl ToExpr for Expr { async fn to_gen(self) -> GExpr { self.slot() } async fn to_expr(self) -> Expr { self } } impl ToExpr for OrcRes { async fn to_gen(self) -> GExpr { match self { Err(e) => bot(e), Ok(t) => t.to_gen().await, } } } impl ToExpr for Never { async fn to_gen(self) -> GExpr { match self {} } }