1use crate::action::{Action, ActionId, GlobalState};
2use anyhow::{anyhow, Result};
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::fmt;
6use std::sync::{Arc, Mutex};
7
8#[derive(Default)]
9pub struct StateMap {
10 pub states: HashMap<TypeId, Box<dyn GlobalState>>,
11}
12
13#[derive(Clone, Debug, PartialEq, Eq, Hash)]
14pub struct LocalStateKey {
15 component: &'static str,
16 field: &'static str,
17 key_path: Vec<String>,
18 ordinal: usize,
19}
20
21impl LocalStateKey {
22 pub(crate) fn new_scoped(
23 component: &'static str,
24 field: &'static str,
25 key_path: Vec<String>,
26 ordinal: usize,
27 ) -> Self {
28 Self {
29 component,
30 field,
31 key_path,
32 ordinal,
33 }
34 }
35
36 pub fn component(&self) -> &'static str {
37 self.component
38 }
39
40 pub fn field(&self) -> &'static str {
41 self.field
42 }
43
44 pub fn ordinal(&self) -> usize {
45 self.ordinal
46 }
47
48 pub fn explicit_key(&self) -> Option<&str> {
49 self.key_path.last().map(String::as_str)
50 }
51
52 pub fn key_path(&self) -> &[String] {
53 &self.key_path
54 }
55
56 pub fn action_id<A: Action>(&self) -> ActionId {
57 ActionId::from_name(&format!(
58 "fission.local_state.v1:{}:{}:{}:{}:{}",
59 self.component,
60 self.field,
61 self.key_path.join("/"),
62 self.ordinal,
63 A::static_id().as_u128()
64 ))
65 }
66}
67
68type BoxedLocalValue = Box<dyn Any + Send + Sync>;
69
70#[derive(Clone, Default)]
71pub struct LocalStateStore {
72 values: Arc<Mutex<HashMap<LocalStateKey, BoxedLocalValue>>>,
73}
74
75impl fmt::Debug for LocalStateStore {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 let len = self.values.lock().map(|values| values.len()).unwrap_or(0);
78 f.debug_struct("LocalStateStore")
79 .field("len", &len)
80 .finish()
81 }
82}
83
84impl GlobalState for LocalStateStore {}
85
86impl LocalStateStore {
87 pub fn get_or_insert_with<T>(&self, key: LocalStateKey, make_default: impl FnOnce() -> T) -> T
88 where
89 T: Clone + Send + Sync + 'static,
90 {
91 let mut values = self
92 .values
93 .lock()
94 .expect("Fission local widget state store mutex poisoned");
95 let entry = values
96 .entry(key)
97 .or_insert_with(|| Box::new(make_default()) as BoxedLocalValue);
98 entry
99 .downcast_ref::<T>()
100 .unwrap_or_else(|| {
101 panic!(
102 "Fission local widget state type mismatch: requested `{}` for an existing field with a different type",
103 std::any::type_name::<T>()
104 )
105 })
106 .clone()
107 }
108
109 pub fn update<T>(&self, key: &LocalStateKey, update: impl FnOnce(&mut T)) -> Result<()>
110 where
111 T: Send + Sync + 'static,
112 {
113 let mut values = self
114 .values
115 .lock()
116 .map_err(|_| anyhow!("Fission local widget state store mutex poisoned"))?;
117 let value = values.get_mut(key).ok_or_else(|| {
118 anyhow!(
119 "Fission local widget state field `{}` on `{}` was not found",
120 key.field,
121 key.component
122 )
123 })?;
124 let value = value.downcast_mut::<T>().ok_or_else(|| {
125 anyhow!(
126 "Fission local widget state type mismatch while updating `{}` on `{}`",
127 key.field,
128 key.component
129 )
130 })?;
131 update(value);
132 Ok(())
133 }
134
135 pub fn len(&self) -> usize {
136 self.values
137 .lock()
138 .map(|values| values.len())
139 .unwrap_or_default()
140 }
141
142 pub(crate) fn retain_active(&self, active: &std::collections::HashSet<LocalStateKey>) {
143 let mut values = self
144 .values
145 .lock()
146 .expect("Fission local widget state store mutex poisoned");
147 values.retain(|key, _| active.contains(key));
148 }
149}
150
151#[derive(Clone, Debug)]
152pub struct StateField<T> {
153 key: LocalStateKey,
154 value: T,
155}
156
157impl<T> StateField<T>
158where
159 T: Clone + Send + Sync + 'static,
160{
161 pub fn new(component: &'static str, field: &'static str, value: T) -> Self {
162 Self::new_with(component, field, || value)
163 }
164
165 pub fn new_with(
166 component: &'static str,
167 field: &'static str,
168 make_default: impl FnOnce() -> T,
169 ) -> Self {
170 crate::build::resolve_local_state(component, field, make_default)
171 }
172
173 pub(crate) fn resolved(key: LocalStateKey, value: T) -> Self {
174 Self { key, value }
175 }
176
177 pub fn get(&self) -> T {
178 self.value.clone()
179 }
180
181 pub fn component(&self) -> &'static str {
182 self.key.component()
183 }
184
185 pub fn field(&self) -> &'static str {
186 self.key.field()
187 }
188
189 pub fn key(&self) -> &LocalStateKey {
190 &self.key
191 }
192
193 pub fn action_id<A: Action>(&self) -> ActionId {
194 self.key.action_id::<A>()
195 }
196}