rsonpath/
error.rs

1//! Common errors shared across the library.
2use std::fmt::{self, Display};
3use thiserror::Error;
4
5pub(crate) const FEATURE_REQUEST_URL: &str =
6    "https://github.com/V0ldek/rsonpath/issues/new?template=feature_request.md";
7pub(crate) const BUG_REPORT_URL: &str = "https://github.com/V0ldek/rsonpath/issues/new?template=bug_report.md";
8
9/// Internal irrecoverable error. These are caused solely
10/// by bugs – broken invariants or assertions in internal logic –
11/// but we return those instead of panicking.
12#[derive(Error, Debug)]
13pub struct InternalRsonpathError {
14    details: &'static str,
15    #[source]
16    source: Option<InternalErrorSource>,
17}
18
19/// Errors in internal depth tracking of execution engines.
20#[derive(Error, Debug)]
21pub enum DepthError {
22    /// The engine's maximum depth limit was exceeded.
23    /// The inner [`usize`] indicates that limit.
24    #[error("Maximum depth of {0} exceeded.")]
25    AboveLimit(usize),
26    /// The document has unmatched closing characters
27    /// and is malformed.
28    #[error("Depth fell below zero.")]
29    BelowZero,
30}
31
32struct InternalErrorSource(Box<dyn std::error::Error + Send + Sync>);
33
34impl fmt::Debug for InternalErrorSource {
35    #[inline(always)]
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        fmt::Debug::fmt(&self.0, f)
38    }
39}
40
41impl Display for InternalErrorSource {
42    #[inline(always)]
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        fmt::Display::fmt(&self.0, f)
45    }
46}
47
48impl std::error::Error for InternalErrorSource {
49    #[inline(always)]
50    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
51        self.0.source()
52    }
53}
54
55impl InternalRsonpathError {
56    #[allow(unused)]
57    pub(crate) fn from_expectation(details: &'static str) -> Self {
58        Self { details, source: None }
59    }
60
61    #[allow(unused)]
62    pub(crate) fn from_error<E: std::error::Error + Send + Sync + 'static>(err: E, details: &'static str) -> Self {
63        Self {
64            details,
65            source: Some(InternalErrorSource(Box::new(err))),
66        }
67    }
68}
69
70impl Display for InternalRsonpathError {
71    #[inline]
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        let details_are_enabled = std::env::var("RUST_BACKTRACE").unwrap_or_default() == "0";
74        write!(
75            f,
76            "an internal error has occurred; this is a bug, please report it at {BUG_REPORT_URL}"
77        )?;
78
79        if details_are_enabled {
80            writeln!(f, "; the error details follow")?;
81            write!(f, "{}", self.details)?;
82            if let Some(source) = &self.source {
83                write!(f, "; source: {}", source)?;
84            }
85        }
86
87        Ok(())
88    }
89}
90
91/// Error raised when rsonpath is asked to perform an operation that is currently
92/// unsupported. This may be either because the feature is in the works, or
93/// because it is not planned to ever be supported.
94#[derive(Error, Debug)]
95pub struct UnsupportedFeatureError {
96    issue: Option<usize>,
97    feature: &'static str,
98}
99
100impl UnsupportedFeatureError {
101    #[must_use]
102    #[allow(dead_code)]
103    #[inline(always)]
104    fn tracked(issue: usize, feature: &'static str) -> Self {
105        Self {
106            issue: Some(issue),
107            feature,
108        }
109    }
110
111    #[must_use]
112    #[inline(always)]
113    fn untracked(feature: &'static str) -> Self {
114        Self { issue: None, feature }
115    }
116
117    /// Large JSON Depths feature &ndash; supporting JSON documents
118    /// with nesting depth exceeding 255. Unsupported and not planned.
119    #[must_use]
120    #[inline(always)]
121    pub fn large_json_depths() -> Self {
122        Self::untracked("Large JSON Depths")
123    }
124
125    /// Large Automaton Queries feature &ndash; supporting queries that
126    /// cause compiled DFAs to exceed 256 states. Unsupported and not planned.
127    #[must_use]
128    #[inline(always)]
129    pub fn large_automaton_queries() -> Self {
130        Self::untracked("Large Automaton Queries")
131    }
132
133    /// Multiple Selector per Segment feature &ndash; supporting queries
134    /// that contain a union of selectors per step. Unsupported and not planned (yet).
135    #[must_use]
136    #[inline(always)]
137    pub fn multiple_selectors() -> Self {
138        Self::untracked("Multiple Selector per Segment")
139    }
140
141    /// Indexing from End &ndash; supporting index and slice selectors that
142    /// use from-end indexing. Unsupported and not planned (yet).
143    #[must_use]
144    #[inline(always)]
145    pub fn indexing_from_end() -> Self {
146        Self::untracked("Indexing from End")
147    }
148
149    /// Slice Selector &ndash; supporting slice selectors.
150    /// <https://github.com/V0ldek/rsonpath/issues/152>
151    #[must_use]
152    #[inline(always)]
153    pub fn slice_selector() -> Self {
154        Self::tracked(152, "Slice Selector")
155    }
156
157    /// Slice with Backward Step &ndash; supporting slice selectors that step backwards.
158    /// Unsupported and not planned (yet).
159    #[must_use]
160    #[inline(always)]
161    pub fn slice_with_backward_step() -> Self {
162        Self::untracked("Slice with Backward Step")
163    }
164
165    /// Filter Selector &ndash; supporting filter selectors.
166    /// <https://github.com/V0ldek/rsonpath/issues/154>
167    #[must_use]
168    #[inline(always)]
169    pub fn filter_selector() -> Self {
170        Self::tracked(154, "Filter Selector")
171    }
172
173    /// Returns the issue number on GitHub corresponding to the unsupported feature.
174    /// Is [`None`] if the feature is not planned.
175    #[must_use]
176    #[inline(always)]
177    pub fn issue(&self) -> Option<usize> {
178        self.issue
179    }
180
181    /// Returns the descriptive name of the feature.
182    #[must_use]
183    #[inline(always)]
184    pub fn feature(&self) -> &str {
185        self.feature
186    }
187
188    /// Whether the issue is planned to ever be supported.
189    #[must_use]
190    #[inline(always)]
191    pub fn is_planned(&self) -> bool {
192        self.issue.is_some()
193    }
194}
195
196impl Display for UnsupportedFeatureError {
197    #[inline]
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        match self.issue {
200            Some(issue) => {
201                write!(
202                    f,
203                    "the feature {} (#{}) is not supported yet; it is being tracked and discussed at \
204                    https://github.com/V0ldek/rsonpath/issues/{}",
205                    self.feature, issue, issue
206                )
207            }
208            None => {
209                write!(
210                    f,
211                    "the feature {} is not supported, and is not planned; \
212                    if you would like to see it introduced to rsonpath, please raise a feature request at \
213                    {FEATURE_REQUEST_URL}",
214                    self.feature
215                )
216            }
217        }
218    }
219}