Skip to main content

deser_location/
lib.rs

1//! This crate provides source locations (line and column) for deser.
2//!
3//! Formats publish the byte range in the input of every event they emit
4//! into the [`State`] (see [`State::input_range`]) and, if location tracking
5//! is requested, the source these ranges refer to (see [`Source`]).
6//! This crate resolves the ranges into lines and columns with a
7//! [`SourceMap`] (see [`Locations`]) and types can pick them up while they
8//! are deserialized.  The simplest way to do that is the [`Spanned`]
9//! wrapper.  Location tracking is requested with
10//! [`TrackLocations`](deser_core::TrackLocations) in the context of the
11//! deserialization:
12//!
13//! ```
14//! use deser::{Context, Deserialize, TrackLocations};
15//! use deser_location::Spanned;
16//!
17//! #[derive(Deserialize)]
18//! struct Config {
19//!     name: String,
20//!     workers: Spanned<u32>,
21//! }
22//!
23//! let input = "{\n  \"name\": \"web\",\n  \"workers\": 0\n}";
24//! let json = deser_json::DeserializerConfig::builder()
25//!     .context(Context::with(TrackLocations(true)))
26//!     .build();
27//! let config: Config = json.from_str(input).unwrap();
28//!
29//! if config.workers.value == 0 {
30//!     let span = config.workers.span.unwrap();
31//!     assert_eq!(span.to_string(), "3:14-3:15");
32//! }
33//! ```
34//!
35//! # Implementing Location Support in Formats
36//!
37//! Formats do not depend on this crate.  They publish the byte range of
38//! every event with [`State::set_input_range`] before they emit it and set
39//! the [`Source`] if locations are requested.  The source map is built when
40//! a consumer asks for a location for the first time:
41//!
42//! ```
43//! use deser::de::DeserializeDriver;
44//! use deser::Source;
45//! use deser::Event;
46//! use deser_location::Spanned;
47//!
48//! let input = "true";
49//! let mut out = None::<Spanned<bool>>;
50//! {
51//!     let mut driver = DeserializeDriver::new(&mut out);
52//!     Source(input.into()).set(driver.state_mut());
53//!     driver.state_mut().set_input_range(0, 4);
54//!     driver.emit(Event::from(true)).unwrap();
55//! }
56//! let span = out.unwrap().span.unwrap();
57//! assert_eq!((span.start.line, span.start.column), (1, 1));
58//! assert_eq!((span.end.line, span.end.column), (1, 5));
59//! ```
60//!
61//! # Buffering
62//!
63//! Values that are internally buffered with a
64//! [`Recording`](deser_core::de::Recording) (as some enum representations do)
65//! retain their locations when they are replayed as recordings capture the
66//! input range of every event.
67#![doc(html_logo_url = "https://raw.githubusercontent.com/mitsuhiko/deser/main/artwork/logo.svg")]
68
69use std::borrow::Cow;
70use std::fmt;
71use std::sync::Arc;
72use std::sync::OnceLock;
73use std::sync::atomic::{AtomicUsize, Ordering};
74
75use deser_core::State;
76use deser_core::de::{Deserialize, OwnedSink, Sink, SinkHandle};
77use deser_core::ser::{Describe, Emit, Serialize};
78use deser_core::{Atom, ContainerShape, Error, Source};
79
80/// Re-exported from deser, which counts positions the same way for errors.
81pub use deser_core::Position;
82
83/// A range in the input.
84///
85/// The end position is exclusive.
86#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
87pub struct Span {
88    /// The start of the span.
89    pub start: Position,
90    /// The end of the span (exclusive).
91    pub end: Position,
92}
93
94impl fmt::Debug for Span {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "{:?}-{:?}", self.start, self.end)
97    }
98}
99
100impl fmt::Display for Span {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        write!(f, "{}-{}", self.start, self.end)
103    }
104}
105
106/// Maps byte offsets in a source to positions.
107///
108/// The line index is only built when a position is resolved for the first
109/// time.
110pub struct SourceMap {
111    source: Arc<str>,
112    line_starts: OnceLock<Vec<usize>>,
113    // the line index (0-based) of the last lookup.  Lookups tend to be
114    // monotonic so this avoids most binary searches.
115    last_line: AtomicUsize,
116}
117
118impl fmt::Debug for SourceMap {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        f.debug_struct("SourceMap")
121            .field("len", &self.source.len())
122            .finish()
123    }
124}
125
126impl SourceMap {
127    /// Creates a source map for the given source.
128    pub fn new<S: Into<Arc<str>>>(source: S) -> SourceMap {
129        SourceMap {
130            source: source.into(),
131            line_starts: OnceLock::new(),
132            last_line: AtomicUsize::new(0),
133        }
134    }
135
136    /// Returns the source.
137    pub fn source(&self) -> &str {
138        &self.source
139    }
140
141    fn line_starts(&self) -> &[usize] {
142        self.line_starts.get_or_init(|| {
143            let mut rv = vec![0];
144            rv.extend(self.source.match_indices('\n').map(|(idx, _)| idx + 1));
145            rv
146        })
147    }
148
149    /// Returns the index (0-based) of the line containing the offset.
150    fn line_index(&self, offset: usize) -> usize {
151        let line_starts = self.line_starts();
152        let contains = |idx: usize| {
153            line_starts[idx] <= offset
154                && offset < line_starts.get(idx + 1).copied().unwrap_or(usize::MAX)
155        };
156        let hint = self.last_line.load(Ordering::Relaxed);
157        let idx = if hint < line_starts.len() && contains(hint) {
158            hint
159        } else if hint + 1 < line_starts.len() && contains(hint + 1) {
160            hint + 1
161        } else {
162            line_starts.partition_point(|&start| start <= offset) - 1
163        };
164        self.last_line.store(idx, Ordering::Relaxed);
165        idx
166    }
167
168    /// Resolves a byte offset into a position.
169    ///
170    /// Offsets beyond the end of the source are clamped.
171    pub fn position(&self, offset: usize) -> Position {
172        let offset = offset.min(self.source.len());
173        let idx = self.line_index(offset);
174        let line_start = self.line_starts()[idx];
175        let mut rv = Position {
176            offset: line_start,
177            line: idx + 1,
178            column: 1,
179        };
180        rv.advance(&self.source.as_bytes()[line_start..offset]);
181        rv
182    }
183
184    /// Resolves a range of byte offsets into a span.
185    pub fn span(&self, start: usize, end: usize) -> Span {
186        let start = self.position(start);
187        let end = end.min(self.source.len()).max(start.offset);
188        // most spans are on a single line, resolve the end relative to the
189        // start in that case.
190        let bytes = &self.source.as_bytes()[start.offset..end];
191        let end = if bytes.contains(&b'\n') {
192            self.position(end)
193        } else {
194            let mut rv = start;
195            rv.advance(bytes);
196            rv
197        };
198        Span { start, end }
199    }
200}
201
202/// Location information in the [`State`].
203///
204/// This resolves the input ranges of the events (see
205/// [`State::input_range`]) into lines and columns with a [`SourceMap`] of the
206/// [`Source`].  The source map is built on first use and
207/// cached in the state.  Consumers retrieve the resolved span of the current
208/// event with [`current_span`](Self::current_span).
209#[derive(Debug, Default, Clone)]
210pub struct Locations {
211    source_map: Option<Arc<SourceMap>>,
212}
213
214impl Locations {
215    /// Returns the source map for the source in the state.
216    ///
217    /// Returns `None` if the format does not provide the source.
218    pub fn source_map(state: &mut State) -> Option<Arc<SourceMap>> {
219        Locations::cached_source_map(state).cloned()
220    }
221
222    /// Returns the span of the current event if the format provides it.
223    pub fn current_span(state: &mut State) -> Option<Span> {
224        let range = state.input_range()?;
225        let source_map = Locations::cached_source_map(state)?;
226        Some(source_map.span(range.start, range.end))
227    }
228
229    fn cached_source_map(state: &mut State) -> Option<&Arc<SourceMap>> {
230        let source = &state.get::<Source>()?.0;
231        let is_cached = matches!(
232            state.get::<Locations>(),
233            Some(Locations { source_map: Some(source_map) })
234                if Arc::ptr_eq(&source_map.source, source)
235        );
236        if !is_cached {
237            let source_map = SourceMap::new(source.clone());
238            state.get_mut::<Locations>().source_map = Some(Arc::new(source_map));
239        }
240        state.get::<Locations>()?.source_map.as_ref()
241    }
242}
243
244/// A value together with its location in the input.
245///
246/// For primitive values the span covers the value, for maps and sequences
247/// it covers everything from the opening to the closing token.  The span is
248/// `None` if the format does not provide locations.
249///
250/// When serialized, only the value is serialized.  The debug representation
251/// is the value followed by its span, e.g. `42 (@ 3:5-3:7)`.
252#[derive(Clone, PartialEq)]
253pub struct Spanned<T> {
254    /// The value.
255    pub value: T,
256    /// The location of the value in the input.
257    pub span: Option<Span>,
258}
259
260impl<T: fmt::Debug> fmt::Debug for Spanned<T> {
261    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262        fmt::Debug::fmt(&self.value, f)?;
263        match self.span {
264            Some(span) => write!(f, " (@ {:?})", span),
265            None => Ok(()),
266        }
267    }
268}
269
270impl<T> Spanned<T> {
271    /// Creates a new spanned value.
272    pub fn new(value: T, span: Option<Span>) -> Spanned<T> {
273        Spanned { value, span }
274    }
275
276    /// Returns the inner value.
277    pub fn into_inner(self) -> T {
278        self.value
279    }
280}
281
282impl<'de, T: Deserialize<'de>> Deserialize<'de> for Spanned<T> {
283    fn deserialize_into<'out>(
284        out: &'out mut Option<Self>,
285        state: &mut State,
286    ) -> SinkHandle<'out, 'de> {
287        SinkHandle::arena(
288            SpannedSink {
289                out,
290                slot: None,
291                compound: None,
292                span: None,
293            },
294            state,
295        )
296    }
297
298    fn expecting() -> Cow<'static, str> {
299        T::expecting()
300    }
301
302    fn describe_type(d: &mut dyn Describe) {
303        T::describe_type(d)
304    }
305}
306
307struct SpannedSink<'a, 'de, T> {
308    out: &'a mut Option<Spanned<T>>,
309    // primitive values are deserialized directly into this slot, maps and
310    // sequences need a sink that lives across calls
311    slot: Option<T>,
312    compound: Option<OwnedSink<'de, T>>,
313    span: Option<Span>,
314}
315
316impl<'a, 'de, T: Deserialize<'de>> SpannedSink<'a, 'de, T> {
317    fn compound(&mut self, state: &mut State) -> &mut dyn Sink<'de> {
318        self.compound
319            .get_or_insert_with(|| OwnedSink::deserialize(state))
320            .get_mut()
321    }
322}
323
324impl<'a, 'de, T: Deserialize<'de>> Sink<'de> for SpannedSink<'a, 'de, T> {
325    fn atom(&mut self, atom: Atom, state: &mut State) -> Result<(), Error> {
326        self.span = Locations::current_span(state);
327        let mut sink = T::deserialize_into(&mut self.slot, state);
328        sink.atom(atom, state)?;
329        sink.finish(state)
330    }
331
332    fn borrowed_atom(&mut self, atom: Atom<'de>, state: &mut State) -> Result<(), Error> {
333        self.span = Locations::current_span(state);
334        let mut sink = T::deserialize_into(&mut self.slot, state);
335        sink.borrowed_atom(atom, state)?;
336        sink.finish(state)
337    }
338
339    fn map(&mut self, state: &mut State) -> Result<(), Error> {
340        self.span = Locations::current_span(state);
341        self.compound(state).map(state)
342    }
343
344    fn seq(&mut self, state: &mut State) -> Result<(), Error> {
345        self.span = Locations::current_span(state);
346        self.compound(state).seq(state)
347    }
348
349    fn next_key(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
350        self.compound(state).next_key(state)
351    }
352
353    fn next_value(&mut self, state: &mut State) -> Result<SinkHandle<'_, 'de>, Error> {
354        self.compound(state).next_value(state)
355    }
356
357    fn value_for_key(
358        &mut self,
359        key: &str,
360        state: &mut State,
361    ) -> Result<Option<SinkHandle<'_, 'de>>, Error> {
362        self.compound(state).value_for_key(key, state)
363    }
364
365    fn recover(&mut self, err: Error, state: &mut State) -> Result<(), Error> {
366        match self.compound {
367            Some(ref mut compound) => compound.get_mut().recover(err, state),
368            None => Err(err),
369        }
370    }
371
372    fn finish(&mut self, state: &mut State) -> Result<(), Error> {
373        let value = match self.compound {
374            Some(ref mut compound) => {
375                compound.get_mut().finish(state)?;
376                // containers are finished on their closing token, extend the
377                // span to it
378                if let (Some(start), Some(end)) = (self.span, Locations::current_span(state)) {
379                    self.span = Some(Span {
380                        start: start.start,
381                        end: end.end,
382                    });
383                }
384                compound.take()
385            }
386            None => self.slot.take(),
387        };
388        let span = self.span;
389        *self.out = value.map(|value| Spanned { value, span });
390        Ok(())
391    }
392
393    fn expecting(&self) -> Cow<'_, str> {
394        if let Some(ref compound) = self.compound {
395            return compound.get().expecting();
396        }
397        T::expecting()
398    }
399}
400
401impl<T: Serialize> Serialize for Spanned<T> {
402    fn serialize<'a>(this: &'a Self, state: &mut State) -> Result<Emit<'a>, Error> {
403        T::serialize(&this.value, state)
404    }
405
406    fn finish(this: &Self, state: &mut State) -> Result<(), Error> {
407        T::finish(&this.value, state)
408    }
409
410    fn is_optional(this: &Self) -> bool {
411        T::is_optional(&this.value)
412    }
413
414    fn container_shape(this: &Self) -> ContainerShape {
415        T::container_shape(&this.value)
416    }
417
418    fn describe(this: &Self, d: &mut dyn Describe) {
419        T::describe(&this.value, d)
420    }
421}
422
423#[test]
424fn test_source_map() {
425    let source_map = SourceMap::new("ab\ncäd\n\nx");
426    let pos = |offset| format!("{:?}", source_map.position(offset));
427    assert_eq!(pos(0), "1:1");
428    assert_eq!(pos(2), "1:3");
429    assert_eq!(pos(3), "2:1");
430    // ä is two bytes
431    assert_eq!(pos(6), "2:3");
432    assert_eq!(pos(7), "2:4");
433    assert_eq!(pos(8), "3:1");
434    assert_eq!(pos(9), "4:1");
435    assert_eq!(pos(100), "4:2");
436    assert_eq!(format!("{:?}", source_map.span(3, 7)), "2:1-2:4");
437}
438
439#[test]
440fn test_debug() {
441    let span = SourceMap::new("[1, 23]").span(4, 6);
442    assert_eq!(
443        format!("{:?}", Spanned::new(23, Some(span))),
444        "23 (@ 1:5-1:7)"
445    );
446    assert_eq!(format!("{:?}", Spanned::new("x", None)), "\"x\"");
447    assert_eq!(
448        format!("{:#?}", Spanned::new(vec![1], Some(span))),
449        "[\n    1,\n] (@ 1:5-1:7)"
450    );
451}
452
453#[test]
454fn test_auto_traits() {
455    fn assert_send_sync<T: Send + Sync>() {}
456    assert_send_sync::<SourceMap>();
457    assert_send_sync::<Locations>();
458    assert_send_sync::<Spanned<String>>();
459}