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
18pub 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
49pub 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
74pub 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
95pub 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}