Skip to main content

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}