use std::fmt;
use tokio::runtime;
use crate::{FromContext, IntoContext, resource::ResourceId, service::ServiceContext};
#[allow(clippy::type_complexity)] pub(crate) struct WireFn(
pub Box<dyn FnOnce(&runtime::Handle, &mut ServiceContext<'_>) -> Result<(), WiringError>>,
);
impl fmt::Debug for WireFn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WireFn").finish()
}
}
#[async_trait::async_trait]
pub trait WiringLayer: 'static + Send + Sync {
type Input: FromContext;
type Output: IntoContext;
fn layer_name(&self) -> &'static str;
async fn wire(self, input: Self::Input) -> Result<Self::Output, WiringError>;
}
pub(crate) trait WiringLayerExt: WiringLayer {
fn into_wire_fn(self) -> WireFn
where
Self: Sized,
{
WireFn(Box::new(move |rt, ctx| {
let input = Self::Input::from_context(ctx)?;
let output = rt.block_on(self.wire(input))?;
output.into_context(ctx)?;
Ok(())
}))
}
}
impl<T> WiringLayerExt for T where T: WiringLayer {}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum WiringError {
#[error("Layer attempted to add resource {name}, but it is already provided")]
ResourceAlreadyProvided { id: ResourceId, name: String },
#[error("Resource {name} is not provided")]
ResourceLacking { id: ResourceId, name: String },
#[error("Wiring layer has been incorrectly configured: {0}")]
Configuration(String),
#[error(transparent)]
Internal(#[from] eyre::Report),
}
impl WiringError {
pub fn internal(err: impl Into<eyre::Report>) -> Self {
Self::Internal(err.into())
}
}