mirror of
https://github.com/TECHNOFAB11/aerosol.git
synced 2025-12-11 23:50:07 +01:00
Improve documentation
This commit is contained in:
parent
a5395a5d33
commit
119f582327
9 changed files with 486 additions and 154 deletions
|
|
@ -5,18 +5,20 @@ use std::{
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
use frunk::prelude::HList;
|
use crate::{
|
||||||
|
resource::{Resource, ResourceList},
|
||||||
|
slot::SlotDesc,
|
||||||
|
state::Aero,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{resource::Resource, slot::SlotDesc, state::Aerosol};
|
pub(crate) struct WaitForSlot<R: ResourceList, T: Resource> {
|
||||||
|
state: Aero<R>,
|
||||||
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<R: HList, T: Resource> Future for WaitForSlot<R, T> {
|
impl<R: ResourceList, 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> {
|
||||||
|
|
@ -26,7 +28,7 @@ impl<R: HList, T: Resource> Future for WaitForSlot<R, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: HList> Aerosol<R> {
|
impl<R: ResourceList> Aero<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,
|
||||||
|
|
@ -54,13 +56,13 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn try_get_some() {
|
async fn try_get_some() {
|
||||||
let state = Aerosol::new().with(42);
|
let state = Aero::new().with(42);
|
||||||
assert_eq!(state.try_get_async::<i32>().await, Some(42));
|
assert_eq!(state.try_get_async::<i32>().await, Some(42));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn try_get_none() {
|
async fn try_get_none() {
|
||||||
let state = Aerosol::new().with("Hello");
|
let state = Aero::new().with("Hello");
|
||||||
assert_eq!(state.try_get_async::<i32>().await, None);
|
assert_eq!(state.try_get_async::<i32>().await, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use frunk::prelude::HList;
|
use frunk::{hlist::Sculptor, HCons, HNil};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
resource::{unwrap_constructed, Resource},
|
resource::{unwrap_constructed, unwrap_constructed_hlist, Resource, ResourceList},
|
||||||
slot::SlotDesc,
|
slot::SlotDesc,
|
||||||
state::Aerosol,
|
state::Aero,
|
||||||
sync_constructible::Constructible,
|
sync_constructible::Constructible,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -17,13 +17,13 @@ pub trait AsyncConstructible: Sized + Any + Send + Sync {
|
||||||
/// Error type for when resource fails to be constructed.
|
/// Error type for when resource fails to be constructed.
|
||||||
type Error: Into<anyhow::Error> + Send + Sync;
|
type Error: Into<anyhow::Error> + Send + Sync;
|
||||||
/// Construct the resource with the provided application state.
|
/// Construct the resource with the provided application state.
|
||||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error>;
|
async fn construct_async(aero: &Aero) -> Result<Self, Self::Error>;
|
||||||
/// Called after construction with the concrete resource to allow the callee
|
/// Called after construction with the concrete resource to allow the callee
|
||||||
/// to provide additional resources. Can be used by eg. an `Arc<Foo>` to also
|
/// to provide additional resources. Can be used by eg. an `Arc<Foo>` to also
|
||||||
/// provide an implementation of `Arc<dyn Bar>`.
|
/// provide an implementation of `Arc<dyn Bar>`.
|
||||||
async fn after_construction_async(
|
async fn after_construction_async(
|
||||||
_this: &(dyn Any + Send + Sync),
|
_this: &(dyn Any + Send + Sync),
|
||||||
_aero: &Aerosol,
|
_aero: &Aero,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -32,12 +32,12 @@ pub trait AsyncConstructible: Sized + Any + Send + Sync {
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T: Constructible> AsyncConstructible for T {
|
impl<T: Constructible> AsyncConstructible for T {
|
||||||
type Error = <T as Constructible>::Error;
|
type Error = <T as Constructible>::Error;
|
||||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
async fn construct_async(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
Self::construct(aero)
|
Self::construct(aero)
|
||||||
}
|
}
|
||||||
async fn after_construction_async(
|
async fn after_construction_async(
|
||||||
this: &(dyn Any + Send + Sync),
|
this: &(dyn Any + Send + Sync),
|
||||||
aero: &Aerosol,
|
aero: &Aero,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
Self::after_construction(this, aero)
|
Self::after_construction(this, aero)
|
||||||
}
|
}
|
||||||
|
|
@ -50,13 +50,13 @@ pub trait IndirectlyAsyncConstructible: Sized + Any + Send + Sync {
|
||||||
/// Error type for when resource fails to be constructed.
|
/// Error type for when resource fails to be constructed.
|
||||||
type Error: Into<anyhow::Error> + Send + Sync;
|
type Error: Into<anyhow::Error> + Send + Sync;
|
||||||
/// Construct the resource with the provided application state.
|
/// Construct the resource with the provided application state.
|
||||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error>;
|
async fn construct_async(aero: &Aero) -> Result<Self, Self::Error>;
|
||||||
/// Called after construction with the concrete resource to allow the callee
|
/// Called after construction with the concrete resource to allow the callee
|
||||||
/// to provide additional resources. Can be used by eg. an `Arc<Foo>` to also
|
/// to provide additional resources. Can be used by eg. an `Arc<Foo>` to also
|
||||||
/// provide an implementation of `Arc<dyn Bar>`.
|
/// provide an implementation of `Arc<dyn Bar>`.
|
||||||
async fn after_construction_async(
|
async fn after_construction_async(
|
||||||
_this: &(dyn Any + Send + Sync),
|
_this: &(dyn Any + Send + Sync),
|
||||||
_aero: &Aerosol,
|
_aero: &Aero,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -66,14 +66,14 @@ pub trait IndirectlyAsyncConstructible: Sized + Any + Send + Sync {
|
||||||
impl<T: AsyncConstructible> IndirectlyAsyncConstructible for T {
|
impl<T: AsyncConstructible> IndirectlyAsyncConstructible for T {
|
||||||
type Error = T::Error;
|
type Error = T::Error;
|
||||||
|
|
||||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
async fn construct_async(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
let res = <T as AsyncConstructible>::construct_async(aero).await?;
|
let res = <T as AsyncConstructible>::construct_async(aero).await?;
|
||||||
<T as AsyncConstructible>::after_construction_async(&res, aero).await?;
|
<T as AsyncConstructible>::after_construction_async(&res, aero).await?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
async fn after_construction_async(
|
async fn after_construction_async(
|
||||||
this: &(dyn Any + Send + Sync),
|
this: &(dyn Any + Send + Sync),
|
||||||
aero: &Aerosol,
|
aero: &Aero,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
<T as AsyncConstructible>::after_construction_async(this, aero).await
|
<T as AsyncConstructible>::after_construction_async(this, aero).await
|
||||||
}
|
}
|
||||||
|
|
@ -86,13 +86,13 @@ macro_rules! impl_async_constructible {
|
||||||
impl<$t: IndirectlyAsyncConstructible> IndirectlyAsyncConstructible for $x {
|
impl<$t: IndirectlyAsyncConstructible> IndirectlyAsyncConstructible for $x {
|
||||||
type Error = $t::Error;
|
type Error = $t::Error;
|
||||||
|
|
||||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
async fn construct_async(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
let res = $y($t::construct_async(aero).await?);
|
let res = $y($t::construct_async(aero).await?);
|
||||||
<$t as IndirectlyAsyncConstructible>::after_construction_async(&res, aero).await?;
|
<$t as IndirectlyAsyncConstructible>::after_construction_async(&res, aero).await?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn after_construction_async(this: &(dyn Any + Send + Sync), aero: &Aerosol) -> Result<(), Self::Error> {
|
async fn after_construction_async(this: &(dyn Any + Send + Sync), aero: &Aero) -> Result<(), Self::Error> {
|
||||||
<$t as IndirectlyAsyncConstructible>::after_construction_async(this, aero).await
|
<$t as IndirectlyAsyncConstructible>::after_construction_async(this, aero).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,10 +109,36 @@ impl_async_constructible! {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implemented for resources which can be asynchronously constructed from other resources. Requires feature `async`.
|
/// Implemented for resources which can be asynchronously constructed from other resources. Requires feature `async`.
|
||||||
|
/// Do not implement this trait directly, instead implement `AsyncConstructible` and ensure
|
||||||
|
/// the remaining type bounds are met for the automatic implementation of `AsyncConstructibleResource`.
|
||||||
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<R: HList> Aerosol<R> {
|
/// Automatically implemented for resource lists where every resource can be asynchronously constructed.
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AsyncConstructibleResourceList: ResourceList {
|
||||||
|
/// Construct every resource in this list in the provided aerosol instance
|
||||||
|
async fn construct_async<R: ResourceList>(aero: &Aero<R>) -> anyhow::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AsyncConstructibleResourceList for HNil {
|
||||||
|
async fn construct_async<R: ResourceList>(_aero: &Aero<R>) -> anyhow::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<H: AsyncConstructibleResource, T: AsyncConstructibleResourceList>
|
||||||
|
AsyncConstructibleResourceList for HCons<H, T>
|
||||||
|
{
|
||||||
|
async fn construct_async<R: ResourceList>(aero: &Aero<R>) -> anyhow::Result<()> {
|
||||||
|
aero.try_init_async::<H>().await.map_err(Into::into)?;
|
||||||
|
T::construct_async(aero).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: ResourceList> Aero<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() {
|
||||||
|
|
@ -156,12 +182,58 @@ impl<R: HList> Aerosol<R> {
|
||||||
pub async fn init_async<T: AsyncConstructibleResource>(&self) {
|
pub async fn init_async<T: AsyncConstructibleResource>(&self) {
|
||||||
unwrap_constructed::<T, _>(self.try_init_async::<T>().await)
|
unwrap_constructed::<T, _>(self.try_init_async::<T>().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builder method equivalent to calling `try_init_async()` but can be chained.
|
||||||
|
pub async fn try_with_constructed_async<T: AsyncConstructibleResource>(
|
||||||
|
self,
|
||||||
|
) -> Result<Aero<HCons<T, R>>, T::Error> {
|
||||||
|
self.try_init_async::<T>().await?;
|
||||||
|
Ok(Aero {
|
||||||
|
inner: self.inner,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builder method equivalent to calling `try_init_async()` but can be chained. Panics if construction fails.
|
||||||
|
pub async fn with_constructed_async<T: AsyncConstructibleResource>(self) -> Aero<HCons<T, R>> {
|
||||||
|
unwrap_constructed::<T, _>(self.try_with_constructed_async().await)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert into a different variant of the Aero type. Any missing required resources
|
||||||
|
/// will be automatically asynchronously constructed.
|
||||||
|
pub async fn try_construct_remaining_async<R2: ResourceList, I>(
|
||||||
|
self,
|
||||||
|
) -> anyhow::Result<Aero<R2>>
|
||||||
|
where
|
||||||
|
R2: Sculptor<R, I>,
|
||||||
|
<R2 as Sculptor<R, I>>::Remainder: AsyncConstructibleResourceList,
|
||||||
|
{
|
||||||
|
<<R2 as Sculptor<R, I>>::Remainder>::construct_async(&self).await?;
|
||||||
|
Ok(Aero {
|
||||||
|
inner: self.inner,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert into a different variant of the Aero type. Any missing required resources
|
||||||
|
/// will be automatically asynchronously constructed. Panics if construction of any missing resource fails.
|
||||||
|
pub async fn construct_remaining_async<R2: ResourceList, I>(self) -> Aero<R2>
|
||||||
|
where
|
||||||
|
R2: Sculptor<R, I>,
|
||||||
|
<R2 as Sculptor<R, I>>::Remainder: AsyncConstructibleResourceList,
|
||||||
|
{
|
||||||
|
unwrap_constructed_hlist::<<R2 as Sculptor<R, I>>::Remainder, _>(
|
||||||
|
self.try_construct_remaining_async().await,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{convert::Infallible, time::Duration};
|
use std::{convert::Infallible, time::Duration};
|
||||||
|
|
||||||
|
use crate::Aero;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -171,7 +243,7 @@ mod tests {
|
||||||
impl AsyncConstructible for Dummy {
|
impl AsyncConstructible for Dummy {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
async fn construct_async(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
async fn construct_async(_app_state: &Aero) -> Result<Self, Self::Error> {
|
||||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -179,13 +251,13 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn obtain() {
|
async fn obtain() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.obtain_async::<Dummy>().await;
|
state.obtain_async::<Dummy>().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn obtain_race() {
|
async fn obtain_race() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
let mut handles = Vec::new();
|
let mut handles = Vec::new();
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
|
|
@ -205,7 +277,7 @@ mod tests {
|
||||||
impl AsyncConstructible for DummyRecursive {
|
impl AsyncConstructible for DummyRecursive {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
async fn construct_async(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
aero.obtain_async::<Dummy>().await;
|
aero.obtain_async::<Dummy>().await;
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -213,13 +285,13 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn obtain_recursive() {
|
async fn obtain_recursive() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.obtain_async::<DummyRecursive>().await;
|
state.obtain_async::<DummyRecursive>().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn obtain_recursive_race() {
|
async fn obtain_recursive_race() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
let mut handles = Vec::new();
|
let mut handles = Vec::new();
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
|
|
@ -236,7 +308,7 @@ mod tests {
|
||||||
impl AsyncConstructible for DummyCyclic {
|
impl AsyncConstructible for DummyCyclic {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
async fn construct_async(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
aero.obtain_async::<DummyCyclic>().await;
|
aero.obtain_async::<DummyCyclic>().await;
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +317,7 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[should_panic(expected = "Cycle detected")]
|
#[should_panic(expected = "Cycle detected")]
|
||||||
async fn obtain_cyclic() {
|
async fn obtain_cyclic() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.obtain_async::<DummyCyclic>().await;
|
state.obtain_async::<DummyCyclic>().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,7 +327,7 @@ mod tests {
|
||||||
impl Constructible for DummySync {
|
impl Constructible for DummySync {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn construct(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
fn construct(_app_state: &Aero) -> Result<Self, Self::Error> {
|
||||||
std::thread::sleep(Duration::from_millis(100));
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +340,7 @@ mod tests {
|
||||||
impl AsyncConstructible for DummySyncRecursive {
|
impl AsyncConstructible for DummySyncRecursive {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
async fn construct_async(aero: &Aerosol) -> Result<Self, Self::Error> {
|
async fn construct_async(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
aero.obtain_async::<DummySync>().await;
|
aero.obtain_async::<DummySync>().await;
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -276,13 +348,13 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn obtain_sync_recursive() {
|
async fn obtain_sync_recursive() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.obtain_async::<DummySyncRecursive>().await;
|
state.obtain_async::<DummySyncRecursive>().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn obtain_sync_recursive_race() {
|
async fn obtain_sync_recursive_race() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
let mut handles = Vec::new();
|
let mut handles = Vec::new();
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
let state = state.clone();
|
let state = state.clone();
|
||||||
|
|
@ -299,7 +371,7 @@ mod tests {
|
||||||
impl AsyncConstructible for DummyNonClone {
|
impl AsyncConstructible for DummyNonClone {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
async fn construct_async(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
async fn construct_async(_app_state: &Aero) -> Result<Self, Self::Error> {
|
||||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -307,7 +379,7 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn obtain_non_clone() {
|
async fn obtain_non_clone() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.obtain_async::<Arc<DummyNonClone>>().await;
|
state.obtain_async::<Arc<DummyNonClone>>().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -322,13 +394,13 @@ mod tests {
|
||||||
impl AsyncConstructible for DummyImpl {
|
impl AsyncConstructible for DummyImpl {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
async fn construct_async(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
async fn construct_async(_app_state: &Aero) -> Result<Self, Self::Error> {
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn after_construction_async(
|
async fn after_construction_async(
|
||||||
this: &(dyn Any + Send + Sync),
|
this: &(dyn Any + Send + Sync),
|
||||||
aero: &Aerosol,
|
aero: &Aero,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
if let Some(arc) = this.downcast_ref::<Arc<Self>>() {
|
if let Some(arc) = this.downcast_ref::<Arc<Self>>() {
|
||||||
aero.insert(arc.clone() as Arc<dyn DummyTrait>)
|
aero.insert(arc.clone() as Arc<dyn DummyTrait>)
|
||||||
|
|
@ -339,8 +411,29 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn obtain_impl() {
|
async fn obtain_impl() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.init_async::<Arc<DummyImpl>>().await;
|
state.init_async::<Arc<DummyImpl>>().await;
|
||||||
state.try_get_async::<Arc<dyn DummyTrait>>().await.unwrap();
|
state.try_get_async::<Arc<dyn DummyTrait>>().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn with_constructed_async() {
|
||||||
|
let state = Aero::new()
|
||||||
|
.with(42)
|
||||||
|
.with_constructed_async::<Dummy>()
|
||||||
|
.await
|
||||||
|
.with("hi");
|
||||||
|
state.get::<Dummy, _>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn construct_remaining_async() {
|
||||||
|
let state: Aero![i32, Dummy, DummyRecursive, &str] = Aero::new()
|
||||||
|
.with(42)
|
||||||
|
.with("hi")
|
||||||
|
.construct_remaining_async()
|
||||||
|
.await;
|
||||||
|
state.get::<Dummy, _>();
|
||||||
|
state.get::<DummyRecursive, _>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
src/axum.rs
16
src/axum.rs
|
|
@ -4,7 +4,7 @@
|
||||||
//! resources from within route handlers.
|
//! resources from within route handlers.
|
||||||
//!
|
//!
|
||||||
//! To make use of these extractors, your application state must either be
|
//! To make use of these extractors, your application state must either be
|
||||||
//! an `Aerosol`, or you must implement `FromRef<YourState>` for `Aerosol`.
|
//! an `Aero`, or you must implement `FromRef<YourState>` for `Aero`.
|
||||||
|
|
||||||
use std::any::type_name;
|
use std::any::type_name;
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ use axum::{
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Aerosol, AsyncConstructibleResource, ConstructibleResource, Resource};
|
use crate::{Aero, AsyncConstructibleResource, ConstructibleResource, Resource};
|
||||||
|
|
||||||
/// Type of axum Rejection returned when a resource cannot be acquired
|
/// Type of axum Rejection returned when a resource cannot be acquired
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
|
@ -59,18 +59,18 @@ impl DependencyError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an already-existing resource from the state. Equivalent to calling `Aerosol::try_get_async`.
|
/// Get an already-existing resource from the state. Equivalent to calling `Aero::try_get_async`.
|
||||||
pub struct Dep<T: Resource>(pub T);
|
pub struct Dep<T: Resource>(pub T);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T: ConstructibleResource, S: Send + Sync> FromRequestParts<S> for Dep<T>
|
impl<T: ConstructibleResource, S: Send + Sync> FromRequestParts<S> for Dep<T>
|
||||||
where
|
where
|
||||||
Aerosol: FromRef<S>,
|
Aero: FromRef<S>,
|
||||||
{
|
{
|
||||||
type Rejection = DependencyError;
|
type Rejection = DependencyError;
|
||||||
|
|
||||||
async fn from_request_parts(_parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
async fn from_request_parts(_parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||||
Aerosol::from_ref(state)
|
Aero::from_ref(state)
|
||||||
.try_get_async()
|
.try_get_async()
|
||||||
.await
|
.await
|
||||||
.map(Self)
|
.map(Self)
|
||||||
|
|
@ -78,18 +78,18 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a resource from the state, or construct it if it doesn't exist. Equivalent to calling `Aerosol::try_obtain_async`.
|
/// Get a resource from the state, or construct it if it doesn't exist. Equivalent to calling `Aero::try_obtain_async`.
|
||||||
pub struct Obtain<T: AsyncConstructibleResource>(pub T);
|
pub struct Obtain<T: AsyncConstructibleResource>(pub T);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<T: AsyncConstructibleResource, S: Send + Sync> FromRequestParts<S> for Obtain<T>
|
impl<T: AsyncConstructibleResource, S: Send + Sync> FromRequestParts<S> for Obtain<T>
|
||||||
where
|
where
|
||||||
Aerosol: FromRef<S>,
|
Aero: FromRef<S>,
|
||||||
{
|
{
|
||||||
type Rejection = DependencyError;
|
type Rejection = DependencyError;
|
||||||
|
|
||||||
async fn from_request_parts(_parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
async fn from_request_parts(_parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||||
Aerosol::from_ref(state)
|
Aero::from_ref(state)
|
||||||
.try_obtain_async()
|
.try_obtain_async()
|
||||||
.await
|
.await
|
||||||
.map(Self)
|
.map(Self)
|
||||||
|
|
|
||||||
128
src/lib.rs
128
src/lib.rs
|
|
@ -1,18 +1,127 @@
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
//! # aerosol
|
//! # aerosol
|
||||||
//! Simple dependency injection for Rust
|
//! Simple but powerful dependency injection for Rust.
|
||||||
//!
|
//!
|
||||||
//! Optional features:
|
//! This crate provides the `Aero` type, which stores dependencies (called resources) keyed by their
|
||||||
|
//! type. Resources can be constructed eagerly at application startup, or on-demand when they are
|
||||||
|
//! first needed. Resources can access and/or initialize other resources on creation.
|
||||||
//!
|
//!
|
||||||
//! ## `async`
|
//! The crate will detect dependency cycles (if constructing resource A requires resource B which
|
||||||
|
//! itself requires resource A) and will panic rather than stack overflow in that case.
|
||||||
|
//!
|
||||||
|
//! The `Aero` type has an optional type parameter to make certain resources *required*. When
|
||||||
|
//! a resource is required it can be accessed infallibly. The `Aero![...]` macro exists to
|
||||||
|
//! easily name an `Aero` with a specific set of required resources.
|
||||||
|
//!
|
||||||
|
//! ## Optional features
|
||||||
|
//!
|
||||||
|
//! ### `async`
|
||||||
//!
|
//!
|
||||||
//! Allows resources to be constructed asynchrously, and provides a corresponding
|
//! Allows resources to be constructed asynchrously, and provides a corresponding
|
||||||
//! `AsyncConstructibleResource` trait.
|
//! `AsyncConstructibleResource` trait.
|
||||||
//!
|
//!
|
||||||
//! ## `axum`
|
//! ### `axum`
|
||||||
//!
|
//!
|
||||||
//! 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.
|
||||||
|
//!
|
||||||
|
//! ## Example usage
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use std::{sync::Arc, any::Any};
|
||||||
|
//!
|
||||||
|
//! # struct PostmarkClient;
|
||||||
|
//! # #[derive(Clone)]
|
||||||
|
//! # struct ConnectionPool;
|
||||||
|
//! # #[derive(Clone)]
|
||||||
|
//! # struct MessageQueue;
|
||||||
|
//! # #[derive(Clone)]
|
||||||
|
//! # struct MagicNumber(i32);
|
||||||
|
//! # trait EmailSender: Send + Sync { fn send(&self) {} }
|
||||||
|
//! # impl EmailSender for PostmarkClient {}
|
||||||
|
//! # impl PostmarkClient { fn new() -> anyhow::Result<Self> { Ok(Self) }}
|
||||||
|
//! use aerosol::{Aero, Constructible};
|
||||||
|
//!
|
||||||
|
//! // Here, we can list all the things we want to guarantee are in
|
||||||
|
//! // our app state. This is entirely optional, we could also just
|
||||||
|
//! // use the `Aero` type with default arguments and check that
|
||||||
|
//! // resources are present at runtime.
|
||||||
|
//! type AppState = Aero![
|
||||||
|
//! Arc<PostmarkClient>,
|
||||||
|
//! Arc<dyn EmailSender>,
|
||||||
|
//! ConnectionPool,
|
||||||
|
//! MessageQueue,
|
||||||
|
//! MagicNumber,
|
||||||
|
//! ];
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let app_state: AppState = Aero::new()
|
||||||
|
//! // Directly add a resource which doesn't implement `Constructible`.
|
||||||
|
//! .with(MagicNumber(42))
|
||||||
|
//! // Construct an `Arc<PostmarkClient>` resource in the AppState
|
||||||
|
//! .with_constructed::<Arc<PostmarkClient>>()
|
||||||
|
//! // Check that an implementation of `EmailSender` was added as a result
|
||||||
|
//! .assert::<Arc<dyn EmailSender>>()
|
||||||
|
//! // Automatically construct anything else necessary for our AppState
|
||||||
|
//! // (in this case, `ConnectionPool` and `MessageQueue`)
|
||||||
|
//! .construct_remaining();
|
||||||
|
//!
|
||||||
|
//! // Add an extra resource
|
||||||
|
//! app_state.insert("Hello, world");
|
||||||
|
//!
|
||||||
|
//! run(app_state);
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn run(app_state: AppState) {
|
||||||
|
//! // The `get()` method is infallible because the `Arc<dyn EmailSender>` was
|
||||||
|
//! // explicitly listed when defining our `AppState`.
|
||||||
|
//! let email_sender: Arc<dyn EmailSender> = app_state.get();
|
||||||
|
//! email_sender.send(/* email */);
|
||||||
|
//!
|
||||||
|
//! // We have to use `try_get()` here because a `&str` is not guaranteed to
|
||||||
|
//! // exist on our `AppState`.
|
||||||
|
//! let hello_message: &str = app_state.try_get().unwrap();
|
||||||
|
//! println!("{hello_message}");
|
||||||
|
//!
|
||||||
|
//! // ... more application logic
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // The `Constructible` trait can be implemented to allow resources to be automatically
|
||||||
|
//! // constructed.
|
||||||
|
//! impl Constructible for PostmarkClient {
|
||||||
|
//! type Error = anyhow::Error;
|
||||||
|
//!
|
||||||
|
//! fn construct(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
|
//! PostmarkClient::new(/* initialize using environment variables */)
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! fn after_construction(this: &(dyn Any + Send + Sync), aero: &Aero) -> Result<(), Self::Error> {
|
||||||
|
//! // We can use this to automatically populate extra resources on the context.
|
||||||
|
//! // For example, in this case we can make it so that if an `Arc<PostmarkClient>` gets
|
||||||
|
//! // constructed, we also provide `Arc<dyn EmailSender>`.
|
||||||
|
//! if let Some(arc) = this.downcast_ref::<Arc<Self>>() {
|
||||||
|
//! aero.insert(arc.clone() as Arc<dyn EmailSender>)
|
||||||
|
//! }
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl Constructible for ConnectionPool {
|
||||||
|
//! type Error = anyhow::Error;
|
||||||
|
//! fn construct(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
|
//! // ...
|
||||||
|
//! # Ok(ConnectionPool)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl Constructible for MessageQueue {
|
||||||
|
//! type Error = anyhow::Error;
|
||||||
|
//! fn construct(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
|
//! // ...
|
||||||
|
//! # Ok(MessageQueue)
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
pub use frunk;
|
pub use frunk;
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
|
|
@ -28,12 +137,15 @@ mod state;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod sync_constructible;
|
mod sync_constructible;
|
||||||
|
|
||||||
pub use resource::Resource;
|
pub use resource::{Resource, ResourceList};
|
||||||
pub use state::Aerosol;
|
pub use state::Aero;
|
||||||
|
|
||||||
pub use sync_constructible::{Constructible, ConstructibleResource, IndirectlyConstructible};
|
pub use sync_constructible::{
|
||||||
|
Constructible, ConstructibleResource, ConstructibleResourceList, IndirectlyConstructible,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "async")]
|
#[cfg(feature = "async")]
|
||||||
pub use async_constructible::{
|
pub use async_constructible::{
|
||||||
AsyncConstructible, AsyncConstructibleResource, IndirectlyAsyncConstructible,
|
AsyncConstructible, AsyncConstructibleResource, AsyncConstructibleResourceList,
|
||||||
|
IndirectlyAsyncConstructible,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
/// Define a custom `Aerosol` alias with a specific set of required types
|
/// Define a custom `Aero` alias with a specific set of required types
|
||||||
|
///
|
||||||
|
/// Example usage:
|
||||||
|
/// ```rust
|
||||||
|
/// use aerosol::Aero;
|
||||||
|
///
|
||||||
|
/// type AppState = Aero![&'static str, i32, bool];
|
||||||
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! Aero {
|
macro_rules! Aero {
|
||||||
($($tok:tt)*) => {
|
($($tok:tt)*) => {
|
||||||
$crate::Aerosol<$crate::frunk::HList![$($tok)*]>
|
$crate::Aero<$crate::frunk::HList![$($tok)*]>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,38 @@
|
||||||
use std::any::{type_name, Any};
|
use std::any::{type_name, Any};
|
||||||
|
|
||||||
|
use frunk::{prelude::HList, HCons, HNil};
|
||||||
|
|
||||||
|
use crate::Aero;
|
||||||
|
|
||||||
/// Bound on the types that can be used as an aerosol resource.
|
/// Bound on the types that can be used as an aerosol resource.
|
||||||
pub trait Resource: Any + Send + Sync + Clone {}
|
pub trait Resource: Any + Send + Sync + Clone {}
|
||||||
impl<T: Any + Send + Sync + Clone> Resource for T {}
|
impl<T: Any + Send + Sync + Clone> Resource for T {}
|
||||||
|
|
||||||
|
/// A compile-time list of resource types which are statically guaranteed to be present.
|
||||||
|
pub trait ResourceList: HList + Any + Send + Sync + Clone {
|
||||||
|
/// Test at runtmie whether every resource in this list is present in the given Aero instance.
|
||||||
|
fn test<R: ResourceList>(aero: &Aero<R>) -> bool;
|
||||||
|
}
|
||||||
|
impl ResourceList for HNil {
|
||||||
|
fn test<R: ResourceList>(_aero: &Aero<R>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H: Resource, T: ResourceList> ResourceList for HCons<H, T> {
|
||||||
|
fn test<R: ResourceList>(aero: &Aero<R>) -> bool {
|
||||||
|
aero.has::<H>() && T::test(aero)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn missing_resource<T: Resource>() -> ! {
|
||||||
|
panic!("Resource `{}` does not exist", type_name::<T>())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn unwrap_resource<T: Resource>(opt: Option<T>) -> T {
|
pub(crate) fn unwrap_resource<T: Resource>(opt: Option<T>) -> T {
|
||||||
if let Some(value) = opt {
|
if let Some(value) = opt {
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
panic!("Resource `{}` does not exist", type_name::<T>())
|
missing_resource::<T>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,6 +43,17 @@ pub(crate) fn unwrap_constructed<T: Resource, U>(res: Result<U, impl Into<anyhow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn unwrap_constructed_hlist<T, U>(res: Result<U, impl Into<anyhow::Error>>) -> U {
|
||||||
|
match res {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => panic!(
|
||||||
|
"Failed to construct one of `{}`: {}",
|
||||||
|
type_name::<T>(),
|
||||||
|
e.into()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn duplicate_resource<T: Resource>() -> ! {
|
pub(crate) fn duplicate_resource<T: Resource>() -> ! {
|
||||||
panic!(
|
panic!(
|
||||||
"Duplicate resource: attempted to add a second `{}`",
|
"Duplicate resource: attempted to add a second `{}`",
|
||||||
|
|
|
||||||
121
src/state.rs
121
src/state.rs
|
|
@ -3,18 +3,17 @@ use std::{any::Any, marker::PhantomData, sync::Arc, task::Poll};
|
||||||
use anymap::hashbrown::{Entry, Map};
|
use anymap::hashbrown::{Entry, Map};
|
||||||
use frunk::{
|
use frunk::{
|
||||||
hlist::{HFoldRightable, Sculptor},
|
hlist::{HFoldRightable, Sculptor},
|
||||||
prelude::HList,
|
|
||||||
HCons, HNil, Poly,
|
HCons, HNil, Poly,
|
||||||
};
|
};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
resource::{cyclic_resource, duplicate_resource, Resource},
|
resource::{cyclic_resource, duplicate_resource, missing_resource, Resource, ResourceList},
|
||||||
slot::{Slot, SlotDesc, ThreadOrWaker},
|
slot::{Slot, SlotDesc, ThreadOrWaker},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct InnerAerosol {
|
pub(crate) struct InnerAero {
|
||||||
items: Map<dyn Any + Send + Sync>,
|
items: Map<dyn Any + Send + Sync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,12 +22,12 @@ struct InnerAerosol {
|
||||||
/// Can be cheaply cloned.
|
/// Can be cheaply cloned.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Aerosol<R: HList = HNil> {
|
pub struct Aero<R: ResourceList = HNil> {
|
||||||
inner: Arc<RwLock<InnerAerosol>>,
|
pub(crate) inner: Arc<RwLock<InnerAero>>,
|
||||||
phantom: PhantomData<Arc<R>>,
|
pub(crate) phantom: PhantomData<Arc<R>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Aerosol {
|
impl Aero {
|
||||||
/// 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 {
|
Self {
|
||||||
|
|
@ -38,7 +37,7 @@ impl Aerosol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: HList> Clone for Aerosol<R> {
|
impl<R: ResourceList> Clone for Aero<R> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: self.inner.clone(),
|
inner: self.inner.clone(),
|
||||||
|
|
@ -47,41 +46,24 @@ impl<R: HList> Clone for Aerosol<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AerosolBuilderFolder;
|
struct AerosolDefaultFolder;
|
||||||
impl<R: HList, T: Resource> frunk::Func<(Aerosol<R>, T)> for AerosolBuilderFolder {
|
impl<R: ResourceList, T: Resource> frunk::Func<(Aero<R>, T)> for AerosolDefaultFolder {
|
||||||
type Output = Aerosol<HCons<T, R>>;
|
type Output = Aero<HCons<T, R>>;
|
||||||
fn call((aero, value): (Aerosol<R>, T)) -> Self::Output {
|
fn call((aero, value): (Aero<R>, T)) -> Self::Output {
|
||||||
aero.with(value)
|
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<
|
impl<
|
||||||
R: Default + HFoldRightable<Poly<AerosolBuilderFolder>, Aerosol, Output = Aerosol<R>> + HList,
|
R: Default + HFoldRightable<Poly<AerosolDefaultFolder>, Aero, Output = Aero<R>> + ResourceList,
|
||||||
> Default for Aerosol<R>
|
> Default for Aero<R>
|
||||||
{
|
{
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
R::default().foldr(Poly(AerosolBuilderFolder), Aerosol::new())
|
R::default().foldr(Poly(AerosolDefaultFolder), Aero::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: HList> Aerosol<R> {
|
impl<R: ResourceList> Aero<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) {
|
||||||
|
|
@ -94,44 +76,44 @@ impl<R: HList> Aerosol<R> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) -> Aerosol<HCons<T, R>> {
|
pub fn with<T: Resource>(self, value: T) -> Aero<HCons<T, R>> {
|
||||||
self.insert(value);
|
self.insert(value);
|
||||||
Aerosol {
|
Aero {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert into a different variant of the Aerosol type. The new variant must
|
/// Convert into a different variant of the Aero type. The new variant must
|
||||||
/// not require any resources which are not required as part of this type.
|
/// not require any resources which are not required as part of this type.
|
||||||
pub fn into<R2: HList, I>(self) -> Aerosol<R2>
|
pub fn into<R2: ResourceList, I>(self) -> Aero<R2>
|
||||||
where
|
where
|
||||||
R: Sculptor<R2, I>,
|
R: Sculptor<R2, I>,
|
||||||
{
|
{
|
||||||
Aerosol {
|
Aero {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reborrow as a different variant of the Aerosol type. The new variant must
|
/// Reborrow as a different variant of the Aero type. The new variant must
|
||||||
/// not require any resources which are not required as part of this type.
|
/// not require any resources which are not required as part of this type.
|
||||||
#[allow(clippy::should_implement_trait)]
|
#[allow(clippy::should_implement_trait)]
|
||||||
pub fn as_ref<R2: HList, I>(&self) -> &Aerosol<R2>
|
pub fn as_ref<R2: ResourceList, I>(&self) -> &Aero<R2>
|
||||||
where
|
where
|
||||||
R: Sculptor<R2, I>,
|
R: Sculptor<R2, I>,
|
||||||
{
|
{
|
||||||
// Safety: all Aerosol variants are `#[repr(transparent)]` wrappers around
|
// Safety: all Aero variants are `#[repr(transparent)]` wrappers around
|
||||||
// the same concrete type.
|
// the same concrete type.
|
||||||
unsafe { std::mem::transmute(self) }
|
unsafe { std::mem::transmute(self) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to convert into a different variant of the Aerosol type. Returns the
|
/// Try to convert into a different variant of the Aero type. Returns the
|
||||||
/// original type if one or more of the required resources are not fully
|
/// original type if one or more of the required resources are not fully
|
||||||
/// constructed.
|
/// constructed.
|
||||||
pub fn try_into<R2: HList + HTestable>(self) -> Result<Aerosol<R2>, Self> {
|
pub fn try_into<R2: ResourceList>(self) -> Result<Aero<R2>, Self> {
|
||||||
if R2::test(&self) {
|
if R2::test(&self) {
|
||||||
Ok(Aerosol {
|
Ok(Aero {
|
||||||
inner: self.inner,
|
inner: self.inner,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
})
|
})
|
||||||
|
|
@ -140,13 +122,13 @@ impl<R: HList> Aerosol<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to convert into a different variant of the Aerosol type. Returns
|
/// Try to convert into a different variant of the Aero type. Returns
|
||||||
/// `None` if one or more of the required resources are not fully
|
/// `None` if one or more of the required resources are not fully
|
||||||
/// constructed.
|
/// constructed.
|
||||||
pub fn try_as_ref<R2: HList + HTestable>(&self) -> Option<&Aerosol<R2>> {
|
pub fn try_as_ref<R2: ResourceList>(&self) -> Option<&Aero<R2>> {
|
||||||
if R2::test(self) {
|
if R2::test(self) {
|
||||||
Some(
|
Some(
|
||||||
// Safety: all Aerosol variants are `#[repr(transparent)]` wrappers around
|
// Safety: all Aero variants are `#[repr(transparent)]` wrappers around
|
||||||
// the same concrete type.
|
// the same concrete type.
|
||||||
unsafe { std::mem::transmute(self) },
|
unsafe { std::mem::transmute(self) },
|
||||||
)
|
)
|
||||||
|
|
@ -164,6 +146,24 @@ impl<R: HList> Aerosol<R> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Assert that a resource exists, returns `self` unchanged if not
|
||||||
|
pub fn try_assert<T: Resource>(self) -> Result<Aero<HCons<T, R>>, Self> {
|
||||||
|
if self.has::<T>() {
|
||||||
|
Ok(Aero {
|
||||||
|
inner: self.inner,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assert that a resource exists, panic if not
|
||||||
|
pub fn assert<T: Resource>(self) -> Aero<HCons<T, R>> {
|
||||||
|
self.try_assert()
|
||||||
|
.unwrap_or_else(|_| missing_resource::<T>())
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -211,14 +211,14 @@ impl<R: HList> Aerosol<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: HList> AsRef<Aerosol> for Aerosol<R> {
|
impl<R: ResourceList> AsRef<Aero> for Aero<R> {
|
||||||
fn as_ref(&self) -> &Aerosol {
|
fn as_ref(&self) -> &Aero {
|
||||||
Aerosol::as_ref(self)
|
Aero::as_ref(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H, T: HList> From<Aerosol<HCons<H, T>>> for Aerosol {
|
impl<H: Resource, T: ResourceList> From<Aero<HCons<H, T>>> for Aero {
|
||||||
fn from(value: Aerosol<HCons<H, T>>) -> Self {
|
fn from(value: Aero<HCons<H, T>>) -> Self {
|
||||||
value.into()
|
value.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -227,32 +227,37 @@ impl<H, T: HList> From<Aerosol<HCons<H, T>>> for Aerosol {
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::Aero;
|
use crate::Aero;
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create() {
|
fn create() {
|
||||||
let state = Aerosol::new().with(42);
|
let state = Aero::new().with(42);
|
||||||
state.insert("Hello, world!");
|
state.insert("Hello, world!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn duplicate() {
|
fn duplicate() {
|
||||||
let state = Aerosol::new().with(13);
|
let state = Aero::new().with(13);
|
||||||
state.insert(42);
|
state.insert(42);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default() {
|
fn default() {
|
||||||
let state: Aero![i32] = Aerosol::default();
|
let state: Aero![i32] = Aero::default();
|
||||||
state.insert("Hello, world!");
|
state.insert("Hello, world!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn convert() {
|
fn convert() {
|
||||||
let state: Aero![i32, String, f32] = Aerosol::default();
|
let state: Aero![i32, String, f32] = Aero::default();
|
||||||
state.insert("Hello, world!");
|
state.insert("Hello, world!");
|
||||||
let state2: Aero![f32, String] = state.into();
|
let state2: Aero![f32, String] = state.into();
|
||||||
let _state3: Aero![i32, String, f32] = state2.try_into().unwrap();
|
let _state3: Aero![i32, String, f32] = state2.try_into().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assert() {
|
||||||
|
let state: Aero![i32, String, f32] = Aero::default();
|
||||||
|
state.insert("Hello, world!");
|
||||||
|
let _state2: Aero![&str, f32] = state.assert::<&str>().into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
src/sync.rs
14
src/sync.rs
|
|
@ -1,11 +1,11 @@
|
||||||
use std::{task::Poll, thread};
|
use std::{task::Poll, thread};
|
||||||
|
|
||||||
use frunk::{hlist::Plucker, prelude::HList};
|
use frunk::hlist::Plucker;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
resource::{unwrap_resource, Resource},
|
resource::{unwrap_resource, Resource, ResourceList},
|
||||||
slot::SlotDesc,
|
slot::SlotDesc,
|
||||||
state::Aerosol,
|
state::Aero,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_family = "wasm")]
|
#[cfg(target_family = "wasm")]
|
||||||
|
|
@ -18,7 +18,7 @@ pub fn safe_park() {
|
||||||
std::thread::park();
|
std::thread::park();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: HList> Aerosol<R> {
|
impl<R: ResourceList> Aero<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> {
|
||||||
|
|
@ -54,19 +54,19 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_with() {
|
fn get_with() {
|
||||||
let state = Aerosol::new().with(42);
|
let state = Aero::new().with(42);
|
||||||
assert_eq!(state.get::<i32, _>(), 42);
|
assert_eq!(state.get::<i32, _>(), 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn try_get_some() {
|
fn try_get_some() {
|
||||||
let state = Aerosol::new().with(42);
|
let state = Aero::new().with(42);
|
||||||
assert_eq!(state.try_get::<i32>(), Some(42));
|
assert_eq!(state.try_get::<i32>(), Some(42));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn try_get_none() {
|
fn try_get_none() {
|
||||||
let state = Aerosol::new().with("Hello");
|
let state = Aero::new().with("Hello");
|
||||||
assert_eq!(state.try_get::<i32>(), None);
|
assert_eq!(state.try_get::<i32>(), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
use frunk::prelude::HList;
|
use frunk::{hlist::Sculptor, HCons, HNil};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
resource::{unwrap_constructed, Resource},
|
resource::{unwrap_constructed, unwrap_constructed_hlist, Resource, ResourceList},
|
||||||
slot::SlotDesc,
|
slot::SlotDesc,
|
||||||
state::Aerosol,
|
state::Aero,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Implemented for values which can be constructed from other resources.
|
/// Implemented for values which can be constructed from other resources.
|
||||||
|
|
@ -13,14 +13,14 @@ pub trait Constructible: Sized + Any + Send + Sync {
|
||||||
/// Error type for when resource fails to be constructed.
|
/// Error type for when resource fails to be constructed.
|
||||||
type Error: Into<anyhow::Error> + Send + Sync;
|
type Error: Into<anyhow::Error> + Send + Sync;
|
||||||
/// Construct the resource with the provided application state.
|
/// Construct the resource with the provided application state.
|
||||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error>;
|
fn construct(aero: &Aero) -> Result<Self, Self::Error>;
|
||||||
|
|
||||||
/// Called after construction with the concrete resource to allow the callee
|
/// Called after construction with the concrete resource to allow the callee
|
||||||
/// to provide additional resources. Can be used by eg. an `Arc<Foo>` to also
|
/// to provide additional resources. Can be used by eg. an `Arc<Foo>` to also
|
||||||
/// provide an implementation of `Arc<dyn Bar>`.
|
/// provide an implementation of `Arc<dyn Bar>`.
|
||||||
fn after_construction(
|
fn after_construction(
|
||||||
_this: &(dyn Any + Send + Sync),
|
_this: &(dyn Any + Send + Sync),
|
||||||
_aero: &Aerosol,
|
_aero: &Aero,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -31,13 +31,13 @@ pub trait IndirectlyConstructible: Sized + Any + Send + Sync {
|
||||||
/// Error type for when resource fails to be constructed.
|
/// Error type for when resource fails to be constructed.
|
||||||
type Error: Into<anyhow::Error> + Send + Sync;
|
type Error: Into<anyhow::Error> + Send + Sync;
|
||||||
/// Construct the resource with the provided application state.
|
/// Construct the resource with the provided application state.
|
||||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error>;
|
fn construct(aero: &Aero) -> Result<Self, Self::Error>;
|
||||||
/// Called after construction with the concrete resource to allow the callee
|
/// Called after construction with the concrete resource to allow the callee
|
||||||
/// to provide additional resources. Can be used by eg. an `Arc<Foo>` to also
|
/// to provide additional resources. Can be used by eg. an `Arc<Foo>` to also
|
||||||
/// provide an implementation of `Arc<dyn Bar>`.
|
/// provide an implementation of `Arc<dyn Bar>`.
|
||||||
fn after_construction(
|
fn after_construction(
|
||||||
_this: &(dyn Any + Send + Sync),
|
_this: &(dyn Any + Send + Sync),
|
||||||
_aero: &Aerosol,
|
_aero: &Aero,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -46,16 +46,13 @@ pub trait IndirectlyConstructible: Sized + Any + Send + Sync {
|
||||||
impl<T: Constructible> IndirectlyConstructible for T {
|
impl<T: Constructible> IndirectlyConstructible for T {
|
||||||
type Error = T::Error;
|
type Error = T::Error;
|
||||||
|
|
||||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error> {
|
fn construct(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
let res = <T as Constructible>::construct(aero)?;
|
let res = <T as Constructible>::construct(aero)?;
|
||||||
<T as Constructible>::after_construction(&res, aero)?;
|
<T as Constructible>::after_construction(&res, aero)?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_construction(
|
fn after_construction(this: &(dyn Any + Send + Sync), aero: &Aero) -> Result<(), Self::Error> {
|
||||||
this: &(dyn Any + Send + Sync),
|
|
||||||
aero: &Aerosol,
|
|
||||||
) -> Result<(), Self::Error> {
|
|
||||||
<T as Constructible>::after_construction(this, aero)
|
<T as Constructible>::after_construction(this, aero)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,13 +63,13 @@ macro_rules! impl_constructible {
|
||||||
impl<$t: IndirectlyConstructible> IndirectlyConstructible for $x {
|
impl<$t: IndirectlyConstructible> IndirectlyConstructible for $x {
|
||||||
type Error = $t::Error;
|
type Error = $t::Error;
|
||||||
|
|
||||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error> {
|
fn construct(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
let res = $y($t::construct(aero)?);
|
let res = $y($t::construct(aero)?);
|
||||||
<$t as IndirectlyConstructible>::after_construction(&res, aero)?;
|
<$t as IndirectlyConstructible>::after_construction(&res, aero)?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_construction(this: &(dyn Any + Send + Sync), aero: &Aerosol) -> Result<(), Self::Error> {
|
fn after_construction(this: &(dyn Any + Send + Sync), aero: &Aero) -> Result<(), Self::Error> {
|
||||||
<$t as IndirectlyConstructible>::after_construction(this, aero)
|
<$t as IndirectlyConstructible>::after_construction(this, aero)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,10 +86,33 @@ impl_constructible! {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implemented for resources which can be constructed from other resources.
|
/// Implemented for resources which can be constructed from other resources.
|
||||||
|
/// Do not implement this trait directly, instead implement `Constructible` and ensure
|
||||||
|
/// the remaining type bounds are met for the automatic implementation of `ConstructibleResource`.
|
||||||
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<R: HList> Aerosol<R> {
|
/// Automatically implemented for resource lists where every resource can be constructed.
|
||||||
|
pub trait ConstructibleResourceList: ResourceList {
|
||||||
|
/// Construct every resource in this list in the provided aerosol instance
|
||||||
|
fn construct<R: ResourceList>(aero: &Aero<R>) -> anyhow::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstructibleResourceList for HNil {
|
||||||
|
fn construct<R: ResourceList>(_aero: &Aero<R>) -> anyhow::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: ConstructibleResource, T: ConstructibleResourceList> ConstructibleResourceList
|
||||||
|
for HCons<H, T>
|
||||||
|
{
|
||||||
|
fn construct<R: ResourceList>(aero: &Aero<R>) -> anyhow::Result<()> {
|
||||||
|
aero.try_init::<H>().map_err(Into::into)?;
|
||||||
|
T::construct(aero)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: ResourceList> Aero<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() {
|
||||||
|
|
@ -136,12 +156,56 @@ impl<R: HList> Aerosol<R> {
|
||||||
pub fn init<T: ConstructibleResource>(&self) {
|
pub fn init<T: ConstructibleResource>(&self) {
|
||||||
unwrap_constructed::<T, _>(self.try_init::<T>())
|
unwrap_constructed::<T, _>(self.try_init::<T>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builder method equivalent to calling `try_init()` but can be chained.
|
||||||
|
pub fn try_with_constructed<T: ConstructibleResource>(
|
||||||
|
self,
|
||||||
|
) -> Result<Aero<HCons<T, R>>, T::Error> {
|
||||||
|
self.try_init::<T>()?;
|
||||||
|
Ok(Aero {
|
||||||
|
inner: self.inner,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builder method equivalent to calling `try_init()` but can be chained. Panics if construction fails.
|
||||||
|
pub fn with_constructed<T: ConstructibleResource>(self) -> Aero<HCons<T, R>> {
|
||||||
|
unwrap_constructed::<T, _>(self.try_with_constructed())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert into a different variant of the Aero type. Any missing required resources
|
||||||
|
/// will be automatically constructed.
|
||||||
|
pub fn try_construct_remaining<R2: ResourceList, I>(self) -> anyhow::Result<Aero<R2>>
|
||||||
|
where
|
||||||
|
R2: Sculptor<R, I>,
|
||||||
|
<R2 as Sculptor<R, I>>::Remainder: ConstructibleResourceList,
|
||||||
|
{
|
||||||
|
<<R2 as Sculptor<R, I>>::Remainder>::construct(&self)?;
|
||||||
|
Ok(Aero {
|
||||||
|
inner: self.inner,
|
||||||
|
phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert into a different variant of the Aero type. Any missing required resources
|
||||||
|
/// will be automatically constructed. Panics if construction of any missing resource fails.
|
||||||
|
pub fn construct_remaining<R2: ResourceList, I>(self) -> Aero<R2>
|
||||||
|
where
|
||||||
|
R2: Sculptor<R, I>,
|
||||||
|
<R2 as Sculptor<R, I>>::Remainder: ConstructibleResourceList,
|
||||||
|
{
|
||||||
|
unwrap_constructed_hlist::<<R2 as Sculptor<R, I>>::Remainder, _>(
|
||||||
|
self.try_construct_remaining(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{convert::Infallible, thread::scope, time::Duration};
|
use std::{convert::Infallible, thread::scope, time::Duration};
|
||||||
|
|
||||||
|
use crate::Aero;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -150,7 +214,7 @@ mod tests {
|
||||||
impl Constructible for Dummy {
|
impl Constructible for Dummy {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn construct(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
fn construct(_app_state: &Aero) -> Result<Self, Self::Error> {
|
||||||
std::thread::sleep(Duration::from_millis(100));
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -158,13 +222,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn obtain() {
|
fn obtain() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.obtain::<Dummy>();
|
state.obtain::<Dummy>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn obtain_race() {
|
fn obtain_race() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
scope(|s| {
|
scope(|s| {
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
s.spawn(|| state.obtain::<Dummy>());
|
s.spawn(|| state.obtain::<Dummy>());
|
||||||
|
|
@ -178,7 +242,7 @@ mod tests {
|
||||||
impl Constructible for DummyRecursive {
|
impl Constructible for DummyRecursive {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error> {
|
fn construct(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
aero.obtain::<Dummy>();
|
aero.obtain::<Dummy>();
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -186,13 +250,13 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn obtain_recursive() {
|
fn obtain_recursive() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.obtain::<DummyRecursive>();
|
state.obtain::<DummyRecursive>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn obtain_recursive_race() {
|
fn obtain_recursive_race() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
scope(|s| {
|
scope(|s| {
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
s.spawn(|| state.obtain::<DummyRecursive>());
|
s.spawn(|| state.obtain::<DummyRecursive>());
|
||||||
|
|
@ -206,7 +270,7 @@ mod tests {
|
||||||
impl Constructible for DummyCyclic {
|
impl Constructible for DummyCyclic {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn construct(aero: &Aerosol) -> Result<Self, Self::Error> {
|
fn construct(aero: &Aero) -> Result<Self, Self::Error> {
|
||||||
aero.obtain::<DummyCyclic>();
|
aero.obtain::<DummyCyclic>();
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -215,7 +279,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Cycle detected")]
|
#[should_panic(expected = "Cycle detected")]
|
||||||
fn obtain_cyclic() {
|
fn obtain_cyclic() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.obtain::<DummyCyclic>();
|
state.obtain::<DummyCyclic>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,7 +289,7 @@ mod tests {
|
||||||
impl Constructible for DummyNonClone {
|
impl Constructible for DummyNonClone {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn construct(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
fn construct(_app_state: &Aero) -> Result<Self, Self::Error> {
|
||||||
std::thread::sleep(Duration::from_millis(100));
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +297,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn obtain_non_clone() {
|
fn obtain_non_clone() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.obtain::<Arc<DummyNonClone>>();
|
state.obtain::<Arc<DummyNonClone>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,13 +311,13 @@ mod tests {
|
||||||
impl Constructible for DummyImpl {
|
impl Constructible for DummyImpl {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn construct(_app_state: &Aerosol) -> Result<Self, Self::Error> {
|
fn construct(_app_state: &Aero) -> Result<Self, Self::Error> {
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn after_construction(
|
fn after_construction(
|
||||||
this: &(dyn Any + Send + Sync),
|
this: &(dyn Any + Send + Sync),
|
||||||
aero: &Aerosol,
|
aero: &Aero,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
if let Some(arc) = this.downcast_ref::<Arc<Self>>() {
|
if let Some(arc) = this.downcast_ref::<Arc<Self>>() {
|
||||||
aero.insert(arc.clone() as Arc<dyn DummyTrait>)
|
aero.insert(arc.clone() as Arc<dyn DummyTrait>)
|
||||||
|
|
@ -264,8 +328,22 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn obtain_impl() {
|
fn obtain_impl() {
|
||||||
let state = Aerosol::new();
|
let state = Aero::new();
|
||||||
state.init::<Arc<DummyImpl>>();
|
state.init::<Arc<DummyImpl>>();
|
||||||
state.try_get::<Arc<dyn DummyTrait>>().unwrap();
|
state.try_get::<Arc<dyn DummyTrait>>().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_constructed() {
|
||||||
|
let state = Aero::new().with(42).with_constructed::<Dummy>().with("hi");
|
||||||
|
state.get::<Dummy, _>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn construct_remaining() {
|
||||||
|
let state: Aero![i32, Dummy, DummyRecursive, &str] =
|
||||||
|
Aero::new().with(42).with("hi").construct_remaining();
|
||||||
|
state.get::<Dummy, _>();
|
||||||
|
state.get::<DummyRecursive, _>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue