kamu_snap_response_actix/lib.rs
1//! `actix-web::Responder` adapter for [`kamu_snap_response::SnapResponse`].
2//!
3//! Add this crate to enable `actix_web::Responder` on `SnapResponse<T>`. The
4//! impl is defensive: if `responseCode` cannot be parsed back into an HTTP
5//! status (malformed wire code), the response falls back to
6//! `INTERNAL_SERVER_ERROR` instead of panicking. Closes review F-02.
7
8#![forbid(unsafe_code)]
9
10use actix_web::{HttpRequest, HttpResponse, HttpResponseBuilder, Responder, body::BoxBody};
11use kamu_snap_response::SnapResponse;
12use serde::Serialize;
13
14/// Newtype wrapping `SnapResponse<T>` for the actix `Responder` impl.
15///
16/// This indirection is required by Rust's orphan rule — neither
17/// `kamu-snap-response` nor `actix-web` is owned by this crate. Convert via
18/// `SnapResponderExt::into_actix` (or wrap by hand: `ActixResponder(resp)`).
19pub struct ActixResponder<T>(pub SnapResponse<T>);
20
21impl<T: Serialize> Responder for ActixResponder<T> {
22 type Body = BoxBody;
23
24 fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
25 let status = self.0.envelope.response_code.http().unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR);
26 // actix::StatusCode and http::StatusCode share the underlying type;
27 // re-wrap so the builder is satisfied across actix-web versions.
28 let actix_status = actix_web::http::StatusCode::from_u16(status.as_u16())
29 .unwrap_or(actix_web::http::StatusCode::INTERNAL_SERVER_ERROR);
30 HttpResponseBuilder::new(actix_status).json(self.0)
31 }
32}
33
34/// Extension trait providing `.into_actix()` for ergonomic conversion.
35pub trait SnapResponderExt<T> {
36 /// Wrap into [`ActixResponder`] for use as an actix handler return value.
37 fn into_actix(self) -> ActixResponder<T>;
38}
39
40impl<T> SnapResponderExt<T> for SnapResponse<T> {
41 fn into_actix(self) -> ActixResponder<T> {
42 ActixResponder(self)
43 }
44}