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}