opendal-core 0.56.0

Apache OpenDALâ„¢: One Layer, All Storage.
- Proposal Name: `create-dir`
- Start Date: 2022-04-06
- RFC PR: [apache/opendal#221]https://github.com/apache/opendal/pull/221
- Tracking Issue: [apache/opendal#222]https://github.com/apache/opendal/issues/222

# Summary

Add creating dir support for OpenDAL.

# Motivation

Interoperability between OpenDAL services requires dir support. The object storage system will simulate dir operations with `/` via object ends. But we can't share the same behavior with `fs`, as `mkdir` is a separate syscall.

So we need to unify the behavior about dir across different services.

# Guide-level explanation

After this proposal got merged, we will treat all paths that end with `/` as a dir.

For example:

- `read("abc/")` will return an `IsDir` error.
- `write("abc/")` will return an `IsDir` error.
- `stat("abc/")` will be guaranteed to return a dir or a `NotDir` error.
- `delete("abc/")` will be guaranteed to delete a dir or `NotDir` / `NotEmpty` error.
- `list("abc/")` will be guaranteed to list a dir or a `NotDir` error.

And we will support create an empty object:

```rust
// create a dir object "abc/"
let _ = op.object("abc/").create().await?;
// create a file object "abc"
let _ = op.object("abc").create().await?;
```

# Reference-level explanation

And we will add a new API called `create` to create an empty object.

```rust
struct OpCreate {
    path: String,
    mode: ObjectMode,
}

pub trait Accessor: Send + Sync + Debug {
    async fn create(&self, args: &OpCreate) -> Result<Metadata>;
}
```

`Object` will expose API like `create` which will call `Accessor::create()` internally.

# Drawbacks

None

# Rationale and alternatives

None

# Prior art

None

# Unresolved questions

When writing this proposal, [io_error_more](https://github.com/rust-lang/rust/issues/86442) is not stabilized yet. We can't use `NotADirectory` nor `IsADirectory` directly.

Using `from_raw_os_error` is unacceptable because we can't carry our error context.

```rust
use std::io;

let error = io::Error::from_raw_os_error(22);
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);
```

So we will use `ErrorKind::Other` for now, which means our users can't check the following errors:

- `IsADirectory`
- `DirectoryNotEmpty`
- `NotADirectory`

Until they get stabilized.

# Future possibilities

None