use std::{any::TypeId, collections::HashMap, sync::Arc};
use crate::{
callable::{BoxedCallable, CallableFetch},
platform::{self, PlatformHookRequest},
};
use super::{App, AppState};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HookRequestCaller {
Service,
}
#[derive(Debug)]
pub enum HookBody {
Json(String),
None,
}
#[allow(unused)]
pub struct HookRequest {
pub caller: HookRequestCaller,
pub method: String,
pub data: HookBody,
state: Arc<AppState>,
raw: platform::RawHookRequest,
}
#[derive(Debug, thiserror::Error)]
pub enum HookRequestError {
#[error("hook request init already consumed")]
InitConsumed,
}
impl HookRequest {
pub fn new(
state: Arc<AppState>,
raw: platform::RawHookRequest,
) -> Result<Self, HookRequestError> {
let init = raw.init()?;
Ok(Self {
caller: init.caller.into(),
data: init.data.into(),
method: init.method,
state,
raw,
})
}
pub fn respond(&self, body: HookBody) -> Result<(), platform::SendError> {
self.raw.respond(body)
}
}
pub struct HookContext {
pub app: App,
pub request: Arc<HookRequest>,
}
pub struct HookResponse {
pub body: HookBody,
}
pub struct HookRequestInit {
pub caller: HookRequestCaller,
pub method: String,
pub data: HookBody,
}
#[derive(Debug, thiserror::Error)]
pub enum HookError {
#[error("hook method `{0}` not found")]
MethodNotFound(String),
#[error("hook function in `{0}` error: {1}")]
FunctionError(String, anyhow::Error),
#[error("hook response serialization error: {0}")]
ResponseSerializationError(#[from] serde_json::Error),
#[error("hook response error: {0}")]
ResponseError(#[from] platform::SendError),
#[error("infalliable error: {0}")]
Infalliable(#[from] std::convert::Infallible),
}
#[allow(unused)]
pub struct HookFunction {
pub type_id: TypeId,
pub method: String,
pub callable: BoxedCallable<HookContext, HookResponse, HookError>,
}
#[derive(Default)]
pub struct HookStore {
hooks: HashMap<String, HookFunction>,
}
impl HookStore {
pub fn add_hook_function(&mut self, hook: HookFunction) {
self.hooks.insert(hook.method.clone(), hook);
}
pub async fn handle_hook_request(
&self,
app: App,
request: HookRequest,
) -> Result<(), HookError> {
let method = request.method.clone();
if let Some(hook) = self.hooks.get(&method) {
let request = Arc::new(request);
let context = HookContext {
app,
request: request.clone(),
};
let response = (hook.callable)(context)
.await
.map_err(|err| HookError::FunctionError(method.clone(), anyhow::anyhow!(err)))?;
request.respond(response.body)?;
return Ok(());
}
Err(HookError::MethodNotFound(method))
}
}
impl CallableFetch<App> for HookContext {
fn fetch(&self) -> App {
self.app.clone()
}
}