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 pub fn new() -> Self {
84 Self {
85 store: Arc::new(DashMap::new()),
86 }
87 }
88
89 /// Gets a value from the store by key.
90 ///
91 /// Returns `None` if the key doesn't exist.
92 ///
93 /// Note: This method clones the value. For read-heavy workloads,
94 /// consider caching values or using primitives that are cheap to clone.
95 #[inline]
96 pub fn get(&self, key: &str) -> Option<serde_json::Value> {
97 self.store.get(key).map(|entry| entry.value().clone())
98 }
99
100 /// Gets a value and applies a function to it without cloning.
101 ///
102 /// This is more efficient than `get()` when you only need to inspect the value.
103 ///
104 /// # Examples
105 /// ```
106 /// # use ash_flare::WorkerContext;
107 /// let ctx = WorkerContext::new();
108 /// ctx.set("count", serde_json::json!(42));
109 /// let is_positive = ctx.with_value("count", |v| {
110 /// v.and_then(|val| val.as_i64()).map(|n| n > 0).unwrap_or(false)
111 /// });
112 /// assert!(is_positive);
113 /// ```
114 #[inline]
115 pub fn with_value<F, R>(&self, key: &str, f: F) -> R
116 where
117 F: FnOnce(Option<&serde_json::Value>) -> R,
118 {
119 match self.store.get(key) {
120 Some(entry) => f(Some(entry.value())),
121 None => f(None),
122 }
123 }
124
125 /// Sets a value in the store.
126 #[inline]
127 pub fn set(&self, key: impl Into<String>, value: serde_json::Value) {
128 self.store.insert(key.into(), value);
129 }
130
131 /// Deletes a key from the store.
132 ///
133 /// Returns the previous value if it existed.
134 #[inline]
135 pub fn delete(&self, key: &str) -> Option<serde_json::Value> {
136 self.store.remove(key).map(|(_, v)| v)
137 }
138
139 /// Returns the number of key-value pairs in the store.
140 #[inline]
141 pub fn len(&self) -> usize {
142 self.store.len()
143 }
144
145 /// Returns `true` if the store contains no key-value pairs.
146 #[inline]
147 pub fn is_empty(&self) -> bool {
148 self.store.is_empty()
149 }
150
151 /// Checks if a key exists in the store without retrieving the value.
152 #[inline]
153 pub fn contains_key(&self, key: &str) -> bool {
154 self.store.contains_key(key)
155 }
156
157 /// Updates a value in the store using a function.
158 ///
159 /// The function receives the current value (or `None` if the key doesn't exist)
160 /// and returns an `Option` to control the update:
161 /// - `Some(value)` - Insert or update the key with the new value
162 /// - `None` - **Remove the key from the store** (if it existed)
163 ///
164 /// # Warning
165 /// Returning `None` from the update function will **delete the key** from the store.
166 /// This is useful for conditional removal but can lead to unexpected data loss if not intended.
167 ///
168 /// # Examples
169 /// ```
170 /// # use ash_flare::WorkerContext;
171 /// let ctx = WorkerContext::new();
172 ///
173 /// // Increment a counter, initializing to 1 if it doesn't exist
174 /// ctx.update("counter", |v| {
175 /// let count = v.and_then(|v| v.as_u64()).unwrap_or(0);
176 /// Some(serde_json::json!(count + 1))
177 /// });
178 /// assert_eq!(ctx.get("counter").and_then(|v| v.as_u64()), Some(1));
179 ///
180 /// // Conditionally remove a key by returning None
181 /// ctx.set("temp", serde_json::json!("remove me"));
182 /// ctx.update("temp", |_| None); // Key is now deleted
183 /// assert!(!ctx.contains_key("temp"));
184 /// ```
185 pub fn update<F>(&self, key: &str, f: F)
186 where
187 F: FnOnce(Option<serde_json::Value>) -> Option<serde_json::Value>,
188 {
189 match self.store.entry(key.to_owned()) {
190 dashmap::mapref::entry::Entry::Occupied(mut entry) => {
191 let old_value = entry.get().clone();
192 match f(Some(old_value)) {
193 Some(new_value) => {
194 entry.insert(new_value);
195 }
196 None => {
197 entry.remove();
198 }
199 }
200 }
201 dashmap::mapref::entry::Entry::Vacant(entry) => {
202 if let Some(new_value) = f(None) {
203 entry.insert(new_value);
204 }
205 }
206 }
207 }
208}
209
210impl Default for WorkerContext {
211 fn default() -> Self {
212 Self::new()
213 }
214}
215
216// ============================================================================
217// Beam-ready Foundation Types
218// ============================================================================
219
220/// Process identifier (Beam-style) - foundation for future linking/monitoring
221#[allow(dead_code)]
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
223pub struct Pid(pub u64);
224
225/// Extended exit reason for future Beam-like semantics
226#[allow(dead_code)]
227#[derive(Debug, Clone)]
228pub enum ExitReason {
229 /// Normal termination
230 Normal,
231 /// Killed/aborted
232 Killed,
233 /// Terminated with error
234 Error(String),
235}