Skip to main content

error_chain/
error_chain.rs

1//! Inspecting validation errors: the `ErrorKind` classification and the
2//! `std::error::Error::source()` cause chain.
3//!
4//! Every [`ValidationError`] carries a machine-readable [`ErrorKind`] so callers
5//! can branch on *why* a rule failed — the object was invalid, the rule hit a
6//! runtime error, or the rule used a CEL feature this build cannot evaluate. For
7//! the runtime cases the underlying `cel` execution error is preserved and
8//! reachable via `source()`, so you can walk the cause chain like any other
9//! `std::error::Error`.
10//!
11//! Run with: `cargo run --example error_chain --features validation`
12
13use std::error::Error;
14
15use kube_cel::{ErrorKind, Validator};
16use serde_json::json;
17
18fn main() {
19    // Three rules, each failing a different way against the same object.
20    let schema = json!({
21        "type": "object",
22        "x-kubernetes-validations": [
23            // (1) the object simply violates the rule -> ValidationFailure
24            {"rule": "self.replicas >= 1", "message": "replicas must be at least 1"},
25            // (2) the rule references a field that isn't there -> EvaluationError
26            //     (compiles fine, errors at runtime; the cel error is the cause)
27            {"rule": "self.maxSurge > 0", "message": "maxSurge must be positive"},
28            // (3) the rule uses a CEL macro cel 0.13 can't evaluate -> UnsupportedReference
29            {"rule": "self.zones.sortBy(z, z) == self.zones", "message": "zones must be sorted"}
30        ],
31        "properties": {
32            "replicas": {"type": "integer"},
33            "zones": {"type": "array", "items": {"type": "string"}}
34        }
35    });
36
37    let object = json!({"replicas": 0, "zones": ["b", "a"]});
38
39    let errors = Validator::new().validate(&schema, &object, None);
40
41    println!("{} validation error(s)\n", errors.len());
42    for err in &errors {
43        println!("rule: {}", err.rule);
44        println!("  kind: {:?}", err.kind);
45        println!("  message: {err}");
46
47        // Branch on the classification. `ErrorKind` is `#[non_exhaustive]`, so a
48        // wildcard arm is required.
49        match err.kind {
50            ErrorKind::ValidationFailure => {
51                println!("  -> the object violated the rule; reject it");
52            }
53            ErrorKind::EvaluationError => {
54                println!("  -> the rule failed at runtime; likely a malformed object");
55            }
56            ErrorKind::UnsupportedReference => {
57                println!("  -> coverage gap: this kube-cel build can't evaluate the rule");
58            }
59            _ => println!("  -> other ({:?})", err.kind),
60        }
61
62        // Walk the `source()` cause chain. Runtime failures carry the owned `cel`
63        // execution error as their cause; pure ValidationFailure has none.
64        let mut cause = err.source();
65        if cause.is_none() {
66            println!("  (no underlying cause)");
67        }
68        let mut depth = 1;
69        while let Some(e) = cause {
70            println!("  {:indent$}caused by: {e}", "", indent = depth * 2);
71            cause = e.source();
72            depth += 1;
73        }
74        println!();
75    }
76}