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, Debug, 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> crate::lib::std::ops::Deref for LocatingSlice<I> {
85    type Target = I;
86
87    #[inline(always)]
88    fn deref(&self) -> &Self::Target {
89        &self.input
90    }
91}
92
93impl<I: crate::lib::std::fmt::Display> crate::lib::std::fmt::Display for LocatingSlice<I> {
94    fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result {
95        self.input.fmt(f)
96    }
97}
98
99impl<I> SliceLen for LocatingSlice<I>
100where
101    I: SliceLen,
102{
103    #[inline(always)]
104    fn slice_len(&self) -> usize {
105        self.input.slice_len()
106    }
107}
108
109impl<I: Stream> Stream for LocatingSlice<I> {
110    type Token = <I as Stream>::Token;
111    type Slice = <I as Stream>::Slice;
112
113    type IterOffsets = <I as Stream>::IterOffsets;
114
115    type Checkpoint = Checkpoint<I::Checkpoint, Self>;
116
117    #[inline(always)]
118    fn iter_offsets(&self) -> Self::IterOffsets {
119        self.input.iter_offsets()
120    }
121    #[inline(always)]
122    fn eof_offset(&self) -> usize {
123        self.input.eof_offset()
124    }
125
126    #[inline(always)]
127    fn next_token(&mut self) -> Option<Self::Token> {
128        self.input.next_token()
129    }
130
131    #[inline(always)]
132    fn peek_token(&self) -> Option<Self::Token> {
133        self.input.peek_token()
134    }
135
136    #[inline(always)]
137    fn offset_for<P>(&self, predicate: P) -> Option<usize>
138    where
139        P: Fn(Self::Token) -> bool,
140    {
141        self.input.offset_for(predicate)
142    }
143    #[inline(always)]
144    fn offset_at(&self, tokens: usize) -> Result<usize, Needed> {
145        self.input.offset_at(tokens)
146    }
147    #[inline(always)]
148    fn next_slice(&mut self, offset: usize) -> Self::Slice {
149        self.input.next_slice(offset)
150    }
151    #[inline(always)]
152    unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice {
153        // SAFETY: Passing up invariants
154        unsafe { self.input.next_slice_unchecked(offset) }
155    }
156    #[inline(always)]
157    fn peek_slice(&self, offset: usize) -> Self::Slice {
158        self.input.peek_slice(offset)
159    }
160    #[inline(always)]
161    unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice {
162        // SAFETY: Passing up invariants
163        unsafe { self.input.peek_slice_unchecked(offset) }
164    }
165
166    #[inline(always)]
167    fn checkpoint(&self) -> Self::Checkpoint {
168        Checkpoint::<_, Self>::new(self.input.checkpoint())
169    }
170    #[inline(always)]
171    fn reset(&mut self, checkpoint: &Self::Checkpoint) {
172        self.input.reset(&checkpoint.inner);
173    }
174
175    #[inline(always)]
176    fn raw(&self) -> &dyn crate::lib::std::fmt::Debug {
177        &self.input
178    }
179}
180
181impl<I> Location for LocatingSlice<I>
182where
183    I: Clone + Offset,
184{
185    #[inline(always)]
186    fn previous_token_end(&self) -> usize {
187        self.previous_token_end()
188    }
189    #[inline(always)]
190    fn current_token_start(&self) -> usize {
191        self.current_token_start()
192    }
193}
194
195#[cfg(feature = "unstable-recover")]
196#[cfg(feature = "std")]
197impl<I, E> Recover<E> for LocatingSlice<I>
198where
199    I: Recover<E>,
200    I: Stream,
201{
202    #[inline(always)]
203    fn record_err(
204        &mut self,
205        _token_start: &Self::Checkpoint,
206        _err_start: &Self::Checkpoint,
207        err: E,
208    ) -> Result<(), E> {
209        Err(err)
210    }
211
212    /// Report whether the [`Stream`] can save off errors for recovery
213    #[inline(always)]
214    fn is_recovery_supported() -> bool {
215        false
216    }
217}
218
219impl<I> StreamIsPartial for LocatingSlice<I>
220where
221    I: StreamIsPartial,
222{
223    type PartialState = I::PartialState;
224
225    #[inline]
226    fn complete(&mut self) -> Self::PartialState {
227        self.input.complete()
228    }
229
230    #[inline]
231    fn restore_partial(&mut self, state: Self::PartialState) {
232        self.input.restore_partial(state);
233    }
234
235    #[inline(always)]
236    fn is_partial_supported() -> bool {
237        I::is_partial_supported()
238    }
239
240    #[inline(always)]
241    fn is_partial(&self) -> bool {
242        self.input.is_partial()
243    }
244}
245
246impl<I> Offset for LocatingSlice<I>
247where
248    I: Stream,
249{
250    #[inline(always)]
251    fn offset_from(&self, other: &Self) -> usize {
252        self.offset_from(&other.checkpoint())
253    }
254}
255
256impl<I> Offset<<LocatingSlice<I> as Stream>::Checkpoint> for LocatingSlice<I>
257where
258    I: Stream,
259{
260    #[inline(always)]
261    fn offset_from(&self, other: &<LocatingSlice<I> as Stream>::Checkpoint) -> usize {
262        self.checkpoint().offset_from(other)
263    }
264}
265
266impl<I> AsBytes for LocatingSlice<I>
267where
268    I: AsBytes,
269{
270    #[inline(always)]
271    fn as_bytes(&self) -> &[u8] {
272        self.input.as_bytes()
273    }
274}
275
276impl<I> AsBStr for LocatingSlice<I>
277where
278    I: AsBStr,
279{
280    #[inline(always)]
281    fn as_bstr(&self) -> &[u8] {
282        self.input.as_bstr()
283    }
284}
285
286impl<I, U> Compare<U> for LocatingSlice<I>
287where
288    I: Compare<U>,
289{
290    #[inline(always)]
291    fn compare(&self, other: U) -> CompareResult {
292        self.input.compare(other)
293    }
294}
295
296impl<I, T> FindSlice<T> for LocatingSlice<I>
297where
298    I: FindSlice<T>,
299{
300    #[inline(always)]
301    fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>> {
302        self.input.find_slice(substr)
303    }
304}
305
306impl<I> UpdateSlice for LocatingSlice<I>
307where
308    I: UpdateSlice,
309{
310    #[inline(always)]
311    fn update_slice(mut self, inner: Self::Slice) -> Self {
312        self.input = I::update_slice(self.input, inner);
313        self
314    }
315}