Skip to main content

Storage

Trait Storage 

Source
pub trait Storage: ReadStorage {
    // Required methods
    fn write(
        &self,
        path: &Path,
        contents: &[u8],
    ) -> impl Future<Output = Result<()>>;
    fn create_dir_all(&self, path: &Path) -> impl Future<Output = Result<()>>;
    fn remove_file(&self, path: &Path) -> impl Future<Output = Result<()>>;
    fn remove_dir_all(&self, path: &Path) -> impl Future<Output = Result<()>>;
    fn rename(&self, from: &Path, to: &Path) -> impl Future<Output = Result<()>>;

    // Provided methods
    fn capabilities(&self) -> Capabilities { ... }
    fn sync(
        &self,
        path: &Path,
        need: Durability,
    ) -> impl Future<Output = Result<()>> { ... }
    fn write_atomic(
        &self,
        path: &Path,
        contents: &[u8],
    ) -> impl Future<Output = Result<()>> { ... }
}
Expand description

An async filesystem backend prov can drive — ReadStorage plus everything that changes bytes on disk.

Each method mirrors the std::fs function of the same name. Backends implement the write/mutate/durability surface here and the read surface on ReadStorage.

Required Methods§

Source

fn write( &self, path: &Path, contents: &[u8], ) -> impl Future<Output = Result<()>>

Write a file, replacing it if it already exists. Mirrors std::fs::write.

Source

fn create_dir_all(&self, path: &Path) -> impl Future<Output = Result<()>>

Create a directory and all missing parents. Mirrors std::fs::create_dir_all.

Source

fn remove_file(&self, path: &Path) -> impl Future<Output = Result<()>>

Remove a regular file. Mirrors std::fs::remove_file.

Source

fn remove_dir_all(&self, path: &Path) -> impl Future<Output = Result<()>>

Recursively remove a directory and its contents. Mirrors std::fs::remove_dir_all.

Source

fn rename(&self, from: &Path, to: &Path) -> impl Future<Output = Result<()>>

Rename or move a file or directory. Mirrors std::fs::rename.

Provided Methods§

Source

fn capabilities(&self) -> Capabilities

What durability guarantees this backend can make. Defaults to Capabilities::NONE — a backend promises a guarantee only by saying so, so an adapter that forgets to override this degrades to the most defensive path rather than to a false promise.

Source

fn sync( &self, path: &Path, need: Durability, ) -> impl Future<Output = Result<()>>

Flush path — and nothing else — to the strength need asks for.

path names one object, and only that object is flushed. To make a directory entry durable (the naming half of a create or a rename), sync the directory itself: on a POSIX filesystem a directory is a thing that can be opened and fsynced, and prov’s own write_atomic does exactly that after its rename. Folding the parent into every call instead would flush twice as much as any single step needs, and would leave the caller unable to say which of the two it actually meant.

need is the weakest guarantee that is still correct at the call site, not a wish. Durability::Ordered asks only that everything written to path before this call land before anything written after it — enough to stop a rename overtaking the bytes it publishes, and on some platforms far cheaper than the real thing. Durability::Durable asks that the bytes survive power loss. A backend may always answer with something stronger than it was asked for; it may never answer with something weaker.

The default is a no-op, which is the correct behavior for any backend whose capabilities report SyncGuarantee::None: it cannot make the promise, so it must not pretend to. A backend that can flush must both override this and report the strongest request it genuinely honors — the two always travel together, and SyncGuarantee::satisfies is how a caller asks.

Source

fn write_atomic( &self, path: &Path, contents: &[u8], ) -> impl Future<Output = Result<()>>

Replace path’s contents with contents atomically and durably: no observer — concurrent reader or post-crash survivor — ever sees a splice of old and new bytes, and once this returns the new contents outlive a power loss.

The default composes the primitives into the standard protocol, whenever capabilities report atomic_replace:

  1. write the bytes to a temporary sibling;
  2. sync that sibling Ordered, so the rename cannot be reordered ahead of the bytes it publishes;
  3. rename it over the target — this is the atomic instant;
  4. sync the target’s parent directory Durable, which is what carries the rename itself through a power cut.

Two flushes, and each one is load-bearing. Neither of the two this protocol conspicuously does not do would buy anything. The bytes are never flushed under their final name, because a rename does not move an inode: the file the target now names is the very one step 2 flushed, and nothing has been written to it since. The sibling’s own directory entry is never flushed either, because nobody is owed a temporary that survives a crash — only the directory state after the rename is worth a barrier.

A backend that cannot rename atomically falls back to a plain durable write, which is not crash-atomic; a caller that needs the guarantee consults capabilities and leans on the journal instead of pretending this call gave it. A backend with a better native path — a transactional store — overrides this method wholesale.

The temporary is removed on any failure, so a torn attempt leaves the target exactly as it was and no litter behind. It is a dotted sibling in the target’s own directory, so the follow-up rename stays within one filesystem (a cross-device rename is neither atomic nor, often, even permitted).

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementations on Foreign Types§

Source§

impl<S: Storage + ?Sized> Storage for &S

Source§

async fn write(&self, path: &Path, contents: &[u8]) -> Result<()>

Source§

async fn create_dir_all(&self, path: &Path) -> Result<()>

Source§

async fn remove_file(&self, path: &Path) -> Result<()>

Source§

async fn remove_dir_all(&self, path: &Path) -> Result<()>

Source§

async fn rename(&self, from: &Path, to: &Path) -> Result<()>

Source§

fn capabilities(&self) -> Capabilities

Source§

async fn sync(&self, path: &Path, need: Durability) -> Result<()>

Source§

async fn write_atomic(&self, path: &Path, contents: &[u8]) -> Result<()>

Source§

impl<S: Storage + ?Sized> Storage for Arc<S>

Source§

async fn write(&self, path: &Path, contents: &[u8]) -> Result<()>

Source§

async fn create_dir_all(&self, path: &Path) -> Result<()>

Source§

async fn remove_file(&self, path: &Path) -> Result<()>

Source§

async fn remove_dir_all(&self, path: &Path) -> Result<()>

Source§

async fn rename(&self, from: &Path, to: &Path) -> Result<()>

Source§

fn capabilities(&self) -> Capabilities

Source§

async fn sync(&self, path: &Path, need: Durability) -> Result<()>

Source§

async fn write_atomic(&self, path: &Path, contents: &[u8]) -> Result<()>

Implementors§