cratestack_axum/codec/
set.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::transport::{
8 CBOR_SEQUENCE_CONTENT_TYPE, CborCodecMarker, HttpTransport, encode_cbor_sequence_response,
9 encode_cbor_sequence_stream_response,
10};
11
12use super::encode::encode_codec_response;
13
14#[derive(Debug, Clone)]
15pub struct CodecSet<Primary, Secondary> {
16 pub(super) primary: Primary,
17 pub(super) secondary: Secondary,
18}
19
20impl<Primary, Secondary> CodecSet<Primary, Secondary> {
21 pub fn new(primary: Primary, secondary: Secondary) -> Self {
22 Self { primary, secondary }
23 }
24}
25
26impl<Primary, Secondary> HttpTransport for CodecSet<Primary, Secondary>
27where
28 Primary: CratestackCodec,
29 Secondary: CratestackCodec,
30{
31 fn can_encode(&self, content_type: &str) -> bool {
32 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
33 Primary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE
34 || Secondary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE
35 } else {
36 content_type == Primary::CONTENT_TYPE || content_type == Secondary::CONTENT_TYPE
37 }
38 }
39
40 fn decode_request<T>(&self, content_type: &str, body: &[u8]) -> Result<T, CratestackError>
41 where
42 T: for<'de> Deserialize<'de>,
43 {
44 if content_type == Primary::CONTENT_TYPE {
45 self.primary.decode(body)
46 } else if content_type == Secondary::CONTENT_TYPE {
47 self.secondary.decode(body)
48 } else {
49 Err(CratestackError::UnsupportedMediaType(format!(
50 "unsupported request Content-Type {content_type}"
51 )))
52 }
53 }
54
55 fn encode_response<T>(
56 &self,
57 content_type: &str,
58 status: StatusCode,
59 value: &T,
60 ) -> Result<Response, CratestackError>
61 where
62 T: Serialize + ?Sized,
63 {
64 if content_type == Primary::CONTENT_TYPE {
65 encode_codec_response(&self.primary, status, value)
66 } else if content_type == Secondary::CONTENT_TYPE {
67 encode_codec_response(&self.secondary, status, value)
68 } else {
69 Err(CratestackError::NotAcceptable(format!(
70 "no encoder configured for response Content-Type {content_type}"
71 )))
72 }
73 }
74
75 fn encode_sequence_response<T>(
76 &self,
77 content_type: &str,
78 status: StatusCode,
79 values: &[T],
80 ) -> Result<Response, CratestackError>
81 where
82 T: Serialize,
83 {
84 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
85 if Primary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
86 encode_cbor_sequence_response(&self.primary, status, values)
87 } else if Secondary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
88 encode_cbor_sequence_response(&self.secondary, status, values)
89 } else {
90 Err(CratestackError::NotAcceptable(
91 "router does not have a CBOR codec for cbor-seq responses".to_owned(),
92 ))
93 }
94 } else if content_type == Primary::CONTENT_TYPE || content_type == Secondary::CONTENT_TYPE {
95 self.encode_response(content_type, status, values)
96 } else {
97 Err(CratestackError::NotAcceptable(format!(
98 "no encoder configured for response Content-Type {content_type}"
99 )))
100 }
101 }
102
103 fn encode_sequence_error_response(
104 &self,
105 content_type: &str,
106 status: StatusCode,
107 value: &CratestackErrorResponse,
108 ) -> Result<Response, CratestackError> {
109 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
110 if Primary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
111 encode_cbor_sequence_response(&self.primary, status, std::slice::from_ref(value))
112 } else if Secondary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
113 encode_cbor_sequence_response(&self.secondary, status, std::slice::from_ref(value))
114 } else {
115 Err(CratestackError::NotAcceptable(
116 "router does not have a CBOR codec for cbor-seq responses".to_owned(),
117 ))
118 }
119 } else if content_type == Primary::CONTENT_TYPE || content_type == Secondary::CONTENT_TYPE {
120 self.encode_response(content_type, status, value)
121 } else {
122 Err(CratestackError::NotAcceptable(format!(
123 "no encoder configured for response Content-Type {content_type}"
124 )))
125 }
126 }
127
128 fn encode_sequence_stream_response<T, S>(
129 &self,
130 content_type: &str,
131 status: StatusCode,
132 values: S,
133 ) -> Result<Response, CratestackError>
134 where
135 T: Serialize + Send + 'static,
136 S: Stream<Item = Result<T, CratestackError>> + Send + 'static,
137 {
138 if content_type != CBOR_SEQUENCE_CONTENT_TYPE {
139 return Err(CratestackError::NotAcceptable(format!(
140 "incremental sequence streaming requires {CBOR_SEQUENCE_CONTENT_TYPE}, got \
141 response Content-Type {content_type}"
142 )));
143 }
144 if Primary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
145 encode_cbor_sequence_stream_response(self.primary.clone(), status, values)
146 } else if Secondary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
147 encode_cbor_sequence_stream_response(self.secondary.clone(), status, values)
148 } else {
149 Err(CratestackError::NotAcceptable(
150 "router does not have a CBOR codec for cbor-seq responses".to_owned(),
151 ))
152 }
153 }
154}
155
156#[cfg(test)]
157mod tests;