Skip to main content

Client

Struct Client 

Source
pub struct Client { /* private fields */ }
Expand description

Async HTTP client for LoonFS.

Cloning is cheap: clones share one connection pool and one capability cache.

Implementations§

Source§

impl Client

Source

pub fn new(config: ClientConfig) -> Result<Self>

Creates a client, validating the config exactly as ClientConfig::load does — direct Rust construction cannot bypass validation.

Source

pub async fn capabilities(&self) -> Result<CapabilityDocument>

Returns the server’s capability document, fetched once and cached for the life of this client and its clones (API spec, “Capability discovery”).

Feature keys that are not parented by an advertised profile are dropped rather than trusted, per the spec’s client guidance for malformed documents.

Source

pub async fn create_namespace( &self, namespace_id: &NamespaceId, ) -> Result<NamespaceSummary>

Source

pub async fn namespace_status( &self, namespace_id: &NamespaceId, ) -> Result<NamespaceStatusResponse>

Source

pub async fn delete_namespace( &self, namespace_id: &NamespaceId, expected_head_seq: Option<ChangeSeq>, ) -> Result<DeleteNamespaceResponse>

Deletes a namespace (feature core.namespaces.delete): terminal, and the id is permanently retired. Pass expected_head_seq to delete only if the namespace is still where you last observed it (stale_head on mismatch). Deleting an already-deleted namespace fails with namespace_deleted.

Source

pub async fn fork_namespace( &self, source_namespace_id: &NamespaceId, new_namespace_id: &NamespaceId, ) -> Result<NamespaceSummary>

Source

pub async fn list_path_entries_all( &self, spec: &NamespacePath, ) -> Result<ListPathEntriesResponse>

Lists a directory by aggregating every page into one response.

Listing cursors tolerate commits landing mid-listing — each page resumes in name-key order against the head the server has loaded — so aggregation never restarts. The envelope’s head_seq reports the newest head that served a page. Use Self::list_path_entries_page for page-level control.

Source

pub async fn list_path_entries_page( &self, spec: &NamespacePath, limit: Option<u32>, cursor: Option<&str>, ) -> Result<ListPathEntriesResponse>

Source

pub async fn stat_path( &self, spec: &NamespacePath, ) -> Result<AuthoritativePathEntry>

Source

pub async fn get_file_bytes(&self, spec: &NamespacePath) -> Result<Vec<u8>>

Source

pub async fn get_file_revision_bytes( &self, spec: &NamespacePath, revision_no: RevisionNo, ) -> Result<Vec<u8>>

Source

pub async fn offers_direct_download(&self, size_bytes: u64) -> bool

Whether this deployment would refuse to proxy a file of this size but can authorize a direct read of it.

The two halves are one question. Under the advertised proxy cap the proxied read is the simpler path and stays the default; over it the proxied read answers content_too_large, and a deployment that advertises core.downloads.direct_get can hand the object back instead — which is the whole point of the capability, because that same deployment is one that let a client create the object directly.

A deployment that advertises no cap is left on the proxied path: nothing here knows it would refuse.

Source

pub async fn begin_download( &self, spec: &NamespacePath, revision_no: Option<RevisionNo>, ) -> Result<BeginDownloadResponse>

Asks for one short-lived capability to read a file’s content object straight from the store.

Source

pub async fn open_direct_download( &self, download: &BeginDownloadResponse, ) -> Result<DirectDownloadStream>

Opens the response body authorized by a download grant as a bounded, verified stream.

Source

pub async fn open_direct_download_at( &self, download: &BeginDownloadResponse, start_offset: u64, ) -> Result<DirectDownloadStream>

Opens a download grant’s body from start_offset, for a caller that already holds the bytes below it.

The offset rides a Range header, which the presigned signature does not cover: one grant serves the whole object or any part of it, so a resumed download needs no different grant than a fresh one. The stream still reports on the whole object, so a nonzero offset obliges the caller to hand over what it holds through DirectDownloadStream::fold_resumed_prefix before driving it.

Source

pub async fn download_via_presigned_url<W>( &self, download: &BeginDownloadResponse, sink: &mut W, ) -> Result<u64>
where W: AsyncWrite + Unpin,

Streams a granted object’s bytes into sink, checking them against the reference the grant carried, and reports how many arrived.

The payload is never held: each chunk is hashed and written as it arrives, so this costs one chunk of memory whatever the object’s length. That is the entire reason the grant exists — a file past the deployment’s proxy cap has no other way home.

Verification is what keeps a direct read no weaker than a proxied one: the length always, and the whole-file SHA-256 whenever the reference carries one. A direct-multipart object carries none — nobody ever hashed it that way — and its length is then the whole check, exactly as it is for the server’s own reads.

A failure is reported after the sink has already received bytes, because that is the only order a streamed read allows. Callers must treat the sink as provisional until this returns: write to a temporary and install it on success.

Source

pub async fn list_file_revisions_page( &self, spec: &NamespacePath, limit: Option<u32>, cursor: Option<&str>, ) -> Result<ListFileRevisionsResponse>

Source

pub async fn list_trash_page( &self, namespace_id: &NamespaceId, limit: Option<u32>, cursor: Option<&str>, ) -> Result<ListTrashResponse>

Source

pub async fn health(&self) -> Result<()>

Source

pub async fn begin_upload( &self, namespace_id: &NamespaceId, request: &BeginUploadRequest, ) -> Result<BeginUploadResponse>

Source

pub async fn begin_direct_put( &self, namespace_id: &NamespaceId, claim: DirectPutContentClaim, ) -> Result<BeginUploadResponse>

Starts a direct upload of bytes the caller already has.

The claim is what the client can know about its own bytes; the server answers with the content object it minted for them, and that reference — not this claim — is what completion and the later commit name.

Source

pub async fn begin_direct_multipart( &self, namespace_id: &NamespaceId, options: DirectMultipartUploadOptions, ) -> Result<BeginUploadResponse>

Opens a direct multipart upload session.

Nothing about the payload is declared here — not its length, not its digest — so one pass over the bytes is enough and a stream of unknown length can start uploading immediately. The server answers with the part geometry to cut to and nothing else: not the bucket, not the key, not the provider’s upload id, and not the content identity, which it names back at completion.

Source

pub async fn sign_upload_parts( &self, namespace_id: &NamespaceId, upload_id: &UploadId, parts: Vec<UploadPartChecksumClaim>, ) -> Result<SignUploadPartsResponse>

Asks for one wave of checksum-bound part-upload capabilities.

Asking again for a part already uploaded is how a client retries it: a repeated part is last-write-wins at the provider, and the object’s checksum follows the bytes that stuck.

Source

pub async fn upload_part_via_presigned_url( &self, part_number: u32, access: &ObjectTransferAccess, crc64nvme: String, bytes: Bytes, ) -> Result<CompletedUploadPart>

Uploads one part and reports what the provider recorded for it.

The etag comes back to the caller rather than to the server: parts are the uploader’s bookkeeping all the way to completion, exactly as they are in the provider’s own multipart API.

Source

pub async fn upload_via_presigned_url( &self, access: &ObjectTransferAccess, bytes: &[u8], ) -> Result<()>

Source

pub async fn upload_content( &self, namespace_id: &NamespaceId, upload_id: &UploadId, bytes: &[u8], ) -> Result<UploadContentResponse>

Source

pub async fn upload_streamed_content( &self, namespace_id: &NamespaceId, upload_id: &UploadId, source: PayloadSource, ) -> Result<UploadContentResponse>

Stages a payload that arrives in pieces, forwarding it to the server as it is read.

This is Self::upload_content for a caller that does not hold its bytes: the payload crosses the client in bounded chunks and the server hashes it as it forwards it on, so neither side ever holds the object. A source whose length is unknown is sent with chunked transfer encoding, and the server’s own limit is what bounds it.

Unlike the buffered call this one never resends: a stream is consumed by the attempt that reads it, so a failure here is the caller’s to handle with a fresh source.

Source

pub async fn abort_upload( &self, namespace_id: &NamespaceId, upload_id: &UploadId, ) -> Result<AbortUploadResponse>

Ends an open upload session and deletes the object it was writing.

Repeating it succeeds and reports the abort that stands. This is what a one-pass upload does when its source fails partway: the session it opened must not be left holding a half-written object.

Source

pub async fn read_upload_status( &self, namespace_id: &NamespaceId, upload_id: &UploadId, ) -> Result<UploadStatusResponse>

Reads one upload session back.

A completed session answers with the exact content reference it settled on and a freshly minted validation token, so a caller that lost its completion response — or the whole process — can commit that content without re-uploading a byte. The upload id is the only thing it has to have kept.

Source

pub async fn complete_upload( &self, namespace_id: &NamespaceId, upload_id: &UploadId, request: &CompleteUploadRequest, ) -> Result<CompleteUploadResponse>

Source

pub async fn list_changes( &self, namespace_id: &NamespaceId, after_seq: ChangeSeq, limit: Option<u32>, ) -> Result<ChangesResponse>

Source

pub async fn create_checkpoint( &self, namespace_id: &NamespaceId, request: &CreateCheckpointRequest, ) -> Result<CreateCheckpointResponse>

Creates or reuses a named, user-owned checkpoint pinning the namespace’s current view (admin plane). This is a maintenance operation, not a file mutation. The record is a garbage-collection root until released or expired.

Source

pub async fn list_checkpoints( &self, namespace_id: &NamespaceId, ) -> Result<ListCheckpointsResponse>

Lists the namespace’s active checkpoint records, oldest first (admin plane).

A checkpoint name is a label rather than a key, so this is how a pin is found again once its creation response is gone. An expired record that no collection pass has released yet is still listed, with its expiry in the entry.

Source

pub async fn release_checkpoint( &self, namespace_id: &NamespaceId, checkpoint_id: &CheckpointId, ) -> Result<ReleaseCheckpointResponse>

Releases a user-owned checkpoint pin by id (admin plane). Idempotent: releasing an already-released or reaped record succeeds.

Source

pub async fn maintenance_step( &self, namespace_id: &NamespaceId, request: &MaintenanceStepRequest, ) -> Result<MaintenanceStepResponse>

Runs one bounded maintenance step against a namespace (admin plane). Absent request fields use the server’s defaults; garbage collection runs only when the request opts in.

Source

pub async fn probe_store( &self, request: &StoreProbeRequest, ) -> Result<StoreProbeResponse>

Proves the server’s backing store honours the object-store contract LoonFS depends on (admin plane).

The probe writes and deletes objects under a scratch prefix, so it runs only when asked. A store that fails a check answers with that check reported failed rather than with an error: the probe ran, and the answer is that the store is wrong.

Source

pub async fn grep( &self, namespace_id: &NamespaceId, request: &GrepRequest, ) -> Result<GrepResponse>

Content search over the namespace’s grep index (query plane). Gate on the query.grep capability before calling against unknown deployments; the namespace must also have a materialized steady-state grep root or the server answers not_supported.

Source

pub async fn grep_index_status( &self, namespace_id: &NamespaceId, ) -> Result<GrepIndexStatusResponse>

Reads the namespace’s grep-index lifecycle (admin plane): disabled, backfilling toward a captured sequence, or steady at a watermark. One grep root read on the server, with no side effects.

Source

pub async fn enable_grep_index( &self, namespace_id: &NamespaceId, ) -> Result<EnableGrepIndexResponse>

Enables the namespace’s grep root (admin plane); embedded mode starts that namespace’s event-driven backfill. Idempotent.

Source

pub async fn disable_grep_index( &self, namespace_id: &NamespaceId, ) -> Result<DisableGrepIndexResponse>

Disables the namespace’s grep root (admin plane); garbage collection reclaims the segments. Idempotent.

Source

pub async fn gc_grep_index( &self, namespace_id: &NamespaceId, request: &GrepGcRequest, ) -> Result<GrepGcResponse>

Runs one explicit grep-index garbage-collection pass for a namespace.

max_objects bounds the reads the pass spends; when keys remain the response carries a next_cursor to resume from.

Source

pub async fn commit( &self, namespace_id: &NamespaceId, request: &CommitRequest, ) -> Result<ApiCommitResponse>

Applies one commit: its operations land together, in order, under one commit id.

The convenience methods below are the one-element case of this call. Operations that introduce new external content carry their proofs in the request’s content_tokens; stage the bytes with the upload methods first.

Source

pub async fn put_file_bytes( &self, spec: &NamespacePath, bytes: &[u8], options: &PutFileOptions, ) -> Result<ApiCommitResponse>

Uploads bytes and commits them at a path.

Re-running this with a commit_id that already committed is safe when the request is the same. A commit’s identity names which content object it wrote, and a re-run necessarily uploads a fresh one, so the server sees a different commit and reports commit_id_reuse_conflict. This resolves that the only honest way available: it reads back what the commit id actually committed and compares it against the request just made. The same content under the same message means the operation had already succeeded and the original answer is returned; anything else — different bytes, a different message, or content this build cannot compare — surfaces the conflict.

The freshly uploaded duplicate object is then referenced by nothing. That is by design, not a leak: content garbage collection reclaims an unreferenced completed upload once its grace passes.

Source

pub async fn put_file_stream( &self, spec: &NamespacePath, source: PayloadSource, options: &PutFileOptions, ) -> Result<ApiCommitResponse>

Uploads a payload read once from its source and commits it at a path.

This is Self::put_file_bytes for a caller that should not hold its payload: the source is read forward in bounded pieces, hashed as it goes, and never assembled. What the memory costs is the transport’s window and nothing about the payload’s size — so a file larger than this process could hold, or a pipe whose length nobody knows, both upload the same way.

Where the deployment authorizes direct part uploads the payload goes straight to object storage in parts, and otherwise it streams through the server. One bound is worth knowing on the direct path: a provider assembles at most 10,000 parts, so a session carries at most part_size_bytes × 10_000 and a longer payload is refused when it asks to authorize the part past that ceiling. A caller that knows its payload is enormous should not be taking the default geometry.

Retrying with a commit_id that already committed is safe here in the same way it is for Self::put_file_bytes, and by the same evidence: this pass measured the payload’s length and folded its digest, which is what the reconciliation compares. The digest is CRC-64/NVME on the direct path, because that is what a provider computes over an assembled object.

Source

pub async fn put_file_stream_resumable( &self, spec: &NamespacePath, source: PayloadSource, options: &PutFileOptions, journal: &dyn MultipartUploadJournal, resume: Option<&MultipartUploadResume>, ) -> Result<ApiCommitResponse>

Self::put_file_stream for a caller that intends to survive an interruption.

journal is told the session’s id and geometry when it opens and told every part as it lands; resume hands back what an earlier run of the same upload recorded, so this one sends only the parts still missing. Both apply to the direct multipart transport alone: a proxied upload is a single request with no session behind it and nothing to resume from, and it ignores them.

The source is read from its first byte either way. That is not waste — the checksum the assembly is verified against covers the whole object, so every byte has to be folded whether or not it also has to be sent — and it means the source has to be one that can be opened again. A pipe cannot, which is why nothing that reads one should be calling this.

Source

pub async fn commit_completed_upload( &self, spec: &NamespacePath, content_ref: ContentRef, validated_content_token: Option<String>, options: &PutFileOptions, ) -> Result<ApiCommitResponse>

Commits content an upload session already completed, for a caller that finished the transfer and was interrupted before the commit.

The reference and the token come from Self::read_upload_status reporting the session Completed: the bytes are in object storage and admitted, so the only thing left to do is name them at a path. Nothing is re-uploaded.

Source

pub async fn create_directory( &self, spec: &NamespacePath, options: &CreateDirectoryOptions, ) -> Result<ApiCommitResponse>

Source

pub async fn delete_path( &self, spec: &NamespacePath, options: &DeleteOptions, ) -> Result<ApiCommitResponse>

Source

pub async fn move_path( &self, from: &NamespacePath, to: &NamespacePath, options: &MoveOptions, ) -> Result<ApiCommitResponse>

Source

pub async fn copy_path( &self, from: &NamespacePath, to: &NamespacePath, options: &CopyOptions, ) -> Result<ApiCommitResponse>

Source

pub async fn undelete( &self, namespace: &NamespaceId, inode_id: InodeId, deleted_at_seq: ChangeSeq, path: Option<&AbsolutePath>, options: &UndeleteOptions, ) -> Result<ApiCommitResponse>

Recovers a deleted file or subtree: clears the tombstone rooted at inode_id (the id the delete reported) and re-binds it at the spec’s path.

Source

pub async fn restore_file_revision( &self, spec: &NamespacePath, source_revision_no: RevisionNo, options: &RestoreRevisionOptions, ) -> Result<ApiCommitResponse>

Trait Implementations§

Source§

impl Clone for Client

Source§

fn clone(&self) -> Client

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Client

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more