1use std::fmt::Debug;
19use std::sync::Arc;
20
21use http::StatusCode;
22use log::debug;
23use opendal_core::raw::*;
24use opendal_core::*;
25
26use super::UPYUN_SCHEME;
27use super::config::UpyunConfig;
28use super::core::parse_error;
29use super::core::*;
30use super::deleter::UpyunDeleter;
31use super::lister::UpyunLister;
32use super::reader::*;
33use super::writer::UpyunWriter;
34use super::writer::UpyunWriters;
35
36#[doc = include_str!("docs.md")]
38#[derive(Default)]
39pub struct UpyunBuilder {
40 pub(super) config: UpyunConfig,
41}
42
43impl Debug for UpyunBuilder {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.debug_struct("UpyunBuilder")
46 .field("config", &self.config)
47 .finish_non_exhaustive()
48 }
49}
50
51impl UpyunBuilder {
52 pub fn root(mut self, root: &str) -> Self {
56 self.config.root = if root.is_empty() {
57 None
58 } else {
59 Some(root.to_string())
60 };
61
62 self
63 }
64
65 pub fn bucket(mut self, bucket: &str) -> Self {
69 self.config.bucket = bucket.to_string();
70
71 self
72 }
73
74 pub fn operator(mut self, operator: &str) -> Self {
78 self.config.operator = if operator.is_empty() {
79 None
80 } else {
81 Some(operator.to_string())
82 };
83
84 self
85 }
86
87 pub fn password(mut self, password: &str) -> Self {
91 self.config.password = if password.is_empty() {
92 None
93 } else {
94 Some(password.to_string())
95 };
96
97 self
98 }
99}
100
101impl Builder for UpyunBuilder {
102 type Config = UpyunConfig;
103
104 fn build(self) -> Result<impl Service> {
106 debug!("backend build started: {:?}", self);
107
108 let root = normalize_root(&self.config.root.clone().unwrap_or_default());
109 debug!("backend use root {}", root);
110
111 if self.config.bucket.is_empty() {
113 return Err(Error::new(ErrorKind::ConfigInvalid, "bucket is empty")
114 .with_operation("Builder::build")
115 .with_context("service", UPYUN_SCHEME));
116 }
117
118 debug!("backend use bucket {}", self.config.bucket);
119
120 let operator = match &self.config.operator {
121 Some(operator) => Ok(operator.clone()),
122 None => Err(Error::new(ErrorKind::ConfigInvalid, "operator is empty")
123 .with_operation("Builder::build")
124 .with_context("service", UPYUN_SCHEME)),
125 }?;
126
127 let password = match &self.config.password {
128 Some(password) => Ok(password.clone()),
129 None => Err(Error::new(ErrorKind::ConfigInvalid, "password is empty")
130 .with_operation("Builder::build")
131 .with_context("service", UPYUN_SCHEME)),
132 }?;
133
134 let signer = UpyunSigner {
135 operator: operator.clone(),
136 password: password.clone(),
137 };
138
139 Ok(UpyunBackend {
140 core: Arc::new(UpyunCore {
141 info: ServiceInfo::new(UPYUN_SCHEME, &root, ""),
142 capability: Capability {
143 stat: true,
144
145 create_dir: true,
146
147 read: true,
148 read_with_suffix: true,
149
150 write: true,
151 write_can_empty: true,
152 write_can_multi: true,
153 write_with_cache_control: true,
154 write_with_content_type: true,
155
156 write_multi_min_size: Some(1024 * 1024),
158 write_multi_max_size: Some(50 * 1024 * 1024),
159
160 delete: true,
161 rename: true,
162 copy: true,
163
164 list: true,
165 list_with_limit: true,
166
167 shared: true,
168
169 ..Default::default()
170 },
171 root,
172 operator,
173 bucket: self.config.bucket.clone(),
174 signer,
175 }),
176 })
177 }
178}
179
180#[derive(Debug, Clone)]
182pub struct UpyunBackend {
183 pub(crate) core: Arc<UpyunCore>,
184}
185
186impl Service for UpyunBackend {
187 type Reader = oio::StreamReader<UpyunReader>;
188 type Writer = UpyunWriters;
189 type Lister = oio::PageLister<UpyunLister>;
190 type Deleter = oio::OneShotDeleter<UpyunDeleter>;
191 type Copier = oio::OneShotCopier;
192
193 fn info(&self) -> ServiceInfo {
194 self.core.info.clone()
195 }
196
197 fn capability(&self) -> Capability {
198 self.core.capability
199 }
200
201 async fn create_dir(
202 &self,
203 ctx: &OperationContext,
204 path: &str,
205 _: OpCreateDir,
206 ) -> Result<RpCreateDir> {
207 let resp = self.core.create_dir(ctx, path).await?;
208
209 let status = resp.status();
210
211 match status {
212 StatusCode::OK => Ok(RpCreateDir::default()),
213 _ => Err(parse_error(resp)),
214 }
215 }
216
217 async fn stat(&self, ctx: &OperationContext, path: &str, _args: OpStat) -> Result<RpStat> {
218 let resp = self.core.info(ctx, path).await?;
219
220 let status = resp.status();
221
222 match status {
223 StatusCode::OK => parse_info(resp.headers()).map(RpStat::new),
224 _ => Err(parse_error(resp)),
225 }
226 }
227 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
228 let output: oio::StreamReader<UpyunReader> = {
229 Ok(oio::StreamReader::new(UpyunReader::new(
230 self.clone(),
231 ctx.clone(),
232 path,
233 args,
234 )))
235 }?;
236
237 Ok(output)
238 }
239
240 fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
241 let output: UpyunWriters = {
242 let concurrent = args.concurrent();
243 let writer = UpyunWriter::new(self.core.clone(), ctx.clone(), args, path.to_string());
244
245 let w = oio::MultipartWriter::new(ctx.executor().clone(), writer, concurrent);
246
247 Ok(w)
248 }?;
249
250 Ok(output)
251 }
252
253 fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
254 let output: oio::OneShotDeleter<UpyunDeleter> = {
255 Ok(oio::OneShotDeleter::new(UpyunDeleter::new(
256 self.core.clone(),
257 ctx.clone(),
258 )))
259 }?;
260
261 Ok(output)
262 }
263
264 fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister> {
265 let output: oio::PageLister<UpyunLister> = {
266 let l = UpyunLister::new(self.core.clone(), ctx.clone(), path, args.limit());
267 Ok(oio::PageLister::new(l))
268 }?;
269
270 Ok(output)
271 }
272
273 fn copy(
274 &self,
275 ctx: &OperationContext,
276 from: &str,
277 to: &str,
278 _args: OpCopy,
279 _opts: OpCopier,
280 ) -> Result<Self::Copier> {
281 let core = self.core.clone();
282 let ctx = ctx.clone();
283 let from = from.to_string();
284 let to = to.to_string();
285
286 Ok(oio::OneShotCopier::new(async move {
287 let resp = core.copy(&ctx, &from, &to).await?;
288 let status = resp.status();
289
290 match status {
291 StatusCode::OK => Ok(Metadata::default()),
292 _ => Err(parse_error(resp)),
293 }
294 }))
295 }
296
297 async fn rename(
298 &self,
299 ctx: &OperationContext,
300 from: &str,
301 to: &str,
302 _args: OpRename,
303 ) -> Result<RpRename> {
304 let resp = self.core.move_object(ctx, from, to).await?;
305
306 let status = resp.status();
307
308 match status {
309 StatusCode::OK => Ok(RpRename::default()),
310 _ => Err(parse_error(resp)),
311 }
312 }
313
314 async fn presign(
315 &self,
316 _ctx: &OperationContext,
317 _path: &str,
318 _args: OpPresign,
319 ) -> Result<RpPresign> {
320 Err(Error::new(
321 ErrorKind::Unsupported,
322 "operation is not supported",
323 ))
324 }
325}