use std::sync::Arc;
use nest_rs_core::injectable;
use nest_rs_guards::{Guard, denial_to_http_response};
use nest_rs_mcp::{BoxFuture, McpOperationGuard};
use poem::http::StatusCode;
use poem::{Error, Request, Response, Result};
use sea_orm::EntityTrait;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::{Ability, ActionMarker, with_ability};
#[injectable]
pub struct McpAbilityBridge<A: Guard, G: Guard> {
#[inject]
auth: Arc<A>,
#[inject]
ability: Arc<G>,
}
impl<A: Guard, G: Guard> McpOperationGuard for McpAbilityBridge<A, G> {
fn before<'a>(&'a self, req: &'a mut Request) -> BoxFuture<'a, Result<()>> {
Box::pin(async move {
if self.auth.check_http(req).await.is_err() {
return Err(Error::from_response(
Response::builder()
.status(StatusCode::UNAUTHORIZED)
.body("Unauthorized"),
));
}
self.ability
.check_http(req)
.await
.map_err(|denial| Error::from_response(denial_to_http_response(denial)))
})
}
}
pub async fn with_request_ability<F>(req: &Request, inner: F) -> Response
where
F: std::future::Future<Output = Response>,
{
match req.extensions().get::<Arc<Ability>>().cloned() {
Some(ability) => with_ability(ability, inner).await,
None => inner.await,
}
}
pub fn masked_output<A, E, O>(model: &E::Model) -> Result<O, serde_json::Error>
where
A: ActionMarker,
E: EntityTrait,
E::Model: Serialize,
O: DeserializeOwned,
{
crate::masked_output_ambient::<A, E, O>(model)
}