ash_flare/types.rs
1//! Common types used throughout the supervision tree
2
3use crate::restart::RestartPolicy;
4use serde::{Deserialize, Serialize};
5
6use dashmap::DashMap;
7use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
8use std::sync::Arc;
9
10/// Result of a worker's execution
11#[derive(
12 Debug,
13 Clone,
14 Copy,
15 PartialEq,
16 Eq,
17 Serialize,
18 Deserialize,
19 Archive,
20 RkyvSerialize,
21 RkyvDeserialize,
22)]
23#[rkyv(derive(Debug))]
24pub enum ChildExitReason {
25 /// Normal termination
26 Normal,
27 /// Abnormal termination (error or panic)
28 Abnormal,
29 /// Shutdown requested
30 Shutdown,
31}
32
33/// Child identifier type
34pub type ChildId = String;
35
36/// Information about a child process
37#[derive(Debug, Clone)]
38pub struct ChildInfo {
39 /// Unique identifier for the child
40 pub id: ChildId,
41 /// Type of child (Worker or Supervisor)
42 pub child_type: ChildType,
43 /// Restart policy for the child (None for supervisors)
44 pub restart_policy: Option<RestartPolicy>,
45}
46
47/// Type of child in supervision tree
48#[derive(
49 Debug,
50 Clone,
51 Copy,
52 PartialEq,
53 Eq,
54 Serialize,
55 Deserialize,
56 Archive,
57 RkyvSerialize,
58 RkyvDeserialize,
59)]
60#[rkyv(derive(Debug))]
61pub enum ChildType {
62 /// A worker process
63 Worker,
64 /// A nested supervisor
65 Supervisor,
66}
67
68/// Shared context for stateful workers with in-memory key-value store.
69///
70/// Provides a process-local, concurrency-safe storage for workers to share state.
71/// The store is backed by `DashMap` for lock-free concurrent access.
72///
73/// # Performance
74/// - Uses `DashMap` for lock-free concurrent operations
75/// - Cloning is cheap (only clones the `Arc`, not the data)
76#[derive(Clone, Debug)]
77pub struct WorkerContext {
78 store: Arc<DashMap<String, serde_json::Value>>,
79}
80
81impl WorkerContext {
82 /// Creates a new empty `WorkerContext`.
83 #[must_use]
84 pub fn new() -> Self {
85 Self {
86 store: Arc::new(DashMap::new()),
87 }
88 }
89
90 /// Gets a value from the store by key.
91 ///
92 /// Returns `None` if the key doesn't exist.
93 ///
94 /// Note: This method clones the value. For read-heavy workloads,
95 /// consider caching values or using primitives that are cheap to clone.
96 #[inline]
97 #[must_use]
98 pub fn get(&self, key: &str) -> Option<serde_json::Value> {
99 self.store.get(key).map(|entry| entry.value().clone())
100 }
101
102 /// Gets a value and applies a function to it without cloning.
103 ///
104 /// This is more efficient than `get()` when you only need to inspect the value.
105 ///
106 /// # Examples
107 /// ```
108 /// # use ash_flare::WorkerContext;
109 /// let ctx = WorkerContext::new();
110 /// ctx.set("count", serde_json::json!(42));
111 /// let is_positive = ctx.with_value("count", |v| {
112 /// v.and_then(|val| val.as_i64()).map(|n| n > 0).unwrap_or(false)
113 /// });
114 /// assert!(is_positive);
115 /// ```
116 #[inline]
117 pub fn with_value<F, R>(&self, key: &str, f: F) -> R
118 where
119 F: FnOnce(Option<&serde_json::Value>) -> R,
120 {
121 match self.store.get(key) {
122 Some(entry) => f(Some(entry.value())),
123 None => f(None),
124 }
125 }
126
127 /// Sets a value in the store.
128 #[inline]
129 pub fn set(&self, key: impl Into<String>, value: serde_json::Value) {
130 self.store.insert(key.into(), value);
131 }
132
133 /// Deletes a key from the store.
134 ///
135 /// Returns the previous value if it existed.
136 #[inline]
137 #[must_use]
138 pub fn delete(&self, key: &str) -> Option<serde_json::Value> {
139 self.store.remove(key).map(|(_, v)| v)
140 }
141
142 /// Returns the number of key-value pairs in the store.
143 #[inline]
144 #[must_use]
145 pub fn len(&self) -> usize {
146 self.store.len()
147 }
148
149 /// Returns `true` if the store contains no key-value pairs.
150 #[inline]
151 #[must_use]
152 pub fn is_empty(&self) -> bool {
153 self.store.is_empty()
154 }
155
156 /// Checks if a key exists in the store without retrieving the value.
157 #[inline]
158 #[must_use]
159 pub fn contains_key(&self, key: &str) -> bool {
160 self.store.contains_key(key)
161 }
162
163 /// Updates a value in the store using a function.
164 ///
165 /// The function receives the current value (or `None` if the key doesn't exist)
166 /// and returns an `Option` to control the update:
167 /// - `Some(value)` - Insert or update the key with the new value
168 /// - `None` - **Remove the key from the store** (if it existed)
169 ///
170 /// # Warning
171 /// Returning `None` from the update function will **delete the key** from the store.
172 /// This is useful for conditional removal but can lead to unexpected data loss if not intended.
173 ///
174 /// # Examples
175 /// ```
176 /// # use ash_flare::WorkerContext;
177 /// let ctx = WorkerContext::new();
178 ///
179 /// // Increment a counter, initializing to 1 if it doesn't exist
180 /// ctx.update("counter", |v| {
181 /// let count = v.and_then(|v| v.as_u64()).unwrap_or(0);
182 /// Some(serde_json::json!(count + 1))
183 /// });
184 /// assert_eq!(ctx.get("counter").and_then(|v| v.as_u64()), Some(1));
185 ///
186 /// // Conditionally remove a key by returning None
187 /// ctx.set("temp", serde_json::json!("remove me"));
188 /// ctx.update("temp", |_| None); // Key is now deleted
189 /// assert!(!ctx.contains_key("temp"));
190 /// ```
191 pub fn update<F>(&self, key: &str, f: F)
192 where
193 F: FnOnce(Option<serde_json::Value>) -> Option<serde_json::Value>,
194 {
195 match self.store.entry(key.to_owned()) {
196 dashmap::mapref::entry::Entry::Occupied(mut entry) => {
197 let old_value = entry.get().clone();
198 match f(Some(old_value)) {
199 Some(new_value) => {
200 entry.insert(new_value);
201 }
202 None => {
203 entry.remove();
204 }
205 }
206 }
207 dashmap::mapref::entry::Entry::Vacant(entry) => {
208 if let Some(new_value) = f(None) {
209 entry.insert(new_value);
210 }
211 }
212 }
213 }
214}
215
216impl Default for WorkerContext {
217 fn default() -> Self {
218 Self::new()
219 }
220}
221
222// ============================================================================
223// Beam-ready Foundation Types
224// ============================================================================
225
226/// Process identifier (Beam-style) - foundation for future linking/monitoring
227#[allow(dead_code)]
228#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
229pub struct Pid(pub u64);
230
231/// Extended exit reason for future Beam-like semantics
232#[allow(dead_code)]
233#[derive(Debug, Clone)]
234pub enum ExitReason {
235 /// Normal termination
236 Normal,
237 /// Killed/aborted
238 Killed,
239 /// Terminated with error
240 Error(String),
241}