cratestack_axum/transport/
http_transport.rs1use axum::http::StatusCode;
2use axum::response::Response;
3use cratestack_core::{CratestackCodec, CratestackError, CratestackErrorResponse};
4use futures_util::Stream;
5use serde::{Deserialize, Serialize};
6
7use crate::codec::encode_codec_response;
8
9use super::CBOR_SEQUENCE_CONTENT_TYPE;
10use super::internal::encode_cbor_sequence_response;
11use super::media_type::media_type_matches;
12use super::stream_sequence::encode_cbor_sequence_stream_response;
13
14pub trait HttpTransport: Clone + Send + Sync + 'static {
15 fn can_encode(&self, _content_type: &str) -> bool {
47 true
48 }
49
50 fn decode_request<T>(&self, content_type: &str, body: &[u8]) -> Result<T, CratestackError>
51 where
52 T: for<'de> Deserialize<'de>;
53
54 fn encode_response<T>(
55 &self,
56 content_type: &str,
57 status: StatusCode,
58 value: &T,
59 ) -> Result<Response, CratestackError>
60 where
61 T: Serialize + ?Sized;
62
63 fn encode_sequence_response<T>(
64 &self,
65 content_type: &str,
66 status: StatusCode,
67 values: &[T],
68 ) -> Result<Response, CratestackError>
69 where
70 T: Serialize;
71
72 fn encode_sequence_error_response(
73 &self,
74 content_type: &str,
75 status: StatusCode,
76 value: &CratestackErrorResponse,
77 ) -> Result<Response, CratestackError>;
78
79 fn encode_sequence_stream_response<T, S>(
89 &self,
90 content_type: &str,
91 status: StatusCode,
92 values: S,
93 ) -> Result<Response, CratestackError>
94 where
95 T: Serialize + Send + 'static,
96 S: Stream<Item = Result<T, CratestackError>> + Send + 'static;
97}
98
99impl<C> HttpTransport for C
100where
101 C: CratestackCodec,
102{
103 fn can_encode(&self, content_type: &str) -> bool {
104 media_type_matches(content_type, C::CONTENT_TYPE)
105 || (content_type == CBOR_SEQUENCE_CONTENT_TYPE
106 && C::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE)
107 }
108
109 fn decode_request<T>(&self, content_type: &str, body: &[u8]) -> Result<T, CratestackError>
110 where
111 T: for<'de> Deserialize<'de>,
112 {
113 if media_type_matches(content_type, C::CONTENT_TYPE) {
114 crate::codec::decode_codec_request(self, body)
115 } else {
116 Err(CratestackError::UnsupportedMediaType(format!(
117 "unsupported request Content-Type {content_type}"
118 )))
119 }
120 }
121
122 fn encode_response<T>(
123 &self,
124 content_type: &str,
125 status: StatusCode,
126 value: &T,
127 ) -> Result<Response, CratestackError>
128 where
129 T: Serialize + ?Sized,
130 {
131 if media_type_matches(content_type, C::CONTENT_TYPE) {
132 encode_codec_response(self, status, value)
133 } else {
134 Err(CratestackError::NotAcceptable(format!(
135 "no encoder configured for response Content-Type {content_type}"
136 )))
137 }
138 }
139
140 fn encode_sequence_response<T>(
141 &self,
142 content_type: &str,
143 status: StatusCode,
144 values: &[T],
145 ) -> Result<Response, CratestackError>
146 where
147 T: Serialize,
148 {
149 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
150 encode_cbor_sequence_response(self, status, values)
151 } else {
152 self.encode_response(content_type, status, values)
153 }
154 }
155
156 fn encode_sequence_error_response(
157 &self,
158 content_type: &str,
159 status: StatusCode,
160 value: &CratestackErrorResponse,
161 ) -> Result<Response, CratestackError> {
162 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
163 encode_cbor_sequence_response(self, status, std::slice::from_ref(value))
164 } else {
165 self.encode_response(content_type, status, value)
166 }
167 }
168
169 fn encode_sequence_stream_response<T, S>(
170 &self,
171 content_type: &str,
172 status: StatusCode,
173 values: S,
174 ) -> Result<Response, CratestackError>
175 where
176 T: Serialize + Send + 'static,
177 S: Stream<Item = Result<T, CratestackError>> + Send + 'static,
178 {
179 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
180 encode_cbor_sequence_stream_response(self.clone(), status, values)
181 } else {
182 Err(CratestackError::NotAcceptable(format!(
183 "incremental sequence streaming requires {CBOR_SEQUENCE_CONTENT_TYPE}, got \
184 response Content-Type {content_type}"
185 )))
186 }
187 }
188}
189
190pub(crate) struct CborCodecMarker;
191
192impl CborCodecMarker {
193 pub(crate) const CONTENT_TYPE: &'static str = "application/cbor";
194}