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