use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct SecretaryContext<State> {
state: State,
metadata: HashMap<String, Box<dyn Any + Send + Sync>>,
resources: HashMap<String, Arc<dyn Any + Send + Sync>>,
}
impl<State> SecretaryContext<State> {
pub fn new(state: State) -> Self {
Self {
state,
metadata: HashMap::new(),
resources: HashMap::new(),
}
}
pub fn state(&self) -> &State {
&self.state
}
pub fn state_mut(&mut self) -> &mut State {
&mut self.state
}
pub fn set_state(&mut self, state: State) {
self.state = state;
}
pub fn set_metadata<T: Any + Send + Sync>(&mut self, key: impl Into<String>, value: T) {
self.metadata.insert(key.into(), Box::new(value));
}
pub fn get_metadata<T: Any + Send + Sync>(&self, key: &str) -> Option<&T> {
self.metadata.get(key).and_then(|v| v.downcast_ref())
}
pub fn remove_metadata(&mut self, key: &str) -> bool {
self.metadata.remove(key).is_some()
}
pub fn register_resource<T: Any + Send + Sync>(
&mut self,
key: impl Into<String>,
resource: Arc<T>,
) {
self.resources.insert(key.into(), resource);
}
pub fn get_resource<T: Any + Send + Sync>(&self, key: &str) -> Option<Arc<T>> {
self.resources
.get(key)
.and_then(|r| r.clone().downcast::<T>().ok())
}
pub fn has_resource(&self, key: &str) -> bool {
self.resources.contains_key(key)
}
}
pub type SharedSecretaryContext<State> = Arc<RwLock<SecretaryContext<State>>>;
pub struct SecretaryContextBuilder<State> {
state: State,
metadata: HashMap<String, Box<dyn Any + Send + Sync>>,
resources: HashMap<String, Arc<dyn Any + Send + Sync>>,
}
impl<State> SecretaryContextBuilder<State> {
pub fn new(state: State) -> Self {
Self {
state,
metadata: HashMap::new(),
resources: HashMap::new(),
}
}
pub fn with_metadata<T: Any + Send + Sync>(mut self, key: impl Into<String>, value: T) -> Self {
self.metadata.insert(key.into(), Box::new(value));
self
}
pub fn with_resource<T: Any + Send + Sync>(
mut self,
key: impl Into<String>,
resource: Arc<T>,
) -> Self {
self.resources.insert(key.into(), resource);
self
}
pub fn build(self) -> SecretaryContext<State> {
SecretaryContext {
state: self.state,
metadata: self.metadata,
resources: self.resources,
}
}
pub fn build_shared(self) -> SharedSecretaryContext<State> {
Arc::new(RwLock::new(self.build()))
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestState {
value: i32,
}
#[test]
fn test_context_state() {
let mut ctx = SecretaryContext::new(TestState { value: 42 });
assert_eq!(ctx.state().value, 42);
ctx.state_mut().value = 100;
assert_eq!(ctx.state().value, 100);
}
#[test]
fn test_context_metadata() {
let mut ctx = SecretaryContext::new(TestState { value: 0 });
ctx.set_metadata("key", "value".to_string());
assert_eq!(
ctx.get_metadata::<String>("key"),
Some(&"value".to_string())
);
ctx.remove_metadata("key");
assert!(ctx.get_metadata::<String>("key").is_none());
}
#[test]
fn test_context_builder() {
let ctx = SecretaryContextBuilder::new(TestState { value: 1 })
.with_metadata("name", "test".to_string())
.build();
assert_eq!(ctx.state().value, 1);
assert_eq!(
ctx.get_metadata::<String>("name"),
Some(&"test".to_string())
);
}
#[tokio::test]
async fn test_shared_context() {
let shared = SecretaryContextBuilder::new(TestState { value: 0 }).build_shared();
{
let mut ctx = shared.write().await;
ctx.state_mut().value = 42;
}
let ctx = shared.read().await;
assert_eq!(ctx.state().value, 42);
}
}