cddl_cat/
util.rs

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