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}