Skip to main content

cddl_cat/
util.rs

1//! This module defines error and result types.
2//!
3
4use std::result::Result;
5
6use thiserror::Error;
7
8use crate::parser;
9
10/// A basic error type that contains a string.
11#[allow(missing_docs)]
12#[non_exhaustive]
13#[derive(Debug, Error)]
14pub enum ValidateError {
15    /// An error during CDDL parsing.
16    #[error(transparent)]
17    ParseError(#[from] parser::ParseError),
18    /// A logical error in the CDDL structure.
19    #[error("Structural({0})")]
20    Structural(String),
21    /// A data mismatch during validation.
22    // The difference between Mismatch and MapCut is that they trigger
23    // slightly different internal behavior; to a human reader they mean
24    // the same thing so we will Display them the same way.
25    #[error("Mismatch(expected {})", .0.expected)]
26    Mismatch(Mismatch),
27    /// A map key-value cut error.
28    #[error("Mismatch(expected {})", .0.expected)]
29    MapCut(Mismatch),
30    /// A CDDL rule lookup failed.
31    #[error("MissingRule({0})")]
32    MissingRule(String),
33    /// A CDDL feature that is unsupported.
34    #[error("Unsupported {0}")]
35    Unsupported(String),
36    /// A data value that can't be validated by CDDL.
37    #[error("ValueError({0})")]
38    ValueError(String),
39    /// A generic type parameter was used incorrectly.
40    #[error("GenericError")]
41    GenericError,
42}
43
44impl ValidateError {
45    /// Identify whether this error is fatal
46    ///
47    /// A "fatal" error is one that should fail the entire validation, even if
48    /// it occurs inside a choice or occurrence that might otherwise succeed.
49    pub(crate) fn is_fatal(&self) -> bool {
50        !matches!(self, ValidateError::Mismatch(_) | ValidateError::MapCut(_))
51    }
52
53    /// Convert a MapCut error to a Mismatch error; otherwise return the original error.
54    pub(crate) fn erase_mapcut(self) -> ValidateError {
55        match self {
56            ValidateError::MapCut(m) => ValidateError::Mismatch(m),
57            _ => self,
58        }
59    }
60
61    pub(crate) fn is_mismatch(&self) -> bool {
62        matches!(self, ValidateError::Mismatch(_))
63    }
64}
65
66/// A data mismatch during validation.
67///
68/// If the CDDL specified an `int` and the data contained a string, this is
69/// the error that would result.
70#[derive(Debug, PartialEq, Eq)]
71pub struct Mismatch {
72    expected: String,
73}
74
75/// Shortcut for creating mismatch errors.
76#[doc(hidden)]
77pub fn mismatch<E: Into<String>>(expected: E) -> ValidateError {
78    ValidateError::Mismatch(Mismatch {
79        expected: expected.into(),
80    })
81}
82
83/// A validation that doesn't return anything.
84pub type ValidateResult = Result<(), ValidateError>;
85
86// Some utility functions that are helpful when testing whether the right
87// error was returned.
88#[doc(hidden)]
89pub trait ErrorMatch {
90    fn err_mismatch(&self);
91    fn err_missing_rule(&self);
92    fn err_generic(&self);
93    fn err_parse(&self);
94    fn err_structural(&self);
95}
96
97impl ErrorMatch for ValidateResult {
98    #[track_caller]
99    fn err_mismatch(&self) {
100        match self {
101            Err(ValidateError::Mismatch(_)) => (),
102            _ => panic!("expected Mismatch, got {:?}", self),
103        }
104    }
105
106    #[track_caller]
107    fn err_missing_rule(&self) {
108        match self {
109            Err(ValidateError::MissingRule(_)) => (),
110            _ => panic!("expected MissingRule, got {:?}", self),
111        }
112    }
113
114    #[track_caller]
115    fn err_generic(&self) {
116        match self {
117            Err(ValidateError::GenericError) => (),
118            _ => panic!("expected GenericError, got {:?}", self),
119        }
120    }
121
122    #[track_caller]
123    fn err_parse(&self) {
124        match self {
125            Err(ValidateError::ParseError(_)) => (),
126            _ => panic!("expected ParseError, got {:?}", self),
127        }
128    }
129
130    #[track_caller]
131    fn err_structural(&self) {
132        match self {
133            Err(ValidateError::Structural(_)) => (),
134            _ => panic!("expected Structural, got {:?}", self),
135        }
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_error_extras() {
145        let e: ValidateError = mismatch("");
146        assert!(!e.is_fatal());
147
148        let e = ValidateError::Structural("".into());
149        assert!(e.is_fatal());
150    }
151}