1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! This module defines error and result types.
//!

use crate::parser;
use std::error;
use std::fmt;
use std::result::Result;

/// A basic error type that contains a string.
#[allow(missing_docs)]
#[rustversion::attr(since(1.40), non_exhaustive)]
#[derive(Debug, PartialEq)]
pub enum ValidateError {
    /// An error during CDDL parsing.
    ParseError(parser::ParseError),
    /// A logical error in the CDDL structure.
    Structural(String),
    /// A data mismatch during validation.
    Mismatch(Mismatch),
    /// A map key-value cut error.
    MapCut(Mismatch),
    /// A CDDL rule lookup failed.
    MissingRule(String),
    /// A CDDL feature that is unsupported.
    Unsupported(String),
    /// A data value that can't be validated by CDDL.
    ValueError(String),
    /// A generic type parameter was used incorrectly.
    GenericError,
}

impl ValidateError {
    /// Identify whether this error is fatal
    ///
    /// A "fatal" error is one that should fail the entire validation, even if
    /// it occurs inside a choice or occurrence that might otherwise succeed.
    pub(crate) fn is_fatal(&self) -> bool {
        match self {
            ValidateError::Mismatch(_) => false,
            ValidateError::MapCut(_) => false,
            _ => true,
        }
    }

    /// Convert a MapCut error to a Mismatch error; otherwise return the original error.
    pub(crate) fn erase_mapcut(self) -> ValidateError {
        match self {
            ValidateError::MapCut(m) => ValidateError::Mismatch(m),
            _ => self,
        }
    }

    pub(crate) fn is_mismatch(&self) -> bool {
        match self {
            ValidateError::Mismatch(_) => true,
            _ => false,
        }
    }
}

/// A data mismatch during validation.
///
/// If the CDDL specified an `int` and the data contained a string, this is
/// the error that would result.
#[derive(Debug, PartialEq)]
pub struct Mismatch {
    expected: String,
}

/// Shortcut for creating mismatch errors.
#[doc(hidden)]
pub fn mismatch<E: Into<String>>(expected: E) -> ValidateError {
    ValidateError::Mismatch(Mismatch {
        expected: expected.into(),
    })
}

impl fmt::Display for ValidateError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use ValidateError::*;
        match self {
            ParseError(p) => p.fmt(f),
            Structural(msg) => write!(f, "Structural({})", msg),
            // The difference between Mismatch and MapCut is that they trigger
            // slightly different internal behaivor; to a human reader they mean
            // the same thing so we will Display them the same way.
            Mismatch(mismatch) => write!(f, "Mismatch(expected {})", mismatch.expected),
            MapCut(mismatch) => write!(f, "Mismatch(expected {})", mismatch.expected),
            MissingRule(rule) => write!(f, "MissingRule({})", rule),
            Unsupported(msg) => write!(f, "Unsupported {}", msg),
            ValueError(msg) => write!(f, "ValueError({})", msg),
            GenericError => write!(f, "GenericError"),
        }
    }
}

// Standard boilerplate, required so other errors can wrap this one.
impl error::Error for ValidateError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        None
    }
}

/// A validation that doesn't return anything.
pub type ValidateResult = Result<(), ValidateError>;

// Some utility functions that are helpful when testing whether the right
// error was returned.
#[doc(hidden)]
pub trait ErrorMatch {
    fn err_mismatch(&self);
    fn err_missing_rule(&self);
    fn err_generic(&self);
    fn err_parse(&self);
}

impl ErrorMatch for ValidateResult {
    #[rustversion::attr(since(1.46), track_caller)]
    fn err_mismatch(&self) {
        match self {
            Err(ValidateError::Mismatch(_)) => (),
            _ => panic!("expected Mismatch, got {:?}", self),
        }
    }

    #[rustversion::attr(since(1.46), track_caller)]
    fn err_missing_rule(&self) {
        match self {
            Err(ValidateError::MissingRule(_)) => (),
            _ => panic!("expected MissingRule, got {:?}", self),
        }
    }

    #[rustversion::attr(since(1.46), track_caller)]
    fn err_generic(&self) {
        match self {
            Err(ValidateError::GenericError) => (),
            _ => panic!("expected GenericError, got {:?}", self),
        }
    }

    #[rustversion::attr(since(1.46), track_caller)]
    fn err_parse(&self) {
        match self {
            Err(ValidateError::ParseError(_)) => (),
            _ => panic!("expected ParseError, got {:?}", self),
        }
    }
}