Skip to main content

ObjectStore

Trait ObjectStore 

Source
pub trait ObjectStore: Send + Sync {
    // Required methods
    fn put(&self, key: &str, data: Vec<u8>) -> Result<(), Error>;
    fn get(&self, key: &str) -> Result<Option<Vec<u8>>, Error>;
    fn delete(&self, key: &str) -> Result<(), Error>;
    fn list_prefix(&self, prefix: &str) -> Result<Vec<String>, Error>;

    // Provided methods
    fn put_cached(
        &self,
        _key: &str,
        _data: Vec<u8>,
        _cache_control: &str,
    ) -> Result<(), Error> { ... }
    fn head(&self, key: &str) -> Result<bool, Error> { ... }
    fn put_if(
        &self,
        _key: &str,
        _data: Vec<u8>,
        _cond: Precondition,
    ) -> Result<String, Error> { ... }
    fn etag(&self, _key: &str) -> Result<Option<String>, Error> { ... }
    fn locate(&self, key: &str) -> String { ... }
}
Expand description

Minimal synchronous object-store surface.

Production impls connect to R2 / MinIO via AWS Sig V4. Tests inject InMemoryObjectStore so no network is required.

All methods are synchronous; async backends should block_on internally or expose a separate async trait alongside this one if the consumer is in a tokio context. (Scryer’s long-tier rollover runs on a blocking thread.)

Required Methods§

Source

fn put(&self, key: &str, data: Vec<u8>) -> Result<(), Error>

Write data at key. Overwrites any existing object unconditionally.

Sets no Cache-Control. For an object a browser or CDN will re-read — anything at a fixed, mutable key — reach for put_cached instead.

Source

fn get(&self, key: &str) -> Result<Option<Vec<u8>>, Error>

Read bytes at key. Returns None when the key does not exist — NotFound is reserved for ambiguous cases (HEAD-then-GET race etc.).

Source

fn delete(&self, key: &str) -> Result<(), Error>

Remove key. Idempotent — succeeds whether or not the key existed.

Source

fn list_prefix(&self, prefix: &str) -> Result<Vec<String>, Error>

List all keys with the given prefix (prefix-match, not glob).

Provided Methods§

Source

fn put_cached( &self, _key: &str, _data: Vec<u8>, _cache_control: &str, ) -> Result<(), Error>

Write data at key with an explicit Cache-Control (R703-B8).

Every CLI-driven publish went through put, which sets no cache directive at all — so yah-desktop/latest.json, the object the Tauri updater polls forever, shipped with nothing telling a client how long it may hold it. .github/workflows/release.yml has always tagged the same objects correctly, which is why the CI-published manifests answer no-cache, max-age=0 and the CLI-published ones do not. Use CACHE_CONTROL_IMMUTABLE for versioned, content-addressed keys and CACHE_CONTROL_NO_CACHE for mutable pointers.

The default impl fails rather than falling back to put, for the same reason put_if does: a backend that cannot set the header must not report success as though it had. A caller that only wants best-effort can call put explicitly and mean it.

Source

fn head(&self, key: &str) -> Result<bool, Error>

Returns true when key exists. Cheaper than get for backends that support HEAD; the default impl falls back to get(...).is_some().

Source

fn put_if( &self, _key: &str, _data: Vec<u8>, _cond: Precondition, ) -> Result<String, Error>

Conditionally write data at key, returning the resulting ETag.

An atomic compare-and-swap against cond. On a failed precondition the store is left untouched and Error::PreconditionFailed is returned — the caller re-reads (etag) and retries. The returned ETag is the comparand for the next Precondition::IfMatch in a CAS chain, so a single writer can advance a pointer without re-reading.

The default impl returns Error::Backend: a backend that cannot offer an atomic conditional write must not silently emulate it with get-then-put — that would break the linearizability callers depend on (W243’s cross-cell pointer fence). Backends that support it override this.

Source

fn etag(&self, _key: &str) -> Result<Option<String>, Error>

Current ETag of key, or None if absent.

The comparand a caller reads before a Precondition::IfMatch CAS. The default impl returns Error::Backend; backends supporting put_if override it.

Source

fn locate(&self, key: &str) -> String

Where key lives, in a form an operator can act on — a URL for a remote backend, an opaque descriptor otherwise (R746-F1).

A Result::Err from a store already carries what failed; this carries where it looked, which is the half a caller cannot reconstruct because it holds only a &dyn ObjectStore and the origin is private to the impl. A node reporting “no runtime asset for mesofact/0.8.20” is a shrug; one reporting the URL it GET’d is a curl away from a diagnosis. The default returns the bare key, so a backend that has no meaningful location (the in-memory test double) says nothing untrue.

Dyn Compatibility§

This trait is dyn compatible.

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

Implementors§