dioxus_provider/
global.rs

1//! # Global Provider Management
2//!
3//! This module provides global singletons for cache, disposal, and refresh management
4//! that operate at application scale rather than component lifecycle scale.
5
6use std::sync::OnceLock;
7
8use crate::{cache::ProviderCache, refresh::RefreshRegistry};
9
10/// Error type for global provider operations
11#[derive(Debug, thiserror::Error)]
12pub enum GlobalProviderError {
13    #[error("Global providers not initialized. Call init_global_providers() first.")]
14    NotInitialized,
15    #[error("Failed to initialize global providers: {0}")]
16    InitializationFailed(String),
17}
18
19/// Global singleton instance of the provider cache
20static GLOBAL_CACHE: OnceLock<ProviderCache> = OnceLock::new();
21
22/// Global singleton instance of the refresh registry
23static GLOBAL_REFRESH_REGISTRY: OnceLock<RefreshRegistry> = OnceLock::new();
24
25/// Configuration for initializing the global provider system
26#[derive(Default, Debug, Clone)]
27pub struct ProviderConfig {
28    enable_dependency_injection: bool,
29}
30
31impl ProviderConfig {
32    /// Create a new provider configuration with default settings
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    /// Enable dependency injection support
38    pub fn with_dependency_injection(mut self) -> Self {
39        self.enable_dependency_injection = true;
40        self
41    }
42
43    /// Initialize the global provider system with this configuration
44    pub fn init(self) -> Result<(), GlobalProviderError> {
45        // Initialize cache first
46        GLOBAL_CACHE.get_or_init(ProviderCache::new);
47
48        // Initialize refresh registry
49        let _refresh_registry = GLOBAL_REFRESH_REGISTRY.get_or_init(RefreshRegistry::new);
50
51        // Initialize dependency injection if enabled
52        if self.enable_dependency_injection {
53            crate::injection::ensure_dependency_injection_initialized();
54        }
55
56        Ok(())
57    }
58}
59
60/// Initialize the global provider system with all features enabled
61///
62/// This is the recommended way to initialize dioxus-provider. It sets up:
63/// - Global cache for provider results
64/// - Refresh registry for reactive updates
65/// - Dependency injection system
66///
67/// ## Example
68///
69/// ```rust,no_run
70/// use dioxus::prelude::*;
71/// use dioxus_provider::global::init;
72///
73/// fn main() {
74///     // Initialize global provider system
75///     init();
76///     
77///     // Launch your app
78///     dioxus::launch(app);
79/// }
80///
81/// #[component]
82/// fn app() -> Element {
83///     rsx! {
84///         div { "Hello World!" }
85///     }
86/// }
87/// ```
88pub fn init() -> Result<(), GlobalProviderError> {
89    ProviderConfig::new().with_dependency_injection().init()
90}
91
92/// Initialize the global provider management system (without dependency injection)
93///
94/// This should be called once at the start of your application,
95/// typically in your main function or app initialization.
96///
97/// ## Example
98///
99/// ```rust,no_run
100/// use dioxus::prelude::*;
101/// use dioxus_provider::global::init_global_providers;
102///
103/// fn main() {
104///     // Initialize global provider system
105///     init_global_providers();
106///     
107///     // Launch your app
108///     dioxus::launch(app);
109/// }
110///
111/// #[component]
112/// fn app() -> Element {
113///     rsx! {
114///         div { "Hello World!" }
115///     }
116/// }
117/// ```
118#[deprecated(
119    since = "0.1.0",
120    note = "Use init() or ProviderConfig::new().init() instead"
121)]
122pub fn init_global_providers() -> Result<(), GlobalProviderError> {
123    ProviderConfig::new().init()
124}
125
126/// Get the global provider cache instance
127///
128/// Returns the global cache that persists across the entire application lifecycle.
129/// This cache is shared by all providers regardless of component boundaries.
130///
131/// ## Errors
132///
133/// Returns `GlobalProviderError::NotInitialized` if `init_global_providers()` has not been called yet.
134pub fn get_global_cache() -> Result<&'static ProviderCache, GlobalProviderError> {
135    GLOBAL_CACHE
136        .get()
137        .ok_or(GlobalProviderError::NotInitialized)
138}
139
140/// Get the global refresh registry instance
141///
142/// Returns the global refresh registry that manages reactive updates and intervals
143/// across the entire application.
144///
145/// ## Errors
146///
147/// Returns `GlobalProviderError::NotInitialized` if `init_global_providers()` has not been called yet.
148pub fn get_global_refresh_registry() -> Result<&'static RefreshRegistry, GlobalProviderError> {
149    GLOBAL_REFRESH_REGISTRY
150        .get()
151        .ok_or(GlobalProviderError::NotInitialized)
152}
153
154/// Check if global providers have been initialized
155pub fn is_initialized() -> bool {
156    GLOBAL_CACHE.get().is_some() && GLOBAL_REFRESH_REGISTRY.get().is_some()
157}
158
159/// Ensure that global providers have been initialized
160///
161/// This helper function returns an error if the global providers have not been initialized yet.
162/// It's useful for providing better error messages in hooks and other functions that depend
163/// on the global provider system.
164///
165/// ## Example
166///
167/// ```rust,no_run
168/// use dioxus_provider::global::ensure_initialized;
169/// use dioxus_provider::errors::ProviderError;
170///
171/// fn my_hook() -> Result<(), ProviderError> {
172///     ensure_initialized()?;
173///     // ... rest of hook logic
174///     Ok(())
175/// }
176/// ```
177pub fn ensure_initialized() -> Result<(), crate::errors::ProviderError> {
178    if !is_initialized() {
179        return Err(crate::errors::ProviderError::Configuration(
180            "Global providers not initialized. Call init() at application startup.".to_string(),
181        ));
182    }
183    Ok(())
184}
185
186/// Reset global providers (mainly for testing)
187///
188/// This is primarily intended for testing scenarios where you need
189/// to reset the global state between tests.
190///
191/// ## Warning
192///
193/// This function is not thread-safe and should only be used in single-threaded
194/// test environments. Do not use this in production code.
195#[cfg(test)]
196pub fn reset_global_providers() {
197    // Note: OnceLock doesn't have a public reset method, so this is mainly
198    // for documentation. In real tests, you'd typically use a different
199    // approach or restart the test process.
200    panic!("Global provider reset is not currently supported. Restart the application.");
201}
202
203// Note: Deprecated functions get_global_cache_panic() and get_global_refresh_registry_panic()
204// have been removed in version 0.1.0. Use get_global_cache() and get_global_refresh_registry()
205// with proper error handling instead.
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn test_global_provider_initialization() {
213        // If already initialized, just test that we can get the instances
214        if is_initialized() {
215            let _cache = get_global_cache().unwrap();
216            let _refresh = get_global_refresh_registry().unwrap();
217            return;
218        }
219
220        // Test initialization from scratch
221        assert!(!is_initialized());
222
223        init_global_providers().unwrap();
224
225        assert!(is_initialized());
226
227        // Test that we can get all instances
228        let _cache = get_global_cache().unwrap();
229        let _refresh = get_global_refresh_registry().unwrap();
230    }
231
232    #[test]
233    fn test_error_when_not_initialized() {
234        // Check if already initialized - skip test if so
235        if is_initialized() {
236            return;
237        }
238
239        // This should return an error since we haven't called init_global_providers()
240        assert!(get_global_cache().is_err());
241        assert!(get_global_refresh_registry().is_err());
242    }
243
244    #[test]
245    fn test_get_functions_with_error_handling() {
246        // Test that get functions work with proper error handling
247        init().unwrap();
248
249        let _cache = get_global_cache().unwrap();
250        let _refresh = get_global_refresh_registry().unwrap();
251    }
252}