Skip to main content

winnow/stream/
locating.rs

1use crate::stream::AsBStr;
2use crate::stream::AsBytes;
3use crate::stream::Checkpoint;
4use crate::stream::Compare;
5use crate::stream::CompareResult;
6use crate::stream::FindSlice;
7use crate::stream::Location;
8use crate::stream::Needed;
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> core::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: core::fmt::Display> core::fmt::Display for LocatingSlice<I> {
100    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::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    fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
182        self.input.trace(f)
183    }
184}
185
186impl<I> Location for LocatingSlice<I>
187where
188    I: Clone + Offset,
189{
190    #[inline(always)]
191    fn previous_token_end(&self) -> usize {
192        self.previous_token_end()
193    }
194    #[inline(always)]
195    fn current_token_start(&self) -> usize {
196        self.current_token_start()
197    }
198}
199
200#[cfg(feature = "unstable-recover")]
201#[cfg(feature = "std")]
202impl<I, E> Recover<E> for LocatingSlice<I>
203where
204    I: Recover<E>,
205    I: Stream,
206{
207    #[inline(always)]
208    fn record_err(
209        &mut self,
210        _token_start: &Self::Checkpoint,
211        _err_start: &Self::Checkpoint,
212        err: E,
213    ) -> Result<(), E> {
214        Err(err)
215    }
216
217    /// Report whether the [`Stream`] can save off errors for recovery
218    #[inline(always)]
219    fn is_recovery_supported() -> bool {
220        false
221    }
222}
223
224impl<I> StreamIsPartial for LocatingSlice<I>
225where
226    I: StreamIsPartial,
227{
228    type PartialState = I::PartialState;
229
230    #[inline]
231    fn complete(&mut self) -> Self::PartialState {
232        self.input.complete()
233    }
234
235    #[inline]
236    fn restore_partial(&mut self, state: Self::PartialState) {
237        self.input.restore_partial(state);
238    }
239
240    #[inline(always)]
241    fn is_partial_supported() -> bool {
242        I::is_partial_supported()
243    }
244
245    #[inline(always)]
246    fn is_partial(&self) -> bool {
247        self.input.is_partial()
248    }
249}
250
251impl<I> Offset for LocatingSlice<I>
252where
253    I: Stream,
254{
255    #[inline(always)]
256    fn offset_from(&self, other: &Self) -> usize {
257        self.offset_from(&other.checkpoint())
258    }
259}
260
261impl<I> Offset<<LocatingSlice<I> as Stream>::Checkpoint> for LocatingSlice<I>
262where
263    I: Stream,
264{
265    #[inline(always)]
266    fn offset_from(&self, other: &<LocatingSlice<I> as Stream>::Checkpoint) -> usize {
267        self.checkpoint().offset_from(other)
268    }
269}
270
271impl<I> AsBytes for LocatingSlice<I>
272where
273    I: AsBytes,
274{
275    #[inline(always)]
276    fn as_bytes(&self) -> &[u8] {
277        self.input.as_bytes()
278    }
279}
280
281impl<I> AsBStr for LocatingSlice<I>
282where
283    I: AsBStr,
284{
285    #[inline(always)]
286    fn as_bstr(&self) -> &[u8] {
287        self.input.as_bstr()
288    }
289}
290
291impl<I, U> Compare<U> for LocatingSlice<I>
292where
293    I: Compare<U>,
294{
295    #[inline(always)]
296    fn compare(&self, other: U) -> CompareResult {
297        self.input.compare(other)
298    }
299}
300
301impl<I, T> FindSlice<T> for LocatingSlice<I>
302where
303    I: FindSlice<T>,
304{
305    #[inline(always)]
306    fn find_slice(&self, substr: T) -> Option<core::ops::Range<usize>> {
307        self.input.find_slice(substr)
308    }
309}
310
311impl<I> UpdateSlice for LocatingSlice<I>
312where
313    I: UpdateSlice,
314{
315    #[inline(always)]
316    fn update_slice(mut self, inner: Self::Slice) -> Self {
317        self.input = I::update_slice(self.input, inner);
318        self
319    }
320}