Skip to main content

ddquery_core/
error.rs

1//! Parse error type.
2//!
3//! A [`ParseError`] is intentionally small: a byte `offset` into the original
4//! query plus a one-line, human-readable `reason`. It never carries the input
5//! string itself, so it is cheap to clone and safe to log. The parser never
6//! panics on hostile input — every failure path returns one of these.
7
8use std::fmt;
9
10use serde::{Deserialize, Serialize};
11
12/// An error produced while parsing a monitor query.
13///
14/// Carries the byte `offset` at which parsing failed and a short `reason`.
15/// Offsets are always on a UTF-8 character boundary of the original input.
16///
17/// # Examples
18///
19/// ```
20/// use ddquery_core::parse;
21///
22/// let err = parse("avg(last_5m)").unwrap_err();
23/// assert!(err.reason().contains("expected"));
24/// ```
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "camelCase")]
27pub struct ParseError {
28    offset: usize,
29    reason: String,
30}
31
32impl ParseError {
33    /// Create a new error at `offset` with the given `reason`.
34    #[must_use]
35    pub fn new(offset: usize, reason: impl Into<String>) -> Self {
36        Self {
37            offset,
38            reason: reason.into(),
39        }
40    }
41
42    /// Byte offset into the original query where parsing failed.
43    #[must_use]
44    pub fn offset(&self) -> usize {
45        self.offset
46    }
47
48    /// One-line, human-readable failure reason.
49    #[must_use]
50    pub fn reason(&self) -> &str {
51        &self.reason
52    }
53}
54
55impl fmt::Display for ParseError {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        write!(f, "parse error at byte {}: {}", self.offset, self.reason)
58    }
59}
60
61impl std::error::Error for ParseError {}