pub trait ApplyObserver: Send + Sync {
// Provided methods
fn on_chunk_applied(&mut self, ev: ChunkEvent) -> ControlFlow<(), ()> { ... }
fn should_cancel(&mut self) -> bool { ... }
}Expand description
Hook trait for observing apply-time progress and signalling cancellation.
All methods have no-op defaults so implementors override only what they need.
§Threading
An observer is borrowed mutably by the apply driver for the lifetime of
the apply_patch call. There is no
internal synchronisation: implementors that need to forward events to
another thread should do so via channels they own.
The trait has Send + Sync supertrait bounds so a boxed observer can
be constructed on one thread and driven on another — the typical UI
pattern is to construct the observer (often around an
mpsc::Sender or an
AtomicBool cancellation flag) on the
UI thread, hand it to an ApplyConfig, and ship
the context to an apply worker. Sync costs nothing for the realistic
implementations (channel senders, atomics, Arc<Mutex<_>>) and lets the
observer be shared by reference if a downstream consumer ever needs to.
§Async usage
Both Self::on_chunk_applied and Self::should_cancel run inline
with the apply loop and are intentionally synchronous — see the
crate-level “Async usage” section for the rationale. The cancellation
poll in particular is called once per SqpkFile AddFile block and
must be cheap (an atomic-bool load is the canonical implementation),
which makes it a poor fit for async even hypothetically.
Async consumers wrap the whole apply call in
tokio::task::spawn_blocking and use an
AtomicBool cancellation flag the
async side can flip from a tokio::select! arm or a cancellation
token. Per-chunk events that need to reach an async UI go through a
channel whose Sender lives inside Self::on_chunk_applied and
whose Receiver is polled from the async task.
§Example
use std::ops::ControlFlow;
use zipatch_rs::{ApplyConfig, ApplyObserver, ChunkEvent, open_patch};
struct Progress {
total: u64,
applied: u64,
}
impl ApplyObserver for Progress {
fn on_chunk_applied(&mut self, ev: ChunkEvent) -> ControlFlow<(), ()> {
self.applied = ev.bytes_read;
println!("progress: {}/{}", self.applied, self.total);
ControlFlow::Continue(())
}
}
let mut ctx = ApplyConfig::new("/opt/ffxiv/game")
.with_observer(Progress { total: 12_345_678, applied: 0 });
let reader = open_patch("patch.patch").unwrap();
ctx.apply_patch(reader).unwrap();Provided Methods§
Sourcefn on_chunk_applied(&mut self, ev: ChunkEvent) -> ControlFlow<(), ()>
fn on_chunk_applied(&mut self, ev: ChunkEvent) -> ControlFlow<(), ()>
Called after each top-level chunk has been applied successfully.
Returning ControlFlow::Break aborts the apply loop immediately;
the apply call returns ApplyError::Cancelled.
Not invoked when the chunk’s apply itself fails — the error
propagates from apply_patch without
firing this method. The event is therefore a “chunk succeeded”
signal, not a “chunk attempted” one.
The default implementation does nothing and continues.
Sourcefn should_cancel(&mut self) -> bool
fn should_cancel(&mut self) -> bool
Polled inside long-running chunks to check for user cancellation.
Implementors should make this method cheap — it is called once
per block inside the
SqpkFile AddFile loop and on
every iteration of any future fine-grained loop the apply layer adds.
A simple atomic-bool load is the recommended implementation.
Polled before each block within long-running chunks (currently only
SQPK F AddFile). Once a block’s I/O has started, it completes —
cancellation takes effect at the next block boundary, not mid-write.
This means the last block of any chunk always finishes once started.
Returning true causes the current apply operation to abort at the
next checkpoint with ApplyError::Cancelled.
The default implementation always returns false.