pub mod usage;
use crate::utils::lock::{read_or_recover, write_or_recover};
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct StoreId(pub u64);
impl StoreId {
pub fn new() -> Self {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(1);
Self(COUNTER.fetch_add(1, Ordering::Relaxed))
}
}
impl Default for StoreId {
fn default() -> Self {
Self::new()
}
}
pub trait Store: Any + Send + Sync {
fn id(&self) -> StoreId;
fn name(&self) -> &str;
fn get_state(&self) -> HashMap<String, String>;
fn get_getters(&self) -> HashMap<String, String>;
}
pub trait StoreExt: Store {
fn subscribe(&self) -> StoreSubscription;
fn is_name(&self, name: &str) -> bool {
self.name() == name
}
}
impl<T: Store> StoreExt for T {
fn subscribe(&self) -> StoreSubscription {
StoreSubscription {
store_id: self.id(),
_phantom: std::marker::PhantomData,
}
}
}
#[derive(Clone, Debug)]
pub struct StoreSubscription {
#[allow(dead_code)]
store_id: StoreId,
_phantom: std::marker::PhantomData<()>,
}
impl Drop for StoreSubscription {
fn drop(&mut self) {
}
}
pub struct StoreRegistry {
stores: Arc<std::sync::RwLock<HashMap<StoreId, Arc<dyn Store>>>>,
}
impl StoreRegistry {
pub fn new() -> Self {
Self {
stores: Arc::new(std::sync::RwLock::new(HashMap::new())),
}
}
pub fn register(&self, store: Arc<dyn Store>) {
let mut stores = write_or_recover(&self.stores);
stores.insert(store.id(), store);
}
pub fn unregister(&self, id: StoreId) {
let mut stores = write_or_recover(&self.stores);
stores.remove(&id);
}
pub fn get(&self, id: StoreId) -> Option<Arc<dyn Store>> {
let stores = read_or_recover(&self.stores);
stores.get(&id).cloned()
}
pub fn all(&self) -> Vec<Arc<dyn Store>> {
let stores = read_or_recover(&self.stores);
stores.values().cloned().collect()
}
pub fn find_by_name(&self, name: &str) -> Option<Arc<dyn Store>> {
let stores = read_or_recover(&self.stores);
stores.values().find(|s| s.name() == name).cloned()
}
}
impl Default for StoreRegistry {
fn default() -> Self {
Self::new()
}
}
pub fn store_registry() -> &'static StoreRegistry {
use std::sync::OnceLock;
static REGISTRY: OnceLock<StoreRegistry> = OnceLock::new();
REGISTRY.get_or_init(StoreRegistry::new)
}
pub use usage::{create_store, use_store};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_store_id_unique() {
let id1 = StoreId::new();
let id2 = StoreId::new();
assert_ne!(id1, id2);
}
#[test]
fn test_store_registry_register() {
let registry = StoreRegistry::new();
#[derive(Debug)]
struct MockStore {
id: StoreId,
name: String,
}
impl Store for MockStore {
fn id(&self) -> StoreId {
self.id
}
fn name(&self) -> &str {
&self.name
}
fn get_state(&self) -> HashMap<String, String> {
HashMap::new()
}
fn get_getters(&self) -> HashMap<String, String> {
HashMap::new()
}
}
let store = Arc::new(MockStore {
id: StoreId::new(),
name: "test".to_string(),
});
registry.register(store.clone());
assert!(registry.get(store.id).is_some());
}
#[test]
fn test_store_registry_find() {
let registry = StoreRegistry::new();
#[derive(Debug)]
struct MockStore {
id: StoreId,
name: String,
}
impl Store for MockStore {
fn id(&self) -> StoreId {
self.id
}
fn name(&self) -> &str {
&self.name
}
fn get_state(&self) -> HashMap<String, String> {
HashMap::new()
}
fn get_getters(&self) -> HashMap<String, String> {
HashMap::new()
}
}
let store = Arc::new(MockStore {
id: StoreId::new(),
name: "my_store".to_string(),
});
registry.register(store);
assert!(registry.find_by_name("my_store").is_some());
assert!(registry.find_by_name("nonexistent").is_none());
}
#[test]
fn test_store_subscription() {
let _registry = StoreRegistry::new();
#[derive(Debug)]
struct MockStore {
id: StoreId,
name: String,
}
impl Store for MockStore {
fn id(&self) -> StoreId {
self.id
}
fn name(&self) -> &str {
&self.name
}
fn get_state(&self) -> HashMap<String, String> {
HashMap::new()
}
fn get_getters(&self) -> HashMap<String, String> {
HashMap::new()
}
}
let store = Arc::new(MockStore {
id: StoreId::new(),
name: "test".to_string(),
});
let sub = store.subscribe();
assert_eq!(sub.store_id, store.id());
}
#[test]
fn test_use_store_singleton() {
use super::usage::use_store;
struct SingletonTestStore {
_value: i32,
}
impl Store for SingletonTestStore {
fn id(&self) -> StoreId {
StoreId(42)
}
fn name(&self) -> &str {
"SingletonTestStore"
}
fn get_state(&self) -> HashMap<String, String> {
HashMap::new()
}
fn get_getters(&self) -> HashMap<String, String> {
HashMap::new()
}
}
impl Default for SingletonTestStore {
fn default() -> Self {
Self { _value: 0 }
}
}
let store1 = use_store::<SingletonTestStore>();
let store2 = use_store::<SingletonTestStore>();
assert!(Arc::ptr_eq(&store1, &store2));
}
}