1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
11pub struct AgentId(pub u64);
12
13impl AgentId {
14 pub const SYSTEM: Self = Self(0);
16 pub const USER: Self = Self(1);
18 pub const AGENT_START: Self = Self(100);
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
24pub struct AgentPriority(pub u32);
25
26impl AgentPriority {
27 pub const LOW: Self = Self(0);
28 pub const NORMAL: Self = Self(100);
29 pub const HIGH: Self = Self(1000);
30 pub const CRITICAL: Self = Self(u32::MAX);
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct MutationMetadata {
36 pub agent_id: AgentId,
37 pub priority: AgentPriority,
38 pub timestamp_ms: u64,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
43pub enum ConflictResolution {
44 #[default]
46 LastWriterWins,
47 PriorityWins,
50 Merge,
52}
53
54thread_local! {
55 static CURRENT_AGENT: std::cell::RefCell<Option<MutationMetadata>> = const { std::cell::RefCell::new(None) };
56}
57
58pub fn with_agent<F, R>(agent_id: AgentId, priority: AgentPriority, f: F) -> R
61where
62 F: FnOnce() -> R,
63{
64 let now = std::time::SystemTime::now()
65 .duration_since(std::time::UNIX_EPOCH)
66 .unwrap_or_default()
67 .as_millis() as u64;
68
69 let meta = MutationMetadata {
70 agent_id,
71 priority,
72 timestamp_ms: now,
73 };
74
75 let prev = CURRENT_AGENT.with(|a| a.replace(Some(meta)));
76 let result = f();
77 CURRENT_AGENT.with(|a| a.replace(prev));
78 result
79}
80
81pub struct AgentTransaction<F> {
83 pub agent_id: AgentId,
84 pub priority: AgentPriority,
85 pub mutation: F,
86}
87
88impl<F, R> AgentTransaction<F>
89where
90 F: FnOnce() -> R,
91{
92 pub fn new(agent_id: AgentId, priority: AgentPriority, mutation: F) -> Self {
93 Self {
94 agent_id,
95 priority,
96 mutation,
97 }
98 }
99
100 pub fn execute(self) -> R {
101 with_agent(self.agent_id, self.priority, self.mutation)
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct ConflictEvent {
108 pub agent_id: AgentId,
109 pub priority: AgentPriority,
110 pub existing_agent_id: AgentId,
111 pub existing_priority: AgentPriority,
112 pub timestamp_ms: u64,
113}
114
115type ConflictHandlerList =
116 std::sync::Arc<std::sync::Mutex<Vec<Box<dyn Fn(ConflictEvent) + Send + Sync>>>>;
117static CONFLICT_HANDLERS: once_cell::sync::Lazy<ConflictHandlerList> =
118 once_cell::sync::Lazy::new(|| std::sync::Arc::new(std::sync::Mutex::new(Vec::new())));
119
120pub fn on_conflict<F>(handler: F)
122where
123 F: Fn(ConflictEvent) + Send + Sync + 'static,
124{
125 CONFLICT_HANDLERS.lock().unwrap().push(Box::new(handler));
126}
127
128pub(crate) fn notify_conflict(event: ConflictEvent) {
129 let handlers = CONFLICT_HANDLERS.lock().unwrap();
130 for handler in handlers.iter() {
131 handler(event.clone());
132 }
133}
134
135pub trait AgentSurface: Send + Sync {
137 fn agent_id(&self) -> AgentId;
139
140 fn query<T: Clone + Send + Sync + 'static>(&self, state: &crate::State<T>) -> T;
142
143 fn update<T: Clone + Send + Sync + 'static>(
145 &self,
146 state: &crate::State<T>,
147 value: T,
148 priority: AgentPriority,
149 );
150
151 fn transact<R, F>(&self, priority: AgentPriority, f: F) -> R
153 where
154 F: FnOnce() -> R;
155}
156
157pub struct DefaultAgentSurface {
159 id: AgentId,
160}
161
162impl DefaultAgentSurface {
163 pub fn new(id: AgentId) -> Self {
164 Self { id }
165 }
166}
167
168impl AgentSurface for DefaultAgentSurface {
169 fn agent_id(&self) -> AgentId {
170 self.id
171 }
172
173 fn query<T: Clone + Send + Sync + 'static>(&self, state: &crate::State<T>) -> T {
174 state.get()
175 }
176
177 fn update<T: Clone + Send + Sync + 'static>(
178 &self,
179 state: &crate::State<T>,
180 value: T,
181 priority: AgentPriority,
182 ) {
183 with_agent(self.id, priority, || {
184 state.set(value);
185 });
186 }
187
188 fn transact<R, F>(&self, priority: AgentPriority, f: F) -> R
189 where
190 F: FnOnce() -> R,
191 {
192 with_agent(self.id, priority, f)
193 }
194}
195
196pub(crate) fn get_current_mutation_metadata() -> Option<MutationMetadata> {
198 CURRENT_AGENT.with(|a| *a.borrow())
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204 use crate::State;
205
206 #[test]
207 fn test_conflict_resolution() {
208 let state = State::new(0).with_resolution(ConflictResolution::PriorityWins);
209 let surface_a = DefaultAgentSurface::new(AgentId(101));
210 let surface_b = DefaultAgentSurface::new(AgentId(102));
211
212 surface_a.update(&state, 10, AgentPriority::NORMAL);
214 assert_eq!(state.get(), 10);
215
216 surface_b.update(&state, 20, AgentPriority::LOW);
218 assert_eq!(state.get(), 10); surface_b.update(&state, 30, AgentPriority::HIGH);
222 assert_eq!(state.get(), 30);
223 }
224}