1use rocket::{http::Status, State};
2use sms_core::{Headers, InboundRegistry};
3use sms_web_generic::{ResponseConverter, WebhookProcessor};
4
5#[derive(Clone)]
6pub struct AppState {
7 pub registry: InboundRegistry,
8}
9
10#[derive(Debug)]
12pub struct RawBody(pub Vec<u8>);
13
14#[rocket::async_trait]
15impl<'r> rocket::data::FromData<'r> for RawBody {
16 type Error = Box<dyn std::error::Error + Send + Sync>;
17
18 async fn from_data(
19 _req: &'r rocket::Request<'_>,
20 data: rocket::Data<'r>,
21 ) -> rocket::data::Outcome<'r, Self> {
22 use rocket::data::ToByteUnit;
23
24 match data.open(2.megabytes()).into_bytes().await {
25 Ok(bytes) if bytes.is_complete() => {
26 rocket::data::Outcome::Success(RawBody(bytes.into_inner()))
27 }
28 Ok(_) => rocket::data::Outcome::Error((
29 Status::PayloadTooLarge,
30 Box::new(std::io::Error::other("Body too large")),
31 )),
32 Err(e) => rocket::data::Outcome::Error((Status::BadRequest, Box::new(e))),
33 }
34 }
35}
36
37pub struct RocketResponseConverter;
39
40impl ResponseConverter for RocketResponseConverter {
41 type ResponseType = (Status, (rocket::http::ContentType, String));
42
43 fn from_webhook_response(response: sms_core::WebhookResponse) -> Self::ResponseType {
44 let status = match response.status.as_u16() {
45 200 => Status::Ok,
46 400 => Status::BadRequest,
47 401 => Status::Unauthorized,
48 404 => Status::NotFound,
49 _ => Status::InternalServerError,
50 };
51
52 let content_type = match response.content_type.as_str() {
53 "application/json" => rocket::http::ContentType::JSON,
54 _ => rocket::http::ContentType::Plain,
55 };
56
57 (status, (content_type, response.body))
58 }
59}
60
61#[rocket::post("/webhooks/<provider>", data = "<body>")]
66pub fn unified_webhook(
67 provider: String,
68 body: RawBody,
69 state: &State<AppState>,
70) -> (Status, (rocket::http::ContentType, String)) {
71 let processor = WebhookProcessor::new(state.registry.clone());
72 let headers: Headers = vec![];
75 let response = processor.process_webhook(&provider, headers, &body.0);
76 RocketResponseConverter::from_webhook_response(response)
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn rocket_types_compile() {
85 let registry = InboundRegistry::new();
86 let _state = AppState { registry };
87 }
90}