1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
use std::any::TypeId;
use flume::Sender;
use crate::{
bus::error::{CallEvent, CallTrace}, core::dyn_var::DynVar, util::dyn_debug::DynDebug, EventDef,
};
#[derive(Debug)]
pub enum BusInterfaceEvent {
Fire {
def: TypeId,
args: DynVar,
responder: Sender<Result<DynVar, CallTrace>>,
trace_data: CallEvent,
},
FwdBusError {
error: CallTrace,
blocker: Sender<()>,
},
}
/// Provides a limited [`DABus`] like api for handler implementations.
///
/// This is passed to handlers, giving them a way of running actions on the bus that they are being run from.
///
/// [`DABus`]: crate::bus::DABus
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct BusInterface {
pub(crate) channel: Sender<BusInterfaceEvent>,
}
impl BusInterface {
pub(crate) const fn new(sender: Sender<BusInterfaceEvent>) -> Self {
Self { channel: sender }
}
/// Fires an event on the bus, running appropreate handlers and returning the result.
/// This function is similar to [`DABus::fire`] in the sense that from the outside, it behaves the same
/// however internally it does not. see the `Notes` section for more details
///
/// # Returns
///
/// on success, this returns the return value sent by the handler, as well as a call trace (this will change)
///
/// on failure, this returns only the call trace, which can be used to find what went wrong
///
/// # Panics
///
/// if a handler that is called panics (or the runtime is broken)
///
/// # Errors
///
/// if there is some (expected) error with the runtime. currently this only includes not finding an appropreate handler
///
/// # Notes
/// like all functions on this struct, this does not execute an event iself but rather forwards it to the current runtime.
/// this means that if useing this after the scope of the handler it was given to has ended should be considered **Undefined Behavior** (eventually there will be some safeguard to fix this)
///
/// [`DABus::fire`]: crate::bus::DABus::fire
pub async fn fire<
Tag: unique_type::Unique,
At: DynDebug + Sync + Send + 'static,
Rt: DynDebug + Sync + Send + 'static,
>(
&mut self,
def: &'static EventDef<Tag, At, Rt>,
args: At,
) -> Result<Rt, CallTrace> {
let trace_data = CallEvent::from_event_def(def, &args);
let _ = def;
let def = TypeId::of::<Tag>();
let args = DynVar::new(args);
let (responder, response) = flume::bounded::<Result<DynVar, CallTrace>>(1);
self.channel
.send(BusInterfaceEvent::Fire {
def,
args,
responder,
trace_data,
})
.unwrap();
Ok(response
.into_recv_async()
.await
.unwrap()?
.try_to::<Rt>()
.unwrap())
}
/// takes a error (from a nested call, presumablely) and forwards it to the caller of the current event (via the runtime and a deal with the devil)
///
/// this is a easy way to handle errors, as it will forward the error, and can produce nice backtraces
///
/// # Panics
///
/// it shouldent, unless something is horribly wrong with the library
///
/// # Footguns
///
/// - this function (from the perspective of the handler) will never return, but from the persepective of the program it will, so keep that in mind.
///
/// - see the `Notes` section in [`BusInterface::fire`]
pub async fn fwd_bus_err(
&self,
error: CallTrace,
) -> ! {
let (blocker, blocks) = flume::bounded::<()>(1);
self.channel
.send(BusInterfaceEvent::FwdBusError { error, blocker })
.unwrap();
blocks.recv_async().await.unwrap();
unreachable!()
}
}
/// Utility for handling bus errors inside of bus handlers
#[async_trait]
pub trait BusErrorUtil<T> {
/// unwraps an `Result`, or forwards the error to [`BusInterface::fwd_bus_err`].
///
/// see [`BusInterface::fwd_bus_err`] for more information
async fn unwrap_or_fwd(self, bus: &BusInterface) -> T;
}
#[async_trait]
impl<T: Send> BusErrorUtil<T> for Result<T, CallTrace> {
/// unwraps an `Result`, or forwards the error to [`BusInterface::fwd_bus_err`].
///
/// see [`BusInterface::fwd_bus_err`] for more information
async fn unwrap_or_fwd(self, bus: &BusInterface) -> T {
match self {
Ok(x) => x,
Err(err) => bus.fwd_bus_err(err).await,
}
}
}