1use std::sync::Arc;
19
20use bytes::Buf;
21use http::StatusCode;
22
23use super::core::*;
24use super::deleter::DropboxDeleter;
25use super::lister::DropboxLister;
26use super::reader::*;
27use super::writer::DropboxWriter;
28use opendal_core::raw::*;
29use opendal_core::*;
30
31use std::fmt::Debug;
32
33use mea::mutex::Mutex;
34
35use super::DROPBOX_SCHEME;
36use super::config::DropboxConfig;
37use super::core::DropboxCore;
38use super::core::DropboxSigner;
39
40#[doc = include_str!("docs.md")]
42#[derive(Default)]
43pub struct DropboxBuilder {
44 pub(super) config: DropboxConfig,
45}
46
47impl Debug for DropboxBuilder {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("Builder")
50 .field("config", &self.config)
51 .finish_non_exhaustive()
52 }
53}
54
55impl DropboxBuilder {
56 pub fn root(mut self, root: &str) -> Self {
60 self.config.root = if root.is_empty() {
61 None
62 } else {
63 Some(root.to_string())
64 };
65
66 self
67 }
68
69 pub fn access_token(mut self, access_token: &str) -> Self {
76 self.config.access_token = Some(access_token.to_string());
77 self
78 }
79
80 pub fn refresh_token(mut self, refresh_token: &str) -> Self {
86 self.config.refresh_token = Some(refresh_token.to_string());
87 self
88 }
89
90 pub fn client_id(mut self, client_id: &str) -> Self {
94 self.config.client_id = Some(client_id.to_string());
95 self
96 }
97
98 pub fn client_secret(mut self, client_secret: &str) -> Self {
102 self.config.client_secret = Some(client_secret.to_string());
103 self
104 }
105}
106
107impl Builder for DropboxBuilder {
108 type Config = DropboxConfig;
109
110 fn build(self) -> Result<impl Service> {
111 let root = normalize_root(&self.config.root.unwrap_or_default());
112
113 let signer = match (self.config.access_token, self.config.refresh_token) {
114 (Some(access_token), None) => DropboxSigner {
115 access_token,
116 expires_in: Timestamp::MAX,
118 ..Default::default()
119 },
120 (None, Some(refresh_token)) => {
121 let client_id = self.config.client_id.ok_or_else(|| {
122 Error::new(
123 ErrorKind::ConfigInvalid,
124 "client_id must be set when refresh_token is set",
125 )
126 .with_context("service", DROPBOX_SCHEME)
127 })?;
128 let client_secret = self.config.client_secret.ok_or_else(|| {
129 Error::new(
130 ErrorKind::ConfigInvalid,
131 "client_secret must be set when refresh_token is set",
132 )
133 .with_context("service", DROPBOX_SCHEME)
134 })?;
135
136 DropboxSigner {
137 refresh_token,
138 client_id,
139 client_secret,
140 ..Default::default()
141 }
142 }
143 (Some(_), Some(_)) => {
144 return Err(Error::new(
145 ErrorKind::ConfigInvalid,
146 "access_token and refresh_token can not be set at the same time",
147 )
148 .with_context("service", DROPBOX_SCHEME));
149 }
150 (None, None) => {
151 return Err(Error::new(
152 ErrorKind::ConfigInvalid,
153 "access_token or refresh_token must be set",
154 )
155 .with_context("service", DROPBOX_SCHEME));
156 }
157 };
158
159 Ok(DropboxBackend {
160 core: Arc::new(DropboxCore {
161 info: ServiceInfo::new(DROPBOX_SCHEME, &root, ""),
162 capability: Capability {
163 stat: true,
164
165 read: true,
166 read_with_suffix: true,
167
168 write: true,
169
170 create_dir: true,
171
172 delete: true,
173
174 list: true,
175 list_with_recursive: true,
176
177 copy: true,
178
179 rename: true,
180
181 shared: true,
182
183 ..Default::default()
184 },
185 root,
186 signer: Arc::new(Mutex::new(signer)),
187 }),
188 })
189 }
190}
191
192#[derive(Clone, Debug)]
193pub struct DropboxBackend {
194 pub core: Arc<DropboxCore>,
195}
196
197impl Service for DropboxBackend {
198 type Reader = oio::StreamReader<DropboxReader>;
199 type Writer = oio::OneShotWriter<DropboxWriter>;
200 type Lister = oio::PageLister<DropboxLister>;
201 type Deleter = oio::OneShotDeleter<DropboxDeleter>;
202 type Copier = oio::OneShotCopier;
203
204 fn info(&self) -> ServiceInfo {
205 self.core.info.clone()
206 }
207
208 fn capability(&self) -> Capability {
209 self.core.capability
210 }
211
212 async fn create_dir(
213 &self,
214 ctx: &OperationContext,
215 path: &str,
216 _args: OpCreateDir,
217 ) -> Result<RpCreateDir> {
218 let resp = self.core.dropbox_get_metadata(ctx, path).await?;
220 if StatusCode::OK == resp.status() {
221 let bytes = resp.into_body();
222 let decoded_response: DropboxMetadataResponse =
223 serde_json::from_reader(bytes.reader()).map_err(new_json_deserialize_error)?;
224 if "folder" == decoded_response.tag {
225 return Ok(RpCreateDir::default());
226 }
227 if "file" == decoded_response.tag {
228 return Err(Error::new(
229 ErrorKind::NotADirectory,
230 format!("it's not a directory {path}"),
231 ));
232 }
233 }
234
235 let res = self.core.dropbox_create_folder(ctx, path).await?;
236 Ok(res)
237 }
238
239 async fn stat(&self, ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
240 let resp = self.core.dropbox_get_metadata(ctx, path).await?;
241 let status = resp.status();
242 match status {
243 StatusCode::OK => {
244 let bytes = resp.into_body();
245 let decoded_response: DropboxMetadataResponse =
246 serde_json::from_reader(bytes.reader()).map_err(new_json_deserialize_error)?;
247 let entry_mode: EntryMode = match decoded_response.tag.as_str() {
248 "file" => EntryMode::FILE,
249 "folder" => EntryMode::DIR,
250 _ => EntryMode::Unknown,
251 };
252
253 let mut metadata = Metadata::new(entry_mode);
254 if entry_mode == EntryMode::FILE {
258 let date_utc_last_modified =
259 decoded_response.client_modified.parse::<Timestamp>()?;
260 metadata.set_last_modified(date_utc_last_modified);
261
262 if let Some(size) = decoded_response.size {
263 metadata.set_content_length(size);
264 } else {
265 return Err(Error::new(
266 ErrorKind::Unexpected,
267 format!("no size found for file {path}"),
268 ));
269 }
270 }
271 Ok(RpStat::new(metadata))
272 }
273 _ => Err(parse_error(resp)),
274 }
275 }
276 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
277 let output: oio::StreamReader<DropboxReader> = {
278 Ok(oio::StreamReader::new(DropboxReader::new(
279 self.clone(),
280 ctx.clone(),
281 path,
282 args,
283 )))
284 }?;
285
286 Ok(output)
287 }
288
289 fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
290 let output: oio::OneShotWriter<DropboxWriter> = {
291 Ok(oio::OneShotWriter::new(DropboxWriter::new(
292 self.core.clone(),
293 ctx.clone(),
294 args,
295 String::from(path),
296 )))
297 }?;
298
299 Ok(output)
300 }
301
302 fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
303 let output: oio::OneShotDeleter<DropboxDeleter> = {
304 Ok(oio::OneShotDeleter::new(DropboxDeleter::new(
305 self.core.clone(),
306 ctx.clone(),
307 )))
308 }?;
309
310 Ok(output)
311 }
312
313 fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister> {
314 let output: oio::PageLister<DropboxLister> = {
315 Ok(oio::PageLister::new(DropboxLister::new(
316 self.core.clone(),
317 ctx.clone(),
318 path.to_string(),
319 args.recursive(),
320 args.limit(),
321 )))
322 }?;
323
324 Ok(output)
325 }
326
327 fn copy(
328 &self,
329 ctx: &OperationContext,
330 from: &str,
331 to: &str,
332 _: OpCopy,
333 _opts: OpCopier,
334 ) -> Result<Self::Copier> {
335 let core = self.core.clone();
336 let ctx = ctx.clone();
337 let from = from.to_string();
338 let to = to.to_string();
339
340 Ok(oio::OneShotCopier::new(async move {
341 let resp = core.dropbox_copy(&ctx, &from, &to).await?;
342 let status = resp.status();
343
344 match status {
345 StatusCode::OK => Ok(Metadata::default()),
346 _ => {
347 let err = parse_error(resp);
348 match err.kind() {
349 ErrorKind::NotFound => Ok(Metadata::default()),
350 _ => Err(err),
351 }
352 }
353 }
354 }))
355 }
356
357 async fn rename(
358 &self,
359 ctx: &OperationContext,
360 from: &str,
361 to: &str,
362 _: OpRename,
363 ) -> Result<RpRename> {
364 let resp = self.core.dropbox_move(ctx, from, to).await?;
365
366 let status = resp.status();
367
368 match status {
369 StatusCode::OK => Ok(RpRename::default()),
370 _ => {
371 let err = parse_error(resp);
372 match err.kind() {
373 ErrorKind::NotFound => Ok(RpRename::default()),
374 _ => Err(err),
375 }
376 }
377 }
378 }
379
380 async fn presign(
381 &self,
382 _ctx: &OperationContext,
383 _path: &str,
384 _args: OpPresign,
385 ) -> Result<RpPresign> {
386 Err(Error::new(
387 ErrorKind::Unsupported,
388 "operation is not supported",
389 ))
390 }
391}