Skip to main content

span_lang/
spanned.rs

1//! Pairing a value with the span it came from.
2
3use core::fmt;
4
5use crate::Span;
6
7/// A value together with the source [`Span`] it was parsed from.
8///
9/// `Spanned<T>` is the wrapper a parser puts around every token and AST node so
10/// the value carries its location without the value type itself knowing anything
11/// about positions. It is a transparent pair — both fields are public — and it is
12/// `Copy` whenever `T` is, so wrapping a small token in a span costs nothing.
13///
14/// Spans order before values, so a slice of `Spanned<T>` sorts into source order
15/// when `T: Ord`.
16///
17/// # Examples
18///
19/// ```
20/// use span_lang::{Span, Spanned};
21///
22/// // A token: the identifier `width` at bytes 4..9.
23/// let ident = Spanned::new(Span::new(4, 9), "width");
24/// assert_eq!(ident.value, "width");
25/// assert_eq!(ident.span, Span::new(4, 9));
26///
27/// // Transform the value, keeping the span.
28/// let len = ident.map(|s| s.len());
29/// assert_eq!(len.value, 5);
30/// assert_eq!(len.span, Span::new(4, 9));
31/// ```
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub struct Spanned<T> {
35    /// The source span the value was read from.
36    pub span: Span,
37    /// The wrapped value.
38    pub value: T,
39}
40
41impl<T> Spanned<T> {
42    /// Pairs a value with its span.
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// use span_lang::{Span, Spanned};
48    ///
49    /// let node = Spanned::new(Span::new(0, 3), 42);
50    /// assert_eq!(node.value, 42);
51    /// ```
52    #[inline]
53    pub const fn new(span: Span, value: T) -> Self {
54        Self { span, value }
55    }
56
57    /// Applies `f` to the value, keeping the span unchanged.
58    ///
59    /// This is how a parser lifts a raw token into a typed node without losing
60    /// where it came from — for example, turning a `Spanned<&str>` lexeme into a
61    /// `Spanned<Ident>`.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use span_lang::{Span, Spanned};
67    ///
68    /// let raw = Spanned::new(Span::new(2, 5), "10");
69    /// let parsed = raw.map(|s| s.parse::<u32>().unwrap());
70    /// assert_eq!(parsed.value, 10);
71    /// assert_eq!(parsed.span, Span::new(2, 5));
72    /// ```
73    #[inline]
74    #[must_use]
75    pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> {
76        Spanned {
77            span: self.span,
78            value: f(self.value),
79        }
80    }
81
82    /// Borrows the value, yielding a `Spanned<&T>` with the same span.
83    ///
84    /// Mirrors [`Option::as_ref`]: it lets you inspect or `map` the value without
85    /// consuming the original `Spanned`.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use span_lang::{Span, Spanned};
91    ///
92    /// let owned = Spanned::new(Span::new(0, 4), String::from("name"));
93    /// let borrowed = owned.as_ref().map(|s| s.len());
94    /// assert_eq!(borrowed.value, 4);
95    /// // `owned` is still usable.
96    /// assert_eq!(owned.value, "name");
97    /// ```
98    #[inline]
99    #[must_use]
100    pub fn as_ref(&self) -> Spanned<&T> {
101        Spanned {
102            span: self.span,
103            value: &self.value,
104        }
105    }
106}
107
108impl<T: fmt::Display> fmt::Display for Spanned<T> {
109    /// Formats as `value @ start..end`.
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        write!(f, "{} @ {}", self.value, self.span)
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    extern crate alloc;
118    use alloc::string::{String, ToString};
119
120    use super::*;
121
122    #[test]
123    fn test_spanned_map_keeps_span() {
124        let s = Spanned::new(Span::new(4, 9), "width");
125        let mapped = s.map(str::len);
126        assert_eq!(mapped.value, 5);
127        assert_eq!(mapped.span, Span::new(4, 9));
128    }
129
130    #[test]
131    fn test_spanned_as_ref_does_not_consume() {
132        let owned = Spanned::new(Span::new(0, 4), String::from("name"));
133        let len = owned.as_ref().map(String::len);
134        assert_eq!(len.value, 4);
135        assert_eq!(owned.value, "name");
136    }
137
138    #[test]
139    fn test_spanned_orders_by_span_first() {
140        let a = Spanned::new(Span::new(0, 1), 99u32);
141        let b = Spanned::new(Span::new(1, 2), 0u32);
142        assert!(a < b);
143    }
144
145    #[test]
146    fn test_spanned_display() {
147        let s = Spanned::new(Span::new(2, 5), "tok");
148        assert_eq!(s.to_string(), "tok @ 2..5");
149    }
150}