1use std::fmt::Debug;
19use std::sync::Arc;
20
21use http::Response;
22use http::StatusCode;
23use log::debug;
24
25use super::UPYUN_SCHEME;
26use super::config::UpyunConfig;
27use super::core::*;
28use super::deleter::UpyunDeleter;
29use super::error::parse_error;
30use super::lister::UpyunLister;
31use super::writer::UpyunWriter;
32use super::writer::UpyunWriters;
33use crate::raw::*;
34use crate::*;
35
36#[doc = include_str!("docs.md")]
38#[derive(Default)]
39pub struct UpyunBuilder {
40 pub(super) config: UpyunConfig,
41
42 #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` instead")]
43 pub(super) http_client: Option<HttpClient>,
44}
45
46impl Debug for UpyunBuilder {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 f.debug_struct("UpyunBuilder")
49 .field("config", &self.config)
50 .finish_non_exhaustive()
51 }
52}
53
54impl UpyunBuilder {
55 pub fn root(mut self, root: &str) -> Self {
59 self.config.root = if root.is_empty() {
60 None
61 } else {
62 Some(root.to_string())
63 };
64
65 self
66 }
67
68 pub fn bucket(mut self, bucket: &str) -> Self {
72 self.config.bucket = bucket.to_string();
73
74 self
75 }
76
77 pub fn operator(mut self, operator: &str) -> Self {
81 self.config.operator = if operator.is_empty() {
82 None
83 } else {
84 Some(operator.to_string())
85 };
86
87 self
88 }
89
90 pub fn password(mut self, password: &str) -> Self {
94 self.config.password = if password.is_empty() {
95 None
96 } else {
97 Some(password.to_string())
98 };
99
100 self
101 }
102
103 #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` instead")]
110 #[allow(deprecated)]
111 pub fn http_client(mut self, client: HttpClient) -> Self {
112 self.http_client = Some(client);
113 self
114 }
115}
116
117impl Builder for UpyunBuilder {
118 type Config = UpyunConfig;
119
120 fn build(self) -> Result<impl Access> {
122 debug!("backend build started: {:?}", &self);
123
124 let root = normalize_root(&self.config.root.clone().unwrap_or_default());
125 debug!("backend use root {}", &root);
126
127 if self.config.bucket.is_empty() {
129 return Err(Error::new(ErrorKind::ConfigInvalid, "bucket is empty")
130 .with_operation("Builder::build")
131 .with_context("service", UPYUN_SCHEME));
132 }
133
134 debug!("backend use bucket {}", &self.config.bucket);
135
136 let operator = match &self.config.operator {
137 Some(operator) => Ok(operator.clone()),
138 None => Err(Error::new(ErrorKind::ConfigInvalid, "operator is empty")
139 .with_operation("Builder::build")
140 .with_context("service", UPYUN_SCHEME)),
141 }?;
142
143 let password = match &self.config.password {
144 Some(password) => Ok(password.clone()),
145 None => Err(Error::new(ErrorKind::ConfigInvalid, "password is empty")
146 .with_operation("Builder::build")
147 .with_context("service", UPYUN_SCHEME)),
148 }?;
149
150 let signer = UpyunSigner {
151 operator: operator.clone(),
152 password: password.clone(),
153 };
154
155 Ok(UpyunBackend {
156 core: Arc::new(UpyunCore {
157 info: {
158 let am = AccessorInfo::default();
159 am.set_scheme(UPYUN_SCHEME)
160 .set_root(&root)
161 .set_native_capability(Capability {
162 stat: true,
163
164 create_dir: true,
165
166 read: true,
167
168 write: true,
169 write_can_empty: true,
170 write_can_multi: true,
171 write_with_cache_control: true,
172 write_with_content_type: true,
173
174 write_multi_min_size: Some(1024 * 1024),
176 write_multi_max_size: Some(50 * 1024 * 1024),
177
178 delete: true,
179 rename: true,
180 copy: true,
181
182 list: true,
183 list_with_limit: true,
184
185 shared: true,
186
187 ..Default::default()
188 });
189
190 #[allow(deprecated)]
192 if let Some(client) = self.http_client {
193 am.update_http_client(|_| client);
194 }
195
196 am.into()
197 },
198 root,
199 operator,
200 bucket: self.config.bucket.clone(),
201 signer,
202 }),
203 })
204 }
205}
206
207#[derive(Debug, Clone)]
209pub struct UpyunBackend {
210 core: Arc<UpyunCore>,
211}
212
213impl Access for UpyunBackend {
214 type Reader = HttpBody;
215 type Writer = UpyunWriters;
216 type Lister = oio::PageLister<UpyunLister>;
217 type Deleter = oio::OneShotDeleter<UpyunDeleter>;
218
219 fn info(&self) -> Arc<AccessorInfo> {
220 self.core.info.clone()
221 }
222
223 async fn create_dir(&self, path: &str, _: OpCreateDir) -> Result<RpCreateDir> {
224 let resp = self.core.create_dir(path).await?;
225
226 let status = resp.status();
227
228 match status {
229 StatusCode::OK => Ok(RpCreateDir::default()),
230 _ => Err(parse_error(resp)),
231 }
232 }
233
234 async fn stat(&self, path: &str, _args: OpStat) -> Result<RpStat> {
235 let resp = self.core.info(path).await?;
236
237 let status = resp.status();
238
239 match status {
240 StatusCode::OK => parse_info(resp.headers()).map(RpStat::new),
241 _ => Err(parse_error(resp)),
242 }
243 }
244
245 async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
246 let resp = self.core.download_file(path, args.range()).await?;
247
248 let status = resp.status();
249
250 match status {
251 StatusCode::OK | StatusCode::PARTIAL_CONTENT => {
252 Ok((RpRead::default(), resp.into_body()))
253 }
254 _ => {
255 let (part, mut body) = resp.into_parts();
256 let buf = body.to_buffer().await?;
257 Err(parse_error(Response::from_parts(part, buf)))
258 }
259 }
260 }
261
262 async fn write(&self, path: &str, args: OpWrite) -> Result<(RpWrite, Self::Writer)> {
263 let concurrent = args.concurrent();
264 let writer = UpyunWriter::new(self.core.clone(), args, path.to_string());
265
266 let w = oio::MultipartWriter::new(self.core.info.clone(), writer, concurrent);
267
268 Ok((RpWrite::default(), w))
269 }
270
271 async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
272 Ok((
273 RpDelete::default(),
274 oio::OneShotDeleter::new(UpyunDeleter::new(self.core.clone())),
275 ))
276 }
277
278 async fn list(&self, path: &str, args: OpList) -> Result<(RpList, Self::Lister)> {
279 let l = UpyunLister::new(self.core.clone(), path, args.limit());
280 Ok((RpList::default(), oio::PageLister::new(l)))
281 }
282
283 async fn copy(&self, from: &str, to: &str, _args: OpCopy) -> Result<RpCopy> {
284 let resp = self.core.copy(from, to).await?;
285
286 let status = resp.status();
287
288 match status {
289 StatusCode::OK => Ok(RpCopy::default()),
290 _ => Err(parse_error(resp)),
291 }
292 }
293
294 async fn rename(&self, from: &str, to: &str, _args: OpRename) -> Result<RpRename> {
295 let resp = self.core.move_object(from, to).await?;
296
297 let status = resp.status();
298
299 match status {
300 StatusCode::OK => Ok(RpRename::default()),
301 _ => Err(parse_error(resp)),
302 }
303 }
304}