graven/validate.rs
1//! Results of the validation rules declared in a schema (`range`, `len`, `finite`, `known`,
2//! `check(name)`).
3//!
4//! Validation is separate from `verify`: `verify` checks that bytes form a well-formed
5//! message, while validation checks that its values follow the schema's rules. Generated code
6//! exposes it as `XVerified::validate(&checks)` and `XW::to_bytes_validated`. Reading never
7//! runs it implicitly, so data written under older rules stays readable.
8
9use core::fmt;
10
11use crate::Error;
12
13/// The rule a value broke.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Rule {
16 /// `range(min = …, max = …)`: a number outside its bounds (NaN is always outside).
17 Range,
18 /// `len(min = …, max = …)`: a string or `bytes` length (in bytes), or a list's element
19 /// count, outside its bounds.
20 Len,
21 /// `finite`: a float that is NaN or infinite.
22 Finite,
23 /// `known`: an enum value the reader's schema does not declare.
24 Known,
25 /// `check(name)`: the application's function `name` returned `false`.
26 Check(&'static str),
27}
28
29impl fmt::Display for Rule {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 Rule::Range => f.write_str("range"),
33 Rule::Len => f.write_str("len"),
34 Rule::Finite => f.write_str("finite"),
35 Rule::Known => f.write_str("known"),
36 Rule::Check(name) => write!(f, "check({name})"),
37 }
38 }
39}
40
41/// A value that breaks a rule of the schema.
42///
43/// `table` and `field` name where the rule is declared (`field` is `None` for a check on the
44/// whole table). `index` is the position in the nearest enclosing list: the element that
45/// failed in a list of enums, or the element of a list of tables in which the failing value
46/// sits.
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct Invalid {
49 pub table: &'static str,
50 pub field: Option<&'static str>,
51 pub index: Option<u32>,
52 pub rule: Rule,
53}
54
55impl fmt::Display for Invalid {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.write_str(self.table)?;
58 if let Some(field) = self.field {
59 write!(f, ".{field}")?;
60 }
61 if let Some(i) = self.index {
62 write!(f, "[{i}]")?;
63 }
64 write!(f, ": breaks rule `{}`", self.rule)
65 }
66}
67
68impl core::error::Error for Invalid {}
69
70/// The error of `validate` and `to_bytes_validated`: a rule was broken, or the message could
71/// not be read or written.
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum ValidateError {
74 Invalid(Invalid),
75 Read(Error),
76}
77
78impl ValidateError {
79 /// Records the list position `i` on a failure that does not have one yet (used by
80 /// generated code when it descends into the elements of a list).
81 #[must_use]
82 pub fn at(self, i: usize) -> Self {
83 match self {
84 ValidateError::Invalid(mut v) if v.index.is_none() => {
85 v.index = u32::try_from(i).ok();
86 ValidateError::Invalid(v)
87 }
88 other => other,
89 }
90 }
91}
92
93impl From<Invalid> for ValidateError {
94 fn from(v: Invalid) -> Self {
95 ValidateError::Invalid(v)
96 }
97}
98
99impl From<Error> for ValidateError {
100 fn from(e: Error) -> Self {
101 ValidateError::Read(e)
102 }
103}
104
105impl fmt::Display for ValidateError {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 match self {
108 ValidateError::Invalid(v) => v.fmt(f),
109 ValidateError::Read(e) => e.fmt(f),
110 }
111 }
112}
113
114impl core::error::Error for ValidateError {}