1use std::marker::PhantomData;
29
30use async_trait::async_trait;
31use bytes::Bytes;
32use http::Response;
33use http::StatusCode;
34use http_body_util::Full;
35use ranvier_core::prelude::*;
36use ranvier_core::transition::ResourceRequirement;
37use serde::de::DeserializeOwned;
38
39use crate::extract::HttpRequestBody;
40use crate::response::IntoResponse;
41
42pub struct JsonBody<T, R = ()> {
52 _phantom: PhantomData<(T, R)>,
53}
54
55impl<T, R> JsonBody<T, R> {
56 pub fn new() -> Self {
57 Self {
58 _phantom: PhantomData,
59 }
60 }
61}
62
63impl<T, R> Default for JsonBody<T, R> {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl<T, R> Clone for JsonBody<T, R> {
70 fn clone(&self) -> Self {
71 Self::new()
72 }
73}
74
75impl<T, R> std::fmt::Debug for JsonBody<T, R> {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 write!(f, "JsonBody<{}>", std::any::type_name::<T>())
78 }
79}
80
81#[derive(Debug, thiserror::Error)]
82pub enum JsonBodyError {
83 #[error("missing HttpRequestBody in bus — use .post_body()/.put_body()/.patch_body() for this route")]
84 MissingBody,
85 #[error("failed to parse JSON body: {0}")]
86 ParseError(String),
87}
88
89impl IntoResponse for JsonBodyError {
90 fn into_response(self) -> Response<Full<Bytes>> {
91 let status = match self {
92 Self::MissingBody => StatusCode::INTERNAL_SERVER_ERROR,
93 Self::ParseError(_) => StatusCode::BAD_REQUEST,
94 };
95 Response::builder()
96 .status(status)
97 .body(Full::new(Bytes::from(self.to_string())))
98 .unwrap()
99 }
100}
101
102#[async_trait]
103impl<T, R> Transition<(), T> for JsonBody<T, R>
104where
105 T: DeserializeOwned + Send + Sync + 'static,
106 R: ResourceRequirement + Clone + Send + Sync + 'static,
107{
108 type Error = JsonBodyError;
109 type Resources = R;
110
111 async fn run(&self, _input: (), _res: &R, bus: &mut Bus) -> Outcome<T, JsonBodyError> {
112 let bytes = match bus.read::<HttpRequestBody>() {
113 Some(body) => body.0.clone(),
114 None => return Outcome::Fault(JsonBodyError::MissingBody),
115 };
116
117 match serde_json::from_slice::<T>(&bytes) {
118 Ok(value) => Outcome::Next(value),
119 Err(e) => Outcome::Fault(JsonBodyError::ParseError(e.to_string())),
120 }
121 }
122}