cratestack_axum/codec/
set.rs1use axum::http::StatusCode;
2use axum::response::Response;
3use cratestack_core::{CoolCodec, CoolError, CoolErrorResponse};
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: CoolCodec,
29 Secondary: CoolCodec,
30{
31 fn decode_request<T>(&self, content_type: &str, body: &[u8]) -> Result<T, CoolError>
32 where
33 T: for<'de> Deserialize<'de>,
34 {
35 if content_type == Primary::CONTENT_TYPE {
36 self.primary.decode(body)
37 } else if content_type == Secondary::CONTENT_TYPE {
38 self.secondary.decode(body)
39 } else {
40 Err(CoolError::UnsupportedMediaType(format!(
41 "unsupported request Content-Type {content_type}"
42 )))
43 }
44 }
45
46 fn encode_response<T>(
47 &self,
48 content_type: &str,
49 status: StatusCode,
50 value: &T,
51 ) -> Result<Response, CoolError>
52 where
53 T: Serialize + ?Sized,
54 {
55 if content_type == Primary::CONTENT_TYPE {
56 encode_codec_response(&self.primary, status, value)
57 } else if content_type == Secondary::CONTENT_TYPE {
58 encode_codec_response(&self.secondary, status, value)
59 } else {
60 Err(CoolError::NotAcceptable(format!(
61 "no encoder configured for response Content-Type {content_type}"
62 )))
63 }
64 }
65
66 fn encode_sequence_response<T>(
67 &self,
68 content_type: &str,
69 status: StatusCode,
70 values: &[T],
71 ) -> Result<Response, CoolError>
72 where
73 T: Serialize,
74 {
75 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
76 if Primary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
77 encode_cbor_sequence_response(&self.primary, status, values)
78 } else if Secondary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
79 encode_cbor_sequence_response(&self.secondary, status, values)
80 } else {
81 Err(CoolError::NotAcceptable(
82 "router does not have a CBOR codec for cbor-seq responses".to_owned(),
83 ))
84 }
85 } else if content_type == Primary::CONTENT_TYPE || content_type == Secondary::CONTENT_TYPE {
86 self.encode_response(content_type, status, values)
87 } else {
88 Err(CoolError::NotAcceptable(format!(
89 "no encoder configured for response Content-Type {content_type}"
90 )))
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 if Primary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
102 encode_cbor_sequence_response(&self.primary, status, std::slice::from_ref(value))
103 } else if Secondary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
104 encode_cbor_sequence_response(&self.secondary, status, std::slice::from_ref(value))
105 } else {
106 Err(CoolError::NotAcceptable(
107 "router does not have a CBOR codec for cbor-seq responses".to_owned(),
108 ))
109 }
110 } else if content_type == Primary::CONTENT_TYPE || content_type == Secondary::CONTENT_TYPE {
111 self.encode_response(content_type, status, value)
112 } else {
113 Err(CoolError::NotAcceptable(format!(
114 "no encoder configured for response Content-Type {content_type}"
115 )))
116 }
117 }
118
119 fn encode_sequence_stream_response<T, S>(
120 &self,
121 content_type: &str,
122 status: StatusCode,
123 values: S,
124 ) -> Result<Response, CoolError>
125 where
126 T: Serialize + Send + 'static,
127 S: Stream<Item = Result<T, CoolError>> + Send + 'static,
128 {
129 if content_type != CBOR_SEQUENCE_CONTENT_TYPE {
130 return Err(CoolError::NotAcceptable(format!(
131 "incremental sequence streaming requires {CBOR_SEQUENCE_CONTENT_TYPE}, got \
132 response Content-Type {content_type}"
133 )));
134 }
135 if Primary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
136 encode_cbor_sequence_stream_response(self.primary.clone(), status, values)
137 } else if Secondary::CONTENT_TYPE == CborCodecMarker::CONTENT_TYPE {
138 encode_cbor_sequence_stream_response(self.secondary.clone(), status, values)
139 } else {
140 Err(CoolError::NotAcceptable(
141 "router does not have a CBOR codec for cbor-seq responses".to_owned(),
142 ))
143 }
144 }
145}