use std::any::Any;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
#[derive(Clone, Default)]
pub struct StateHandle {
inner: Arc<RwLock<HashMap<String, Box<dyn Any + Send + Sync>>>>,
}
impl StateHandle {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn publish<T: Any + Send + Sync + 'static>(&self, key: &str, value: T) {
self.inner
.write()
.expect("RwLock poisoned: prior task panicked")
.insert(key.to_string(), Box::new(value));
}
#[must_use]
pub fn get<T: Any + Clone + Send + Sync + 'static>(&self, key: &str) -> Option<T> {
self.inner
.read()
.expect("RwLock poisoned: prior task panicked")
.get(key)
.and_then(|v| v.downcast_ref::<T>())
.cloned()
}
#[must_use]
pub fn contains(&self, key: &str) -> bool {
self.inner
.read()
.expect("RwLock poisoned: prior task panicked")
.contains_key(key)
}
}
impl std::fmt::Debug for StateHandle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let state_count = self
.inner
.read()
.expect("RwLock poisoned: prior task panicked")
.len();
f.debug_struct("StateHandle")
.field("state_keys", &state_count)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_publish_and_get() {
let state = StateHandle::new();
state.publish("count", 42u64);
assert_eq!(state.get::<u64>("count"), Some(42));
}
#[test]
fn test_get_wrong_type_returns_none() {
let state = StateHandle::new();
state.publish("count", 42u64);
assert_eq!(state.get::<String>("count"), None);
}
#[test]
fn test_get_missing_key_returns_none() {
let state = StateHandle::new();
assert_eq!(state.get::<u64>("missing"), None);
}
#[test]
fn test_contains() {
let state = StateHandle::new();
assert!(!state.contains("key"));
state.publish("key", "value".to_string());
assert!(state.contains("key"));
}
#[test]
fn test_publish_replaces() {
let state = StateHandle::new();
state.publish("x", 1u64);
state.publish("x", 2u64);
assert_eq!(state.get::<u64>("x"), Some(2));
}
#[test]
fn test_clone_shares_state() {
let state = StateHandle::new();
let clone = state.clone();
state.publish("shared", true);
assert_eq!(clone.get::<bool>("shared"), Some(true));
}
}