Skip to main content

stix_pattern/
error.rs

1//! Error and source-span types for parsing.
2
3use serde::{Deserialize, Serialize};
4use thiserror::Error;
5
6/// A half-open byte range `[start, end)` into the original pattern string.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8pub struct Span {
9    /// The inclusive start byte offset.
10    pub start: usize,
11    /// The exclusive end byte offset.
12    pub end: usize,
13}
14
15impl Span {
16    /// Creates a span from start and end byte offsets.
17    pub fn new(start: usize, end: usize) -> Self {
18        Span { start, end }
19    }
20}
21
22/// An error produced while lexing or parsing a STIX pattern.
23#[derive(Debug, Clone, PartialEq, Eq, Error)]
24#[error("parse error at bytes {}..{}: {message}", .span.start, .span.end)]
25pub struct ParseError {
26    /// A human-readable description of what went wrong.
27    pub message: String,
28    /// Where in the source the error occurred.
29    pub span: Span,
30}
31
32impl ParseError {
33    /// Creates a parse error from a message and the offending span.
34    pub fn new(message: impl Into<String>, span: Span) -> Self {
35        ParseError {
36            message: message.into(),
37            span,
38        }
39    }
40}
41
42/// Convenience alias for results in this crate.
43pub type Result<T> = std::result::Result<T, ParseError>;
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn span_records_offsets() {
51        let span = Span::new(3, 7);
52        assert_eq!(span.start, 3);
53        assert_eq!(span.end, 7);
54    }
55
56    #[test]
57    fn parse_error_displays_with_span() {
58        let err = ParseError::new("unexpected token", Span::new(5, 6));
59        let msg = format!("{err}");
60        assert!(msg.contains("unexpected token"), "got: {msg}");
61        assert!(msg.contains('5'), "span start should appear: {msg}");
62    }
63}