Skip to main content

MeowClient

Struct MeowClient 

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

Main entry point of the rusty-cat SDK.

MeowClient owns runtime state and provides high-level operations: enqueue, pause, resume, cancel, snapshot, and close.

§Usage pattern

  1. Create MeowConfig.
  2. Construct MeowClient::new(config).
  3. Build tasks with upload/download builders.
  4. Call [Self::enqueue] and store returned TaskId.
  5. Control task lifecycle with pause/resume/cancel.
  6. Call Self::close during shutdown.

§Lifecycle contract: you must call Self::close

The background scheduler runs on a dedicated std::thread that drives its own Tokio runtime. The clean shutdown protocol is an explicit close().await command which:

  • cancels in-flight transfers,
  • flushes Paused status events to user callbacks for every known group,
  • drains already submitted callback jobs,
  • joins the scheduler thread and lets the runtime drop.

Forgetting to call close leaves the scheduler thread alive until all command senders are dropped (which does happen when MeowClient is dropped, but only as a fallback). When that fallback path runs, the guarantees above do not hold: callers may miss terminal status events, in-flight HTTP transfers are aborted abruptly, and for long-lived SDK hosts (servers, mobile runtimes, etc.) the misuse is nearly impossible to debug from the outside.

To help surface this misuse the internal executor implements a best-effort Drop that, when close was never called:

  • emits a Warn-level log via the debug log listener (tag "executor_drop"),
  • performs a non-blocking try_send of a final Close command so the worker still has a chance to drain its state,
  • then drops the command sender, causing the worker loop to exit on its own.

This is a safety net, not a substitute for calling close. Treat close().await as a mandatory step in your shutdown sequence.

§Sharing across tasks / threads

MeowClient intentionally does not implement Clone.

The client owns a lazily-initialized [Executor] (a single background worker loop plus its task table, scheduler state and shutdown flag). A naive field-by-field Clone would copy the OnceLock<Executor> before it was initialized, letting different clones each spin up their own executor on first use. The result would be:

To share a client across tasks or threads, wrap it in std::sync::Arc and clone the Arc instead:

use std::sync::Arc;
use rusty_cat::api::{MeowClient, MeowConfig};

let client = Arc::new(MeowClient::new(MeowConfig::default()));
let client_for_task = Arc::clone(&client);
tokio::spawn(async move {
    let _ = client_for_task; // use the shared client here
});

Implementations§

Source§

impl MeowClient

Source

pub fn new(config: MeowConfig) -> Self

Creates a new client with the provided configuration.

The internal executor is initialized lazily on first task operation.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig};

let config = MeowConfig::default();
let client = MeowClient::new(config);
let _ = client;
Source

pub fn http_client(&self) -> Result<Client, MeowError>

Returns a reqwest::Client aligned with this client’s configuration.

  • If MeowConfigBuilder::http_client injected a custom client, this returns its clone.
  • Otherwise, this builds a new client from http_timeout and tcp_keepalive.
§Errors

Returns MeowError with HttpClientBuildFailed when client creation fails.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig};

let client = MeowClient::new(MeowConfig::default());
let http = client.http_client()?;
let _ = http;
Source

pub fn register_global_progress_listener<F>( &self, listener: F, ) -> Result<GlobalProgressListenerId, MeowError>
where F: Fn(FileTransferRecord) + Send + Sync + 'static,

Registers a global progress listener for all tasks.

§Parameters
§Returns

Returns a listener ID used by Self::unregister_global_progress_listener.

§Usage rules

Keep callback execution short and panic-free. A heavy callback can slow down global event delivery.

§Errors

Returns LockPoisoned when listener storage lock is poisoned.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig};

let client = MeowClient::new(MeowConfig::default());
let listener_id = client.register_global_progress_listener(|record| {
    println!("task={} progress={:.2}", record.task_id(), record.progress());
})?;
let _ = listener_id;
Source

pub fn unregister_global_progress_listener( &self, id: GlobalProgressListenerId, ) -> Result<bool, MeowError>

Unregisters one previously registered global progress listener.

Returns Ok(false) when the ID does not exist.

§Errors

Returns LockPoisoned when listener storage lock is poisoned.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig};

let client = MeowClient::new(MeowConfig::default());
let id = client.register_global_progress_listener(|_| {})?;
let removed = client.unregister_global_progress_listener(id)?;
assert!(removed);
Source

pub fn clear_global_listener(&self) -> Result<(), MeowError>

Removes all registered global progress listeners.

§Errors

Returns LockPoisoned when listener storage lock is poisoned.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig};

let client = MeowClient::new(MeowConfig::default());
client.clear_global_listener()?;
Source

pub fn set_debug_log_listener( &self, listener: Option<DebugLogListener>, ) -> Result<(), DebugLogListenerError>

Sets or clears the global debug log listener.

  • Pass Some(listener) to set/replace.
  • Pass None to clear.

This affects all MeowClient instances in the current process.

§Errors

Returns DebugLogListenerError when the internal global listener lock is poisoned.

§Examples
use std::sync::Arc;
use rusty_cat::api::{Log, MeowClient, MeowConfig};

let client = MeowClient::new(MeowConfig::default());
client.set_debug_log_listener(Some(Arc::new(|log: Log| {
    println!("{log}");
})))?;

// Clear listener when no longer needed.
client.set_debug_log_listener(None)?;
Source§

impl MeowClient

Source

pub async fn try_enqueue<PCB, CCB>( &self, task: PounceTask, progress_cb: PCB, complete_cb: CCB, ) -> Result<TaskId, MeowError>
where PCB: Fn(FileTransferRecord) + Send + Sync + 'static, CCB: Fn(TaskId, Option<String>) + Send + Sync + 'static,

Submits a transfer task to the internal scheduler and returns its TaskId.

The actual upload/download execution is dispatched to an internal worker system thread. This method only performs lightweight validation and submission, so it does not block the caller thread waiting for full transfer completion.

try_enqueue is also the recovery entrypoint after process restart. If the application was killed during a previous upload/download, restart your process and call try_enqueue again to resume that transfer workflow.

§Back-pressure semantics (why the try_ prefix)

Internally this method uses tokio::sync::mpsc::Sender::try_send to hand the Enqueue command to the scheduler worker, not send().await. That means:

  • The await point in this function is used for task normalization (e.g. resolving upload breakpoints, building [InnerTask]), not for waiting on command-queue capacity.
  • If the command queue is momentarily full (bursty enqueue under MeowConfig::command_queue_capacity), this method returns an immediate CommandSendFailed error instead of suspending the caller until a slot frees up.
  • Other control APIs (Self::pause, Self::resume, Self::cancel, Self::snapshot) use send().await and do wait for queue capacity. Only enqueue is fail-fast.

Callers that want to batch-enqueue under burst load should either:

  1. size MeowConfig::command_queue_capacity appropriately, or
  2. retry on CommandSendFailed with their own back-off, or
  3. rate-limit enqueue calls on the caller side.

The name explicitly carries try_ so this fail-fast behavior is visible at the call site. If a fully-awaiting variant is introduced later it should be named enqueue (without the try_ prefix).

§Parameters
  • task: Built by upload/download task builders.
  • progress_cb: Per-task callback invoked with transfer progress.
  • complete_cb: Callback fired once when task reaches crate::transfer_status::TransferStatus::Complete. The second argument is provider-defined payload returned by upload protocol complete_upload; download tasks usually receive None.
§Usage rules
  • task must be non-empty (required path/name/url and valid upload size).
  • Callback should be lightweight and non-blocking.
  • Store returned task ID for subsequent task control operations.
  • try_enqueue is asynchronous task submission, not synchronous transfer.
  • For restart recovery, re-enqueue the same logical task (same upload/download target and compatible checkpoint context) so the runtime can continue from existing local/remote progress.
§Errors

Returns:

  • ClientClosed if the client was closed.
  • ParameterEmpty if the task is invalid/empty.
  • CommandSendFailed if the scheduler command queue is full at the moment of submission (see back-pressure semantics above).
  • Any runtime initialization errors from the executor.
§Examples
use rusty_cat::api::{DownloadPounceBuilder, MeowClient, MeowConfig};

let client = MeowClient::new(MeowConfig::default());
let task = DownloadPounceBuilder::new(
    "example.bin",
    "./downloads/example.bin",
    1024 * 1024,
    "https://example.com/example.bin",
)
.build();

let task_id = client
    .try_enqueue(
        task,
        |record| {
            println!("status={:?} progress={:.2}", record.status(), record.progress());
        },
        |task_id, payload| {
            println!("task {task_id} completed, payload={payload:?}");
        },
    )
    .await?;
println!("enqueued task: {task_id}");
Source

pub async fn pause(&self, task_id: TaskId) -> Result<(), MeowError>

Pauses a running or pending task by ID.

This API sends a control command to the internal scheduler worker thread. It does not execute transfer pause logic on the caller thread.

§Usage rules

Call this with a valid task ID returned by [Self::enqueue].

§Errors

Returns ClientClosed, TaskNotFound, or state-transition errors.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig, TaskId};

let client = MeowClient::new(MeowConfig::default());
client.pause(task_id).await?;
Source

pub async fn resume(&self, task_id: TaskId) -> Result<(), MeowError>

Resumes a previously paused task.

The same TaskId continues to identify the task after resume. The resume command is forwarded to the internal scheduler worker thread, so caller thread is not responsible for running transfer logic.

§Errors

Returns ClientClosed, TaskNotFound, or InvalidTaskState.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig, TaskId};

let client = MeowClient::new(MeowConfig::default());
client.resume(task_id).await?;
Source

pub async fn cancel(&self, task_id: TaskId) -> Result<(), MeowError>

Cancels a task by ID.

Cancellation is requested through the internal scheduler worker thread. Transfer cancellation execution happens in background runtime workers.

§Usage rules

Cancellation is best-effort; protocol-specific cleanup may run.

§Errors

Returns ClientClosed, TaskNotFound, or runtime cancellation errors.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig, TaskId};

let client = MeowClient::new(MeowConfig::default());
client.cancel(task_id).await?;
Source

pub async fn snapshot(&self) -> Result<TransferSnapshot, MeowError>

Returns a snapshot of queue and active transfer groups.

Useful for diagnostics and external monitoring dashboards. Snapshot collection is coordinated by internal scheduler worker state.

§Errors

Returns ClientClosed, runtime command delivery errors, or scheduler snapshot retrieval errors.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig};

let client = MeowClient::new(MeowConfig::default());
let snap = client.snapshot().await?;
println!("queued={}, active={}", snap.queued_groups, snap.active_groups);
Source

pub async fn close(&self) -> Result<(), MeowError>

Closes this client and its underlying executor.

close is the terminal lifecycle operation for a MeowClient. After it succeeds, this client stays permanently closed; submit more work by constructing a new MeowClient and enqueueing tasks there.

After a successful close:

  • New task and control operations on this client are rejected with ClientClosed.
  • All known unfinished task groups (queued, paused, or active) receive a Paused progress notification through their task callback and all registered global listeners.
  • In-flight transfers are cancelled and the scheduler state is cleared.
  • Already submitted callback jobs are drained before returning.
  • The internal scheduler thread is joined, which drops its Tokio runtime and releases SDK-owned background execution resources.

Paused is used for shutdown notifications rather than Canceled so callers can recreate a client later and re-enqueue the same logical transfer when they want to resume from available breakpoint state.

§Idempotency

Calling close more than once returns ClientClosed.

§Retry behavior

If executor close fails, the closed flag is rolled back so caller can retry close. A successful close is not restartable.

§Errors

Returns ClientClosed when already closed, or underlying executor close errors when shutdown is not completed.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig};

let client = MeowClient::new(MeowConfig::default());
client.close().await?;
Source

pub fn is_closed(&self) -> bool

Returns whether this client is currently closed.

§Examples
use rusty_cat::api::{MeowClient, MeowConfig};

let client = MeowClient::new(MeowConfig::default());
let _closed = client.is_closed();

Trait Implementations§

Source§

impl Debug for MeowClient

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> 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: 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: 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, 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