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}