Skip to main content

ArtifactStore

Trait ArtifactStore 

Source
pub trait ArtifactStore: Send + Sync {
    // Required methods
    fn put(&self, payload: &[u8]) -> Result<ContentHash, ArtifactError>;
    fn get(&self, hash: &ContentHash) -> Result<Vec<u8>, ArtifactError>;
    fn list(&self) -> Result<Vec<ContentHash>, ArtifactError>;
    fn contains(&self, hash: &ContentHash) -> Result<bool, ArtifactError>;
    fn remove(&self, hash: &ContentHash) -> Result<bool, ArtifactError>;

    // Provided method
    fn get_to(
        &self,
        hash: &ContentHash,
        out: &mut dyn Write,
    ) -> Result<u64, ArtifactError> { ... }
}
Expand description

Artifact store trait. v0.3.7 ships an in-memory and a disk implementation.

Implementations MUST be Send + Sync so the store can be shared across worker threads behind an Arc. They MUST also be safe under concurrent put for the same payload — a put after a put for the same content hash is idempotent.

Required Methods§

Source

fn put(&self, payload: &[u8]) -> Result<ContentHash, ArtifactError>

Insert payload into the store. The returned ContentHash is blake3(payload); repeated calls with identical payloads return identical hashes and overwrite the existing entry in place.

Source

fn get(&self, hash: &ContentHash) -> Result<Vec<u8>, ArtifactError>

Fetch the payload previously inserted under hash. Returns ArtifactError::NotFound on a genuine miss; integrity-failure variants (ArtifactError::BadMagic, ArtifactError::BadVersion, ArtifactError::BadHmac, ArtifactError::HashMismatch) are returned when a record was present but the format checks rejected it.

Source

fn list(&self) -> Result<Vec<ContentHash>, ArtifactError>

Enumerate the content hashes currently stored. Order is implementation-defined; callers that need a deterministic order must sort the result themselves.

Returns ArtifactError::Io on an underlying enumeration failure (e.g. a read_dir permissions/I/O fault). This is deliberately distinct from an empty result so GC/audit callers can tell “store is empty” apart from “could not read the store”.

Source

fn contains(&self, hash: &ContentHash) -> Result<bool, ArtifactError>

Cheap existence probe for hash: returns true if an entry is present, false otherwise. This is deliberately a stat-only check — it does NOT decode, decompress, or HMAC-verify the record, so it is dramatically cheaper than Self::get and suitable for the GC/audit roadmap’s “is this content already resident?” question.

Because no integrity work happens, a true result means only that a file (disk) or map entry (in-memory) exists under the content-addressed key; a subsequent Self::get can still fail with an integrity variant if that record was tampered with.

For DiskArtifactStore this probe is rotation-aware: it resolves against every accepted read key (active or retired), so a blob written under a now-retired key — and still visible to Self::get — also reports true here, rather than appearing absent and leaking past GC.

Returns ArtifactError::Io only on an underlying probe fault that is neither “present” nor “absent” (e.g. a metadata call failing for a reason other than not-found).

Source

fn remove(&self, hash: &ContentHash) -> Result<bool, ArtifactError>

Remove the entry stored under hash. Returns true if a record was removed, false if nothing was stored under hash (a no-op delete is not an error — it mirrors HashMap::remove’s “was it there?” boolean and the POSIX unlink-of-missing convention GC callers expect).

For DiskArtifactStore this is rotation-aware: it resolves which accepted key (active or retired) actually holds the blob — the same resolution Self::get / Self::list use — and unlinks the {hash}.{key_fp}.bin file (plus any sidecar) under THAT key, so a blob written under a now-retired key can still be GC’d. For InMemoryArtifactStore it drops the map entry. Returns ArtifactError::Io on an underlying delete fault other than not-found.

Provided Methods§

Source

fn get_to( &self, hash: &ContentHash, out: &mut dyn Write, ) -> Result<u64, ArtifactError>

Stream the verified-then-decoded body of hash into out, returning the number of bytes written. This completes the streaming story Self::put already has on the write side: where Self::get returns an owned Vec<u8> (up to MAX_DECOMPRESSED_LEN resident), get_to lets a caller pipe a large artifact straight into a file, socket, or hashing sink without first materialising the whole decoded payload as a return value.

§Integrity ordering

The HMAC covers the entire compressed blob, so it cannot be verified until the last body byte has been seen. To preserve the crate-wide invariant — no unverified bytes are ever exposed to a caller — implementations MUST authenticate the blob before any decoded byte reaches out. DiskArtifactStore does this with a two-pass scheme (pass 1: stream the compressed body through the HMAC to verify; pass 2: re-open and stream-decode directly to out), so peak heap stays bounded by the I/O buffers regardless of payload size and out only ever sees authenticated bytes. The error variants match Self::get.

The default implementation delegates to Self::get and copies the resulting buffer into out; it is correct (the Vec get returns is already verified) but not memory-bounded. Backends that can stream override it.

Dyn Compatibility§

This trait is dyn compatible.

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

Implementors§