Skip to main content

ReadOnly

Struct ReadOnly 

Source
pub struct ReadOnly<S: ?Sized> { /* private fields */ }
Expand description

An ObjectStore whose writes are refused and whose reads go to the store it wraps. See the module note.

Implementations§

Source§

impl<S: ObjectStore + ?Sized> ReadOnly<S>

Source

pub fn new(inner: Arc<S>) -> Self

Wrap inner. There is deliberately no accessor back to it: a holder of the wrapper that could reach the inner store could write.

Trait Implementations§

Source§

impl<S: ObjectStore + ?Sized> ObjectStore for ReadOnly<S>

Source§

fn bootstrap<'life0, 'life1, 'async_trait>( &'life0 self, prefix: &'life1 str, ) -> Pin<Box<dyn Future<Output = StoreResult<BootstrapReport>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Refused: the S3 bootstrap writes lifecycle rules and a probe object, and a read-only holder has no bucket posture to set up.

Source§

fn presign_get<'life0, 'life1, 'async_trait>( &'life0 self, key: &'life1 str, ttl_secs: u64, ) -> Pin<Box<dyn Future<Output = StoreResult<String>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Forwarded: a presigned GET grants a read, which this holder has.

Source§

fn put_whole<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, key: &'life1 str, _body: Bytes, _condition: &'life2 PutCondition, _stamps: &'life3 GenerationStamps, _crc64: u64, ) -> Pin<Box<dyn Future<Output = StoreResult<ObjectMeta>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Whole-object publish (below the multipart threshold). crc64 is validated server-side; stamps ride as metadata.
Source§

fn copy_object<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>( &'life0 self, _src_key: &'life1 str, _src_if_match: Option<&'life2 str>, dst_key: &'life3 str, _condition: &'life4 PutCondition, _stamps: &'life5 GenerationStamps, ) -> Pin<Box<dyn Future<Output = StoreResult<ObjectMeta>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait, 'life5: 'async_trait,

Assemble and publish one generation from local dirty bytes plus server-side clean copies. MUST abort its partial assembly on every failure path (A9) — an error return means nothing is left pending under the key by this call. Server-side copy of a WHOLE object to a DIFFERENT key. Read more
Source§

fn compose_generation<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, spec: &'life1 ComposeSpec<'life2>, ) -> Pin<Box<dyn Future<Output = StoreResult<ObjectMeta>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Source§

fn delete<'life0, 'life1, 'async_trait>( &'life0 self, key: &'life1 str, ) -> Pin<Box<dyn Future<Output = StoreResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn delete_if_match<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, _etag: &'life2 str, ) -> Pin<Box<dyn Future<Output = StoreResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

DELETE key only while its current ETag is etag (If-Match): PreconditionFailed once the object has moved on, NotFound when there is nothing to delete. Read more
Source§

fn delete_many<'life0, 'life1, 'async_trait>( &'life0 self, keys: &'life1 [String], ) -> Pin<Box<dyn Future<Output = StoreResult<DeleteManyReport>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

DELETE every key in keys, unconditionally, in as few requests as the backend allows (S3: DeleteObjects, a thousand keys per request). Absent keys succeed; see DeleteManyReport. Read more
Source§

fn delete_version<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, _version_id: &'life2 str, ) -> Pin<Box<dyn Future<Output = StoreResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

DELETE one specific version — the exact reclamation flint’s own per-citation GC uses. Free, and the ONLY reaper that can tell cited from uncited; lifecycle cannot, which is why the noncurrent rule is a long backstop rather than the mechanism (D8).
Source§

fn presign_put<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, _ttl_secs: u64, _sha256: &'life2 [u8; 32], ) -> Pin<Box<dyn Future<Output = StoreResult<PresignedPut>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

A time-limited URL that PUTs key with no credential of the caller’s own. Read more
Source§

fn ensure_noncurrent_retention<'life0, 'life1, 'async_trait>( &'life0 self, prefix: &'life1 str, _days: u64, ) -> Pin<Box<dyn Future<Output = StoreResult<RetentionOutcome>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Provision the noncurrent-version retention backstop on prefix with read-merge-append, and report what is installed. Read more
Source§

fn abort_upload<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, _upload_id: &'life2 str, ) -> Pin<Box<dyn Future<Output = StoreResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Abort one assembly. Aborting an already-absent upload is Ok — the sweep and the abort-on-failure paths both race the lifecycle rule.
Source§

fn epoch_acquire<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, key: &'life1 str, _holder_id: &'life2 str, _supersede: Option<&'life3 EpochState>, ) -> Pin<Box<dyn Future<Output = StoreResult<EpochLease>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Claim: supersede: None creates (If-None-Match:*); Some replaces the OBSERVED state via CAS on its token — the caller (step 7) is responsible for having JUDGED that holder dead against the store’s clock. The new epoch is observed+1.
Source§

fn epoch_renew<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, key: &'life1 str, _lease: &'life2 EpochLease, _echo: Option<&'life3 str>, ) -> Pin<Box<dyn Future<Output = StoreResult<EpochLease>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Heartbeat CAS. PreconditionFailed means deposed: self-fence. Read more
Source§

fn epoch_release<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, _lease: &'life2 EpochLease, ) -> Pin<Box<dyn Future<Output = StoreResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Mark the cell released: a clean handoff. The epoch NUMBER must survive (deleting the cell would restart numbering at 1 and break the monotonicity every publish stamp depends on), and the write must be guarded on lease’s own token so a deposed holder cannot mark a live successor’s cell.
Source§

fn epoch_enqueue<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, key: &'life1 str, _observed: &'life2 EpochState, _holder_id: &'life3 str, ) -> Pin<Box<dyn Future<Output = StoreResult<EpochState>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

Append holder_id to the cell’s FIFO of waiters, by CAS on the OBSERVED token, and return the cell as rewritten. A holder that is already queued is not appended twice. The write moves the token: a live holder’s next renew/handoff 412s, re-reads, and adopts the new token with the longer queue (lean lease.rs). Default: unsupported — a backend without a queue serialises claimants by the quiet-poll rule alone.
Source§

fn epoch_handoff<'life0, 'life1, 'life2, 'life3, 'async_trait>( &'life0 self, key: &'life1 str, _lease: &'life2 EpochLease, _echo: Option<&'life3 str>, ) -> Pin<Box<dyn Future<Output = StoreResult<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait,

The per-BARRIER release (lean, design 2026-09-13 §4): mark the cell released like ObjectStore::epoch_release, but hand it to the queue head (handoff = waiters[0], the rest stay queued) and KEEP echo — the cell is at rest between every barrier now, so the last holder’s echo is the only thing that tells an operator which binary ran the last boundary. Guarded on the lease token like every other transition. Default: the plain release.
Source§

fn head<'life0, 'life1, 'async_trait>( &'life0 self, key: &'life1 str, ) -> Pin<Box<dyn Future<Output = StoreResult<ObjectMeta>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn get_whole<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, if_match: Option<&'life2 str>, ) -> Pin<Box<dyn Future<Output = StoreResult<(ObjectMeta, Bytes)>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Whole-object read, optionally guarded (hydration always guards: a 412 here is S3-WINS — the foreign-overwrite posture, opposite of the publish path).
Source§

fn get_range<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, offset: u64, len: u64, if_match: &'life2 str, ) -> Pin<Box<dyn Future<Output = StoreResult<Bytes>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Ranged read (step 11’s streaming restore — a multi-GiB hydration must never buffer the whole object). ALWAYS guarded: each chunk’s If-Match pins the same object version end-to-end; a 412 mid-restore is the S3-wins foreign-overwrite signal. The caller never requests past the object’s end.
Source§

fn get_range_segments<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, offset: u64, len: u64, if_match: &'life2 str, ) -> Pin<Box<dyn Future<Output = StoreResult<Vec<Bytes>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

The same ranged read, WITHOUT flattening the response into one contiguous buffer. Read more
Source§

fn list<'life0, 'life1, 'async_trait>( &'life0 self, prefix: &'life1 str, ) -> Pin<Box<dyn Future<Output = StoreResult<Vec<ListedObject>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn head_version<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, version_id: &'life2 str, ) -> Pin<Box<dyn Future<Output = StoreResult<ObjectMeta>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

HEAD one specific version.
Source§

fn get_version<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, key: &'life1 str, version_id: &'life2 str, ) -> Pin<Box<dyn Future<Output = StoreResult<(ObjectMeta, Bytes)>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

GET one specific version, when the backend keeps versions. Lean’s readers resolve citations by etag alone (version-pinned reads went with gated mode, 2026-09-13); the method stays for tools and probes.
Source§

fn list_versions<'life0, 'life1, 'async_trait>( &'life0 self, prefix: &'life1 str, ) -> Pin<Box<dyn Future<Output = StoreResult<Vec<ListedVersion>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Every version under a prefix, current and noncurrent, oldest first per key. The claim-time/DR fallback when orphans.json is missing or stale — the expensive path, which is why the durable summary is written eagerly.
Source§

fn lifecycle_rules<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = StoreResult<Vec<LifecycleView>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Every lifecycle rule on the bucket, reduced to LifecycleView. Read more
Source§

fn list_uploads<'life0, 'life1, 'async_trait>( &'life0 self, prefix: &'life1 str, ) -> Pin<Box<dyn Future<Output = StoreResult<Vec<PendingUpload>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

A9 hygiene: every in-progress assembly under the prefix.
Source§

fn epoch_read<'life0, 'life1, 'async_trait>( &'life0 self, key: &'life1 str, ) -> Pin<Box<dyn Future<Output = StoreResult<Option<EpochState>>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Source§

fn min_part_size(&self) -> u64

Backend part granularity for the A11 part-size grid.
Source§

fn max_parts(&self) -> usize

Source§

fn upload_gate(&self) -> Option<Arc<ByteGate>>

The store’s upload bytes-in-flight gate (gate::ByteGate), when it was built with one. The store charges every local part of a compose to it; a caller that reads whole bodies ITSELF before put_whole (lean’s upload_one) takes this so those bodies are charged to the SAME budget, and the bound is one number rather than two that must be set together. None = the upload path is bounded only by its counts.
Source§

fn request_counts(&self) -> Option<RequestCounts>

Requests this store has sent since it was built, by kind (counters.rs). None = a store that does not count.

Auto Trait Implementations§

§

impl<S> Freeze for ReadOnly<S>
where Arc<S>: Freeze, S: ?Sized,

§

impl<S> RefUnwindSafe for ReadOnly<S>
where Arc<S>: RefUnwindSafe, S: ?Sized,

§

impl<S> Send for ReadOnly<S>
where Arc<S>: Send, S: ?Sized,

§

impl<S> Sync for ReadOnly<S>
where Arc<S>: Sync, S: ?Sized,

§

impl<S> Unpin for ReadOnly<S>
where Arc<S>: Unpin, S: ?Sized,

§

impl<S> UnsafeUnpin for ReadOnly<S>
where Arc<S>: UnsafeUnpin, S: ?Sized,

§

impl<S> UnwindSafe for ReadOnly<S>
where Arc<S>: UnwindSafe, S: ?Sized,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.