1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//! Common errors shared across the library.
use std::fmt::{self, Display};
use thiserror::Error;

pub(crate) const FEATURE_REQUEST_URL: &str =
    "https://github.com/V0ldek/rsonpath/issues/new?template=feature_request.md";
pub(crate) const BUG_REPORT_URL: &str = "https://github.com/V0ldek/rsonpath/issues/new?template=bug_report.md";

/// Internal irrecoverable error. These are caused solely
/// by bugs – broken invariants or assertions in internal logic –
/// but we return those instead of panicking.
#[derive(Error, Debug)]
pub struct InternalRsonpathError {
    details: &'static str,
    #[source]
    source: Option<InternalErrorSource>,
}

/// Errors in internal depth tracking of execution engines.
#[derive(Error, Debug)]
pub enum DepthError {
    /// The engine's maximum depth limit was exceeded.
    /// The inner [`usize`] indicates that limit.
    #[error("Maximum depth of {0} exceeded.")]
    AboveLimit(usize),
    /// The document has unmatched closing characters
    /// and is malformed.
    #[error("Depth fell below zero.")]
    BelowZero,
}

struct InternalErrorSource(Box<dyn std::error::Error + Send + Sync>);

impl fmt::Debug for InternalErrorSource {
    #[inline(always)]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&self.0, f)
    }
}

impl Display for InternalErrorSource {
    #[inline(always)]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl std::error::Error for InternalErrorSource {
    #[inline(always)]
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.0.source()
    }
}

impl InternalRsonpathError {
    #[allow(unused)]
    pub(crate) fn from_expectation(details: &'static str) -> Self {
        Self { details, source: None }
    }

    #[allow(unused)]
    pub(crate) fn from_error<E: std::error::Error + Send + Sync + 'static>(err: E, details: &'static str) -> Self {
        Self {
            details,
            source: Some(InternalErrorSource(Box::new(err))),
        }
    }
}

impl Display for InternalRsonpathError {
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let details_are_enabled = std::env::var("RUST_BACKTRACE").unwrap_or_default() == "0";
        write!(
            f,
            "an internal error has occurred; this is a bug, please report it at {BUG_REPORT_URL}"
        )?;

        if details_are_enabled {
            writeln!(f, "; the error details follow")?;
            write!(f, "{}", self.details)?;
            if let Some(source) = &self.source {
                write!(f, "; source: {}", source)?;
            }
        }

        Ok(())
    }
}

/// Error raised when rsonpath is asked to perform an operation that is currently
/// unsupported. This may be either because the feature is in the works, or
/// because it is not planned to ever be supported.
#[derive(Error, Debug)]
pub struct UnsupportedFeatureError {
    issue: Option<usize>,
    feature: &'static str,
}

impl UnsupportedFeatureError {
    #[must_use]
    #[allow(dead_code)]
    #[inline(always)]
    fn tracked(issue: usize, feature: &'static str) -> Self {
        Self {
            issue: Some(issue),
            feature,
        }
    }

    #[must_use]
    #[inline(always)]
    fn untracked(feature: &'static str) -> Self {
        Self { issue: None, feature }
    }

    /// Large JSON Depths feature &ndash; supporting JSON documents
    /// with nesting depth exceeding 255. Unsupported and not planned.
    #[must_use]
    #[inline(always)]
    pub fn large_json_depths() -> Self {
        Self::untracked("Large JSON Depths")
    }

    /// Large Automaton Queries feature &ndash; supporting queries that
    /// cause compiled DFAs to exceed 256 states. Unsupported and not planned.
    #[must_use]
    #[inline(always)]
    pub fn large_automaton_queries() -> Self {
        Self::untracked("Large Automaton Queries")
    }

    /// Multiple Selector per Segment feature &ndash; supporting queries
    /// that contain a union of selectors per step. Unsupported and not planned (yet).
    #[must_use]
    #[inline(always)]
    pub fn multiple_selectors() -> Self {
        Self::untracked("Multiple Selector per Segment")
    }

    /// Indexing from End &ndash; supporting index and slice selectors that
    /// use from-end indexing. Unsupported and not planned (yet).
    #[must_use]
    #[inline(always)]
    pub fn indexing_from_end() -> Self {
        Self::untracked("Indexing from End")
    }

    /// Slice Selector &ndash; supporting slice selectors.
    /// <https://github.com/V0ldek/rsonpath/issues/152>
    #[must_use]
    #[inline(always)]
    pub fn slice_selector() -> Self {
        Self::tracked(152, "Slice Selector")
    }

    /// Returns the issue number on GitHub corresponding to the unsupported feature.
    /// Is [`None`] if the feature is not planned.
    #[must_use]
    #[inline(always)]
    pub fn issue(&self) -> Option<usize> {
        self.issue
    }

    /// Returns the descriptive name of the feature.
    #[must_use]
    #[inline(always)]
    pub fn feature(&self) -> &str {
        self.feature
    }

    /// Whether the issue is planned to ever be supported.
    #[must_use]
    #[inline(always)]
    pub fn is_planned(&self) -> bool {
        self.issue.is_some()
    }
}

impl Display for UnsupportedFeatureError {
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.issue {
            Some(issue) => {
                write!(
                    f,
                    "the feature {} (#{}) is not supported yet; it is being tracked and discussed at \
                    https://github.com/V0ldek/rsonpath/issues/{}",
                    self.feature, issue, issue
                )
            }
            None => {
                write!(
                    f,
                    "the feature {} is not supported, and is not planned; \
                    if you would like to see it introduced to rsonpath, please raise a feature request at \
                    {FEATURE_REQUEST_URL}",
                    self.feature
                )
            }
        }
    }
}