Skip to main content

guinea_core/actor/
mod.rs

1use once_cell::sync::Lazy;
2use std::sync::RwLock;
3
4pub mod addr;
5pub mod cancel;
6pub mod ctx;
7pub mod envelope;
8pub mod flow;
9
10pub mod traits;
11
12pub use addr::*;
13pub use cancel::*;
14pub use ctx::*;
15pub use envelope::*;
16pub use traits::*;
17
18/// What [`Cx::spawn_source`] takes: the `Stream` of `futures` and
19/// `tokio-stream`, named here so a source can be written without either.
20pub use futures_core::Stream;
21
22pub mod event_bus;
23
24pub mod registry;
25pub mod shape;
26
27pub type UiTask = Box<dyn FnOnce() + Send>;
28
29pub trait UiDispatcher: Send + Sync {
30    fn init(&self);
31    fn dispatch(&self, task: UiTask);
32}
33
34static UI_DISPATCHER: Lazy<RwLock<Option<Box<dyn UiDispatcher>>>> = Lazy::new(|| RwLock::new(None));
35
36pub fn set_ui_dispatcher(dispatcher: impl UiDispatcher + 'static) {
37    *UI_DISPATCHER.write().unwrap() = Some(Box::new(dispatcher));
38}
39
40#[derive(Clone)]
41pub struct UiThreadToken(std::marker::PhantomData<*const ()>);
42
43impl UiThreadToken {
44    pub fn dangerously_create_token_unchecked() -> Self {
45        Self(std::marker::PhantomData)
46    }
47}
48
49/// Runs `f` on the UI thread.
50///
51/// Under `test-utils` a seeded executor on this thread takes it first, then
52/// the application's dispatcher, then the test queue: the feature adds a
53/// place for work to go and takes none away, so an application built with it
54/// - `cargo build --all-targets` unifies dev-dependencies' features - still
55/// runs.
56pub fn invoke_on_ui<F>(f: F)
57where
58    F: FnOnce() + Send + 'static,
59{
60    if let Err(f) = try_invoke_on_ui(f) {
61        #[cfg(feature = "test-utils")]
62        crate::actor::event_bus::EventBus::queue_test_task(Box::new(f));
63
64        #[cfg(not(feature = "test-utils"))]
65        {
66            drop(f);
67            panic!(
68                "UiDispatcher not initialized! Call guinea_core::actor::set_ui_dispatcher at startup."
69            );
70        }
71    }
72}
73
74/// Like [`invoke_on_ui`], but hands `f` back when there is no UI thread to
75/// run it on yet.
76pub fn try_invoke_on_ui<F>(f: F) -> Result<(), F>
77where
78    F: FnOnce() + Send + 'static,
79{
80    #[cfg(feature = "test-utils")]
81    let f = match crate::executor::queue_ui(f) {
82        Ok(()) => return Ok(()),
83        Err(f) => f,
84    };
85
86    match UI_DISPATCHER.read().unwrap().as_ref() {
87        Some(dispatcher) => {
88            dispatcher.dispatch(Box::new(f));
89            Ok(())
90        }
91        None => Err(f),
92    }
93}
94
95/// The name the trace and the snapshot give `T`: its type and the module it
96/// sits in, without the rest of the path - and with its generic arguments,
97/// written out in full, so `GenericAgentActor` for one agent is not taken for
98/// the same actor for another: `agent::GenericAgentActor<uniproc_agent::windows::WindowsAgent>`.
99pub fn short_type_name<T: ?Sized>() -> &'static str {
100    let full = std::any::type_name::<T>();
101    let head = full.split('<').next().unwrap_or(full);
102
103    let mut parts = head.rsplitn(3, "::");
104    let start = match (parts.next(), parts.next(), parts.next()) {
105        (Some(name), Some(module), Some(_)) => head.len() - module.len() - name.len() - "::".len(),
106        _ => 0,
107    };
108
109    &full[start..]
110}
111
112#[cfg(test)]
113mod tests {
114    use super::short_type_name;
115
116    mod agent {
117        pub struct Agent<T>(pub T);
118        pub struct Plain;
119    }
120
121    mod windows {
122        pub struct WindowsAgent;
123    }
124
125    #[test]
126    fn a_name_keeps_its_module_and_drops_the_rest_of_the_path() {
127        assert_eq!(short_type_name::<agent::Plain>(), "agent::Plain");
128    }
129
130    #[test]
131    fn a_generic_name_keeps_its_arguments_in_full() {
132        assert_eq!(
133            short_type_name::<agent::Agent<windows::WindowsAgent>>(),
134            "agent::Agent<guinea_core::actor::tests::windows::WindowsAgent>"
135        );
136        assert_ne!(
137            short_type_name::<agent::Agent<windows::WindowsAgent>>(),
138            short_type_name::<agent::Agent<agent::Plain>>(),
139            "one generic actor per argument is told apart"
140        );
141    }
142
143    #[test]
144    fn a_name_with_no_module_is_left_as_it_is() {
145        assert_eq!(short_type_name::<u32>(), "u32");
146        assert_eq!(short_type_name::<Vec<u32>>(), "vec::Vec<u32>", "the path, not the arguments");
147    }
148}