origin_jobs/lib.rs
1//! Background jobs.
2//!
3//! Everything long-running — a repository scan, a large export, a report render —
4//! reports progress the same way, so every product reuses one progress UI and one
5//! cancel button.
6//!
7//! ```ignore
8//! let id = jobs.spawn("export", |ctx| async move {
9//! for (index, item) in items.iter().enumerate() {
10//! if ctx.is_cancelled() {
11//! return Ok(());
12//! }
13//! ctx.progress(index as u64 + 1, Some(items.len() as u64)).await;
14//! write(item).await?;
15//! }
16//! Ok(())
17//! });
18//! ```
19//!
20//! Fire-and-forget covers most jobs, but two things `spawn` alone cannot do:
21//!
22//! - **Only one at a time.** `spawn_exclusive` refuses to start a second job of the
23//! same `kind` while one is still running, instead of silently letting both proceed.
24//! - **Give the caller its result.** `Job` (what [`Jobs::get`]/[`Jobs::list`] return) is
25//! deliberately kind-agnostic and IPC-safe, with no slot for a typed value. A caller
26//! that needs what the job actually produced — not just that it finished — uses
27//! `spawn_awaitable` and awaits the returned [`JobResult`].
28//!
29//! `spawn_exclusive_awaitable` is both at once: the common shape for "run this, only
30//! one at a time, and hand me back what it computed" — a flow whose caller already
31//! awaits synchronously (a request/response command handler) rather than polling
32//! [`Jobs::get`] or subscribing to progress events.
33//!
34//! ```ignore
35//! let (_id, result) = jobs.spawn_exclusive_awaitable("crawl", |ctx| async move {
36//! let report = crawl(&ctx).await?;
37//! Ok(report)
38//! })?;
39//! let report = result.wait().await?;
40//! ```
41
42mod context;
43mod registry;
44
45pub use context::JobContext;
46pub use registry::{JobResult, Jobs};