1use core::fmt;
2use std::{
3 marker::PhantomData,
4 ops,
5 pin::Pin,
6 task::{Context, Poll, ready},
7};
8
9use actix_web::{
10 FromRequest, HttpRequest, HttpResponse, Responder, ResponseError,
11 body::EitherBody,
12 http::{StatusCode, header::CONTENT_TYPE},
13 mime::{self, APPLICATION_JSON},
14 web::Bytes,
15};
16use facet::Facet;
17use facet_format::SerializeError;
18use facet_json::{DeserializeError, JsonSerializeError};
19
20#[derive(Debug, facet::Facet)]
21#[facet(transparent)]
22pub struct Json<T>(pub T);
23
24impl<T> Json<T> {
25 pub fn into_inner(self) -> T {
27 self.0
28 }
29}
30
31impl<T> ops::Deref for Json<T> {
32 type Target = T;
33
34 fn deref(&self) -> &T {
35 &self.0
36 }
37}
38
39impl<T> ops::DerefMut for Json<T> {
40 fn deref_mut(&mut self) -> &mut T {
41 &mut self.0
42 }
43}
44
45impl<T: fmt::Display> fmt::Display for Json<T> {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 fmt::Display::fmt(&self.0, f)
48 }
49}
50
51#[derive(Debug)]
52pub enum JsonRejection {
53 Body(actix_web::Error),
55 Deserialize(DeserializeError),
57 MissingContentType,
59 InvalidContentType,
61}
62
63impl fmt::Display for JsonRejection {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match self {
66 JsonRejection::Body(err) => {
67 write!(f, "Failed to read request body: {err}")
68 }
69 JsonRejection::Deserialize(err) => {
70 write!(f, "Failed to deserialize JSON: {err}")
71 }
72 JsonRejection::MissingContentType => {
73 write!(f, "Missing `Content-Type: application/json` header")
74 }
75 JsonRejection::InvalidContentType => {
76 write!(
77 f,
78 "Invalid `Content-Type` header: expected `application/json`"
79 )
80 }
81 }
82 }
83}
84
85impl ResponseError for JsonRejection {
86 fn status_code(&self) -> StatusCode {
87 match self {
88 JsonRejection::Body(_error) => StatusCode::BAD_REQUEST,
89 JsonRejection::Deserialize(_deserialize_error) => StatusCode::UNPROCESSABLE_ENTITY,
90 JsonRejection::MissingContentType | JsonRejection::InvalidContentType => {
91 StatusCode::UNSUPPORTED_MEDIA_TYPE
92 }
93 }
94 }
95}
96
97impl<T: Facet<'static>> actix_web::FromRequest for Json<T> {
98 type Error = JsonRejection;
99 type Future = JsonExtractFut<T>;
100
101 fn from_request(
102 req: &actix_web::HttpRequest,
103 payload: &mut actix_web::dev::Payload,
104 ) -> Self::Future {
105 JsonExtractFut {
106 req: Some(req.clone()),
107 bytes: Bytes::from_request(req, payload),
108 marker: PhantomData,
109 }
110 }
111}
112
113pub struct JsonExtractFut<T: Facet<'static>> {
114 req: Option<HttpRequest>,
115 bytes: <Bytes as FromRequest>::Future,
116 marker: PhantomData<T>,
117}
118
119impl<T: Facet<'static>> Unpin for JsonExtractFut<T> {}
120
121impl<T: Facet<'static>> Future for JsonExtractFut<T> {
122 type Output = Result<Json<T>, JsonRejection>;
123
124 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
125 let JsonExtractFut { req, bytes, .. } = self.get_mut();
126
127 if let Some(req) = req.take() {
128 match req.headers().get(CONTENT_TYPE) {
129 Some(ct) if ct != APPLICATION_JSON.as_ref() => {
130 Err(JsonRejection::InvalidContentType)?
131 }
132 Some(_) => (),
133 None => Err(JsonRejection::MissingContentType)?,
134 }
135 }
136
137 let fut = Pin::new(bytes);
138
139 let res = ready!(fut.poll(cx));
140
141 let res = match res {
142 Err(err) => Err(JsonRejection::Body(err)),
143 Ok(data) => match facet_json::from_slice::<T>(&data) {
144 Ok(data) => Ok(Json(data)),
145 Err(e) => Err(JsonRejection::Deserialize(e))?,
146 },
147 };
148
149 Poll::Ready(res)
150 }
151}
152
153impl<'a, T: Facet<'a>> Responder for Json<T> {
154 type Body = EitherBody<String>;
155
156 fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
157 match facet_json::to_string(&self.0) {
158 Ok(body) => match HttpResponse::Ok()
159 .content_type(mime::APPLICATION_JSON)
160 .message_body(body)
161 {
162 Ok(res) => res.map_into_left_body(),
163 Err(err) => HttpResponse::from_error(err).map_into_right_body(),
164 },
165
166 Err(err) => {
167 HttpResponse::from_error(SerializeErrorToActixError(err)).map_into_right_body()
168 }
169 }
170 }
171}
172
173#[derive(Debug)]
174struct SerializeErrorToActixError(pub SerializeError<JsonSerializeError>);
175
176impl fmt::Display for SerializeErrorToActixError {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 self.0.fmt(f)
179 }
180}
181
182impl actix_web::ResponseError for SerializeErrorToActixError {
183 fn status_code(&self) -> StatusCode {
184 StatusCode::INTERNAL_SERVER_ERROR
185 }
186}