Skip to main content

opendal_core/raw/
accessor.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::fmt::Debug;
19use std::future::Future;
20use std::sync::Arc;
21
22use crate::raw::*;
23use crate::*;
24
25/// Immutable identity and configuration for a storage service.
26///
27/// `ServiceInfo` excludes runtime resources and composed capabilities so that
28/// layers can replace them without mutating the shared service identity.
29#[derive(Clone, PartialEq, Eq, Hash)]
30pub struct ServiceInfo {
31    scheme: &'static str,
32    root: Arc<str>,
33    name: Arc<str>,
34}
35
36impl Debug for ServiceInfo {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        f.debug_struct("ServiceInfo")
39            .field("scheme", &self.scheme())
40            .field("root", &self.root())
41            .field("name", &self.name())
42            .finish_non_exhaustive()
43    }
44}
45
46impl ServiceInfo {
47    /// Create a new `ServiceInfo`.
48    pub fn new(scheme: &'static str, root: impl AsRef<str>, name: impl AsRef<str>) -> Self {
49        Self {
50            scheme,
51            root: Arc::from(root.as_ref()),
52            name: Arc::from(name.as_ref()),
53        }
54    }
55
56    /// Create a new `ServiceInfo` with only scheme.
57    pub fn with_scheme(scheme: &'static str) -> Self {
58        Self::new(scheme, "", "")
59    }
60
61    /// Return a copy of this `ServiceInfo` with a different root.
62    pub fn with_root(&self, root: impl AsRef<str>) -> Self {
63        Self {
64            scheme: self.scheme,
65            root: Arc::from(root.as_ref()),
66            name: self.name.clone(),
67        }
68    }
69
70    /// Scheme of the service.
71    pub fn scheme(&self) -> &'static str {
72        self.scheme
73    }
74
75    /// Root of the service. Follows a format like `/path/to/dir/`.
76    pub fn root(&self) -> Arc<str> {
77        self.root.clone()
78    }
79
80    /// Name of the service. This might be empty if the service has no namespace concept.
81    ///
82    /// For example:
83    ///
84    /// - `s3` => bucket name
85    /// - `azblob` => container name
86    /// - `azdfs` => filesystem name
87    /// - `azfile` => share name
88    pub fn name(&self) -> Arc<str> {
89        self.name.clone()
90    }
91}
92
93/// Foundational trait for storage services.
94///
95/// Every storage service (or backend) in OpenDAL implements [`Service`]. Services
96/// and layers must implement every operation in this trait and declare their
97/// capabilities. This allows callers to detect unsupported operations.
98///
99/// # Operations
100///
101/// - An operator normalizes paths before passing them to a `Service`. Relative to
102///   the configured `root`:
103///   - `/` represents the root.
104///   - A path ending with `/` represents a directory.
105///   - Any other path represents a file.
106/// - Services report their supported operation set through [`Service::capability`].
107/// - The [`OperationContext`] carries layer-composed runtime resources for each
108///   operation.
109pub trait Service: Send + Sync + Debug + Unpin + 'static {
110    /// Reader returned by `read`.
111    type Reader: oio::Read;
112    /// Writer returned by `write`.
113    type Writer: oio::Write;
114    /// Lister returned by `list`.
115    type Lister: oio::List;
116    /// Deleter returned by `delete`.
117    type Deleter: oio::Delete;
118    /// Copier returned by `copy`.
119    type Copier: oio::Copy;
120    /// Composer returned by `compose`.
121    type Composer: oio::Compose;
122
123    /// Return the immutable identity and configuration for this service.
124    fn info(&self) -> ServiceInfo;
125
126    /// Return the capability of this service stack.
127    ///
128    /// Layers may affect a service's capabilities, so callers should use this
129    /// value for the current stack instead of assuming the backend's native
130    /// capability.
131    fn capability(&self) -> Capability;
132
133    /// Invoke the `create` operation on the specified path.
134    ///
135    /// Requires [`Capability::create_dir`].
136    ///
137    /// # Behavior
138    ///
139    /// - `path` is a normalized directory path.
140    /// - Creating an existing directory should succeed.
141    fn create_dir(
142        &self,
143        ctx: &OperationContext,
144        path: &str,
145        args: OpCreateDir,
146    ) -> impl Future<Output = Result<RpCreateDir>> + MaybeSend;
147
148    /// Invoke the `stat` operation on the specified path.
149    ///
150    /// Requires [`Capability::stat`].
151    ///
152    /// # Behavior
153    ///
154    /// - `/` means the service root.
155    /// - A path ending with `/` stats a directory.
156    /// - Returned metadata must set `mode` and `content_length`.
157    fn stat(
158        &self,
159        ctx: &OperationContext,
160        path: &str,
161        args: OpStat,
162    ) -> impl Future<Output = Result<RpStat>> + MaybeSend;
163
164    /// Invoke the `read` operation on the specified path.
165    ///
166    /// Requires [`Capability::read`].
167    ///
168    /// # Behavior
169    ///
170    /// - `path` is a normalized file path.
171    /// - Range I/O is handled by the returned reader.
172    fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader>;
173
174    /// Invoke the `write` operation on the specified path.
175    ///
176    /// Requires [`Capability::write`].
177    ///
178    /// # Behavior
179    ///
180    /// - `path` is a normalized file path.
181    fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer>;
182
183    /// Invoke the `delete` operation.
184    ///
185    /// Requires [`Capability::delete`].
186    ///
187    /// # Behavior
188    ///
189    /// - The returned deleter handles one or more delete requests.
190    /// - Deleting a missing path should succeed unless a condition requires a live target.
191    fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter>;
192
193    /// Invoke the `list` operation on the specified path.
194    ///
195    /// Requires [`Capability::list`].
196    ///
197    /// # Behavior
198    ///
199    /// - `path` is a normalized directory path or prefix.
200    /// - Listing a non-existing directory should return an empty stream.
201    fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister>;
202
203    /// Invoke the `copy` operation on the specified `from` path and `to` path.
204    ///
205    /// Requires [`Capability::copy`].
206    ///
207    /// # Behavior
208    ///
209    /// - `from` and `to` are normalized file paths.
210    /// - Copying to an existing file should overwrite and truncate it.
211    fn copy(
212        &self,
213        ctx: &OperationContext,
214        from: &str,
215        to: &str,
216        args: OpCopy,
217    ) -> Result<Self::Copier>;
218
219    /// Invoke the `compose` operation for the specified destination path.
220    ///
221    /// Requires [`Capability::compose`].
222    fn compose(
223        &self,
224        _ctx: &OperationContext,
225        _to: &str,
226        _args: OpCompose,
227    ) -> Result<Self::Composer> {
228        Err(Error::new(
229            ErrorKind::Unsupported,
230            "operation is not supported",
231        ))
232    }
233
234    /// Invoke the `rename` operation on the specified `from` path and `to` path.
235    ///
236    /// Requires [`Capability::rename`].
237    ///
238    /// # Behavior
239    ///
240    /// - `from` and `to` are normalized file paths.
241    fn rename(
242        &self,
243        ctx: &OperationContext,
244        from: &str,
245        to: &str,
246        args: OpRename,
247    ) -> impl Future<Output = Result<RpRename>> + MaybeSend;
248
249    /// Invoke the `restore` operation on the specified path.
250    ///
251    /// Requires [`Capability::restore`].
252    ///
253    /// # Behavior
254    ///
255    /// - `path` is a normalized file path.
256    /// - Without a version, restore reverses the latest recoverable deletion state.
257    /// - With a version, restore promotes that historical version to the current version.
258    fn restore(
259        &self,
260        _ctx: &OperationContext,
261        _path: &str,
262        _args: OpRestore,
263    ) -> impl Future<Output = Result<RpRestore>> + MaybeSend {
264        std::future::ready(Err(Error::new(
265            ErrorKind::Unsupported,
266            "operation is not supported",
267        )))
268    }
269
270    /// Invoke the `presign` operation on the specified path.
271    ///
272    /// Requires [`Capability::presign`] and the matching presign operation
273    /// capability.
274    fn presign(
275        &self,
276        ctx: &OperationContext,
277        path: &str,
278        args: OpPresign,
279    ) -> impl Future<Output = Result<RpPresign>> + MaybeSend;
280}
281
282/// `ServiceDyn` is the dyn version of [`Service`].
283pub trait ServiceDyn: Send + Sync + Debug + Unpin + 'static {
284    /// Dyn version of [`Service::info`].
285    fn info_dyn(&self) -> ServiceInfo;
286
287    /// Dyn version of [`Service::capability`].
288    fn capability_dyn(&self) -> Capability;
289
290    /// Dyn version of [`Service::create_dir`].
291    fn create_dir_dyn<'a>(
292        &'a self,
293        ctx: &'a OperationContext,
294        path: &'a str,
295        args: OpCreateDir,
296    ) -> BoxedFuture<'a, Result<RpCreateDir>>;
297
298    /// Dyn version of [`Service::stat`].
299    fn stat_dyn<'a>(
300        &'a self,
301        ctx: &'a OperationContext,
302        path: &'a str,
303        args: OpStat,
304    ) -> BoxedFuture<'a, Result<RpStat>>;
305
306    /// Dyn version of [`Service::read`].
307    fn read_dyn<'a>(
308        &'a self,
309        ctx: &'a OperationContext,
310        path: &'a str,
311        args: OpRead,
312    ) -> Result<oio::Reader>;
313
314    /// Dyn version of [`Service::write`].
315    fn write_dyn<'a>(
316        &'a self,
317        ctx: &'a OperationContext,
318        path: &'a str,
319        args: OpWrite,
320    ) -> Result<oio::Writer>;
321
322    /// Dyn version of [`Service::delete`].
323    fn delete_dyn<'a>(&'a self, ctx: &'a OperationContext) -> Result<oio::Deleter>;
324
325    /// Dyn version of [`Service::list`].
326    fn list_dyn<'a>(
327        &'a self,
328        ctx: &'a OperationContext,
329        path: &'a str,
330        args: OpList,
331    ) -> Result<oio::Lister>;
332
333    /// Dyn version of [`Service::copy`].
334    fn copy_dyn<'a>(
335        &'a self,
336        ctx: &'a OperationContext,
337        from: &'a str,
338        to: &'a str,
339        args: OpCopy,
340    ) -> Result<oio::Copier>;
341
342    /// Dyn version of [`Service::compose`].
343    fn compose_dyn<'a>(
344        &'a self,
345        ctx: &'a OperationContext,
346        to: &'a str,
347        args: OpCompose,
348    ) -> Result<oio::Composer>;
349
350    /// Dyn version of [`Service::rename`].
351    fn rename_dyn<'a>(
352        &'a self,
353        ctx: &'a OperationContext,
354        from: &'a str,
355        to: &'a str,
356        args: OpRename,
357    ) -> BoxedFuture<'a, Result<RpRename>>;
358
359    /// Dyn version of [`Service::restore`].
360    fn restore_dyn<'a>(
361        &'a self,
362        ctx: &'a OperationContext,
363        path: &'a str,
364        args: OpRestore,
365    ) -> BoxedFuture<'a, Result<RpRestore>>;
366
367    /// Dyn version of [`Service::presign`].
368    fn presign_dyn<'a>(
369        &'a self,
370        ctx: &'a OperationContext,
371        path: &'a str,
372        args: OpPresign,
373    ) -> BoxedFuture<'a, Result<RpPresign>>;
374}
375
376/// Type-erased service handle used by layer composition and operators.
377pub type Servicer = Arc<dyn ServiceDyn>;
378
379impl<S: Service + ?Sized> ServiceDyn for S {
380    fn info_dyn(&self) -> ServiceInfo {
381        self.info()
382    }
383
384    fn capability_dyn(&self) -> Capability {
385        self.capability()
386    }
387
388    fn create_dir_dyn<'a>(
389        &'a self,
390        ctx: &'a OperationContext,
391        path: &'a str,
392        args: OpCreateDir,
393    ) -> BoxedFuture<'a, Result<RpCreateDir>> {
394        Box::pin(self.create_dir(ctx, path, args))
395    }
396
397    fn stat_dyn<'a>(
398        &'a self,
399        ctx: &'a OperationContext,
400        path: &'a str,
401        args: OpStat,
402    ) -> BoxedFuture<'a, Result<RpStat>> {
403        Box::pin(self.stat(ctx, path, args))
404    }
405
406    fn read_dyn<'a>(
407        &'a self,
408        ctx: &'a OperationContext,
409        path: &'a str,
410        args: OpRead,
411    ) -> Result<oio::Reader> {
412        Ok(Box::new(self.read(ctx, path, args)?) as oio::Reader)
413    }
414
415    fn write_dyn<'a>(
416        &'a self,
417        ctx: &'a OperationContext,
418        path: &'a str,
419        args: OpWrite,
420    ) -> Result<oio::Writer> {
421        Ok(Box::new(self.write(ctx, path, args)?) as oio::Writer)
422    }
423
424    fn delete_dyn<'a>(&'a self, ctx: &'a OperationContext) -> Result<oio::Deleter> {
425        Ok(Box::new(self.delete(ctx)?) as oio::Deleter)
426    }
427
428    fn list_dyn<'a>(
429        &'a self,
430        ctx: &'a OperationContext,
431        path: &'a str,
432        args: OpList,
433    ) -> Result<oio::Lister> {
434        Ok(Box::new(self.list(ctx, path, args)?) as oio::Lister)
435    }
436
437    fn copy_dyn<'a>(
438        &'a self,
439        ctx: &'a OperationContext,
440        from: &'a str,
441        to: &'a str,
442        args: OpCopy,
443    ) -> Result<oio::Copier> {
444        Ok(Box::new(self.copy(ctx, from, to, args)?) as oio::Copier)
445    }
446
447    fn compose_dyn<'a>(
448        &'a self,
449        ctx: &'a OperationContext,
450        to: &'a str,
451        args: OpCompose,
452    ) -> Result<oio::Composer> {
453        Ok(Box::new(self.compose(ctx, to, args)?) as oio::Composer)
454    }
455
456    fn rename_dyn<'a>(
457        &'a self,
458        ctx: &'a OperationContext,
459        from: &'a str,
460        to: &'a str,
461        args: OpRename,
462    ) -> BoxedFuture<'a, Result<RpRename>> {
463        Box::pin(self.rename(ctx, from, to, args))
464    }
465
466    fn restore_dyn<'a>(
467        &'a self,
468        ctx: &'a OperationContext,
469        path: &'a str,
470        args: OpRestore,
471    ) -> BoxedFuture<'a, Result<RpRestore>> {
472        Box::pin(self.restore(ctx, path, args))
473    }
474
475    fn presign_dyn<'a>(
476        &'a self,
477        ctx: &'a OperationContext,
478        path: &'a str,
479        args: OpPresign,
480    ) -> BoxedFuture<'a, Result<RpPresign>> {
481        Box::pin(self.presign(ctx, path, args))
482    }
483}
484
485/// Implement `Service` for type-erased services so they use the same API.
486impl<T: ServiceDyn + ?Sized> Service for Arc<T> {
487    type Reader = oio::Reader;
488    type Writer = oio::Writer;
489    type Lister = oio::Lister;
490    type Deleter = oio::Deleter;
491    type Copier = oio::Copier;
492    type Composer = oio::Composer;
493
494    fn info(&self) -> ServiceInfo {
495        self.as_ref().info_dyn()
496    }
497
498    fn capability(&self) -> Capability {
499        self.as_ref().capability_dyn()
500    }
501
502    async fn create_dir(
503        &self,
504        ctx: &OperationContext,
505        path: &str,
506        args: OpCreateDir,
507    ) -> Result<RpCreateDir> {
508        self.as_ref().create_dir_dyn(ctx, path, args).await
509    }
510
511    async fn stat(&self, ctx: &OperationContext, path: &str, args: OpStat) -> Result<RpStat> {
512        self.as_ref().stat_dyn(ctx, path, args).await
513    }
514
515    fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<oio::Reader> {
516        self.as_ref().read_dyn(ctx, path, args)
517    }
518
519    fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<oio::Writer> {
520        self.as_ref().write_dyn(ctx, path, args)
521    }
522
523    fn delete(&self, ctx: &OperationContext) -> Result<oio::Deleter> {
524        self.as_ref().delete_dyn(ctx)
525    }
526
527    fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<oio::Lister> {
528        self.as_ref().list_dyn(ctx, path, args)
529    }
530
531    fn copy(
532        &self,
533        ctx: &OperationContext,
534        from: &str,
535        to: &str,
536        args: OpCopy,
537    ) -> Result<oio::Copier> {
538        self.as_ref().copy_dyn(ctx, from, to, args)
539    }
540
541    fn compose(&self, ctx: &OperationContext, to: &str, args: OpCompose) -> Result<oio::Composer> {
542        self.as_ref().compose_dyn(ctx, to, args)
543    }
544
545    async fn rename(
546        &self,
547        ctx: &OperationContext,
548        from: &str,
549        to: &str,
550        args: OpRename,
551    ) -> Result<RpRename> {
552        self.as_ref().rename_dyn(ctx, from, to, args).await
553    }
554
555    async fn restore(
556        &self,
557        ctx: &OperationContext,
558        path: &str,
559        args: OpRestore,
560    ) -> Result<RpRestore> {
561        self.as_ref().restore_dyn(ctx, path, args).await
562    }
563
564    async fn presign(
565        &self,
566        ctx: &OperationContext,
567        path: &str,
568        args: OpPresign,
569    ) -> Result<RpPresign> {
570        self.as_ref().presign_dyn(ctx, path, args).await
571    }
572}
573
574/// Dummy implementation of service.
575impl Service for () {
576    type Reader = ();
577    type Writer = ();
578    type Lister = ();
579    type Deleter = ();
580    type Copier = ();
581    type Composer = ();
582
583    fn info(&self) -> ServiceInfo {
584        ServiceInfo::with_scheme("dummy")
585    }
586
587    fn capability(&self) -> Capability {
588        Capability::default()
589    }
590
591    async fn create_dir(
592        &self,
593        _: &OperationContext,
594        _: &str,
595        _: OpCreateDir,
596    ) -> Result<RpCreateDir> {
597        Err(Error::new(
598            ErrorKind::Unsupported,
599            "operation is not supported",
600        ))
601    }
602
603    async fn stat(&self, _: &OperationContext, _: &str, _: OpStat) -> Result<RpStat> {
604        Err(Error::new(
605            ErrorKind::Unsupported,
606            "operation is not supported",
607        ))
608    }
609
610    fn read(&self, _: &OperationContext, _: &str, _: OpRead) -> Result<Self::Reader> {
611        Err(Error::new(
612            ErrorKind::Unsupported,
613            "operation is not supported",
614        ))
615    }
616
617    fn write(&self, _: &OperationContext, _: &str, _: OpWrite) -> Result<Self::Writer> {
618        Err(Error::new(
619            ErrorKind::Unsupported,
620            "operation is not supported",
621        ))
622    }
623
624    fn delete(&self, _: &OperationContext) -> Result<Self::Deleter> {
625        Err(Error::new(
626            ErrorKind::Unsupported,
627            "operation is not supported",
628        ))
629    }
630
631    fn list(&self, _: &OperationContext, _: &str, _: OpList) -> Result<Self::Lister> {
632        Err(Error::new(
633            ErrorKind::Unsupported,
634            "operation is not supported",
635        ))
636    }
637
638    fn copy(&self, _: &OperationContext, _: &str, _: &str, _: OpCopy) -> Result<Self::Copier> {
639        Err(Error::new(
640            ErrorKind::Unsupported,
641            "operation is not supported",
642        ))
643    }
644
645    async fn rename(
646        &self,
647        _: &OperationContext,
648        _: &str,
649        _: &str,
650        _: OpRename,
651    ) -> Result<RpRename> {
652        Err(Error::new(
653            ErrorKind::Unsupported,
654            "operation is not supported",
655        ))
656    }
657
658    async fn presign(&self, _: &OperationContext, _: &str, _: OpPresign) -> Result<RpPresign> {
659        Err(Error::new(
660            ErrorKind::Unsupported,
661            "operation is not supported",
662        ))
663    }
664}