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()
40 .validate(&schema, &object, None)
41 .expect_err("object violates every rule, so validation must fail");
42
43 println!("{} validation error(s)\n", errors.len());
44 for err in &errors {
45 println!("rule: {}", err.rule);
46 println!(" kind: {:?}", err.kind);
47 println!(" message: {err}");
48
49 // Branch on the classification. `ErrorKind` is `#[non_exhaustive]`, so a
50 // wildcard arm is required.
51 match err.kind {
52 ErrorKind::ValidationFailure => {
53 println!(" -> the object violated the rule; reject it");
54 }
55 ErrorKind::EvaluationError => {
56 println!(" -> the rule failed at runtime; likely a malformed object");
57 }
58 ErrorKind::UnsupportedReference => {
59 println!(" -> coverage gap: this kube-cel build can't evaluate the rule");
60 }
61 _ => println!(" -> other ({:?})", err.kind),
62 }
63
64 // Walk the `source()` cause chain. Runtime failures carry the owned `cel`
65 // execution error as their cause; pure ValidationFailure has none.
66 let mut cause = err.source();
67 if cause.is_none() {
68 println!(" (no underlying cause)");
69 }
70 let mut depth = 1;
71 while let Some(e) = cause {
72 println!(" {:indent$}caused by: {e}", "", indent = depth * 2);
73 cause = e.source();
74 depth += 1;
75 }
76 println!();
77 }
78}