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}