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