pub trait Transport: Send + Sync {
Show 13 methods
// Required methods
fn upload_pack(&self, bytes: &[u8], key: &PackKey) -> TransportResult<()>;
fn download_pack(&self, key: &PackKey) -> TransportResult<Vec<u8>>;
fn pack_exists(&self, key: &PackKey) -> TransportResult<bool>;
fn update_ref(
&self,
name: &str,
condition: RefWriteCondition,
hash: &Hash,
) -> TransportResult<()>;
fn read_ref(&self, name: &str) -> TransportResult<Option<Hash>>;
fn list_refs(&self, prefix: &str) -> TransportResult<Vec<Ref>>;
// Provided methods
fn upload_pack_streaming(
&self,
key: &PackKey,
total_bytes: u64,
chunks: &mut dyn Iterator<Item = TransportResult<PackChunk>>,
) -> TransportResult<()> { ... }
fn download_pack_streaming(
&self,
key: &PackKey,
) -> TransportResult<Box<dyn Iterator<Item = TransportResult<PackChunk>> + '_>> { ... }
fn upload_blob(&self, bytes: &[u8], key: &PackKey) -> TransportResult<()> { ... }
fn download_blob(&self, key: &PackKey) -> TransportResult<Vec<u8>> { ... }
fn write_ref(&self, name: &str, hash: &Hash) -> TransportResult<()> { ... }
fn advance_refs(
&self,
head_ref: &str,
head_condition: RefWriteCondition,
head_value: &Hash,
packmap_ref: &str,
packmap_condition: RefWriteCondition,
packmap_value: &Hash,
) -> TransportResult<AdvanceOutcome> { ... }
fn supports_atomic_advance(&self) -> bool { ... }
}Expand description
The mkit transport vtable.
Every transport (memory, file, HTTP, S3, SSH) implements this trait.
Methods are synchronous and take &self; transports that need
interior mutability (e.g. connection pools) MUST use a Mutex /
RwLock internally. This keeps the trait object-safe.
All implementations MUST honour the retry policy in
SPEC-TRANSPORT §7 internally OR document that the caller is
responsible — the abstract trait takes no position. The
is_retryable and BackoffIterator helpers are provided for
implementations that embed the policy.
Required Methods§
Sourcefn upload_pack(&self, bytes: &[u8], key: &PackKey) -> TransportResult<()>
fn upload_pack(&self, bytes: &[u8], key: &PackKey) -> TransportResult<()>
Upload a pack. The digest is computed by the caller (BLAKE3 of the full pack bytes) and used as the object key — servers MAY dedupe on this key.
Sourcefn download_pack(&self, key: &PackKey) -> TransportResult<Vec<u8>>
fn download_pack(&self, key: &PackKey) -> TransportResult<Vec<u8>>
Download a pack by its digest.
Returns TransportError::PackNotFound if the remote does not
hold this digest.
Sourcefn pack_exists(&self, key: &PackKey) -> TransportResult<bool>
fn pack_exists(&self, key: &PackKey) -> TransportResult<bool>
HEAD-check a pack. Cheaper than Self::download_pack on
network transports.
Sourcefn update_ref(
&self,
name: &str,
condition: RefWriteCondition,
hash: &Hash,
) -> TransportResult<()>
fn update_ref( &self, name: &str, condition: RefWriteCondition, hash: &Hash, ) -> TransportResult<()>
CAS ref write. See RefWriteCondition.
On .missing / .match CAS failure, returns
TransportError::RefConflict. Callers retrying after a
timeout MUST follow up with Self::read_ref to confirm
whether the first attempt actually landed (SPEC-TRANSPORT §7).
Provided Methods§
Sourcefn upload_pack_streaming(
&self,
key: &PackKey,
total_bytes: u64,
chunks: &mut dyn Iterator<Item = TransportResult<PackChunk>>,
) -> TransportResult<()>
fn upload_pack_streaming( &self, key: &PackKey, total_bytes: u64, chunks: &mut dyn Iterator<Item = TransportResult<PackChunk>>, ) -> TransportResult<()>
Upload a pack by streaming bounded-size PackChunks instead of
requiring the whole pack materialized as one &[u8] up front.
total_bytes is the caller-declared pack length — the wire
header most streaming transports send before the first chunk.
chunks MUST yield its segments in ascending contiguous offset
order and end with exactly one item whose last field is true
(an empty pack still yields one last = true chunk with empty
data); the accumulated data length across every yielded chunk
MUST equal total_bytes. The digest (key) is still computed by
the caller up front, exactly as for Self::upload_pack — this
method does not hash the stream itself.
This is an additive, opt-in entry point: no existing transport is
forced to implement real streaming. The default impl buffers
chunks into one Vec (bounded by PACK_BODY_LIMIT_USIZE) and
delegates to Self::upload_pack, so every transport gets a
working implementation with zero code — callers may always use
this entry point, even against a transport that has not opted
into streaming. Transports that can forward chunks directly to
their own wire (SSH, enc — see mkit-transport-ssh’s existing
PackChunk frame loop) SHOULD override this to avoid the buffer
and stay in bounded memory regardless of pack size.
§Errors
Returns TransportError::ProtocolError if chunks never
yields a last = true item, or if the accumulated byte count
does not equal total_bytes. Returns
TransportError::PayloadTooLarge if total_bytes (or the
accumulated count) would exceed PACK_BODY_LIMIT. Propagates
any error yielded by chunks itself (e.g. the caller’s own I/O
error while reading a pack off disk).
Sourcefn download_pack_streaming(
&self,
key: &PackKey,
) -> TransportResult<Box<dyn Iterator<Item = TransportResult<PackChunk>> + '_>>
fn download_pack_streaming( &self, key: &PackKey, ) -> TransportResult<Box<dyn Iterator<Item = TransportResult<PackChunk>> + '_>>
Download a pack as a lazy stream of bounded-size PackChunks
instead of one big Vec<u8>.
This is an additive, opt-in entry point mirroring
Self::upload_pack_streaming. The default impl calls
Self::download_pack eagerly (so it does not save memory by
itself) and wraps the whole result as a single last = true
chunk — every transport gets a working implementation with zero
code. Transports that can read their own wire incrementally (SSH,
enc) SHOULD override this to yield each wire chunk as it arrives,
keeping memory bounded to roughly one chunk at a time regardless
of total pack size.
§Errors
Returns TransportError::PackNotFound immediately if the
remote does not hold key — a conforming implementation never
returns a stream that then fails its first item with
PackNotFound. Errors surfacing mid-stream (a malformed frame, a
connection drop) are yielded as Err items from the returned
iterator rather than failing this call itself, since an
overridden implementation may not know the transfer will fail
until partway through.
Sourcefn upload_blob(&self, bytes: &[u8], key: &PackKey) -> TransportResult<()>
fn upload_blob(&self, bytes: &[u8], key: &PackKey) -> TransportResult<()>
Upload a content-addressed auxiliary blob — transfer metadata
that is NOT a packfile (e.g. a packlist chain node, SPEC-PACKFILE is
silent on these). The key is BLAKE3 of bytes, exactly like a pack.
Auxiliary blobs share the digest-keyed content-addressed store with
packs (the store is a general blob store; “pack” is just the primary
content kind), so the default impl delegates to Self::upload_pack.
The distinct verb keeps the kind explicit at the call site so a
caller never has to infer “is this blob a packfile or metadata?”.
Sourcefn download_blob(&self, key: &PackKey) -> TransportResult<Vec<u8>>
fn download_blob(&self, key: &PackKey) -> TransportResult<Vec<u8>>
Download an auxiliary blob by digest. Counterpart to
Self::upload_blob; default impl delegates to
Self::download_pack. Returns TransportError::PackNotFound if
the remote does not hold this digest.
Sourcefn write_ref(&self, name: &str, hash: &Hash) -> TransportResult<()>
fn write_ref(&self, name: &str, hash: &Hash) -> TransportResult<()>
Unconditional ref write — equivalent to
update_ref(name, RefWriteCondition::Any, hash).
Default impl delegates to Self::update_ref so transports only
implement one entry point.
Sourcefn advance_refs(
&self,
head_ref: &str,
head_condition: RefWriteCondition,
head_value: &Hash,
packmap_ref: &str,
packmap_condition: RefWriteCondition,
packmap_value: &Hash,
) -> TransportResult<AdvanceOutcome>
fn advance_refs( &self, head_ref: &str, head_condition: RefWriteCondition, head_value: &Hash, packmap_ref: &str, packmap_condition: RefWriteCondition, packmap_value: &Hash, ) -> TransportResult<AdvanceOutcome>
Advance a branch by updating its head ref and its packmap ref together, each under its own CAS precondition.
This exists so the delta-transfer invariant — “if head_ref resolves
to T, the packmap reconstructs closure(T)” — can be upheld without a
window where the two refs disagree. A transport backed by a
transactional ref store SHOULD override this to apply both writes in
ONE transaction: then a failed advance changes nothing, and the head
is never observed past a packmap that can’t yet reconstruct it.
The default impl is the safe non-transactional approximation used by
stores without multi-ref transactions: it writes the packmap first
(durable before the head moves) then the head. A crash in between
leaves the head at its prior value and the packmap a superset — still
consistent for fetch. The AdvanceOutcome distinguishes a packmap
precondition failure (caller re-reads and retries the chain) from a
head precondition failure (caller treats it as non-fast-forward),
which a single RefConflict could not.
Sourcefn supports_atomic_advance(&self) -> bool
fn supports_atomic_advance(&self) -> bool
Whether Self::advance_refs commits the head + packmap advance as
one indivisible transaction, rather than the default’s ordered
packmap-then-head writes.
The default (non-transactional) advance_refs is safe for an
appending packmap write: per its doc comment, a crash or lost
head-CAS race between the two writes leaves the packmap a strict
superset of what the (unmoved) head needs — still reconstructable.
That safety argument does NOT extend to a packmap reset (a fresh
node with prev = None, produced by the pack-chain re-baseline,
mkit #406): a reset is not a superset of the prior chain, so a
packmap write that commits while the paired head write loses its CAS
would strand the (still-unmoved) head pointing at a commit whose
closure the reset packmap can no longer reconstruct (mkit #521).
Callers MUST treat false (the default) as “never request a
packmap reset against this transport” — see
remote_dispatch::push_branch’s re-baseline gate. Override to
true ONLY when Self::advance_refs is overridden with a
genuinely transactional implementation (e.g. the HTTP transport’s
single-request /refs/advance endpoint, mkit #408).
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".