use hyper::HeaderMap;
use rhai::{AST, Dynamic, Engine, Map, Scope, serde::to_dynamic};
use serde_json::Value;
use std::{path::Path, sync::Arc};
use crate::core::{
error::{AppError, AppResult},
server::{middleware::middleware_response::MiddlewareResponse, types::BoxBody},
};
#[derive(Clone)]
pub struct MiddlewareHandler {
pub engine: Arc<Engine>,
pub file_path: String,
pub ast: AST,
}
impl MiddlewareHandler {
pub fn new(file_path: &str) -> AppResult<Self> {
let path = Path::new(file_path);
if !path.exists() {
return Err(AppError::MiddlewareMissing {
path: path.to_path_buf(),
});
}
let engine = Engine::new();
let ast = engine
.compile_file(file_path.into())
.map_err(|e| AppError::MiddlewareCompile {
path: path.to_path_buf(),
reason: e.to_string(),
})?;
Ok(MiddlewareHandler {
engine: Arc::new(engine),
file_path: file_path.to_owned(),
ast,
})
}
pub async fn handle(
&self,
request_url_path: &str,
request_body_json_value: Option<&Value>,
request_headers: &HeaderMap,
) -> Option<Result<hyper::Response<BoxBody>, hyper::http::Error>> {
let mut scope = Scope::new();
scope.push("url_path", request_url_path.to_owned());
if let Some(request_body_json_value) = request_body_json_value {
match to_dynamic(request_body_json_value) {
Ok(body_dynamic) => {
scope.push("body", body_dynamic);
}
Err(err) => {
log::warn!(
"middleware `{}`: failed to convert request body to Rhai Dynamic: {}",
self.file_path,
err
);
return None;
}
}
}
let rhai_response = match self
.engine
.eval_ast_with_scope::<Dynamic>(&mut scope, &self.ast)
{
Ok(v) => v,
Err(err) => {
log::warn!(
"middleware `{}`: script evaluation failed: {}",
self.file_path,
err
);
return None;
}
};
if !rhai_response.is_string() && !rhai_response.is_map() {
return None;
}
let middleware_response = MiddlewareResponse::new(self.file_path.as_str(), request_headers);
if let Some(x) = rhai_response.clone().try_cast::<String>() {
middleware_response.file_response(x.as_str()).await
} else if let Some(x) = rhai_response.try_cast::<Map>() {
if let Some(x) = x
.get("file_path")
.and_then(|x| x.clone().try_cast::<String>())
{
middleware_response.file_response(x.as_str()).await
} else if let Some(x) = x.get("json").and_then(|x| x.clone().try_cast::<String>()) {
middleware_response.json_response(x.as_str())
} else if let Some(x) = x.get("text").and_then(|x| x.clone().try_cast::<String>()) {
middleware_response.text_response(x.as_str())
} else {
None
}
} else {
None
}
}
}