Skip to main content

ferroui_reactiveui_testing/
message_bus_extensions.rs

1//! `MessageBusExtensions` (`ReactiveUI.Testing/MessageBusExtensions.cs`).
2
3use ferroui_reactive::disposables::Disposable;
4use ferroui_reactiveui::{IMessageBus, MessageBus};
5use ferroui_system::IDisposable;
6use std::rc::Rc;
7
8/// Restores the message bus when it is dropped: the `using` block of upstream's `With`, which
9/// restores it also when the block throws (here, panics).
10struct RestoreOnDrop(Rc<dyn IDisposable>);
11
12impl Drop for RestoreOnDrop {
13    fn drop(&mut self) {
14        self.0.dispose();
15    }
16}
17
18/// Runs code with a message bus as [`MessageBus::current`] (`MessageBusExtensions`).
19///
20/// Deviation (DEVIATIONS.md, ReactiveUI): the current message bus is per thread, so upstream's
21/// lock (`Monitor.Enter(messageBusGate)`), which keeps the tests of other threads from replacing
22/// it in between, is not needed and not ported.
23pub trait MessageBusExtensions {
24    /// The message bus as the handle that [`MessageBus::current`] holds.
25    fn as_message_bus(&self) -> Rc<dyn IMessageBus>;
26
27    /// Makes the message bus [`MessageBus::current`] until the returned disposable is disposed,
28    /// which restores the message bus that was current before (`WithMessageBus`).
29    fn with_message_bus(&self) -> Rc<dyn IDisposable> {
30        let original = MessageBus::current();
31        MessageBus::set_current(self.as_message_bus());
32        Disposable::create(move || MessageBus::set_current(original))
33    }
34
35    /// Runs `block` with the message bus as [`MessageBus::current`] and returns its result; the
36    /// message bus current before is restored afterwards, also when `block` panics
37    /// (`With<TRet>(Func<TRet>)`, and `With(Action)` with a block that returns `()`).
38    ///
39    /// Not ported: the `ArgumentException` of a null block - argument validation.
40    fn with<R>(&self, block: impl FnOnce() -> R) -> R {
41        let _restore = RestoreOnDrop(self.with_message_bus());
42        block()
43    }
44}
45
46impl MessageBusExtensions for Rc<dyn IMessageBus> {
47    fn as_message_bus(&self) -> Rc<dyn IMessageBus> {
48        self.clone()
49    }
50}
51
52impl<B: IMessageBus + 'static> MessageBusExtensions for Rc<B> {
53    fn as_message_bus(&self) -> Rc<dyn IMessageBus> {
54        self.clone()
55    }
56}