cratestack_axum/codec/
set.rs1use 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}