use std::{any::Any, collections::hash_map::Entry, sync::Arc};
use async_trait::async_trait;
use backtrace::Backtrace;
use thiserror::Error;
use super::{errors, Dependency, Inject, Injector, Key};
#[async_trait]
pub trait Provider<T: ?Sized>: Send + Sync {
async fn provide(self: Arc<Self>, i: Inject) -> Result<Arc<T>>;
}
impl Inject {
pub async fn provide_key<T: Any + Send + Sync>(
&self,
key: Key,
provider: impl Provider<T> + Provider<Dependency> + 'static,
) -> errors::Result<()> {
match self.0.write().await.entry(key.clone()) {
Entry::Occupied(_) => Err(super::Error::Occupied(key)),
Entry::Vacant(entry) => {
let _ = entry.insert(Injector::from_provider::<T>(provider));
Ok(())
}
}
}
pub async fn replace_key_with<T: Any + Send + Sync>(
&self,
key: Key,
provider: impl Provider<T> + Provider<Dependency> + 'static,
) -> errors::Result<()> {
let available = self.get_available_keys().await;
match self.0.write().await.entry(key.clone()) {
Entry::Occupied(mut entry) => {
let _ = entry.insert(Injector::from_provider::<T>(provider));
Ok(())
}
Entry::Vacant(_) => Err(super::Error::NotFound {
missing: key,
available,
backtrace: Arc::new(Backtrace::new()),
}),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Error, Debug, Clone)]
pub enum Error {
#[error("provider failure")]
Any(#[from] Arc<anyhow::Error>),
#[error("injection failure")]
Inject(#[from] errors::Error),
}
pub fn to_provider_error<E>(e: E) -> Error
where
anyhow::Error: From<E>,
{
Error::Any(Arc::new(e.into()))
}
#[cfg(test)]
pub(crate) mod test {
use std::sync::Arc;
use derive_new::new;
use nakago_derive::Provider;
use crate::container::test::{HasId, OtherService, TestService};
use super::*;
#[derive(new)]
pub struct TestServiceProvider {
id: String,
}
#[Provider(internal)]
#[async_trait]
impl Provider<TestService> for TestServiceProvider {
async fn provide(self: Arc<Self>, i: Inject) -> Result<Arc<TestService>> {
i.get_opt::<String>().await?;
Ok(Arc::new(TestService::new(self.id.clone())))
}
}
#[derive(new)]
pub struct OtherServiceProvider {
id: String,
}
#[Provider(internal)]
#[async_trait]
impl Provider<OtherService> for OtherServiceProvider {
async fn provide(self: Arc<Self>, i: Inject) -> Result<Arc<OtherService>> {
i.get_opt::<String>().await?;
Ok(Arc::new(OtherService::new(self.id.clone())))
}
}
#[derive(Default)]
pub struct HasIdProvider {}
#[Provider(internal)]
#[async_trait]
impl Provider<Box<dyn HasId>> for HasIdProvider {
async fn provide(self: Arc<Self>, i: Inject) -> Result<Arc<Box<dyn HasId>>> {
i.get_opt::<String>().await?;
Ok(Arc::new(Box::new(OtherService::new(
"test-service".to_string(),
))))
}
}
}