use std::{any::Any, pin::Pin, sync::Arc};
use futures::{future::Shared, Future, FutureExt};
use tokio::sync::RwLock;
use super::{provider::Result, Provider};
pub(crate) struct Injector {
value: RwLock<Value>,
}
#[derive(Clone)]
enum Value {
Provider(Arc<dyn Provider<Dependency>>),
Pending(Shared<Pending>),
}
pub type Dependency = dyn Any + Send + Sync;
pub type Pending = Pin<Box<dyn Future<Output = Result<Arc<Dependency>>> + Send>>;
impl Injector {
pub(crate) fn from_pending(pending: Shared<Pending>) -> Self {
Self {
value: RwLock::new(Value::Pending(pending)),
}
}
pub(crate) fn from_provider<T: Any + Send + Sync>(
provider: impl Provider<T> + Provider<Dependency> + 'static,
) -> Self {
Self {
value: RwLock::new(Value::Provider(Arc::new(provider))),
}
}
pub(crate) async fn request(&self, inject: crate::Inject) -> Shared<Pending> {
let value = self.value.read().await;
if let Value::Pending(pending) = &*value {
return pending.clone();
}
drop(value);
let mut value = self.value.write().await;
*value = Value::Pending(match value.clone() {
Value::Pending(pending) => pending,
Value::Provider(provider) => provider.provide(inject).shared(),
});
if let Value::Pending(pending) = &*value {
pending.clone()
} else {
unreachable!()
}
}
}