1use std::any::Any;
2use std::collections::HashMap;
3use std::marker::PhantomData;
4use std::sync::Arc;
5use std::time::Duration;
6use parking_lot::RwLock;
7use crossbeam_channel::{Receiver, Sender};
8
9use crate::engine::EngineInner;
10use crate::message::Message;
11use crate::metrics::ActorMetrics;
12
13pub(crate) type StateStore = Arc<RwLock<HashMap<String, Box<dyn Any + Send + Sync>>>>;
14
15#[derive(Clone)]
16pub struct State<T: Clone + Send + Sync + 'static> {
17 key: String,
18 store: StateStore,
19 _phantom: PhantomData<T>,
20}
21
22impl<T: Clone + Send + Sync + 'static> State<T> {
23 pub fn get(&self) -> T {
24 let store = self.store.read();
25 store.get(&self.key)
26 .and_then(|v| v.downcast_ref::<T>())
27 .cloned()
28 .unwrap_or_else(|| panic!("state '{}' not found", self.key))
29 }
30 pub fn set(&self, value: T) {
31 let mut store = self.store.write();
32 store.insert(self.key.clone(), Box::new(value));
33 }
34 pub fn update<F>(&self, f: F)
35 where F: FnOnce(T) -> T,
36 {
37 let mut store = self.store.write();
38 let current = store.get(&self.key)
39 .and_then(|v| v.downcast_ref::<T>())
40 .cloned();
41 if let Some(c) = current {
42 store.insert(self.key.clone(), Box::new(f(c)));
43 }
44 }
45}
46
47pub struct Scratch<T> {
48 val: Option<T>,
49}
50
51impl<T> Scratch<T> {
52 pub fn new(val: T) -> Self { Self { val: Some(val) } }
53 pub fn get(&self) -> Option<&T> { self.val.as_ref() }
54 pub fn set(&mut self, val: T) { self.val = Some(val); }
55 pub fn take(&mut self) -> Option<T> { self.val.take() }
56}
57
58pub struct Context {
59 #[allow(dead_code)]
60 pub(crate) id: u64,
61 #[allow(dead_code)]
62 pub(crate) name: String,
63 pub(crate) state_store: StateStore,
64 pub(crate) rx: Receiver<Message>,
65 #[allow(dead_code)]
66 pub(crate) tx: Sender<Message>,
67 pub(crate) message_handler: Option<Arc<dyn Fn(Message) + Send + Sync>>,
68 pub(crate) panic_handler: Option<Arc<dyn Fn() + Send + Sync>>,
69 pub(crate) mount_handler: Option<Arc<dyn Fn() + Send + Sync>>,
70 pub(crate) unmount_handler: Option<Arc<dyn Fn() + Send + Sync>>,
71 pub(crate) engine: Arc<EngineInner>,
72 pub(crate) metrics: ActorMetrics,
73 pub(crate) is_first_mount: bool,
74}
75
76impl Context {
77 pub(crate) fn new(
78 id: u64, name: String, state_store: StateStore,
79 rx: Receiver<Message>, tx: Sender<Message>,
80 engine: Arc<EngineInner>,
81 ) -> Self {
82 Self {
83 id, name, state_store, rx, tx,
84 message_handler: None,
85 panic_handler: None,
86 mount_handler: None,
87 unmount_handler: None,
88 engine,
89 metrics: ActorMetrics::new(),
90 is_first_mount: true,
91 }
92 }
93
94 pub fn use_state<T: Clone + Send + Sync + 'static>(&self, key: &str, initial: T) -> State<T> {
95 {
96 let mut store = self.state_store.write();
97 if !store.contains_key(key) {
98 store.insert(key.to_string(), Box::new(initial));
99 }
100 }
101 State { key: key.to_string(), store: self.state_store.clone(), _phantom: PhantomData }
102 }
103
104 pub fn use_scratch<T>(&self, initial: T) -> Scratch<T> {
105 Scratch::new(initial)
106 }
107
108 pub fn on_message<F>(&mut self, f: F)
109 where F: Fn(Message) + Send + Sync + 'static,
110 {
111 self.message_handler = Some(Arc::new(f));
112 }
113
114 pub fn on_panic<F>(&mut self, f: F)
115 where F: Fn() + Send + Sync + 'static,
116 {
117 self.panic_handler = Some(Arc::new(f));
118 }
119
120 pub fn on_mount<F>(&mut self, f: F)
121 where F: Fn() + Send + Sync + 'static,
122 {
123 self.mount_handler = Some(Arc::new(f));
124 }
125
126 pub fn on_unmount<F>(&mut self, f: F)
127 where F: Fn() + Send + Sync + 'static,
128 {
129 self.unmount_handler = Some(Arc::new(f));
130 }
131
132 pub fn spawn<F>(&self, name: &str, setup: F) -> crate::engine::Handle
133 where F: Fn(&mut Context) + Send + Sync + 'static,
134 {
135 self.engine.spawn_simple(name, setup)
136 }
137
138 pub fn send_to(&self, id: u64, msg: Message) {
139 self.engine.send_to(id, msg);
140 }
141
142 pub fn send_named(&self, name: &str, msg: Message) {
143 if let Some(id) = self.engine.registry.lookup(name) {
144 self.engine.send_to(id, msg);
145 }
146 }
147
148 pub fn request(&self, id: u64, msg: Message, timeout: Duration) -> Option<Message> {
149 self.engine.request(id, msg, timeout)
150 }
151
152 pub fn reply(&self, _msg: Message) {
153 }
155
156 pub fn metrics(&self) -> &ActorMetrics {
157 &self.metrics
158 }
159
160 pub fn sleep(&self, duration: Duration) {
161 let _ = self.rx.recv_timeout(duration);
162 }
163
164 pub fn poll(&self) -> Option<Message> {
165 self.rx.try_recv().ok()
166 }
167
168 pub fn id(&self) -> u64 { self.id }
169 pub fn name(&self) -> &str { &self.name }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 use crossbeam_channel::unbounded;
176
177 fn dummy_ctx() -> Context {
178 let (tx, rx) = unbounded();
179 Context::new(1, "test".to_string(), Arc::new(RwLock::new(HashMap::new())), rx, tx, Arc::new(EngineInner::new()))
180 }
181
182 #[test]
183 fn use_state_get_set() {
184 let ctx = dummy_ctx();
185 let count = ctx.use_state("count", 42i64);
186 assert_eq!(count.get(), 42);
187 count.set(100);
188 assert_eq!(count.get(), 100);
189 }
190
191 #[test]
192 fn use_state_update() {
193 let ctx = dummy_ctx();
194 let count = ctx.use_state("count", 10i64);
195 count.update(|c| c * 2);
196 assert_eq!(count.get(), 20);
197 }
198
199 #[test]
200 fn state_survives_context_drop() {
201 let store = Arc::new(RwLock::new(HashMap::new()));
202 {
203 let (tx, rx) = unbounded();
204 let ctx = Context::new(1, "test".to_string(), store.clone(), rx, tx, Arc::new(EngineInner::new()));
205 let count = ctx.use_state("count", 42i64);
206 count.set(99);
207 }
208 {
209 let (tx, rx) = unbounded();
210 let ctx = Context::new(1, "test".to_string(), store.clone(), rx, tx, Arc::new(EngineInner::new()));
211 let count = ctx.use_state::<i64>("count", 0);
212 assert_eq!(count.get(), 99);
213 }
214 }
215
216 #[test]
217 fn scratch_does_not_persist() {
218 let ctx = dummy_ctx();
219 let mut s = ctx.use_scratch(42i64);
220 assert_eq!(s.get(), Some(&42));
221 s.set(100);
222 assert_eq!(s.get(), Some(&100));
223 }
224}