Skip to main content

vest_lib/core/exec/
error.rs

1//! Runtime parse errors.
2use core::fmt;
3
4#[cfg(feature = "error-trace")]
5use alloc::vec::Vec;
6
7use vstd::prelude::*;
8
9verus! {
10
11/// A minimal runtime parser failure.
12///
13/// Vest parsers work on progressively sliced inputs, so a globally meaningful byte offset is not
14/// available unless the caller explicitly threads that information through the parser stack.
15/// Instead, this error carries a coarse-grained failure kind plus the name of the innermost
16/// DSL-defined format that failed. With the `error-trace` feature it also retains the full
17/// named-format call stack (at the cost of heap allocation).
18#[derive(Debug, PartialEq, Eq)]
19pub struct ParseError {
20    /// The kind of failure that occurred.
21    pub kind: ParseErrorKind,
22    /// The innermost named format known to have failed.
23    pub failed_format: Option<&'static str>,
24    /// The named-format call stack that led to the failure, stored innermost-first.
25    #[cfg(feature = "error-trace")]
26    pub format_stack: Vec<&'static str>,
27}
28
29impl Clone for ParseError {
30    fn clone(&self) -> Self {
31        Self {
32            kind: self.kind.clone(),
33            failed_format: self.failed_format,
34            #[cfg(feature = "error-trace")]
35            format_stack: self.format_stack.clone(),
36        }
37    }
38}
39
40impl ParseError {
41    /// Creates a new parse error.
42    pub fn new(kind: ParseErrorKind) -> (e: Self)
43        ensures
44            e.kind == kind,
45    {
46        Self {
47            kind,
48            failed_format: None,
49            #[cfg(feature = "error-trace")]
50            format_stack: Vec::new(),
51        }
52    }
53
54    /// Pushes one named format onto the failing format stack.
55    ///
56    /// The first pushed format becomes `current_format`, so `current_format` continues to refer to
57    /// the innermost failing named format even as outer formats append themselves during
58    /// propagation.
59    pub fn push_format(self, current_format: &'static str) -> Self {
60        let mut err = self;
61        if err.failed_format.is_none() {
62            err.failed_format = Some(current_format);
63        }
64        #[cfg(feature = "error-trace")]
65        {
66            err.format_stack.push(current_format);
67        }
68        err
69    }
70
71    /// Returns the recorded format stack as an innermost-first slice.
72    pub fn format_trace(&self) -> &[&'static str] {
73        #[cfg(feature = "error-trace")]
74        { self.format_stack.as_slice() }
75        #[cfg(not(feature = "error-trace"))]
76        { &[] }
77    }
78}
79
80impl ParseError {
81    /// Creates an unexpected end-of-input error.
82    pub fn unexpected_eof() -> (e: Self)
83        ensures
84            e.kind == ParseErrorKind::UnexpectedEof,
85    {
86        Self::new(ParseErrorKind::UnexpectedEof)
87    }
88
89    /// Creates an expecting end-of-input error.
90    pub fn expecting_eof() -> Self {
91        Self::new(ParseErrorKind::ExpectingEof)
92    }
93
94    /// Creates an invalid-tag error.
95    pub fn invalid_tag() -> Self {
96        Self::new(ParseErrorKind::InvalidTag)
97    }
98
99    /// Creates an invalid-choice error.
100    pub fn invalid_choice() -> Self {
101        Self::new(ParseErrorKind::InvalidChoice)
102    }
103
104    /// Creates an invalid-length error.
105    pub fn invalid_length() -> Self {
106        Self::new(ParseErrorKind::InvalidLength)
107    }
108
109    /// Creates a length-mismatch error.
110    pub fn length_mismatch() -> Self {
111        Self::new(ParseErrorKind::LengthMismatch)
112    }
113
114    /// Creates a predicate-failed error.
115    pub fn predicate_failed() -> Self {
116        Self::new(ParseErrorKind::PredicateFailed)
117    }
118
119    /// Creates a condition-rejected error.
120    pub fn cond_rejected() -> Self {
121        Self::new(ParseErrorKind::CondRejected)
122    }
123
124    /// Creates a non-canonical-encoding error.
125    pub fn non_canonical() -> Self {
126        Self::new(ParseErrorKind::NonCanonical)
127    }
128
129    /// Creates an overflow error.
130    pub fn overflow() -> (e: Self)
131        ensures
132            e.kind == ParseErrorKind::Overflow,
133    {
134        Self::new(ParseErrorKind::Overflow)
135    }
136
137    /// Creates a recursion-limit-exceeded error.
138    pub fn recursion_limit_exceeded() -> Self {
139        Self::new(ParseErrorKind::RecursionLimitExceeded)
140    }
141
142    /// Creates a custom error with the given message.
143    pub fn custom(msg: &'static str) -> Self {
144        Self::new(ParseErrorKind::Custom(msg))
145    }
146}
147
148} // verus!
149impl fmt::Display for ParseError {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        let format_trace = self.format_trace();
152        if !format_trace.is_empty() {
153            write!(f, "{} while parsing format stack ", self.kind)?;
154            for (i, format_name) in format_trace.iter().rev().enumerate() {
155                if i > 0 {
156                    f.write_str(" -> ")?;
157                }
158                write!(f, "`{}`", format_name)?;
159            }
160            Ok(())
161        } else {
162            match self.failed_format {
163                Some(current_format) => {
164                    write!(f, "{} while parsing format `{}`", self.kind, current_format)
165                }
166                None => write!(f, "{}", self.kind),
167            }
168        }
169    }
170}
171
172#[cfg(feature = "std")]
173impl std::error::Error for ParseError {}
174
175verus! {
176
177/// The coarse-grained kind of parse failure.
178#[derive(Debug, Copy, PartialEq, Eq)]
179pub enum ParseErrorKind {
180    /// The parser needed more input bytes.
181    UnexpectedEof,
182    /// The parser expected the end of input, but more bytes were present.
183    ExpectingEof,
184    /// A tag or discriminant byte sequence was invalid.
185    InvalidTag,
186    /// Input did not match any branch of a choice-like format.
187    InvalidChoice,
188    /// A parsed length field was invalid.
189    InvalidLength,
190    /// A computed length disagreed with the enclosing format.
191    LengthMismatch,
192    /// A refinement predicate or semantic check failed.
193    PredicateFailed,
194    /// A branch was rejected after partial inspection.
195    CondRejected,
196    /// A non-canonical representation was observed.
197    NonCanonical,
198    /// An integer or size computation overflowed.
199    Overflow,
200    /// Recursion limit exceeded.
201    RecursionLimitExceeded,
202    /// A custom error message.
203    Custom(&'static str),
204}
205
206impl Clone for ParseErrorKind {
207    fn clone(&self) -> Self {
208        *self
209    }
210}
211
212} // verus!
213impl fmt::Display for ParseErrorKind {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        match self {
216            ParseErrorKind::UnexpectedEof => {
217                f.write_str("input ended before the format could finish parsing")
218            }
219            ParseErrorKind::ExpectingEof => f.write_str("unexpected trailing input"),
220            ParseErrorKind::InvalidTag => {
221                f.write_str("tag or discriminant did not match the format")
222            }
223            ParseErrorKind::InvalidChoice => f.write_str("input did not match any choice branch"),
224            ParseErrorKind::InvalidLength => {
225                f.write_str("length field is outside the format's valid range")
226            }
227            ParseErrorKind::LengthMismatch => {
228                f.write_str("a length-delimited parser did not consume the declared length")
229            }
230            ParseErrorKind::PredicateFailed => {
231                f.write_str("parsed value failed a refinement predicate")
232            }
233            ParseErrorKind::CondRejected => f.write_str("conditional format rejected this branch"),
234            ParseErrorKind::NonCanonical => f.write_str("non-canonical encoding"),
235            ParseErrorKind::Overflow => f.write_str("integer or size computation overflowed"),
236            ParseErrorKind::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
237            ParseErrorKind::Custom(s) => f.write_str(s),
238        }
239    }
240}