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 {}