izihawa_ipfs_api_prelude/
global_opts.rs1use crate::{request::ApiRequest, Backend, BoxStream};
10use async_trait::async_trait;
11use bytes::Bytes;
12use common_multipart_rfc7578::client::multipart;
13use serde::{Serialize, Serializer};
14use std::time::Duration;
15
16#[cfg_attr(feature = "with-builder", derive(TypedBuilder))]
21#[derive(Serialize, Default)]
22#[serde(rename_all = "kebab-case")]
23pub struct GlobalOptions {
24 #[cfg_attr(feature = "with-builder", builder(default, setter(strip_option)))]
25 pub offline: Option<bool>,
26
27 #[cfg_attr(feature = "with-builder", builder(default, setter(strip_option)))]
28 #[serde(serialize_with = "duration_as_secs_ns")]
29 pub timeout: Option<Duration>,
30}
31
32fn duration_as_secs_ns<S>(duration: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
33where
34 S: Serializer,
35{
36 match duration {
37 Some(duration) => serializer.serialize_str(&format!(
38 "{}s{}ns",
39 duration.as_secs(),
40 duration.subsec_nanos(),
41 )),
42 None => serializer.serialize_none(),
43 }
44}
45
46pub struct BackendWithGlobalOptions<Back: Backend> {
48 backend: Back,
49 options: GlobalOptions,
50}
51
52#[derive(Serialize)]
53struct OptCombiner<'a, Req>
54where
55 Req: ApiRequest,
56{
57 #[serde(flatten)]
58 global: &'a GlobalOptions,
59
60 #[serde(flatten)]
61 request: Req,
62}
63
64impl<Back: Backend> BackendWithGlobalOptions<Back> {
65 pub fn into_inner(self) -> Back {
67 self.backend
68 }
69
70 pub fn new(backend: Back, options: GlobalOptions) -> Self {
74 Self { backend, options }
75 }
76
77 fn combine<Req>(&self, req: Req) -> OptCombiner<Req>
78 where
79 Req: ApiRequest,
80 {
81 OptCombiner {
82 global: &self.options,
83 request: req,
84 }
85 }
86}
87
88impl<'a, Req> ApiRequest for OptCombiner<'a, Req>
89where
90 Req: ApiRequest,
91{
92 const PATH: &'static str = <Req as ApiRequest>::PATH;
93
94 const METHOD: http::Method = http::Method::POST;
95}
96
97#[cfg(feature = "with-send-sync")]
98#[async_trait]
99impl<Back: Backend + Send + Sync> Backend for BackendWithGlobalOptions<Back> {
100 type HttpRequest = Back::HttpRequest;
101
102 type HttpResponse = Back::HttpResponse;
103
104 type Error = Back::Error;
105
106 fn build_base_request<Req>(
107 &self,
108 req: Req,
109 form: Option<multipart::Form<'static>>,
110 ) -> Result<Self::HttpRequest, Self::Error>
111 where
112 Req: ApiRequest,
113 {
114 self.backend.build_base_request(self.combine(req), form)
115 }
116
117 fn get_header(
118 res: &Self::HttpResponse,
119 key: http::header::HeaderName,
120 ) -> Option<&http::HeaderValue> {
121 Back::get_header(res, key)
122 }
123
124 async fn request_raw<Req>(
125 &self,
126 req: Req,
127 form: Option<multipart::Form<'static>>,
128 ) -> Result<(http::StatusCode, bytes::Bytes), Self::Error>
129 where
130 Req: ApiRequest,
131 {
132 self.backend.request_raw(self.combine(req), form).await
133 }
134
135 fn response_to_byte_stream(res: Self::HttpResponse) -> BoxStream<Bytes, Self::Error> {
136 Back::response_to_byte_stream(res)
137 }
138
139 fn request_stream<Res, F>(
140 &self,
141 req: Self::HttpRequest,
142 process: F,
143 ) -> BoxStream<Res, Self::Error>
144 where
145 F: 'static + Send + Fn(Self::HttpResponse) -> BoxStream<Res, Self::Error>,
146 {
147 self.backend.request_stream(req, process)
148 }
149}
150
151#[cfg(not(feature = "with-send-sync"))]
152#[async_trait(?Send)]
153impl<Back: Backend> Backend for BackendWithGlobalOptions<Back> {
154 type HttpRequest = Back::HttpRequest;
155
156 type HttpResponse = Back::HttpResponse;
157
158 type Error = Back::Error;
159
160 fn build_base_request<Req>(
161 &self,
162 req: Req,
163 form: Option<multipart::Form<'static>>,
164 ) -> Result<Self::HttpRequest, Self::Error>
165 where
166 Req: ApiRequest,
167 {
168 self.backend.build_base_request(self.combine(req), form)
169 }
170
171 fn get_header(
172 res: &Self::HttpResponse,
173 key: http::header::HeaderName,
174 ) -> Option<&http::HeaderValue> {
175 Back::get_header(res, key)
176 }
177
178 async fn request_raw<Req>(
179 &self,
180 req: Req,
181 form: Option<multipart::Form<'static>>,
182 ) -> Result<(http::StatusCode, bytes::Bytes), Self::Error>
183 where
184 Req: ApiRequest,
185 {
186 self.backend.request_raw(self.combine(req), form).await
187 }
188
189 fn response_to_byte_stream(res: Self::HttpResponse) -> BoxStream<Bytes, Self::Error> {
190 Back::response_to_byte_stream(res)
191 }
192
193 fn request_stream<Res, F>(
194 &self,
195 req: Self::HttpRequest,
196 process: F,
197 ) -> BoxStream<Res, Self::Error>
198 where
199 F: 'static + Send + Fn(Self::HttpResponse) -> BoxStream<Res, Self::Error>,
200 {
201 self.backend.request_stream(req, process)
202 }
203}