scouter_state/
lib.rs

1pub mod error;
2pub use error::StateError;
3use std::future::Future;
4use std::sync::{Arc, OnceLock};
5use tokio::runtime::{Handle, Runtime};
6use tracing::debug;
7
8/// Manages the application's global Tokio runtime.
9pub struct ScouterState {
10    pub runtime: Arc<Runtime>,
11}
12
13impl ScouterState {
14    /// Creates a new multi-threaded Tokio runtime.
15    fn new() -> Result<Self, StateError> {
16        debug!("Initializing ScouterState (Multi-threaded Runtime)");
17        let runtime = Arc::new(
18            tokio::runtime::Builder::new_multi_thread() // Multi-thread is generally better for background work
19                .enable_all()
20                .build()
21                .map_err(StateError::RuntimeError)?,
22        );
23        Ok(Self { runtime })
24    }
25
26    /// Provides a Handle to the global runtime.
27    pub fn handle(&self) -> Handle {
28        self.runtime.handle().clone()
29    }
30
31    /// Blocks on a future using this runtime. This is safe because it uses a dedicated
32    /// multi-threaded runtime, isolating it from the main application's event loop.
33    pub fn block_on<F, T>(&self, future: F) -> T
34    where
35        F: std::future::Future<Output = T>,
36    {
37        self.runtime.block_on(future)
38    }
39}
40
41// Global instance of the application state manager
42static INSTANCE: OnceLock<ScouterState> = OnceLock::new();
43
44/// Global accessor for the application state, ensuring the runtime is initialized once.
45pub fn app_state() -> &'static ScouterState {
46    INSTANCE.get_or_init(|| ScouterState::new().expect("Failed to initialize state"))
47}
48
49/// A safe utility function to block on an async future using the global multi-threaded runtime.
50pub fn block_on<F: Future>(future: F) -> F::Output {
51    app_state().block_on(future)
52}