Module opendal::docs::concepts

source ·
Available on docs only.
Expand description

The core concepts of OpenDAL’s public API.

OpenDAL provides a unified abstraction that helps developers access all storage services.

There are two core concepts in OpenDAL:

  • Builder: Builder accepts a series of parameters to set up an instance of underlying services. You can adjust the behaviour of underlying services with these parameters.
  • Operator: Developer can access underlying storage services with manipulating one Operator. The Operator is a delegate for underlying implementation detail, and provides one unified access interface, including read, write, list and so on.

If you are interested in internal implementation details, please have a look at internals.

§Builder

Let’s start with Builder.

A Builder is a trait that is implemented by the underlying services. We can use a Builder to configure and create a service. Developer can only create one service via Builder, in other words, Builder is the only public API provided by services. And other detailed implementation will be hidden.

┌───────────┐                 ┌───────────┐
│           │     build()     │           │
│  Builder  ├────────────────►│  Service  │
│           │                 │           │
└───────────┘                 └───────────┘

All Builder provided by OpenDAL is under services, we can refer to them like opendal::services::S3. By right the builder will be named like OneServiceBuilder, but usually we will export it to public with renaming it as one general name. For example, we will rename S3Builder to S3 and developer will use S3 finally.

For example:

use opendal::services::S3;

let mut builder = S3::default();
builder.bucket("example");
builder.root("/path/to/file");

§Operator

The Operator is a delegate for Service, the underlying implementation detail that implements Accessor, and it also provides one unified access interface. It will hold one reference of Service with its all generic types erased by OpenDAL, which is the reason why we say the Operator is the delegate of one Service.

                  ┌────────────────────┐
                  │      Operator      │
                  │         │delegate  │
┌─────────┐ build │         ▼          │ rely on ┌─────────────────────┐
│ Builder ├───────┼──►┌────────────┐   │◄────────┤ business logic code │
└─────────┘       │   │  Service   │   │         └─────────────────────┘
                  └───┴────────────┴───┘

Operator can be built from Builder:

use opendal::services::S3;
use opendal::Operator;

let mut builder = S3::default();
builder.bucket("example");
builder.root("/path/to/file");

let op = Operator::new(builder)?.finish();
  • Operator has it’s internal Arc, so it’s cheap to clone it.
  • Operator doesn’t have generic parameters or lifetimes, so it’s easy to use it everywhere.
  • Operator implements Send and Sync, so it’s safe to send it between threads.

After get an Operator, we can do operations on different paths.

                           ┌──────────────┐
                 ┌────────►│ read("abc")  │
                 │         └──────────────┘
┌───────────┐    │
│ Operator  │    │         ┌──────────────┐
│ ┌───────┐ ├────┼────────►│ write("def") │
│ │Service│ │    │         └──────────────┘
└─┴───────┴─┘    │
                 │         ┌──────────────┐
                 └────────►│ list("ghi/") │
                           └──────────────┘

We can read data with given path in this way:

use opendal::services::S3;
use opendal::Operator;

let mut builder = S3::default();
builder.bucket("example");
builder.root("/path/to/file");

let op = Operator::new(builder)?.finish();
let bs: Vec<u8> = op.read("abc").await?;