use std::any::Any;
use std::sync::Arc;
#[cfg(feature = "lazy")]
type BoxedOnceFn = Box<dyn FnOnce(&crate::Container) -> Arc<dyn Any + Send + Sync> + Send + Sync>;
#[cfg(feature = "lazy")]
use {crate::Container, late_init::LateInit};
#[derive(Clone)]
pub enum Shared<'a> {
Instance(Arc<dyn Any + Send + Sync>),
#[cfg(feature = "lazy")]
Lazy(Arc<LateInit<Arc<dyn Any + Send + Sync>, &'a Container<'a>, BoxedOnceFn>>),
#[doc(hidden)]
#[cfg(not(feature = "lazy"))]
__NonExhaustive(&'a std::marker::PhantomData<()>),
}
impl<'a> Shared<'a> {
pub fn new<T>(value: T) -> Self
where
T: Send + Sync + 'static,
{
Shared::Instance(Arc::new(value))
}
#[cfg(feature = "lazy")]
pub fn new_lazy<T, F>(f: F) -> Self
where
T: Send + Sync + 'static,
F: FnOnce(&Container) -> T + Send + Sync + 'static,
{
let factory = move |container: &Container| -> Arc<dyn Any + Send + Sync> {
let value = f(container);
Arc::new(value)
};
let boxed: BoxedOnceFn = Box::new(factory);
let lazy = LateInit::new(boxed);
Shared::Lazy(Arc::new(lazy))
}
pub(crate) fn get<T>(&self) -> Option<Arc<T>>
where
T: Send + Sync + 'static,
{
match self {
Shared::Instance(x) => x.clone().downcast().ok(),
#[cfg(feature = "lazy")]
Shared::Lazy(_) => None,
#[cfg(not(feature = "lazy"))]
_ => None,
}
}
#[cfg(feature = "lazy")]
pub(crate) fn get_with<T>(&self, container: &'a Container<'a>) -> Option<Arc<T>>
where
T: Send + Sync + 'static,
{
match self {
Shared::Instance(_) => None,
Shared::Lazy(lazy) => {
if lazy.is_init() {
lazy.get().and_then(|x| x.clone().downcast().ok())
} else {
lazy.get_or_init(container).clone().downcast().ok()
}
}
#[cfg(not(feature = "lazy"))]
_ => None,
}
}
}
#[cfg(feature = "lazy")]
pub mod late_init {
use once_cell::sync::OnceCell;
use std::cell::Cell;
use std::marker::PhantomData;
pub struct LateInit<T, Arg, F = fn(Arg) -> T> {
cell: OnceCell<T>,
init: Cell<Option<F>>,
_marker: PhantomData<Arg>,
}
unsafe impl<T, Arg, F: Send> Sync for LateInit<T, Arg, F> {}
impl<T, Arg, F> LateInit<T, Arg, F> {
pub const fn new(init: F) -> Self {
LateInit {
cell: OnceCell::new(),
init: Cell::new(Some(init)),
_marker: PhantomData,
}
}
#[inline]
pub fn get(&self) -> Option<&T> {
self.cell.get()
}
#[inline]
pub fn is_init(&self) -> bool {
self.get().is_some()
}
}
impl<T, Arg, F> LateInit<T, Arg, F>
where
F: FnOnce(Arg) -> T,
{
pub fn get_or_init(&self, arg: Arg) -> &T {
self.cell.get_or_init(|| match self.init.take() {
Some(init) => init(arg),
None => panic!("LateInit instance has previously been poisoned"),
})
}
}
}