Skip to main content

winnow/stream/
recoverable.rs

1use crate::error::FromRecoverableError;
2use crate::error::Needed;
3use crate::stream::AsBStr;
4use crate::stream::AsBytes;
5use crate::stream::Checkpoint;
6use crate::stream::Compare;
7use crate::stream::CompareResult;
8use crate::stream::FindSlice;
9use crate::stream::Location;
10use crate::stream::Offset;
11#[cfg(feature = "unstable-recover")]
12#[cfg(feature = "std")]
13use crate::stream::Recover;
14use crate::stream::SliceLen;
15use crate::stream::Stream;
16use crate::stream::StreamIsPartial;
17use crate::stream::UpdateSlice;
18
19/// Allow recovering from parse errors, capturing them as the parser continues
20///
21/// Generally, this will be used indirectly via
22/// [`RecoverableParser::recoverable_parse`][crate::RecoverableParser::recoverable_parse].
23#[derive(Clone, Debug)]
24pub struct Recoverable<I, E>
25where
26    I: Stream,
27{
28    input: I,
29    errors: Vec<E>,
30    is_recoverable: bool,
31}
32
33impl<I, E> Default for Recoverable<I, E>
34where
35    I: Default + Stream,
36{
37    #[inline]
38    fn default() -> Self {
39        Self::new(I::default())
40    }
41}
42
43impl<I, E> Recoverable<I, E>
44where
45    I: Stream,
46{
47    /// Track recoverable errors with the stream
48    #[inline]
49    pub fn new(input: I) -> Self {
50        Self {
51            input,
52            errors: Default::default(),
53            is_recoverable: true,
54        }
55    }
56
57    /// Act as a normal stream
58    #[inline]
59    pub fn unrecoverable(input: I) -> Self {
60        Self {
61            input,
62            errors: Default::default(),
63            is_recoverable: false,
64        }
65    }
66
67    /// Access the current input and errors
68    #[inline]
69    pub fn into_parts(self) -> (I, Vec<E>) {
70        (self.input, self.errors)
71    }
72}
73
74impl<I, E> AsRef<I> for Recoverable<I, E>
75where
76    I: Stream,
77{
78    #[inline(always)]
79    fn as_ref(&self) -> &I {
80        &self.input
81    }
82}
83
84impl<I, E> core::ops::Deref for Recoverable<I, E>
85where
86    I: Stream,
87{
88    type Target = I;
89
90    #[inline(always)]
91    fn deref(&self) -> &Self::Target {
92        &self.input
93    }
94}
95
96impl<I: core::fmt::Display, E> core::fmt::Display for Recoverable<I, E>
97where
98    I: Stream,
99{
100    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
101        core::fmt::Display::fmt(&self.input, f)
102    }
103}
104
105impl<I, E> SliceLen for Recoverable<I, E>
106where
107    I: SliceLen,
108    I: Stream,
109{
110    #[inline(always)]
111    fn slice_len(&self) -> usize {
112        self.input.slice_len()
113    }
114}
115
116impl<I, E: core::fmt::Debug> Stream for Recoverable<I, E>
117where
118    I: Stream,
119{
120    type Token = <I as Stream>::Token;
121    type Slice = <I as Stream>::Slice;
122
123    type IterOffsets = <I as Stream>::IterOffsets;
124
125    type Checkpoint = Checkpoint<I::Checkpoint, Self>;
126
127    #[inline(always)]
128    fn iter_offsets(&self) -> Self::IterOffsets {
129        self.input.iter_offsets()
130    }
131    #[inline(always)]
132    fn eof_offset(&self) -> usize {
133        self.input.eof_offset()
134    }
135
136    #[inline(always)]
137    fn next_token(&mut self) -> Option<Self::Token> {
138        self.input.next_token()
139    }
140
141    #[inline(always)]
142    fn peek_token(&self) -> Option<Self::Token> {
143        self.input.peek_token()
144    }
145
146    #[inline(always)]
147    fn offset_for<P>(&self, predicate: P) -> Option<usize>
148    where
149        P: Fn(Self::Token) -> bool,
150    {
151        self.input.offset_for(predicate)
152    }
153    #[inline(always)]
154    fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
155        self.input.offset_at(tokens)
156    }
157    #[inline(always)]
158    fn next_slice(&mut self, offset: usize) -> Self::Slice {
159        self.input.next_slice(offset)
160    }
161    #[inline(always)]
162    unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
163        // SAFETY: Passing up invariants
164        unsafe { self.input.next_slice_unchecked(offset) }
165    }
166    #[inline(always)]
167    fn peek_slice(&self, offset: usize) -> Self::Slice {
168        self.input.peek_slice(offset)
169    }
170    #[inline(always)]
171    unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
172        // SAFETY: Passing up invariants
173        unsafe { self.input.peek_slice_unchecked(offset) }
174    }
175
176    #[inline(always)]
177    fn checkpoint(&self) -> Self::Checkpoint {
178        Checkpoint::<_, Self>::new(self.input.checkpoint())
179    }
180    #[inline(always)]
181    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
182        self.input.reset(&checkpoint.inner);
183    }
184
185    fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186        self.input.trace(f)
187    }
188}
189
190impl<I, E> Location for Recoverable<I, E>
191where
192    I: Location,
193    I: Stream,
194{
195    #[inline(always)]
196    fn previous_token_end(&self) -> usize {
197        self.input.previous_token_end()
198    }
199    #[inline(always)]
200    fn current_token_start(&self) -> usize {
201        self.input.current_token_start()
202    }
203}
204
205impl<I, E, R> Recover<E> for Recoverable<I, R>
206where
207    I: Stream,
208    R: FromRecoverableError<Self, E>,
209    R: core::fmt::Debug,
210    E: crate::error::ParserError<Self>,
211{
212    fn record_err(
213        &mut self,
214        token_start: &Self::Checkpoint,
215        err_start: &Self::Checkpoint,
216        err: E,
217    ) -> Result<(), E> {
218        if self.is_recoverable {
219            if err.is_incomplete() {
220                Err(err)
221            } else {
222                self.errors
223                    .push(R::from_recoverable_error(token_start, err_start, self, err));
224                Ok(())
225            }
226        } else {
227            Err(err)
228        }
229    }
230
231    /// Report whether the [`Stream`] can save off errors for recovery
232    #[inline(always)]
233    fn is_recovery_supported() -> bool {
234        true
235    }
236}
237
238impl<I, E> StreamIsPartial for Recoverable<I, E>
239where
240    I: StreamIsPartial,
241    I: Stream,
242{
243    type PartialState = I::PartialState;
244
245    #[inline]
246    fn complete(&mut self) -> Self::PartialState {
247        self.input.complete()
248    }
249
250    #[inline]
251    fn restore_partial(&mut self, state: Self::PartialState) {
252        self.input.restore_partial(state);
253    }
254
255    #[inline(always)]
256    fn is_partial_supported() -> bool {
257        I::is_partial_supported()
258    }
259
260    #[inline(always)]
261    fn is_partial(&self) -> bool {
262        self.input.is_partial()
263    }
264}
265
266impl<I, E> Offset for Recoverable<I, E>
267where
268    I: Stream,
269    E: core::fmt::Debug,
270{
271    #[inline(always)]
272    fn offset_from(&self, other: &Self) -> usize {
273        self.offset_from(&other.checkpoint())
274    }
275}
276
277impl<I, E> Offset<<Recoverable<I, E> as Stream>::Checkpoint> for Recoverable<I, E>
278where
279    I: Stream,
280    E: core::fmt::Debug,
281{
282    #[inline(always)]
283    fn offset_from(&self, other: &<Recoverable<I, E> as Stream>::Checkpoint) -> usize {
284        self.checkpoint().offset_from(other)
285    }
286}
287
288impl<I, E> AsBytes for Recoverable<I, E>
289where
290    I: Stream,
291    I: AsBytes,
292{
293    #[inline(always)]
294    fn as_bytes(&self) -> &[u8] {
295        self.input.as_bytes()
296    }
297}
298
299impl<I, E> AsBStr for Recoverable<I, E>
300where
301    I: Stream,
302    I: AsBStr,
303{
304    #[inline(always)]
305    fn as_bstr(&self) -> &[u8] {
306        self.input.as_bstr()
307    }
308}
309
310impl<I, E, U> Compare<U> for Recoverable<I, E>
311where
312    I: Stream,
313    I: Compare<U>,
314{
315    #[inline(always)]
316    fn compare(&self, other: U) -> CompareResult {
317        self.input.compare(other)
318    }
319}
320
321impl<I, E, T> FindSlice<T> for Recoverable<I, E>
322where
323    I: Stream,
324    I: FindSlice<T>,
325{
326    #[inline(always)]
327    fn find_slice(&self, substr: T) -> Option<core::ops::Range<usize>> {
328        self.input.find_slice(substr)
329    }
330}
331
332impl<I, E> UpdateSlice for Recoverable<I, E>
333where
334    I: Stream,
335    I: UpdateSlice,
336    E: core::fmt::Debug,
337{
338    #[inline(always)]
339    fn update_slice(mut self, inner: Self::Slice) -> Self {
340        self.input = I::update_slice(self.input, inner);
341        self
342    }
343}