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}