use crate::config::ServiceConfig;
use crate::error::StorageError;
use crate::service::Service;
use doido_core::Result;
use std::collections::HashMap;
use std::sync::{Arc, LazyLock, RwLock};
pub trait ServiceFactory: Send + Sync {
fn build(&self, name: &str, config: &ServiceConfig) -> Result<Arc<dyn Service>>;
}
impl<F> ServiceFactory for F
where
F: Fn(&str, &ServiceConfig) -> Result<Arc<dyn Service>> + Send + Sync,
{
fn build(&self, name: &str, config: &ServiceConfig) -> Result<Arc<dyn Service>> {
self(name, config)
}
}
static REGISTRY: LazyLock<RwLock<HashMap<String, Arc<dyn ServiceFactory>>>> =
LazyLock::new(|| RwLock::new(HashMap::new()));
pub fn register_adapter(kind: impl Into<String>, factory: impl ServiceFactory + 'static) {
REGISTRY
.write()
.unwrap()
.insert(kind.into(), Arc::new(factory));
}
pub fn registered_adapters() -> Vec<String> {
let mut kinds: Vec<String> = REGISTRY.read().unwrap().keys().cloned().collect();
kinds.sort();
kinds
}
pub(crate) fn build_adapter(
kind: &str,
name: &str,
config: &ServiceConfig,
) -> Result<Arc<dyn Service>> {
let factory = REGISTRY.read().unwrap().get(kind).cloned();
match factory {
Some(factory) => factory.build(name, config),
None => Err(StorageError::Config(format!(
"no storage adapter registered for type {kind:?}; register one at boot with \
doido_storage::register_adapter({kind:?}, ...). registered: {:?}",
registered_adapters()
))
.into()),
}
}