use std::sync::Arc;
use async_graphql::Data;
use autumn_web::{AppState, AutumnResult};
use http::request::Parts;
use http::{HeaderMap, Method, Uri};
use crate::persisted::BoxFuture;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Transport {
HttpPost,
HttpGet,
WebSocket,
Sse,
}
impl Transport {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::HttpPost => "http_post",
Self::HttpGet => "http_get",
Self::WebSocket => "websocket",
Self::Sse => "sse",
}
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct GraphqlRequestInfo {
pub method: Method,
pub uri: Uri,
pub headers: HeaderMap,
pub request_id: Option<String>,
pub transport: Transport,
pub endpoint: Arc<str>,
}
impl GraphqlRequestInfo {
pub(crate) fn from_parts(parts: &Parts, transport: Transport, endpoint: &Arc<str>) -> Self {
let request_id = parts
.extensions
.get::<autumn_web::middleware::RequestId>()
.map(ToString::to_string)
.or_else(|| {
parts
.headers
.get("x-request-id")
.and_then(|v| v.to_str().ok())
.map(str::to_owned)
});
Self {
method: parts.method.clone(),
uri: parts.uri.clone(),
headers: parts.headers.clone(),
request_id,
transport,
endpoint: Arc::clone(endpoint),
}
}
}
pub trait ContextHook: Send + Sync + 'static {
fn extend<'a>(
&'a self,
parts: &'a mut Parts,
state: &'a AppState,
data: &'a mut Data,
) -> BoxFuture<'a, AutumnResult<()>>;
}
impl<F> ContextHook for F
where
F: for<'a> Fn(&'a mut Parts, &'a AppState, &'a mut Data) -> BoxFuture<'a, AutumnResult<()>>
+ Send
+ Sync
+ 'static,
{
fn extend<'a>(
&'a self,
parts: &'a mut Parts,
state: &'a AppState,
data: &'a mut Data,
) -> BoxFuture<'a, AutumnResult<()>> {
self(parts, state, data)
}
}
pub trait WsInitHook: Send + Sync + 'static {
fn on_init(
&self,
payload: serde_json::Value,
state: AppState,
) -> BoxFuture<'static, AutumnResult<Data>>;
}
impl<F, Fut> WsInitHook for F
where
F: Fn(serde_json::Value, AppState) -> Fut + Send + Sync + 'static,
Fut: std::future::Future<Output = AutumnResult<Data>> + Send + 'static,
{
fn on_init(
&self,
payload: serde_json::Value,
state: AppState,
) -> BoxFuture<'static, AutumnResult<Data>> {
Box::pin(self(payload, state))
}
}
pub(crate) async fn run_hooks(
hooks: &[Arc<dyn ContextHook>],
parts: &mut Parts,
state: &AppState,
data: &mut Data,
) -> AutumnResult<()> {
for hook in hooks {
hook.extend(parts, state, data).await?;
}
Ok(())
}