winnow/stream/
locating.rs

1use crate::error::Needed;
2use crate::stream::AsBStr;
3use crate::stream::AsBytes;
4use crate::stream::Checkpoint;
5use crate::stream::Compare;
6use crate::stream::CompareResult;
7use crate::stream::FindSlice;
8use crate::stream::Location;
9use crate::stream::Offset;
10#[cfg(feature = "unstable-recover")]
11#[cfg(feature = "std")]
12use crate::stream::Recover;
13use crate::stream::SliceLen;
14use crate::stream::Stream;
15use crate::stream::StreamIsPartial;
16use crate::stream::UpdateSlice;
17
18/// Allow collecting the span of a parsed token within a slice
19///
20/// Converting byte offsets to line or column numbers is left up to the user, as computing column
21/// numbers requires domain knowledge (are columns byte-based, codepoint-based, or grapheme-based?)
22/// and O(n) iteration over the input to determine codepoint and line boundaries.
23///
24/// [The `line-span` crate](https://docs.rs/line-span/latest/line_span/) can help with converting
25/// byte offsets to line numbers.
26///
27/// See [`Parser::span`][crate::Parser::span] and [`Parser::with_span`][crate::Parser::with_span] for more details
28#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
29#[doc(alias = "LocatingSliceSpan")]
30#[doc(alias = "Located")]
31pub struct LocatingSlice<I> {
32    initial: I,
33    input: I,
34}
35
36impl<I> LocatingSlice<I>
37where
38    I: Clone + Offset,
39{
40    /// Wrap another Stream with span tracking
41    pub fn new(input: I) -> Self {
42        let initial = input.clone();
43        Self { initial, input }
44    }
45
46    #[inline]
47    fn previous_token_end(&self) -> usize {
48        // Assumptions:
49        // - Index offsets is sufficient
50        // - Tokens are continuous
51        self.input.offset_from(&self.initial)
52    }
53    #[inline]
54    fn current_token_start(&self) -> usize {
55        // Assumptions:
56        // - Index offsets is sufficient
57        self.input.offset_from(&self.initial)
58    }
59}
60
61impl<I> LocatingSlice<I>
62where
63    I: Clone + Stream + Offset,
64{
65    /// Reset the stream to the start
66    ///
67    /// This is useful for formats that encode a graph with addresses relative to the start of the
68    /// input.
69    #[doc(alias = "fseek")]
70    #[inline]
71    pub fn reset_to_start(&mut self) {
72        let start = self.initial.checkpoint();
73        self.input.reset(&start);
74    }
75}
76
77impl<I> AsRef<I> for LocatingSlice<I> {
78    #[inline(always)]
79    fn as_ref(&self) -> &I {
80        &self.input
81    }
82}
83
84impl<I: core::fmt::Debug> core::fmt::Debug for LocatingSlice<I> {
85    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
86        self.input.fmt(f)
87    }
88}
89
90impl<I> crate::lib::std::ops::Deref for LocatingSlice<I> {
91    type Target = I;
92
93    #[inline(always)]
94    fn deref(&self) -> &Self::Target {
95        &self.input
96    }
97}
98
99impl<I: crate::lib::std::fmt::Display> crate::lib::std::fmt::Display for LocatingSlice<I> {
100    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
101        self.input.fmt(f)
102    }
103}
104
105impl<I> SliceLen for LocatingSlice<I>
106where
107    I: SliceLen,
108{
109    #[inline(always)]
110    fn slice_len(&self) -> usize {
111        self.input.slice_len()
112    }
113}
114
115impl<I: Stream> Stream for LocatingSlice<I> {
116    type Token = <I as Stream>::Token;
117    type Slice = <I as Stream>::Slice;
118
119    type IterOffsets = <I as Stream>::IterOffsets;
120
121    type Checkpoint = Checkpoint<I::Checkpoint, Self>;
122
123    #[inline(always)]
124    fn iter_offsets(&self) -> Self::IterOffsets {
125        self.input.iter_offsets()
126    }
127    #[inline(always)]
128    fn eof_offset(&self) -> usize {
129        self.input.eof_offset()
130    }
131
132    #[inline(always)]
133    fn next_token(&mut self) -> Option<Self::Token> {
134        self.input.next_token()
135    }
136
137    #[inline(always)]
138    fn peek_token(&self) -> Option<Self::Token> {
139        self.input.peek_token()
140    }
141
142    #[inline(always)]
143    fn offset_for<P>(&self, predicate: P) -> Option<usize>
144    where
145        P: Fn(Self::Token) -> bool,
146    {
147        self.input.offset_for(predicate)
148    }
149    #[inline(always)]
150    fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
151        self.input.offset_at(tokens)
152    }
153    #[inline(always)]
154    fn next_slice(&mut self, offset: usize) -> Self::Slice {
155        self.input.next_slice(offset)
156    }
157    #[inline(always)]
158    unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
159        // SAFETY: Passing up invariants
160        unsafe { self.input.next_slice_unchecked(offset) }
161    }
162    #[inline(always)]
163    fn peek_slice(&self, offset: usize) -> Self::Slice {
164        self.input.peek_slice(offset)
165    }
166    #[inline(always)]
167    unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
168        // SAFETY: Passing up invariants
169        unsafe { self.input.peek_slice_unchecked(offset) }
170    }
171
172    #[inline(always)]
173    fn checkpoint(&self) -> Self::Checkpoint {
174        Checkpoint::<_, Self>::new(self.input.checkpoint())
175    }
176    #[inline(always)]
177    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
178        self.input.reset(&checkpoint.inner);
179    }
180
181    #[inline(always)]
182    fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
183        #![allow(deprecated)]
184        self.input.raw()
185    }
186
187    fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
188        self.input.trace(f)
189    }
190}
191
192impl<I> Location for LocatingSlice<I>
193where
194    I: Clone + Offset,
195{
196    #[inline(always)]
197    fn previous_token_end(&self) -> usize {
198        self.previous_token_end()
199    }
200    #[inline(always)]
201    fn current_token_start(&self) -> usize {
202        self.current_token_start()
203    }
204}
205
206#[cfg(feature = "unstable-recover")]
207#[cfg(feature = "std")]
208impl<I, E> Recover<E> for LocatingSlice<I>
209where
210    I: Recover<E>,
211    I: Stream,
212{
213    #[inline(always)]
214    fn record_err(
215        &mut self,
216        _token_start: &Self::Checkpoint,
217        _err_start: &Self::Checkpoint,
218        err: E,
219    ) -> Result<(), E> {
220        Err(err)
221    }
222
223    /// Report whether the [`Stream`] can save off errors for recovery
224    #[inline(always)]
225    fn is_recovery_supported() -> bool {
226        false
227    }
228}
229
230impl<I> StreamIsPartial for LocatingSlice<I>
231where
232    I: StreamIsPartial,
233{
234    type PartialState = I::PartialState;
235
236    #[inline]
237    fn complete(&mut self) -> Self::PartialState {
238        self.input.complete()
239    }
240
241    #[inline]
242    fn restore_partial(&mut self, state: Self::PartialState) {
243        self.input.restore_partial(state);
244    }
245
246    #[inline(always)]
247    fn is_partial_supported() -> bool {
248        I::is_partial_supported()
249    }
250
251    #[inline(always)]
252    fn is_partial(&self) -> bool {
253        self.input.is_partial()
254    }
255}
256
257impl<I> Offset for LocatingSlice<I>
258where
259    I: Stream,
260{
261    #[inline(always)]
262    fn offset_from(&self, other: &Self) -> usize {
263        self.offset_from(&other.checkpoint())
264    }
265}
266
267impl<I> Offset<<LocatingSlice<I> as Stream>::Checkpoint> for LocatingSlice<I>
268where
269    I: Stream,
270{
271    #[inline(always)]
272    fn offset_from(&self, other: &<LocatingSlice<I> as Stream>::Checkpoint) -> usize {
273        self.checkpoint().offset_from(other)
274    }
275}
276
277impl<I> AsBytes for LocatingSlice<I>
278where
279    I: AsBytes,
280{
281    #[inline(always)]
282    fn as_bytes(&self) -> &[u8] {
283        self.input.as_bytes()
284    }
285}
286
287impl<I> AsBStr for LocatingSlice<I>
288where
289    I: AsBStr,
290{
291    #[inline(always)]
292    fn as_bstr(&self) -> &[u8] {
293        self.input.as_bstr()
294    }
295}
296
297impl<I, U> Compare<U> for LocatingSlice<I>
298where
299    I: Compare<U>,
300{
301    #[inline(always)]
302    fn compare(&self, other: U) -> CompareResult {
303        self.input.compare(other)
304    }
305}
306
307impl<I, T> FindSlice<T> for LocatingSlice<I>
308where
309    I: FindSlice<T>,
310{
311    #[inline(always)]
312    fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>> {
313        self.input.find_slice(substr)
314    }
315}
316
317impl<I> UpdateSlice for LocatingSlice<I>
318where
319    I: UpdateSlice,
320{
321    #[inline(always)]
322    fn update_slice(mut self, inner: Self::Slice) -> Self {
323        self.input = I::update_slice(self.input, inner);
324        self
325    }
326}