solti_runner/lib.rs
1//! # solti-runner
2//!
3//! Runner plugin interface, routing, and execution metrics for the solti task system.
4//!
5//! This crate defines how task executors (runners) are declared, selected, and
6//! observed. It sits between the domain model ([`solti_model`]) and the
7//! orchestration layer (`solti-core`), providing a stable plugin boundary.
8//!
9//! ## Architecture
10//!
11//! ```text
12//! TaskSpec ──► RunnerRouter ──► Runner::build_task() ──► TaskRef
13//! │ ▲
14//! │ label matching │ BuildContext
15//! │ + supports() │ (env + metrics)
16//! ▼ │
17//! RunnerEntry MetricsHandle
18//! (runner + labels) (Arc<dyn MetricsBackend>)
19//! ```
20//!
21//! ## Public API
22//!
23//! | Item | Description |
24//! |-----------------------|--------------------------------------------------------------|
25//! | [`Runner`] | Trait that concrete executors implement |
26//! | [`RunnerRouter`] | Selects a runner for a given [`TaskSpec`](solti_model::TaskSpec) by `supports()` + label matching |
27//! | [`BuildContext`] | Shared dependencies injected into runners (env + metrics) |
28//! | [`RunId`] | Human-readable run identifier (`{runner}-{slot}-{seq}`) |
29//! | [`RunnerError`] | Error type for runner operations |
30//! | [`MetricsBackend`] | Trait for collecting task execution metrics |
31//! | [`MetricsHandle`] | `Arc<dyn MetricsBackend>` — cloneable shared handle |
32//! | [`NoOpMetrics`] | Zero-size backend that compiles to nothing |
33//! | [`RunnerType`] | Metric label: `Subprocess`, `Wasm`, `Container` |
34//! | [`TaskOutcome`] | Metric label: `Success`, `Failure`, `Canceled`, `Timeout` |
35//!
36//! ## Runner implementation
37//!
38//! A runner must implement [`Runner`] and be registered in a [`RunnerRouter`]:
39//!
40//! ```text
41//! use solti_runner::{Runner, RunnerError, BuildContext, RunId};
42//! use solti_model::TaskSpec;
43//! use taskvisor::TaskRef;
44//!
45//! struct MyRunner;
46//!
47//! impl Runner for MyRunner {
48//! fn name(&self) -> &'static str { "my-runner" }
49//!
50//! fn supports(&self, spec: &TaskSpec) -> bool {
51//! matches!(spec.kind(), solti_model::TaskKind::Subprocess(_))
52//! }
53//!
54//! fn build_task(&self, spec: &TaskSpec, ctx: &BuildContext) -> Result<TaskRef, RunnerError> {
55//! // build and return a TaskRef
56//! todo!()
57//! }
58//! }
59//! ```
60//!
61//! ## Routing
62//!
63//! [`RunnerRouter`] selects the first registered runner that:
64//! 1. returns `true` from [`Runner::supports`] for the given spec, and
65//! 2. satisfies the [`RunnerSelector`](solti_model::RunnerSelector) label constraints (if any).
66//!
67//! ```text
68//! use solti_runner::{RunnerRouter, BuildContext};
69//!
70//! let mut router = RunnerRouter::new();
71//! router.register(Arc::new(MyRunner));
72//!
73//! let task_ref = router.build(&spec)?;
74//! ```
75//!
76//! ## Metrics
77//!
78//! Metrics are collected via [`MetricsBackend`], injected through [`BuildContext`].
79//! The default is [`NoOpMetrics`] (zero-cost). Backends like `solti-prometheus`
80//! implement [`MetricsBackend`] for production use.
81//!
82//! ## Also
83//!
84//! - [`solti_model`] — domain types consumed by runners ([`TaskSpec`](solti_model::TaskSpec), [`TaskKind`](solti_model::TaskKind)).
85//! - [`taskvisor::TaskRef`] — the concrete task handle returned by [`Runner::build_task`].
86//! - `solti-exec` — subprocess runner implementation.
87//! - `solti-prometheus` — Prometheus [`MetricsBackend`] implementation.
88
89mod error;
90pub use error::RunnerError;
91
92mod runner;
93pub use runner::Runner;
94
95mod context;
96pub use context::BuildContext;
97
98mod id;
99pub use id::{RunId, make_run_id};
100
101mod router;
102pub use router::RunnerRouter;
103
104pub mod metrics;
105pub use metrics::{
106 MetricsBackend, MetricsHandle, NoOpMetrics, RunnerType, TaskOutcome, noop_metrics,
107};