tessera_ui_shard/
lib.rs

1pub mod router;
2pub mod task_handles;
3mod tokio_runtime;
4
5use std::{
6    any::Any,
7    sync::{Arc, OnceLock},
8};
9
10use dashmap::DashMap;
11
12static REGISTRY: OnceLock<ShardRegistry> = OnceLock::new();
13
14/// Trait for shard state that can be auto-injected into `shard component`.
15pub trait ShardState: Any + Send + Sync {}
16
17/// Describes the lifecycle of this ShardState.
18///
19/// The lifecycle of ShardState can be divided into two types:
20///
21/// 1. Application: ShardState exists for the lifetime of the application and will not be destroyed.
22/// 2. Shard: ShardState's lifecycle matches the navigation target, meaning it will be destroyed when the page is popped.
23#[derive(Debug, PartialEq, Eq)]
24pub enum ShardStateLifeCycle {
25    /// ShardState exists for the lifetime of the application and will not be destroyed.
26    Application,
27    /// ShardState's lifecycle matches the navigation target, meaning it will be destroyed when the page is popped.
28    Shard,
29}
30
31impl<T> ShardState for T where T: 'static + Send + Sync + Default {}
32
33pub struct ShardRegistry {
34    shards: DashMap<String, Arc<dyn ShardState>>,
35}
36
37impl ShardRegistry {
38    /// Get the singleton instance of the shard registry.
39    ///
40    /// Should only be called by macro, not manually.
41    pub fn get() -> &'static Self {
42        REGISTRY.get_or_init(|| ShardRegistry {
43            shards: DashMap::new(),
44        })
45    }
46
47    /// Get or initialize and get a shard state, and provide it to the closure `f` as `Arc<T>`. The state type must implement `ShardState`.
48    ///
49    /// This function should never be called manually; it should be automatically generated by the `#[shard]` macro.
50    ///
51    /// # Safety
52    ///
53    /// This function is unsafe because it uses an evil method to cast `Arc<dyn ShardState>` to `Arc<T>`.
54    pub unsafe fn init_or_get<T, F, R>(&self, id: &str, f: F) -> R
55    where
56        T: ShardState + Default + 'static,
57        F: FnOnce(Arc<T>) -> R,
58    {
59        let shard_ref = self
60            .shards
61            .entry(id.to_string())
62            .or_insert_with(|| Arc::new(T::default()));
63
64        // Clone to increase the reference count, ensuring the raw pointer is valid
65        let arc_clone = shard_ref.value().clone();
66
67        // Unsafe cast Arc<dyn ShardState> -> Arc<T>
68        let arc_t = unsafe {
69            let raw_dyn: *const dyn ShardState = Arc::as_ptr(&arc_clone);
70            let raw_t = raw_dyn as *const T;
71            Arc::from_raw(raw_t)
72        };
73
74        // Clone again to return, keeping the reference count
75        let ret = arc_t.clone();
76
77        // Forget arc_t to avoid decreasing the reference count on drop
78        std::mem::forget(arc_t);
79
80        f(ret)
81    }
82}