tokit/utils/
spanned.rs

1use crate::utils::marker::Ignored;
2
3use super::{AsSpan, IntoComponents, IntoSpan, SimpleSpan};
4
5/// A value paired with its source location span.
6///
7/// `Spanned<D>` combines a value of type `D` with a [`Span`] that indicates where in
8/// the source input the value came from. This is fundamental for building parsers and
9/// compilers that need to track source locations for error reporting, debugging, and
10/// IDE integration.
11///
12/// # Design
13///
14/// `Spanned` uses public fields for direct access, but also provides accessor methods
15/// for consistency. It implements `Deref` and `DerefMut` to allow transparent access
16/// to the inner data while keeping span information available when needed.
17///
18/// # Common Patterns
19///
20/// ## Transparent Access via Deref
21///
22/// Thanks to `Deref`, you can call methods on the wrapped value directly:
23///
24/// ```rust
25/// use tokit::utils::{Span, Spanned};
26///
27/// let spanned_str = Spanned::new(Span::new(0, 5), "hello");
28///
29/// // Can call str methods directly
30/// assert_eq!(spanned_str.len(), 5);
31/// assert_eq!(spanned_str.to_uppercase(), "HELLO");
32///
33/// // But can still access the span
34/// assert_eq!(spanned_str.span().start(), 0);
35/// ```
36///
37/// ## Mapping Values While Preserving Spans
38///
39/// ```rust,ignore
40/// use tokit::utils::{Span, Spanned};
41///
42/// let spanned_num = Spanned::new(Span::new(10, 12), "42");
43///
44/// // Parse the string, keeping the same span
45/// let parsed: Spanned<i32> = Spanned::new(
46///     spanned_num.span,
47///     spanned_num.data.parse().unwrap()
48/// );
49///
50/// assert_eq!(*parsed, 42);
51/// assert_eq!(parsed.span().start(), 10);
52/// ```
53///
54/// ## Building AST Nodes with Locations
55///
56/// ```rust,ignore
57/// use tokit::utils::{Span, Spanned};
58///
59/// enum Expr {
60///     Number(i64),
61///     Add(Box<Spanned<Expr>>, Box<Spanned<Expr>>),
62/// }
63///
64/// // Each AST node knows its source location
65/// let left = Spanned::new(Span::new(0, 2), Expr::Number(1));
66/// let right = Spanned::new(Span::new(5, 7), Expr::Number(2));
67///
68/// let add = Spanned::new(
69///     Span::new(0, 7), // Covers the whole expression
70///     Expr::Add(Box::new(left), Box::new(right))
71/// );
72/// ```
73///
74/// ## Error Reporting with Context
75///
76/// ```rust,ignore
77/// fn type_error<T>(expected: &str, got: &Spanned<T>) -> Error
78/// where
79///     T: core::fmt::Debug
80/// {
81///     Error {
82///         message: format!("Expected {}, got {:?}", expected, got.data),
83///         span: *got.span(),
84///         help: Some("Try using a different type".to_string()),
85///     }
86/// }
87/// ```
88///
89/// # Trait Implementations
90///
91/// - **`Deref` / `DerefMut`**: Access the inner data transparently
92/// - **`Display`**: Delegates to the inner data's `Display` implementation
93/// - **`AsSpan` / `IntoSpan`**: Extract just the span information
94/// - **`IntoComponents`**: Destructure into `(Span, D)` tuple
95///
96/// # Examples
97///
98/// ## Basic Usage
99///
100/// ```rust
101/// use tokit::utils::{Span, Spanned};
102///
103/// let span = Span::new(10, 15);
104/// let spanned = Spanned::new(span, "hello");
105///
106/// assert_eq!(spanned.span(), &span);
107/// assert_eq!(spanned.data(), &"hello");
108/// assert_eq!(*spanned, "hello"); // Via Deref
109/// ```
110///
111/// ## Destructuring
112///
113/// ```rust
114/// use tokit::utils::{Span, Spanned};
115///
116/// let spanned = Spanned::new(Span::new(0, 5), 42);
117///
118/// let (span, value) = spanned.into_components();
119/// assert_eq!(span.start(), 0);
120/// assert_eq!(value, 42);
121/// ```
122///
123/// ## Mutable Access
124///
125/// ```rust
126/// use tokit::utils::{Span, Spanned};
127///
128/// let mut spanned = Spanned::new(Span::new(0, 1), 10);
129///
130/// // Modify the data
131/// *spanned += 5;
132/// assert_eq!(*spanned, 15);
133///
134/// // Modify the span
135/// spanned.span_mut().bump_end(4);
136/// assert_eq!(spanned.span().end(), 5);
137/// ```
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
139pub struct Spanned<D, S = SimpleSpan> {
140  /// The source location span of the data.
141  ///
142  /// This indicates where in the source input this value came from,
143  /// expressed as byte offsets.
144  pub span: S,
145
146  /// The wrapped data value.
147  ///
148  /// This is the actual parsed or processed value, paired with its
149  /// source location for error reporting and debugging.
150  pub data: D,
151}
152
153impl<D, S> AsRef<S> for Spanned<D, S> {
154  #[cfg_attr(not(tarpaulin), inline(always))]
155  fn as_ref(&self) -> &S {
156    self.span_ref()
157  }
158}
159
160impl<D, S> AsSpan<S> for Spanned<D, S> {
161  #[cfg_attr(not(tarpaulin), inline(always))]
162  fn as_span(&self) -> &S {
163    AsRef::as_ref(self)
164  }
165}
166
167impl<D, S> IntoSpan<S> for Spanned<D, S> {
168  #[cfg_attr(not(tarpaulin), inline(always))]
169  fn into_span(self) -> S {
170    self.span
171  }
172}
173
174impl<D, S> core::ops::Deref for Spanned<D, S> {
175  type Target = D;
176
177  #[cfg_attr(not(tarpaulin), inline(always))]
178  fn deref(&self) -> &Self::Target {
179    &self.data
180  }
181}
182
183impl<D, S> core::ops::DerefMut for Spanned<D, S> {
184  #[cfg_attr(not(tarpaulin), inline(always))]
185  fn deref_mut(&mut self) -> &mut Self::Target {
186    &mut self.data
187  }
188}
189
190impl<D, S> core::fmt::Display for Spanned<D, S>
191where
192  D: core::fmt::Display,
193{
194  #[cfg_attr(not(tarpaulin), inline(always))]
195  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
196    self.data.fmt(f)
197  }
198}
199
200impl<D, S> core::error::Error for Spanned<D, S>
201where
202  D: core::error::Error,
203  S: core::fmt::Debug,
204{
205}
206
207impl<D, S> IntoComponents for Spanned<D, S> {
208  type Components = (S, D);
209
210  #[cfg_attr(not(tarpaulin), inline(always))]
211  fn into_components(self) -> Self::Components {
212    (self.span, self.data)
213  }
214}
215
216impl<D, S> Spanned<&D, &S> {
217  /// Returns a copied version of the spanned value.
218  #[cfg_attr(not(tarpaulin), inline(always))]
219  pub const fn copied(&self) -> Spanned<D, S>
220  where
221    D: Copy,
222    S: Copy,
223  {
224    Spanned {
225      span: *self.span,
226      data: *self.data,
227    }
228  }
229
230  /// Returns a cloned version of the spanned value.
231  #[cfg_attr(not(tarpaulin), inline(always))]
232  pub fn cloned(&self) -> Spanned<D, S>
233  where
234    D: Clone,
235    S: Clone,
236  {
237    self.map(Clone::clone, Clone::clone)
238  }
239}
240
241impl<D, S> Spanned<D, S> {
242  /// Create a new spanned value.
243  #[cfg_attr(not(tarpaulin), inline(always))]
244  pub const fn new(span: S, data: D) -> Self {
245    Self { span, data }
246  }
247
248  /// Get a reference to the span.
249  ///
250  /// ## Example
251  ///
252  /// ```rust
253  /// use tokit::utils::{Span, Spanned};
254  ///
255  /// let spanned = Spanned::new(Span::new(5, 10), "data");
256  /// assert_eq!(spanned.span(), Span::new(5, 10));
257  /// ```
258  #[cfg_attr(not(tarpaulin), inline(always))]
259  pub const fn span(&self) -> S
260  where
261    S: Copy,
262  {
263    self.span
264  }
265
266  /// Get a reference to the span.
267  ///
268  /// ## Example
269  ///
270  /// ```rust
271  /// use tokit::utils::{Span, Spanned};
272  ///
273  /// let spanned = Spanned::new(Span::new(5, 10), "data");
274  /// assert_eq!(spanned.span_ref(), &Span::new(5, 10));
275  /// ```
276  #[cfg_attr(not(tarpaulin), inline(always))]
277  pub const fn span_ref(&self) -> &S {
278    &self.span
279  }
280
281  /// Get a mutable reference to the span.
282  ///
283  /// ## Example
284  ///
285  /// ```rust
286  /// use tokit::utils::{Span, Spanned};
287  ///
288  /// let mut spanned = Spanned::new(Span::new(5, 10), "data");
289  /// spanned.span_mut().set_end(15);
290  /// assert_eq!(spanned.span().end(), 15);
291  /// ```
292  #[cfg_attr(not(tarpaulin), inline(always))]
293  pub const fn span_mut(&mut self) -> &mut S {
294    &mut self.span
295  }
296
297  /// Get a reference to the data.
298  ///
299  /// ## Example
300  ///
301  /// ```rust
302  /// use tokit::utils::{Span, Spanned};
303  ///
304  /// let spanned = Spanned::new(Span::new(5, 10), 42);
305  /// assert_eq!(*spanned.data(), 42);
306  /// ```
307  #[cfg_attr(not(tarpaulin), inline(always))]
308  pub const fn data(&self) -> &D {
309    &self.data
310  }
311
312  /// Get a mutable reference to the data.
313  ///
314  /// ## Example
315  ///
316  /// ```rust
317  /// use tokit::utils::{Span, Spanned};
318  ///
319  /// let mut spanned = Spanned::new(Span::new(5, 10), 42);
320  /// *spanned.data_mut() = 100;
321  /// assert_eq!(*spanned.data(), 100);
322  /// ```
323  #[cfg_attr(not(tarpaulin), inline(always))]
324  pub const fn data_mut(&mut self) -> &mut D {
325    &mut self.data
326  }
327
328  /// Returns a reference to the span and data.
329  ///
330  /// ## Example
331  ///
332  /// ```rust
333  /// use tokit::utils::{Span, Spanned};
334  ///
335  /// let spanned = Spanned::new(Span::new(5, 10), String::from("hello"));
336  /// let borrowed: Spanned<&String> = spanned.as_ref();
337  /// assert_eq!(borrowed.data(), &"hello");
338  /// ```
339  #[cfg_attr(not(tarpaulin), inline(always))]
340  pub const fn as_ref(&self) -> Spanned<&D, &S> {
341    Spanned {
342      span: &self.span,
343      data: &self.data,
344    }
345  }
346
347  /// Returns a mutable reference to the span and data.
348  ///
349  /// ## Example
350  ///
351  /// ```rust
352  /// use tokit::utils::{Span, Spanned};
353  ///
354  /// let mut spanned = Spanned::new(Span::new(5, 10), String::from("hello"));
355  /// let borrowed: Spanned<&mut String> = spanned.as_mut();
356  /// borrowed.data.push_str(" world");
357  /// assert_eq!(spanned.data(), &"hello world");
358  /// ```
359  #[cfg_attr(not(tarpaulin), inline(always))]
360  pub const fn as_mut(&mut self) -> Spanned<&mut D, &mut S> {
361    Spanned {
362      span: &mut self.span,
363      data: &mut self.data,
364    }
365  }
366
367  /// Consume the spanned value and return the span.
368  #[cfg_attr(not(tarpaulin), inline(always))]
369  pub fn into_span(self) -> S {
370    self.span
371  }
372
373  /// Consume the spanned value and return the data.
374  #[cfg_attr(not(tarpaulin), inline(always))]
375  pub fn into_data(self) -> D {
376    self.data
377  }
378
379  /// Decompose the spanned value into its span and data.
380  #[cfg_attr(not(tarpaulin), inline(always))]
381  pub fn into_components(self) -> (S, D) {
382    (self.span, self.data)
383  }
384
385  /// Map the data to a new value, preserving the span.
386  #[inline]
387  pub fn map_data<F, U>(self, f: F) -> Spanned<U, S>
388  where
389    F: FnOnce(D) -> U,
390  {
391    Spanned {
392      span: self.span,
393      data: f(self.data),
394    }
395  }
396
397  /// Map the span to a new value, preserving the data.
398  #[inline]
399  pub fn map_span<F, T>(self, f: F) -> Spanned<D, T>
400  where
401    F: FnOnce(S) -> T,
402  {
403    Spanned {
404      span: f(self.span),
405      data: self.data,
406    }
407  }
408
409  /// Map both the span and data to new values.
410  #[inline]
411  pub fn map<F, G, U, T>(self, f: F, g: G) -> Spanned<U, T>
412  where
413    F: FnOnce(S) -> T,
414    G: FnOnce(D) -> U,
415  {
416    Spanned {
417      span: f(self.span),
418      data: g(self.data),
419    }
420  }
421}
422
423impl<D, S> From<Spanned<D, S>> for () {
424  #[cfg_attr(not(tarpaulin), inline(always))]
425  fn from(_: Spanned<D, S>) -> Self {}
426}
427
428impl<D, S> From<Spanned<D, S>> for Ignored<()> {
429  #[cfg_attr(not(tarpaulin), inline(always))]
430  fn from(_: Spanned<D, S>) -> Self {
431    Ignored::default()
432  }
433}