Skip to main content

cratestack_axum/codec/
set.rs

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