use rocket::{Request, data::Outcome, http::Status, tokio::io::AsyncRead};
use crate::WebhookError;
pub mod built_in;
pub mod generic;
pub mod interface;
mod utils;
pub trait Webhook {
fn validate_body(
&self,
req: &Request<'_>,
body_reader: impl AsyncRead + Unpin + Send + Sync,
time_bounds: (u32, u32),
) -> impl Future<Output = Outcome<'_, Vec<u8>, WebhookError>> + Send + Sync;
fn validate_timestamp(
&self,
timestamp: &str,
(min, max): (u32, u32),
) -> Outcome<'_, (), WebhookError> {
let unix_timestamp = timestamp.parse::<u32>().ok();
match unix_timestamp.map(|t| t >= min && t <= max) {
Some(true) => Outcome::Success(()),
Some(false) | None => Outcome::Error((
Status::BadRequest,
WebhookError::Timestamp(timestamp.into()),
)),
}
}
fn get_header<'r>(
&self,
req: &'r Request<'_>,
name: &str,
prefix: Option<&str>,
) -> Outcome<'_, &'r str, WebhookError> {
let Some(mut header) = req.headers().get_one(name) else {
return Outcome::Error((Status::BadRequest, WebhookError::MissingHeader(name.into())));
};
if let Some(prefix) = prefix {
let Some(stripped) = header.strip_prefix(prefix) else {
return Outcome::Error((
Status::BadRequest,
WebhookError::InvalidHeader(format!(
"'{name}' is missing prefix '{prefix}': {header}"
)),
));
};
header = stripped;
}
Outcome::Success(header)
}
}