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
121    /// Return the immutable identity and configuration for this service.
122    fn info(&self) -> ServiceInfo;
123
124    /// Return the capability of this service stack.
125    ///
126    /// Layers may affect a service's capabilities, so callers should use this
127    /// value for the current stack instead of assuming the backend's native
128    /// capability.
129    fn capability(&self) -> Capability;
130
131    /// Invoke the `create` operation on the specified path.
132    ///
133    /// Requires [`Capability::create_dir`].
134    ///
135    /// # Behavior
136    ///
137    /// - `path` is a normalized directory path.
138    /// - Creating an existing directory should succeed.
139    fn create_dir(
140        &self,
141        ctx: &OperationContext,
142        path: &str,
143        args: OpCreateDir,
144    ) -> impl Future<Output = Result<RpCreateDir>> + MaybeSend;
145
146    /// Invoke the `stat` operation on the specified path.
147    ///
148    /// Requires [`Capability::stat`].
149    ///
150    /// # Behavior
151    ///
152    /// - `/` means the service root.
153    /// - A path ending with `/` stats a directory.
154    /// - Returned metadata must set `mode` and `content_length`.
155    fn stat(
156        &self,
157        ctx: &OperationContext,
158        path: &str,
159        args: OpStat,
160    ) -> impl Future<Output = Result<RpStat>> + MaybeSend;
161
162    /// Invoke the `read` operation on the specified path.
163    ///
164    /// Requires [`Capability::read`].
165    ///
166    /// # Behavior
167    ///
168    /// - `path` is a normalized file path.
169    /// - Range I/O is handled by the returned reader.
170    fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader>;
171
172    /// Invoke the `write` operation on the specified path.
173    ///
174    /// Requires [`Capability::write`].
175    ///
176    /// # Behavior
177    ///
178    /// - `path` is a normalized file path.
179    fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer>;
180
181    /// Invoke the `delete` operation.
182    ///
183    /// Requires [`Capability::delete`].
184    ///
185    /// # Behavior
186    ///
187    /// - The returned deleter handles one or more delete requests.
188    /// - Deleting a missing path should succeed.
189    fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter>;
190
191    /// Invoke the `list` operation on the specified path.
192    ///
193    /// Requires [`Capability::list`].
194    ///
195    /// # Behavior
196    ///
197    /// - `path` is a normalized directory path or prefix.
198    /// - Listing a non-existing directory should return an empty stream.
199    fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister>;
200
201    /// Invoke the `copy` operation on the specified `from` path and `to` path.
202    ///
203    /// Requires [`Capability::copy`].
204    ///
205    /// # Behavior
206    ///
207    /// - `from` and `to` are normalized file paths.
208    /// - Copying to an existing file should overwrite and truncate it.
209    fn copy(
210        &self,
211        ctx: &OperationContext,
212        from: &str,
213        to: &str,
214        args: OpCopy,
215        opts: OpCopier,
216    ) -> Result<Self::Copier>;
217
218    /// Invoke the `rename` operation on the specified `from` path and `to` path.
219    ///
220    /// Requires [`Capability::rename`].
221    ///
222    /// # Behavior
223    ///
224    /// - `from` and `to` are normalized file paths.
225    fn rename(
226        &self,
227        ctx: &OperationContext,
228        from: &str,
229        to: &str,
230        args: OpRename,
231    ) -> impl Future<Output = Result<RpRename>> + MaybeSend;
232
233    /// Invoke the `presign` operation on the specified path.
234    ///
235    /// Requires [`Capability::presign`] and the matching presign operation
236    /// capability.
237    fn presign(
238        &self,
239        ctx: &OperationContext,
240        path: &str,
241        args: OpPresign,
242    ) -> impl Future<Output = Result<RpPresign>> + MaybeSend;
243}
244
245/// `ServiceDyn` is the dyn version of [`Service`].
246pub trait ServiceDyn: Send + Sync + Debug + Unpin + 'static {
247    /// Dyn version of [`Service::info`].
248    fn info_dyn(&self) -> ServiceInfo;
249
250    /// Dyn version of [`Service::capability`].
251    fn capability_dyn(&self) -> Capability;
252
253    /// Dyn version of [`Service::create_dir`].
254    fn create_dir_dyn<'a>(
255        &'a self,
256        ctx: &'a OperationContext,
257        path: &'a str,
258        args: OpCreateDir,
259    ) -> BoxedFuture<'a, Result<RpCreateDir>>;
260
261    /// Dyn version of [`Service::stat`].
262    fn stat_dyn<'a>(
263        &'a self,
264        ctx: &'a OperationContext,
265        path: &'a str,
266        args: OpStat,
267    ) -> BoxedFuture<'a, Result<RpStat>>;
268
269    /// Dyn version of [`Service::read`].
270    fn read_dyn<'a>(
271        &'a self,
272        ctx: &'a OperationContext,
273        path: &'a str,
274        args: OpRead,
275    ) -> Result<oio::Reader>;
276
277    /// Dyn version of [`Service::write`].
278    fn write_dyn<'a>(
279        &'a self,
280        ctx: &'a OperationContext,
281        path: &'a str,
282        args: OpWrite,
283    ) -> Result<oio::Writer>;
284
285    /// Dyn version of [`Service::delete`].
286    fn delete_dyn<'a>(&'a self, ctx: &'a OperationContext) -> Result<oio::Deleter>;
287
288    /// Dyn version of [`Service::list`].
289    fn list_dyn<'a>(
290        &'a self,
291        ctx: &'a OperationContext,
292        path: &'a str,
293        args: OpList,
294    ) -> Result<oio::Lister>;
295
296    /// Dyn version of [`Service::copy`].
297    fn copy_dyn<'a>(
298        &'a self,
299        ctx: &'a OperationContext,
300        from: &'a str,
301        to: &'a str,
302        args: OpCopy,
303        opts: OpCopier,
304    ) -> Result<oio::Copier>;
305
306    /// Dyn version of [`Service::rename`].
307    fn rename_dyn<'a>(
308        &'a self,
309        ctx: &'a OperationContext,
310        from: &'a str,
311        to: &'a str,
312        args: OpRename,
313    ) -> BoxedFuture<'a, Result<RpRename>>;
314
315    /// Dyn version of [`Service::presign`].
316    fn presign_dyn<'a>(
317        &'a self,
318        ctx: &'a OperationContext,
319        path: &'a str,
320        args: OpPresign,
321    ) -> BoxedFuture<'a, Result<RpPresign>>;
322}
323
324/// Type-erased service handle used by layer composition and operators.
325pub type Servicer = Arc<dyn ServiceDyn>;
326
327impl<S: Service + ?Sized> ServiceDyn for S {
328    fn info_dyn(&self) -> ServiceInfo {
329        self.info()
330    }
331
332    fn capability_dyn(&self) -> Capability {
333        self.capability()
334    }
335
336    fn create_dir_dyn<'a>(
337        &'a self,
338        ctx: &'a OperationContext,
339        path: &'a str,
340        args: OpCreateDir,
341    ) -> BoxedFuture<'a, Result<RpCreateDir>> {
342        Box::pin(self.create_dir(ctx, path, args))
343    }
344
345    fn stat_dyn<'a>(
346        &'a self,
347        ctx: &'a OperationContext,
348        path: &'a str,
349        args: OpStat,
350    ) -> BoxedFuture<'a, Result<RpStat>> {
351        Box::pin(self.stat(ctx, path, args))
352    }
353
354    fn read_dyn<'a>(
355        &'a self,
356        ctx: &'a OperationContext,
357        path: &'a str,
358        args: OpRead,
359    ) -> Result<oio::Reader> {
360        Ok(Box::new(self.read(ctx, path, args)?) as oio::Reader)
361    }
362
363    fn write_dyn<'a>(
364        &'a self,
365        ctx: &'a OperationContext,
366        path: &'a str,
367        args: OpWrite,
368    ) -> Result<oio::Writer> {
369        Ok(Box::new(self.write(ctx, path, args)?) as oio::Writer)
370    }
371
372    fn delete_dyn<'a>(&'a self, ctx: &'a OperationContext) -> Result<oio::Deleter> {
373        Ok(Box::new(self.delete(ctx)?) as oio::Deleter)
374    }
375
376    fn list_dyn<'a>(
377        &'a self,
378        ctx: &'a OperationContext,
379        path: &'a str,
380        args: OpList,
381    ) -> Result<oio::Lister> {
382        Ok(Box::new(self.list(ctx, path, args)?) as oio::Lister)
383    }
384
385    fn copy_dyn<'a>(
386        &'a self,
387        ctx: &'a OperationContext,
388        from: &'a str,
389        to: &'a str,
390        args: OpCopy,
391        opts: OpCopier,
392    ) -> Result<oio::Copier> {
393        Ok(Box::new(self.copy(ctx, from, to, args, opts)?) as oio::Copier)
394    }
395
396    fn rename_dyn<'a>(
397        &'a self,
398        ctx: &'a OperationContext,
399        from: &'a str,
400        to: &'a str,
401        args: OpRename,
402    ) -> BoxedFuture<'a, Result<RpRename>> {
403        Box::pin(self.rename(ctx, from, to, args))
404    }
405
406    fn presign_dyn<'a>(
407        &'a self,
408        ctx: &'a OperationContext,
409        path: &'a str,
410        args: OpPresign,
411    ) -> BoxedFuture<'a, Result<RpPresign>> {
412        Box::pin(self.presign(ctx, path, args))
413    }
414}
415
416/// Implement `Service` for type-erased services so they use the same API.
417impl<T: ServiceDyn + ?Sized> Service for Arc<T> {
418    type Reader = oio::Reader;
419    type Writer = oio::Writer;
420    type Lister = oio::Lister;
421    type Deleter = oio::Deleter;
422    type Copier = oio::Copier;
423
424    fn info(&self) -> ServiceInfo {
425        self.as_ref().info_dyn()
426    }
427
428    fn capability(&self) -> Capability {
429        self.as_ref().capability_dyn()
430    }
431
432    async fn create_dir(
433        &self,
434        ctx: &OperationContext,
435        path: &str,
436        args: OpCreateDir,
437    ) -> Result<RpCreateDir> {
438        self.as_ref().create_dir_dyn(ctx, path, args).await
439    }
440
441    async fn stat(&self, ctx: &OperationContext, path: &str, args: OpStat) -> Result<RpStat> {
442        self.as_ref().stat_dyn(ctx, path, args).await
443    }
444
445    fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<oio::Reader> {
446        self.as_ref().read_dyn(ctx, path, args)
447    }
448
449    fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<oio::Writer> {
450        self.as_ref().write_dyn(ctx, path, args)
451    }
452
453    fn delete(&self, ctx: &OperationContext) -> Result<oio::Deleter> {
454        self.as_ref().delete_dyn(ctx)
455    }
456
457    fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<oio::Lister> {
458        self.as_ref().list_dyn(ctx, path, args)
459    }
460
461    fn copy(
462        &self,
463        ctx: &OperationContext,
464        from: &str,
465        to: &str,
466        args: OpCopy,
467        opts: OpCopier,
468    ) -> Result<oio::Copier> {
469        self.as_ref().copy_dyn(ctx, from, to, args, opts)
470    }
471
472    async fn rename(
473        &self,
474        ctx: &OperationContext,
475        from: &str,
476        to: &str,
477        args: OpRename,
478    ) -> Result<RpRename> {
479        self.as_ref().rename_dyn(ctx, from, to, args).await
480    }
481
482    async fn presign(
483        &self,
484        ctx: &OperationContext,
485        path: &str,
486        args: OpPresign,
487    ) -> Result<RpPresign> {
488        self.as_ref().presign_dyn(ctx, path, args).await
489    }
490}
491
492/// Dummy implementation of service.
493impl Service for () {
494    type Reader = ();
495    type Writer = ();
496    type Lister = ();
497    type Deleter = ();
498    type Copier = ();
499
500    fn info(&self) -> ServiceInfo {
501        ServiceInfo::with_scheme("dummy")
502    }
503
504    fn capability(&self) -> Capability {
505        Capability::default()
506    }
507
508    async fn create_dir(
509        &self,
510        _: &OperationContext,
511        _: &str,
512        _: OpCreateDir,
513    ) -> Result<RpCreateDir> {
514        Err(Error::new(
515            ErrorKind::Unsupported,
516            "operation is not supported",
517        ))
518    }
519
520    async fn stat(&self, _: &OperationContext, _: &str, _: OpStat) -> Result<RpStat> {
521        Err(Error::new(
522            ErrorKind::Unsupported,
523            "operation is not supported",
524        ))
525    }
526
527    fn read(&self, _: &OperationContext, _: &str, _: OpRead) -> Result<Self::Reader> {
528        Err(Error::new(
529            ErrorKind::Unsupported,
530            "operation is not supported",
531        ))
532    }
533
534    fn write(&self, _: &OperationContext, _: &str, _: OpWrite) -> Result<Self::Writer> {
535        Err(Error::new(
536            ErrorKind::Unsupported,
537            "operation is not supported",
538        ))
539    }
540
541    fn delete(&self, _: &OperationContext) -> Result<Self::Deleter> {
542        Err(Error::new(
543            ErrorKind::Unsupported,
544            "operation is not supported",
545        ))
546    }
547
548    fn list(&self, _: &OperationContext, _: &str, _: OpList) -> Result<Self::Lister> {
549        Err(Error::new(
550            ErrorKind::Unsupported,
551            "operation is not supported",
552        ))
553    }
554
555    fn copy(
556        &self,
557        _: &OperationContext,
558        _: &str,
559        _: &str,
560        _: OpCopy,
561        _: OpCopier,
562    ) -> Result<Self::Copier> {
563        Err(Error::new(
564            ErrorKind::Unsupported,
565            "operation is not supported",
566        ))
567    }
568
569    async fn rename(
570        &self,
571        _: &OperationContext,
572        _: &str,
573        _: &str,
574        _: OpRename,
575    ) -> Result<RpRename> {
576        Err(Error::new(
577            ErrorKind::Unsupported,
578            "operation is not supported",
579        ))
580    }
581
582    async fn presign(&self, _: &OperationContext, _: &str, _: OpPresign) -> Result<RpPresign> {
583        Err(Error::new(
584            ErrorKind::Unsupported,
585            "operation is not supported",
586        ))
587    }
588}