cratestack_axum/transport/
encode_sequence.rs1use axum::http::{HeaderMap, StatusCode};
2use axum::response::Response;
3use cratestack_core::{CratestackError, RouteTransportCapabilities};
4use futures_util::{Stream, TryStreamExt};
5use serde::Serialize;
6
7use super::CBOR_SEQUENCE_CONTENT_TYPE;
8use super::http_transport::HttpTransport;
9use super::internal::fallback_error_response;
10use super::media_type::select_transport_response_content_type;
11
12pub fn encode_transport_sequence_result<TTransport, TValue>(
13 transport: &TTransport,
14 headers: &HeaderMap,
15 result: Result<Vec<TValue>, CratestackError>,
16) -> Response
17where
18 TTransport: HttpTransport,
19 TValue: Serialize,
20{
21 encode_transport_sequence_result_with_status_for(
22 transport,
23 headers,
24 &RouteTransportCapabilities {
25 request_types: &[],
26 response_types: &[],
27 default_response_type: "",
28 supports_sequence_response: false,
29 },
30 StatusCode::OK,
31 result,
32 )
33}
34
35pub fn encode_transport_sequence_result_with_status<TTransport, TValue>(
36 transport: &TTransport,
37 headers: &HeaderMap,
38 success_status: StatusCode,
39 result: Result<Vec<TValue>, CratestackError>,
40) -> Response
41where
42 TTransport: HttpTransport,
43 TValue: Serialize,
44{
45 encode_transport_sequence_result_with_status_for(
46 transport,
47 headers,
48 &RouteTransportCapabilities {
49 request_types: &[],
50 response_types: &[],
51 default_response_type: "",
52 supports_sequence_response: false,
53 },
54 success_status,
55 result,
56 )
57}
58
59pub fn encode_transport_sequence_result_with_status_for<TTransport, TValue>(
60 transport: &TTransport,
61 headers: &HeaderMap,
62 capabilities: &RouteTransportCapabilities,
63 success_status: StatusCode,
64 result: Result<Vec<TValue>, CratestackError>,
65) -> Response
66where
67 TTransport: HttpTransport,
68 TValue: Serialize,
69{
70 if !capabilities.supports_sequence_response {
71 return fallback_error_response(CratestackError::Internal(
72 "sequence response encoding requested for a route without sequence capability"
73 .to_owned(),
74 ));
75 }
76 let content_type =
77 match select_transport_response_content_type(transport, headers, capabilities) {
78 Ok(content_type) => content_type,
79 Err(error) => return fallback_error_response(error),
80 };
81 match result {
82 Ok(values) => transport
83 .encode_sequence_response(content_type, success_status, &values)
84 .unwrap_or_else(fallback_error_response),
85 Err(error) => {
86 let (error, tag) = crate::idempotency::answered(error);
87 let status = error.status_code();
88 let challenges = crate::auth_challenge::challenges_of(&error);
89 let body = error.into_response();
90 let response = transport
91 .encode_sequence_error_response(content_type, status, &body)
92 .unwrap_or_else(fallback_error_response);
93 let response = crate::auth_challenge::attach(response, &challenges);
94 crate::idempotency::with_tag(tag, response)
95 }
96 }
97}
98
99pub async fn encode_transport_stream_result_with_status_for<TTransport, TValue, S>(
119 transport: &TTransport,
120 headers: &HeaderMap,
121 capabilities: &RouteTransportCapabilities,
122 success_status: StatusCode,
123 result: Result<S, CratestackError>,
124) -> Response
125where
126 TTransport: HttpTransport,
127 TValue: Serialize + Send + 'static,
128 S: Stream<Item = Result<TValue, CratestackError>> + Send + 'static,
129{
130 if !capabilities.supports_sequence_response {
131 return fallback_error_response(CratestackError::Internal(
132 "sequence response encoding requested for a route without sequence capability"
133 .to_owned(),
134 ));
135 }
136 let content_type =
137 match select_transport_response_content_type(transport, headers, capabilities) {
138 Ok(content_type) => content_type,
139 Err(error) => return fallback_error_response(error),
140 };
141 match result {
142 Ok(stream) => {
143 if content_type == CBOR_SEQUENCE_CONTENT_TYPE {
144 transport
145 .encode_sequence_stream_response(content_type, success_status, stream)
146 .unwrap_or_else(fallback_error_response)
147 } else {
148 let values: Result<Vec<TValue>, CratestackError> = stream.try_collect().await;
149 encode_transport_sequence_result_with_status_for(
150 transport,
151 headers,
152 capabilities,
153 success_status,
154 values,
155 )
156 }
157 }
158 Err(error) => {
159 let (error, tag) = crate::idempotency::answered(error);
160 let status = error.status_code();
161 let challenges = crate::auth_challenge::challenges_of(&error);
162 let body = error.into_response();
163 let response = transport
164 .encode_sequence_error_response(content_type, status, &body)
165 .unwrap_or_else(fallback_error_response);
166 let response = crate::auth_challenge::attach(response, &challenges);
167 crate::idempotency::with_tag(tag, response)
168 }
169 }
170}
171
172#[cfg(test)]
173mod tests;