cratestack_axum/transport/
http_transport.rs1use axum::http::StatusCode;
2use axum::response::Response;
3use cratestack_core::{CoolCodec, CoolError, CoolErrorResponse};
4use serde::{Deserialize, Serialize};
5
6use crate::codec::encode_codec_response;
7
8use super::CBOR_SEQUENCE_CONTENT_TYPE;
9use super::internal::encode_cbor_sequence_response;
10use super::media_type::media_type_matches;
11
12pub trait HttpTransport: Clone + Send + Sync + 'static {
13 fn decode_request<T>(&self, content_type: &str, body: &[u8]) -> Result<T, CoolError>
14 where
15 T: for<'de> Deserialize<'de>;
16
17 fn encode_response<T>(
18 &self,
19 content_type: &str,
20 status: StatusCode,
21 value: &T,
22 ) -> Result<Response, CoolError>
23 where
24 T: Serialize + ?Sized;
25
26 fn encode_sequence_response<T>(
27 &self,
28 content_type: &str,
29 status: StatusCode,
30 values: &[T],
31 ) -> Result<Response, CoolError>
32 where
33 T: Serialize;
34
35 fn encode_sequence_error_response(
36 &self,
37 content_type: &str,
38 status: StatusCode,
39 value: &CoolErrorResponse,
40 ) -> Result<Response, CoolError>;
41}
42
43impl<C> HttpTransport for C
44where
45 C: CoolCodec,
46{
47 fn decode_request<T>(&self, content_type: &str, body: &[u8]) -> Result<T, CoolError>
48 where
49 T: for<'de> Deserialize<'de>,
50 {
51 if media_type_matches(content_type, C::CONTENT_TYPE) {
52 crate::codec::decode_codec_request(self, body)
53 } else {
54 Err(CoolError::UnsupportedMediaType(format!(
55 "unsupported request Content-Type {content_type}"
56 )))
57 }
58 }
59
60 fn encode_response<T>(
61 &self,
62 content_type: &str,
63 status: StatusCode,
64 value: &T,
65 ) -> Result<Response, CoolError>
66 where
67 T: Serialize + ?Sized,
68 {
69 if media_type_matches(content_type, C::CONTENT_TYPE) {
70 encode_codec_response(self, status, value)
71 } else {
72 Err(CoolError::NotAcceptable(format!(
73 "no encoder configured for response Content-Type {content_type}"
74 )))
75 }
76 }
77
78 fn encode_sequence_response<T>(
79 &self,
80 content_type: &str,
81 status: StatusCode,
82 values: &[T],
83 ) -> Result<Response, CoolError>
84 where
85 T: Serialize,
86 {
87 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
88 encode_cbor_sequence_response(self, status, values)
89 } else {
90 self.encode_response(content_type, status, values)
91 }
92 }
93
94 fn encode_sequence_error_response(
95 &self,
96 content_type: &str,
97 status: StatusCode,
98 value: &CoolErrorResponse,
99 ) -> Result<Response, CoolError> {
100 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
101 encode_cbor_sequence_response(self, status, std::slice::from_ref(value))
102 } else {
103 self.encode_response(content_type, status, value)
104 }
105 }
106}
107
108pub(crate) struct CborCodecMarker;
109
110impl CborCodecMarker {
111 pub(crate) const CONTENT_TYPE: &'static str = "application/cbor";
112}