mofa_kernel/agent/secretary/
context.rs1use std::any::Any;
6use std::collections::HashMap;
7use std::sync::Arc;
8use tokio::sync::RwLock;
9
10pub struct SecretaryContext<State> {
34 state: State,
36
37 metadata: HashMap<String, Box<dyn Any + Send + Sync>>,
39
40 resources: HashMap<String, Arc<dyn Any + Send + Sync>>,
42}
43
44impl<State> SecretaryContext<State> {
45 pub fn new(state: State) -> Self {
47 Self {
48 state,
49 metadata: HashMap::new(),
50 resources: HashMap::new(),
51 }
52 }
53
54 pub fn state(&self) -> &State {
56 &self.state
57 }
58
59 pub fn state_mut(&mut self) -> &mut State {
61 &mut self.state
62 }
63
64 pub fn set_state(&mut self, state: State) {
66 self.state = state;
67 }
68
69 pub fn set_metadata<T: Any + Send + Sync>(&mut self, key: impl Into<String>, value: T) {
71 self.metadata.insert(key.into(), Box::new(value));
72 }
73
74 pub fn get_metadata<T: Any + Send + Sync>(&self, key: &str) -> Option<&T> {
76 self.metadata.get(key).and_then(|v| v.downcast_ref())
77 }
78
79 pub fn remove_metadata(&mut self, key: &str) -> bool {
81 self.metadata.remove(key).is_some()
82 }
83
84 pub fn register_resource<T: Any + Send + Sync>(
86 &mut self,
87 key: impl Into<String>,
88 resource: Arc<T>,
89 ) {
90 self.resources.insert(key.into(), resource);
91 }
92
93 pub fn get_resource<T: Any + Send + Sync>(&self, key: &str) -> Option<Arc<T>> {
95 self.resources
96 .get(key)
97 .and_then(|r| r.clone().downcast::<T>().ok())
98 }
99
100 pub fn has_resource(&self, key: &str) -> bool {
102 self.resources.contains_key(key)
103 }
104}
105
106pub type SharedSecretaryContext<State> = Arc<RwLock<SecretaryContext<State>>>;
114
115pub struct SecretaryContextBuilder<State> {
121 state: State,
122 metadata: HashMap<String, Box<dyn Any + Send + Sync>>,
123 resources: HashMap<String, Arc<dyn Any + Send + Sync>>,
124}
125
126impl<State> SecretaryContextBuilder<State> {
127 pub fn new(state: State) -> Self {
129 Self {
130 state,
131 metadata: HashMap::new(),
132 resources: HashMap::new(),
133 }
134 }
135
136 pub fn with_metadata<T: Any + Send + Sync>(mut self, key: impl Into<String>, value: T) -> Self {
138 self.metadata.insert(key.into(), Box::new(value));
139 self
140 }
141
142 pub fn with_resource<T: Any + Send + Sync>(
144 mut self,
145 key: impl Into<String>,
146 resource: Arc<T>,
147 ) -> Self {
148 self.resources.insert(key.into(), resource);
149 self
150 }
151
152 pub fn build(self) -> SecretaryContext<State> {
154 SecretaryContext {
155 state: self.state,
156 metadata: self.metadata,
157 resources: self.resources,
158 }
159 }
160
161 pub fn build_shared(self) -> SharedSecretaryContext<State> {
163 Arc::new(RwLock::new(self.build()))
164 }
165}
166
167#[cfg(test)]
172mod tests {
173 use super::*;
174
175 struct TestState {
176 value: i32,
177 }
178
179 #[test]
180 fn test_context_state() {
181 let mut ctx = SecretaryContext::new(TestState { value: 42 });
182
183 assert_eq!(ctx.state().value, 42);
184 ctx.state_mut().value = 100;
185 assert_eq!(ctx.state().value, 100);
186 }
187
188 #[test]
189 fn test_context_metadata() {
190 let mut ctx = SecretaryContext::new(TestState { value: 0 });
191
192 ctx.set_metadata("key", "value".to_string());
193 assert_eq!(
194 ctx.get_metadata::<String>("key"),
195 Some(&"value".to_string())
196 );
197
198 ctx.remove_metadata("key");
199 assert!(ctx.get_metadata::<String>("key").is_none());
200 }
201
202 #[test]
203 fn test_context_builder() {
204 let ctx = SecretaryContextBuilder::new(TestState { value: 1 })
205 .with_metadata("name", "test".to_string())
206 .build();
207
208 assert_eq!(ctx.state().value, 1);
209 assert_eq!(
210 ctx.get_metadata::<String>("name"),
211 Some(&"test".to_string())
212 );
213 }
214
215 #[tokio::test]
216 async fn test_shared_context() {
217 let shared = SecretaryContextBuilder::new(TestState { value: 0 }).build_shared();
218
219 {
220 let mut ctx = shared.write().await;
221 ctx.state_mut().value = 42;
222 }
223
224 let ctx = shared.read().await;
225 assert_eq!(ctx.state().value, 42);
226 }
227}