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}