#[cfg(feature = "general_renderer")] use serde::Serialize; use crate::Groupped; use crate::error::ChainProcessError; #[doc(hidden)] pub mod group; /// Any type output /// /// Accepts any type that implements `Send + Groupped` /// After being passed into AnyOutput, it will be converted to `Box` /// /// Note: /// - If an enum value that does not belong to this type is incorrectly specified, it will be **unsafely** unwrapped by the scheduler /// - Under the `general_renderer` feature, the passed value must ensure it implements `serde::Serialize` /// - It is recommended to use the `pack!` macro from [mingling_macros](https://crates.io/crates/mingling_macros) to create types that can be converted to `AnyOutput`, which guarantees runtime safety #[derive(Debug)] pub struct AnyOutput { pub(crate) inner: Box, pub type_id: std::any::TypeId, pub member_id: G, } impl AnyOutput { /// Create an AnyOutput from a `Send + Groupped + Serialize` type #[cfg(feature = "general_renderer")] pub fn new(value: T) -> Self where T: Send + Groupped + Serialize + 'static, { Self { inner: Box::new(value), type_id: std::any::TypeId::of::(), member_id: T::member_id(), } } /// Create an AnyOutput from a `Send + Groupped` type #[cfg(not(feature = "general_renderer"))] pub fn new(value: T) -> Self where T: Send + Groupped + 'static, { Self { inner: Box::new(value), type_id: std::any::TypeId::of::(), member_id: T::member_id(), } } /// Downcast the AnyOutput to a concrete type T pub fn downcast(self) -> Result { if self.type_id == std::any::TypeId::of::() { Ok(*self.inner.downcast::().unwrap()) } else { Err(self) } } /// Check if the inner value is of type T pub fn is(&self) -> bool { self.type_id == std::any::TypeId::of::() } /// Route the output to the next Chain pub fn route_chain(self) -> ChainProcess { ChainProcess::Ok((self, Next::Chain)) } /// Route the output to the Renderer, ending execution pub fn route_renderer(self) -> ChainProcess { ChainProcess::Ok((self, Next::Renderer)) } #[cfg(feature = "general_renderer")] /// Restore AnyOutput back to the original Serialize type pub fn restore(self) -> Option { if self.type_id == std::any::TypeId::of::() { match self.inner.downcast::() { Ok(boxed) => Some(*boxed), Err(_) => None, } } else { None } } } impl std::ops::Deref for AnyOutput { type Target = dyn std::any::Any + Send + 'static; fn deref(&self) -> &Self::Target { &*self.inner } } impl std::ops::DerefMut for AnyOutput { fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.inner } } /// Chain exec result type /// /// Stores `Ok` and `Err` types of execution results, used to notify the scheduler what to execute next /// - Returns `Ok((`[`AnyOutput`](./struct.AnyOutput.html)`, `[`Next::Chain`](./enum.Next.html)`))` to continue execution with this type next /// - Returns `Ok((`[`AnyOutput`](./struct.AnyOutput.html)`, `[`Next::Renderer`](./enum.Next.html)`))` to render this type next and output to the terminal /// - Returns `Err(`[`ChainProcessError`](./error/enum.ChainProcessError.html)`]` to terminate the program directly pub enum ChainProcess { Ok((AnyOutput, Next)), Err(ChainProcessError), } /// Indicates the next step after processing /// /// - `Chain`: Continue execution to the next chain /// - `Renderer`: Send output to renderer and end execution pub enum Next { Chain, Renderer, } impl From> for ChainProcess { fn from(value: AnyOutput) -> Self { ChainProcess::Ok((value, Next::Chain)) } }