use crate::{ProviderContext, ProviderError, ProviderResult, ProviderRole, SecretProvider};
use appcore_contracts::{ProviderConfig, ProviderId};
use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter};
use std::sync::Arc;
pub trait ProviderFactory<T>: Send + Sync {
fn role(&self) -> ProviderRole;
fn provider_id(&self) -> &'static str;
fn create(
&self,
config: &ProviderConfig,
context: &ProviderContext,
secrets: &dyn SecretProvider,
) -> ProviderResult<T>;
}
pub struct ProviderRegistry<T> {
factories: BTreeMap<(ProviderRole, String), Arc<dyn ProviderFactory<T>>>,
}
impl<T> Debug for ProviderRegistry<T> {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ProviderRegistry")
.field("factory_count", &self.factories.len())
.finish()
}
}
impl<T> Default for ProviderRegistry<T> {
fn default() -> Self {
Self {
factories: BTreeMap::new(),
}
}
}
impl<T> ProviderRegistry<T> {
pub fn new() -> Self {
Self::default()
}
pub fn register<F>(&mut self, factory: F) -> ProviderResult<()>
where
F: ProviderFactory<T> + 'static,
{
let key = (factory.role(), factory.provider_id().to_string());
if self.factories.contains_key(&key) {
return Err(ProviderError::DuplicateFactory {
role: key.0,
provider_id: key.1,
});
}
self.factories.insert(key, Arc::new(factory));
Ok(())
}
pub fn create(
&self,
role: ProviderRole,
config: &ProviderConfig,
context: &ProviderContext,
secrets: &dyn SecretProvider,
) -> ProviderResult<T> {
let key = (role, config.provider_id().as_str().to_string());
let factory = self
.factories
.get(&key)
.ok_or_else(|| ProviderError::Unavailable {
role,
provider_id: config.provider_id().as_str().to_string(),
})?;
factory.create(config, context, secrets)
}
pub fn contains(&self, role: ProviderRole, provider_id: &ProviderId) -> bool {
self.factories
.contains_key(&(role, provider_id.as_str().to_string()))
}
}