Skip to main content

cratestack_axum/codec/
set.rs

1use 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}