use std::{
future::{Ready, ready},
ops::Deref,
};
use ::actix_web::{
Error as ActixError, FromRequest, HttpRequest, HttpResponse, ResponseError,
dev::Payload,
http::StatusCode,
web::{self, ServiceConfig},
};
use crate::{Error, Injector, runtime::Shared};
#[derive(Clone)]
pub struct InjectorState {
injector: Shared<Injector>,
}
impl InjectorState {
pub fn new(injector: Shared<Injector>) -> Self {
Self { injector }
}
pub fn injector(&self) -> Shared<Injector> {
self.injector.clone()
}
}
pub fn injector_data(injector: Shared<Injector>) -> web::Data<InjectorState> {
web::Data::new(InjectorState::new(injector))
}
pub trait ServiceConfigExt {
fn with_injector(&mut self, injector: Shared<Injector>) -> &mut Self;
}
impl ServiceConfigExt for ServiceConfig {
fn with_injector(&mut self, injector: Shared<Injector>) -> &mut Self {
self.app_data(injector_data(injector))
}
}
#[derive(Debug)]
pub struct ResolveRejection {
pub status: StatusCode,
pub message: String,
}
impl ResolveRejection {
fn internal(message: String) -> Self {
Self {
status: StatusCode::INTERNAL_SERVER_ERROR,
message,
}
}
}
impl From<Error> for ResolveRejection {
fn from(value: Error) -> Self {
Self::internal(value.to_string())
}
}
impl std::fmt::Display for ResolveRejection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl ResponseError for ResolveRejection {
fn status_code(&self) -> StatusCode {
self.status
}
fn error_response(&self) -> HttpResponse {
HttpResponse::build(self.status).body(self.message.clone())
}
}
pub struct Resolved<T: ?Sized + Send + Sync + 'static>(pub Shared<T>);
impl<T: ?Sized + Send + Sync + 'static> Resolved<T> {
pub fn into_shared(self) -> Shared<T> {
self.0
}
}
impl<T: ?Sized + Send + Sync + 'static> Deref for Resolved<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl<T: ?Sized + Send + Sync + 'static> From<Resolved<T>> for Shared<T> {
fn from(value: Resolved<T>) -> Self {
value.0
}
}
impl<T> FromRequest for Resolved<T>
where
T: ?Sized + Send + Sync + 'static,
{
type Error = ActixError;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
let result = req
.app_data::<web::Data<InjectorState>>()
.cloned()
.ok_or_else(|| {
ResolveRejection::internal(
"InjectorState is not configured in app_data".to_string(),
)
})
.and_then(|state| {
state
.injector()
.try_resolve::<T>()
.map_err(ResolveRejection::from)
})
.map(Self)
.map_err(ActixError::from);
ready(result)
}
}