mirror of
https://github.com/TECHNOFAB11/aerosol.git
synced 2025-12-11 23:50:07 +01:00
Implement statically required resources
This commit is contained in:
parent
3d7c0bed49
commit
a5395a5d33
9 changed files with 195 additions and 56 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "aerosol"
|
name = "aerosol"
|
||||||
version = "1.0.0-alpha.4"
|
version = "1.0.0-alpha.5"
|
||||||
authors = ["Diggory Blake <diggsey@googlemail.com>"]
|
authors = ["Diggory Blake <diggsey@googlemail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Simple dependency injection for Rust"
|
description = "Simple dependency injection for Rust"
|
||||||
|
|
@ -23,6 +23,7 @@ axum = { version = "0.6", optional = true }
|
||||||
tracing = { version = "0.1", optional = true }
|
tracing = { version = "0.1", optional = true }
|
||||||
thiserror = { version = "1.0", optional = true }
|
thiserror = { version = "1.0", optional = true }
|
||||||
anyhow = { version = "1.0" }
|
anyhow = { version = "1.0" }
|
||||||
|
frunk = "0.4.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1.0", features = ["macros"] }
|
tokio = { version = "1.0", features = ["macros"] }
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,18 @@ use std::{
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use frunk::prelude::HList;
|
||||||
resource::{unwrap_resource, Resource},
|
|
||||||
slot::SlotDesc,
|
|
||||||
state::Aerosol,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) struct WaitForSlot<T: Resource> {
|
use crate::{resource::Resource, slot::SlotDesc, state::Aerosol};
|
||||||
state: Aerosol,
|
|
||||||
|
pub(crate) struct WaitForSlot<R: HList, T: Resource> {
|
||||||
|
state: Aerosol<R>,
|
||||||
wait_index: Option<usize>,
|
wait_index: Option<usize>,
|
||||||
insert_placeholder: bool,
|
insert_placeholder: bool,
|
||||||
phantom: PhantomData<fn() -> T>,
|
phantom: PhantomData<fn() -> T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Resource> Future for WaitForSlot<T> {
|
impl<R: HList, T: Resource> Future for WaitForSlot<R, T> {
|
||||||
type Output = Option<T>;
|
type Output = Option<T>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
|
@ -28,11 +26,11 @@ impl<T: Resource> Future for WaitForSlot<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aerosol {
|
impl<R: HList> Aerosol<R> {
|
||||||
pub(crate) fn wait_for_slot_async<T: Resource>(
|
pub(crate) fn wait_for_slot_async<T: Resource>(
|
||||||
&self,
|
&self,
|
||||||
insert_placeholder: bool,
|
insert_placeholder: bool,
|
||||||
) -> WaitForSlot<T> {
|
) -> WaitForSlot<R, T> {
|
||||||
WaitForSlot {
|
WaitForSlot {
|
||||||
state: self.clone(),
|
state: self.clone(),
|
||||||
wait_index: None,
|
wait_index: None,
|
||||||
|
|
@ -48,30 +46,12 @@ impl Aerosol {
|
||||||
SlotDesc::Placeholder => self.wait_for_slot_async::<T>(false).await,
|
SlotDesc::Placeholder => self.wait_for_slot_async::<T>(false).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get an instance of `T` from the AppState, and panic if not found.
|
|
||||||
/// This function does not attempt to construct `T` if it does not exist.
|
|
||||||
pub async fn get_async<T: Resource>(&self) -> T {
|
|
||||||
unwrap_resource(self.try_get_async().await)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn get_with() {
|
|
||||||
let state = Aerosol::new().with(42);
|
|
||||||
assert_eq!(state.get_async::<i32>().await, 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn get_inserted() {
|
|
||||||
let state = Aerosol::new();
|
|
||||||
state.insert(42);
|
|
||||||
assert_eq!(state.get_async::<i32>().await, 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn try_get_some() {
|
async fn try_get_some() {
|
||||||
let state = Aerosol::new().with(42);
|
let state = Aerosol::new().with(42);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use frunk::prelude::HList;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
resource::{unwrap_constructed, Resource},
|
resource::{unwrap_constructed, Resource},
|
||||||
|
|
@ -111,14 +112,14 @@ impl_async_constructible! {
|
||||||
pub trait AsyncConstructibleResource: Resource + IndirectlyAsyncConstructible {}
|
pub trait AsyncConstructibleResource: Resource + IndirectlyAsyncConstructible {}
|
||||||
impl<T: Resource + IndirectlyAsyncConstructible> AsyncConstructibleResource for T {}
|
impl<T: Resource + IndirectlyAsyncConstructible> AsyncConstructibleResource for T {}
|
||||||
|
|
||||||
impl Aerosol {
|
impl<R: HList> Aerosol<R> {
|
||||||
/// Try to get or construct an instance of `T` asynchronously. Requires feature `async`.
|
/// Try to get or construct an instance of `T` asynchronously. Requires feature `async`.
|
||||||
pub async fn try_obtain_async<T: AsyncConstructibleResource>(&self) -> Result<T, T::Error> {
|
pub async fn try_obtain_async<T: AsyncConstructibleResource>(&self) -> Result<T, T::Error> {
|
||||||
match self.try_get_slot() {
|
match self.try_get_slot() {
|
||||||
Some(SlotDesc::Filled(x)) => Ok(x),
|
Some(SlotDesc::Filled(x)) => Ok(x),
|
||||||
Some(SlotDesc::Placeholder) | None => match self.wait_for_slot_async::<T>(true).await {
|
Some(SlotDesc::Placeholder) | None => match self.wait_for_slot_async::<T>(true).await {
|
||||||
Some(x) => Ok(x),
|
Some(x) => Ok(x),
|
||||||
None => match T::construct_async(self).await {
|
None => match T::construct_async(self.as_ref()).await {
|
||||||
Ok(x) => {
|
Ok(x) => {
|
||||||
self.fill_placeholder::<T>(x.clone());
|
self.fill_placeholder::<T>(x.clone());
|
||||||
Ok(x)
|
Ok(x)
|
||||||
|
|
@ -139,7 +140,7 @@ impl Aerosol {
|
||||||
pub async fn try_init_async<T: AsyncConstructibleResource>(&self) -> Result<(), T::Error> {
|
pub async fn try_init_async<T: AsyncConstructibleResource>(&self) -> Result<(), T::Error> {
|
||||||
match self.wait_for_slot_async::<T>(true).await {
|
match self.wait_for_slot_async::<T>(true).await {
|
||||||
Some(_) => Ok(()),
|
Some(_) => Ok(()),
|
||||||
None => match T::construct_async(self).await {
|
None => match T::construct_async(self.as_ref()).await {
|
||||||
Ok(x) => {
|
Ok(x) => {
|
||||||
self.fill_placeholder::<T>(x);
|
self.fill_placeholder::<T>(x);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -340,6 +341,6 @@ mod tests {
|
||||||
async fn obtain_impl() {
|
async fn obtain_impl() {
|
||||||
let state = Aerosol::new();
|
let state = Aerosol::new();
|
||||||
state.init_async::<Arc<DummyImpl>>().await;
|
state.init_async::<Arc<DummyImpl>>().await;
|
||||||
state.get_async::<Arc<dyn DummyTrait>>().await;
|
state.try_get_async::<Arc<dyn DummyTrait>>().await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
//!
|
//!
|
||||||
//! Provides integrations with the `axum` web framework. See the `axum` module
|
//! Provides integrations with the `axum` web framework. See the `axum` module
|
||||||
//! for more information.
|
//! for more information.
|
||||||
|
pub use frunk;
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
mod async_;
|
mod async_;
|
||||||
|
|
@ -20,6 +21,7 @@ mod async_;
|
||||||
mod async_constructible;
|
mod async_constructible;
|
||||||
#[cfg(feature = "axum")]
|
#[cfg(feature = "axum")]
|
||||||
pub mod axum;
|
pub mod axum;
|
||||||
|
mod macros;
|
||||||
mod resource;
|
mod resource;
|
||||||
mod slot;
|
mod slot;
|
||||||
mod state;
|
mod state;
|
||||||
|
|
|
||||||
7
src/macros.rs
Normal file
7
src/macros.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
/// Define a custom `Aerosol` alias with a specific set of required types
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! Aero {
|
||||||
|
($($tok:tt)*) => {
|
||||||
|
$crate::Aerosol<$crate::frunk::HList![$($tok)*]>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
fn main() {}
|
|
||||||
162
src/state.rs
162
src/state.rs
|
|
@ -1,6 +1,11 @@
|
||||||
use std::{any::Any, sync::Arc, task::Poll};
|
use std::{any::Any, marker::PhantomData, sync::Arc, task::Poll};
|
||||||
|
|
||||||
use anymap::hashbrown::{Entry, Map};
|
use anymap::hashbrown::{Entry, Map};
|
||||||
|
use frunk::{
|
||||||
|
hlist::{HFoldRightable, Sculptor},
|
||||||
|
prelude::HList,
|
||||||
|
HCons, HNil, Poly,
|
||||||
|
};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -16,16 +21,67 @@ struct InnerAerosol {
|
||||||
/// Stores a collection of resources keyed on resource type.
|
/// Stores a collection of resources keyed on resource type.
|
||||||
/// Provides methods for accessing this collection.
|
/// Provides methods for accessing this collection.
|
||||||
/// Can be cheaply cloned.
|
/// Can be cheaply cloned.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug)]
|
||||||
pub struct Aerosol {
|
#[repr(transparent)]
|
||||||
|
pub struct Aerosol<R: HList = HNil> {
|
||||||
inner: Arc<RwLock<InnerAerosol>>,
|
inner: Arc<RwLock<InnerAerosol>>,
|
||||||
|
phantom: PhantomData<Arc<R>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aerosol {
|
impl Aerosol {
|
||||||
/// Construct a new instance of the type with no initial resources.
|
/// Construct a new instance of the type with no initial resources.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self {
|
||||||
|
inner: Default::default(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: HList> Clone for Aerosol<R> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AerosolBuilderFolder;
|
||||||
|
impl<R: HList, T: Resource> frunk::Func<(Aerosol<R>, T)> for AerosolBuilderFolder {
|
||||||
|
type Output = Aerosol<HCons<T, R>>;
|
||||||
|
fn call((aero, value): (Aerosol<R>, T)) -> Self::Output {
|
||||||
|
aero.with(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub trait HTestable {
|
||||||
|
fn test<R: HList>(aero: &Aerosol<R>) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HTestable for HNil {
|
||||||
|
fn test<R: HList>(_aero: &Aerosol<R>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: Resource, T: HTestable> HTestable for HCons<H, T> {
|
||||||
|
fn test<R: HList>(aero: &Aerosol<R>) -> bool {
|
||||||
|
aero.has::<H>() && T::test(aero)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
R: Default + HFoldRightable<Poly<AerosolBuilderFolder>, Aerosol, Output = Aerosol<R>> + HList,
|
||||||
|
> Default for Aerosol<R>
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
R::default().foldr(Poly(AerosolBuilderFolder), Aerosol::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: HList> Aerosol<R> {
|
||||||
/// Directly insert a resource into the collection. Panics if a resource of the
|
/// Directly insert a resource into the collection. Panics if a resource of the
|
||||||
/// same type already exists.
|
/// same type already exists.
|
||||||
pub fn insert<T: Resource>(&self, value: T) {
|
pub fn insert<T: Resource>(&self, value: T) {
|
||||||
|
|
@ -38,10 +94,76 @@ impl Aerosol {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder method equivalent to calling `insert()` but can be chained.
|
/// Builder method equivalent to calling `insert()` but can be chained.
|
||||||
pub fn with<T: Resource>(self, value: T) -> Self {
|
pub fn with<T: Resource>(self, value: T) -> Aerosol<HCons<T, R>> {
|
||||||
self.insert(value);
|
self.insert(value);
|
||||||
self
|
Aerosol {
|
||||||
|
inner: self.inner,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert into a different variant of the Aerosol type. The new variant must
|
||||||
|
/// not require any resources which are not required as part of this type.
|
||||||
|
pub fn into<R2: HList, I>(self) -> Aerosol<R2>
|
||||||
|
where
|
||||||
|
R: Sculptor<R2, I>,
|
||||||
|
{
|
||||||
|
Aerosol {
|
||||||
|
inner: self.inner,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reborrow as a different variant of the Aerosol type. The new variant must
|
||||||
|
/// not require any resources which are not required as part of this type.
|
||||||
|
#[allow(clippy::should_implement_trait)]
|
||||||
|
pub fn as_ref<R2: HList, I>(&self) -> &Aerosol<R2>
|
||||||
|
where
|
||||||
|
R: Sculptor<R2, I>,
|
||||||
|
{
|
||||||
|
// Safety: all Aerosol variants are `#[repr(transparent)]` wrappers around
|
||||||
|
// the same concrete type.
|
||||||
|
unsafe { std::mem::transmute(self) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to convert into a different variant of the Aerosol type. Returns the
|
||||||
|
/// original type if one or more of the required resources are not fully
|
||||||
|
/// constructed.
|
||||||
|
pub fn try_into<R2: HList + HTestable>(self) -> Result<Aerosol<R2>, Self> {
|
||||||
|
if R2::test(&self) {
|
||||||
|
Ok(Aerosol {
|
||||||
|
inner: self.inner,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to convert into a different variant of the Aerosol type. Returns
|
||||||
|
/// `None` if one or more of the required resources are not fully
|
||||||
|
/// constructed.
|
||||||
|
pub fn try_as_ref<R2: HList + HTestable>(&self) -> Option<&Aerosol<R2>> {
|
||||||
|
if R2::test(self) {
|
||||||
|
Some(
|
||||||
|
// Safety: all Aerosol variants are `#[repr(transparent)]` wrappers around
|
||||||
|
// the same concrete type.
|
||||||
|
unsafe { std::mem::transmute(self) },
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a resource with a specific type is fully constructed in this
|
||||||
|
/// aerosol instance
|
||||||
|
pub fn has<T: Resource>(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self.inner.read().items.get::<Slot<T>>(),
|
||||||
|
Some(Slot::Filled(_))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn try_get_slot<T: Resource>(&self) -> Option<SlotDesc<T>> {
|
pub(crate) fn try_get_slot<T: Resource>(&self) -> Option<SlotDesc<T>> {
|
||||||
self.inner.read().items.get().map(Slot::desc)
|
self.inner.read().items.get().map(Slot::desc)
|
||||||
}
|
}
|
||||||
|
|
@ -89,8 +211,22 @@ impl Aerosol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<R: HList> AsRef<Aerosol> for Aerosol<R> {
|
||||||
|
fn as_ref(&self) -> &Aerosol {
|
||||||
|
Aerosol::as_ref(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H, T: HList> From<Aerosol<HCons<H, T>>> for Aerosol {
|
||||||
|
fn from(value: Aerosol<HCons<H, T>>) -> Self {
|
||||||
|
value.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::Aero;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -105,4 +241,18 @@ mod tests {
|
||||||
let state = Aerosol::new().with(13);
|
let state = Aerosol::new().with(13);
|
||||||
state.insert(42);
|
state.insert(42);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default() {
|
||||||
|
let state: Aero![i32] = Aerosol::default();
|
||||||
|
state.insert("Hello, world!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn convert() {
|
||||||
|
let state: Aero![i32, String, f32] = Aerosol::default();
|
||||||
|
state.insert("Hello, world!");
|
||||||
|
let state2: Aero![f32, String] = state.into();
|
||||||
|
let _state3: Aero![i32, String, f32] = state2.try_into().unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
src/sync.rs
21
src/sync.rs
|
|
@ -1,5 +1,7 @@
|
||||||
use std::{task::Poll, thread};
|
use std::{task::Poll, thread};
|
||||||
|
|
||||||
|
use frunk::{hlist::Plucker, prelude::HList};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
resource::{unwrap_resource, Resource},
|
resource::{unwrap_resource, Resource},
|
||||||
slot::SlotDesc,
|
slot::SlotDesc,
|
||||||
|
|
@ -16,7 +18,7 @@ pub fn safe_park() {
|
||||||
std::thread::park();
|
std::thread::park();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aerosol {
|
impl<R: HList> Aerosol<R> {
|
||||||
/// Synchronously wait for the slot for `T` to not have a placeholder.
|
/// Synchronously wait for the slot for `T` to not have a placeholder.
|
||||||
/// Returns immediately if there is no `T` present, or if `T`'s slot is filled.
|
/// Returns immediately if there is no `T` present, or if `T`'s slot is filled.
|
||||||
pub(crate) fn wait_for_slot<T: Resource>(&self, insert_placeholder: bool) -> Option<T> {
|
pub(crate) fn wait_for_slot<T: Resource>(&self, insert_placeholder: bool) -> Option<T> {
|
||||||
|
|
@ -37,9 +39,11 @@ impl Aerosol {
|
||||||
SlotDesc::Placeholder => self.wait_for_slot::<T>(false),
|
SlotDesc::Placeholder => self.wait_for_slot::<T>(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get an instance of `T` from the AppState, and panic if not found.
|
/// Get an instance of `T` from the AppState which is statically known to be present.
|
||||||
/// This function does not attempt to construct `T` if it does not exist.
|
pub fn get<T: Resource, I>(&self) -> T
|
||||||
pub fn get<T: Resource>(&self) -> T {
|
where
|
||||||
|
R: Plucker<T, I>,
|
||||||
|
{
|
||||||
unwrap_resource(self.try_get())
|
unwrap_resource(self.try_get())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -51,14 +55,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn get_with() {
|
fn get_with() {
|
||||||
let state = Aerosol::new().with(42);
|
let state = Aerosol::new().with(42);
|
||||||
assert_eq!(state.get::<i32>(), 42);
|
assert_eq!(state.get::<i32, _>(), 42);
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_inserted() {
|
|
||||||
let state = Aerosol::new();
|
|
||||||
state.insert(42);
|
|
||||||
assert_eq!(state.get::<i32>(), 42);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, sync::Arc};
|
||||||
|
|
||||||
|
use frunk::prelude::HList;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
resource::{unwrap_constructed, Resource},
|
resource::{unwrap_constructed, Resource},
|
||||||
slot::SlotDesc,
|
slot::SlotDesc,
|
||||||
|
|
@ -90,14 +92,14 @@ impl_constructible! {
|
||||||
pub trait ConstructibleResource: Resource + IndirectlyConstructible {}
|
pub trait ConstructibleResource: Resource + IndirectlyConstructible {}
|
||||||
impl<T: Resource + IndirectlyConstructible> ConstructibleResource for T {}
|
impl<T: Resource + IndirectlyConstructible> ConstructibleResource for T {}
|
||||||
|
|
||||||
impl Aerosol {
|
impl<R: HList> Aerosol<R> {
|
||||||
/// Try to get or construct an instance of `T`.
|
/// Try to get or construct an instance of `T`.
|
||||||
pub fn try_obtain<T: ConstructibleResource>(&self) -> Result<T, T::Error> {
|
pub fn try_obtain<T: ConstructibleResource>(&self) -> Result<T, T::Error> {
|
||||||
match self.try_get_slot() {
|
match self.try_get_slot() {
|
||||||
Some(SlotDesc::Filled(x)) => Ok(x),
|
Some(SlotDesc::Filled(x)) => Ok(x),
|
||||||
Some(SlotDesc::Placeholder) | None => match self.wait_for_slot::<T>(true) {
|
Some(SlotDesc::Placeholder) | None => match self.wait_for_slot::<T>(true) {
|
||||||
Some(x) => Ok(x),
|
Some(x) => Ok(x),
|
||||||
None => match T::construct(self) {
|
None => match T::construct(self.as_ref()) {
|
||||||
Ok(x) => {
|
Ok(x) => {
|
||||||
self.fill_placeholder::<T>(x.clone());
|
self.fill_placeholder::<T>(x.clone());
|
||||||
Ok(x)
|
Ok(x)
|
||||||
|
|
@ -118,7 +120,7 @@ impl Aerosol {
|
||||||
pub fn try_init<T: ConstructibleResource>(&self) -> Result<(), T::Error> {
|
pub fn try_init<T: ConstructibleResource>(&self) -> Result<(), T::Error> {
|
||||||
match self.wait_for_slot::<T>(true) {
|
match self.wait_for_slot::<T>(true) {
|
||||||
Some(_) => Ok(()),
|
Some(_) => Ok(()),
|
||||||
None => match T::construct(self) {
|
None => match T::construct(self.as_ref()) {
|
||||||
Ok(x) => {
|
Ok(x) => {
|
||||||
self.fill_placeholder::<T>(x);
|
self.fill_placeholder::<T>(x);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -264,6 +266,6 @@ mod tests {
|
||||||
fn obtain_impl() {
|
fn obtain_impl() {
|
||||||
let state = Aerosol::new();
|
let state = Aerosol::new();
|
||||||
state.init::<Arc<DummyImpl>>();
|
state.init::<Arc<DummyImpl>>();
|
||||||
state.get::<Arc<dyn DummyTrait>>();
|
state.try_get::<Arc<dyn DummyTrait>>().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue