rydantic = "=0.8.0-beta.1".
See release status and breaking Context changes; the default
installation remains stable 0.7.0.
Typed pipelines
rydantic::pipeline::Pipeline composes ordinary Rust callbacks with a typed input,
output, application context and error type. Closure implementation types stay
private. plain constructs a stage, then transforms its output, and check
borrows the output without changing it. Execution is sequential and fail-fast:
when a stage fails, later stages do not run. Use ValidationError in field hooks
and ValidationErrors in model hooks; the pipeline does not flatten or discard
error collections.
#[model(before = numeric_input)] on a u32 field. The declared type,
built-in field constraints and later callbacks still execute. The normal field
path and parse-stage annotations still apply. Construction transformations are
not rerun by typed validate or dump.
For a complete model stage, start with Pipeline::plain(|value: Value, _: &()| MyModel::parse_value(value)). That preserves all parsing errors and the required
async-validation guard. A later transformation that mutates the model must
explicitly call validate before returning it if model validity is required.
A pipeline itself is neither a validated-value wrapper nor a proof of validity.
Plain and wrap are explicit control flow
plain implements only the selected pipeline stage. It does not replace the
generated Model decoder, its field rules, or its required asynchronous checks.
There are no new derive-level plain or wrap attributes in this increment and
no claim of Pydantic hook parity.
pipeline.wrap(|input, context, next| ...) intercepts that pipeline. next can
be invoked zero times, once, or multiple times. For example,
next(input, context).or_else(|_| next(fallback, context)) retries explicitly.
Skipping it skips that pipeline’s checks. Retrying repeats its side effects;
failed-attempt errors are returned or discarded exactly as the wrapper chooses.
Nothing is automatically retried, rolled back or merged. Do not use retries for
non-idempotent side effects unless the application deliberately handles them.
An ordinary generated field callback containing such a wrapper cannot suppress
the surrounding generated decoder’s rules. Conversely, a wrapper around a
complete model parsing stage can bypass that stage deliberately, so callers must
not call its output validated unless they subsequently validate it.
Async pipelines
AsyncPipeline::plain, then, check and wrap follow the same sequencing and
failure rules. Callbacks return rydantic::BoxFuture; e.g. Box::pin(async move { ... }). The async wrapper receives the preceding pipeline and calls
next.run(input, context).await to continue. To parse a model with async rules,
use its parse_value_async entry point inside an awaited stage.
Async stages use owned Send + 'static inputs, outputs, errors and captured
configuration. The per-call &C is borrowed until the future completes; C must
be Sync + 'static. check also requires a Sync output. Synchronous pipelines
can instead borrow captured configuration and input/output data for their
ordinary Rust lifetimes. There is no requirement to copy application data into a
JSON value or to use global variables.
Dropping a pending run drops the active future and prevents later stages from
starting. No task is spawned by the library. Completed side effects are not
undone, and synchronous work inside a future poll cannot be preempted. There is
no built-in timeout, task scheduler, parallel check execution or cancellation
token; those remain explicit application policy.