rsonpath/result.rs
1//! Result types that can be returned by a JSONPath query engine.
2use crate::{depth::Depth, engine::error::EngineError};
3use std::{convert::Infallible, fmt::Display, io, ops::Deref};
4
5pub mod approx_span;
6pub mod count;
7pub mod empty;
8pub mod index;
9pub mod nodes;
10mod output_queue;
11
12/// Result of counting query matches.
13pub type MatchCount = u64;
14
15/// Representation of the starting index of a match.
16pub type MatchIndex = usize;
17
18/// Span of a match – its start and end index.
19///
20/// The end index is **exclusive**. For example, the value
21/// `true` may have the span of `(17, 21)`, meaning that
22/// the first character, 't', occurs at index 17, and the last
23/// character, `e` occurs at index 20.
24///
25/// This is in line with what a `[17..21]` slice in Rust represents.
26#[derive(Clone, Copy)]
27pub struct MatchSpan {
28 /// Starting index of the match.
29 start_idx: MatchIndex,
30 /// Length of the match
31 len: usize,
32}
33
34/// Full information of a query match – its span and the input bytes
35/// in that span.
36pub struct Match {
37 /// JSON contents of the match.
38 bytes: Vec<u8>,
39 /// Starting index of the match.
40 span_start: usize,
41}
42
43impl MatchSpan {
44 pub(crate) fn from_indices(start_idx: usize, end_idx: usize) -> Self {
45 assert!(
46 start_idx <= end_idx,
47 "start of span {} is greater than end {}",
48 start_idx,
49 end_idx
50 );
51 Self {
52 start_idx,
53 len: end_idx - start_idx,
54 }
55 }
56
57 /// Returns the starting index of the match.
58 #[inline(always)]
59 #[must_use]
60 pub fn start_idx(&self) -> usize {
61 self.start_idx
62 }
63
64 /// Returns the end index of the match.
65 #[inline(always)]
66 #[must_use]
67 pub fn end_idx(&self) -> usize {
68 self.start_idx + self.len
69 }
70
71 /// Returns the length of the match.
72 #[inline(always)]
73 #[must_use]
74 #[allow(clippy::len_without_is_empty)] // is_empty makes no sense for a match (matches are non-empty)
75 pub fn len(&self) -> usize {
76 self.len
77 }
78}
79
80impl Match {
81 pub(crate) fn from_start_and_bytes(span_start: usize, bytes: Vec<u8>) -> Self {
82 Self { bytes, span_start }
83 }
84
85 /// Returns the JSON contents of the match.
86 #[inline(always)]
87 #[must_use]
88 pub fn bytes(&self) -> &[u8] {
89 &self.bytes
90 }
91
92 /// Consumes the [`Match`] to take ownership of the underlying JSON bytes.
93 #[inline(always)]
94 #[must_use]
95 pub fn into_bytes(self) -> Vec<u8> {
96 self.bytes
97 }
98
99 /// Returns the span of this match in the JSON document:
100 /// its starting and ending byte indices.
101 #[inline(always)]
102 #[must_use]
103 pub fn span(&self) -> MatchSpan {
104 MatchSpan {
105 start_idx: self.span_start,
106 len: self.bytes.len(),
107 }
108 }
109}
110
111impl Display for MatchSpan {
112 #[inline]
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 write!(f, "[{}..{}]", self.start_idx, self.end_idx())
115 }
116}
117
118impl Display for Match {
119 #[inline]
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 let display = String::from_utf8_lossy(&self.bytes);
122 write!(f, "{display}")
123 }
124}
125
126/// Output sink consuming matches of the type `D`.
127pub trait Sink<D> {
128 /// Error type that can be raised when consuming a match.
129 type Error: std::error::Error + Send + Sync + 'static;
130
131 /// Consume a single match of type `D`.
132 ///
133 /// # Errors
134 /// An error depending on the implementor can be raised.
135 /// For example, implementations using an underlying [`io::Write`]
136 /// may raise an [`io::Error`].
137 fn add_match(&mut self, data: D) -> Result<(), Self::Error>;
138}
139
140impl<D> Sink<D> for Vec<D> {
141 type Error = Infallible;
142
143 #[inline(always)]
144 fn add_match(&mut self, data: D) -> Result<(), Infallible> {
145 self.push(data);
146 Ok(())
147 }
148}
149
150/// Empty sink that consumes all matches into the void.
151pub struct NullSink;
152
153impl<D> Sink<D> for NullSink {
154 type Error = Infallible;
155
156 #[inline(always)]
157 fn add_match(&mut self, _data: D) -> Result<(), Infallible> {
158 Ok(())
159 }
160}
161
162/// Thin wrapper over an [`io::Write`] to provide a [`Sink`] impl.
163pub struct MatchWriter<W>(W);
164
165impl<W> From<W> for MatchWriter<W>
166where
167 W: io::Write,
168{
169 #[inline(always)]
170 fn from(value: W) -> Self {
171 Self(value)
172 }
173}
174
175impl<D, W> Sink<D> for MatchWriter<W>
176where
177 D: Display,
178 W: io::Write,
179{
180 type Error = io::Error;
181
182 #[inline(always)]
183 fn add_match(&mut self, data: D) -> Result<(), io::Error> {
184 writeln!(self.0, "{data}")
185 }
186}
187
188/// Type of a value being reported to a [`Recorder`].
189#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
190pub enum MatchedNodeType {
191 /// JSON string, number, or literal value.
192 Atomic,
193 /// JSON object or array.
194 Complex,
195}
196
197/// Base trait of any recorder, one that can react to a block of input being processed.
198pub trait InputRecorder<B: Deref<Target = [u8]>> {
199 /// Record that all processing of a block was started
200 ///
201 /// The recorder may assume that only matches or terminators with indices pointing to
202 /// the block that was last recorded as started are reported.
203 fn record_block_start(&self, new_block: B);
204}
205
206/// An observer that can determine the query result
207/// based on match and structural events coming from the execution engine.
208pub trait Recorder<B: Deref<Target = [u8]>>: InputRecorder<B> {
209 /// Record a match of the query at a given `depth`.
210 /// The `idx` is guaranteed to be the first character of the matched value.
211 ///
212 /// The type MUST accurately describe the value being matched.
213 ///
214 /// # Errors
215 /// An error can be raised if an output write occurs and the underlying [`Sink`] implementation
216 /// returns an error ([`EngineError::SinkError`]).
217 fn record_match(&self, idx: usize, depth: Depth, ty: MatchedNodeType) -> Result<(), EngineError>;
218
219 /// Record a structural character signifying the end of a value at a given `idx`
220 /// and with given `depth`.
221 ///
222 /// # Errors
223 /// An error can be raised if an output write occurs and the underlying [`Sink`] implementation
224 /// returns an error ([`EngineError::SinkError`]), or if the terminator was not expected
225 /// ([`EngineError::MissingOpeningCharacter`]).
226 fn record_value_terminator(&self, idx: usize, depth: Depth) -> Result<(), EngineError>;
227}