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> crate::lib::std::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: crate::lib::std::fmt::Display, E> crate::lib::std::fmt::Display for Recoverable<I, E>
97where
98    I: Stream,
99{
100    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
101        crate::lib::std::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: crate::lib::std::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    #[inline(always)]
186    fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
187        &self.input
188    }
189}
190
191impl<I, E> Location for Recoverable<I, E>
192where
193    I: Location,
194    I: Stream,
195{
196    #[inline(always)]
197    fn previous_token_end(&self) -> usize {
198        self.input.previous_token_end()
199    }
200    #[inline(always)]
201    fn current_token_start(&self) -> usize {
202        self.input.current_token_start()
203    }
204}
205
206impl<I, E, R> Recover<E> for Recoverable<I, R>
207where
208    I: Stream,
209    R: FromRecoverableError<Self, E>,
210    R: crate::lib::std::fmt::Debug,
211    E: crate::error::ParserError<Self>,
212{
213    fn record_err(
214        &mut self,
215        token_start: &Self::Checkpoint,
216        err_start: &Self::Checkpoint,
217        err: E,
218    ) -> Result<(), E> {
219        if self.is_recoverable {
220            if err.is_incomplete() {
221                Err(err)
222            } else {
223                self.errors
224                    .push(R::from_recoverable_error(token_start, err_start, self, err));
225                Ok(())
226            }
227        } else {
228            Err(err)
229        }
230    }
231
232    /// Report whether the [`Stream`] can save off errors for recovery
233    #[inline(always)]
234    fn is_recovery_supported() -> bool {
235        true
236    }
237}
238
239impl<I, E> StreamIsPartial for Recoverable<I, E>
240where
241    I: StreamIsPartial,
242    I: Stream,
243{
244    type PartialState = I::PartialState;
245
246    #[inline]
247    fn complete(&mut self) -> Self::PartialState {
248        self.input.complete()
249    }
250
251    #[inline]
252    fn restore_partial(&mut self, state: Self::PartialState) {
253        self.input.restore_partial(state);
254    }
255
256    #[inline(always)]
257    fn is_partial_supported() -> bool {
258        I::is_partial_supported()
259    }
260
261    #[inline(always)]
262    fn is_partial(&self) -> bool {
263        self.input.is_partial()
264    }
265}
266
267impl<I, E> Offset for Recoverable<I, E>
268where
269    I: Stream,
270    E: crate::lib::std::fmt::Debug,
271{
272    #[inline(always)]
273    fn offset_from(&self, other: &Self) -> usize {
274        self.offset_from(&other.checkpoint())
275    }
276}
277
278impl<I, E> Offset<<Recoverable<I, E> as Stream>::Checkpoint> for Recoverable<I, E>
279where
280    I: Stream,
281    E: crate::lib::std::fmt::Debug,
282{
283    #[inline(always)]
284    fn offset_from(&self, other: &<Recoverable<I, E> as Stream>::Checkpoint) -> usize {
285        self.checkpoint().offset_from(other)
286    }
287}
288
289impl<I, E> AsBytes for Recoverable<I, E>
290where
291    I: Stream,
292    I: AsBytes,
293{
294    #[inline(always)]
295    fn as_bytes(&self) -> &[u8] {
296        self.input.as_bytes()
297    }
298}
299
300impl<I, E> AsBStr for Recoverable<I, E>
301where
302    I: Stream,
303    I: AsBStr,
304{
305    #[inline(always)]
306    fn as_bstr(&self) -> &[u8] {
307        self.input.as_bstr()
308    }
309}
310
311impl<I, E, U> Compare<U> for Recoverable<I, E>
312where
313    I: Stream,
314    I: Compare<U>,
315{
316    #[inline(always)]
317    fn compare(&self, other: U) -> CompareResult {
318        self.input.compare(other)
319    }
320}
321
322impl<I, E, T> FindSlice<T> for Recoverable<I, E>
323where
324    I: Stream,
325    I: FindSlice<T>,
326{
327    #[inline(always)]
328    fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>> {
329        self.input.find_slice(substr)
330    }
331}
332
333impl<I, E> UpdateSlice for Recoverable<I, E>
334where
335    I: Stream,
336    I: UpdateSlice,
337    E: crate::lib::std::fmt::Debug,
338{
339    #[inline(always)]
340    fn update_slice(mut self, inner: Self::Slice) -> Self {
341        self.input = I::update_slice(self.input, inner);
342        self
343    }
344}