Skip to main content

toml_spanner/
span.rs

1//! Byte-offset span types for source location tracking.
2
3#[cfg(test)]
4#[path = "./span_tests.rs"]
5mod tests;
6
7/// A byte-offset range within a TOML document.
8///
9/// Convertible to and from [`Range<u32>`](std::ops::Range) and
10/// [`Range<usize>`](std::ops::Range).
11#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
12pub struct Span {
13    /// Start byte offset (inclusive).
14    pub start: u32,
15    /// End byte offset (exclusive).
16    pub end: u32,
17}
18
19impl Span {
20    /// Creates a new [`Span`] from start and end byte offsets.
21    #[inline]
22    pub fn new(start: u32, end: u32) -> Self {
23        Self { start, end }
24    }
25
26    /// Returns `true` if both start and end are zero.
27    #[inline]
28    pub fn is_empty(&self) -> bool {
29        self.start == 0 && self.end == 0
30    }
31}
32
33impl From<Span> for (u32, u32) {
34    fn from(s: Span) -> (u32, u32) {
35        (s.start, s.end)
36    }
37}
38
39impl From<Span> for (usize, usize) {
40    fn from(s: Span) -> (usize, usize) {
41        (s.start as usize, s.end as usize)
42    }
43}
44
45impl From<std::ops::Range<u32>> for Span {
46    fn from(s: std::ops::Range<u32>) -> Self {
47        Self::new(s.start, s.end)
48    }
49}
50
51impl From<Span> for std::ops::Range<u32> {
52    fn from(s: Span) -> Self {
53        s.start..s.end
54    }
55}
56
57impl From<Span> for std::ops::Range<usize> {
58    fn from(s: Span) -> Self {
59        s.start as usize..s.end as usize
60    }
61}
62
63/// Wraps a value `T` with its source [`Span`].
64///
65/// Use this as a field type in your [`Deserialize`](crate::Deserialize) structs
66/// when you need to preserve span information alongside the deserialized value.
67///
68/// # Examples
69///
70/// ```
71/// use toml_spanner::{Arena, Spanned};
72///
73/// let arena = Arena::new();
74/// let mut table = toml_spanner::parse("name = \"hello\"", &arena)?;
75/// let name: Spanned<String> = table.required("name")?;
76/// assert_eq!(name.value, "hello");
77/// assert!(name.span.start < name.span.end);
78/// # Ok::<(), toml_spanner::Error>(())
79/// ```
80pub struct Spanned<T> {
81    /// The deserialized value.
82    pub value: T,
83    /// The byte-offset span in the source document.
84    pub span: Span,
85}
86
87impl<T> Spanned<T> {
88    /// Creates a [`Spanned`] with the given value and a zero span.
89    #[inline]
90    pub const fn new(value: T) -> Self {
91        Self {
92            value,
93            span: Span { start: 0, end: 0 },
94        }
95    }
96
97    /// Creates a [`Spanned`] from a value and a [`Span`].
98    #[inline]
99    pub const fn with_span(value: T, span: Span) -> Self {
100        Self { value, span }
101    }
102
103    /// Consumes the wrapper, returning the inner value.
104    #[inline]
105    pub fn take(self) -> T {
106        self.value
107    }
108
109    /// Maps the inner value via [`From`], preserving the span.
110    #[inline]
111    pub fn map<V>(self) -> Spanned<V>
112    where
113        V: From<T>,
114    {
115        Spanned {
116            value: self.value.into(),
117            span: self.span,
118        }
119    }
120}
121
122impl<T> Default for Spanned<T>
123where
124    T: Default,
125{
126    fn default() -> Self {
127        Self {
128            value: Default::default(),
129            span: Span::default(),
130        }
131    }
132}
133
134impl<T> AsRef<T> for Spanned<T> {
135    fn as_ref(&self) -> &T {
136        &self.value
137    }
138}
139
140impl<T> std::fmt::Debug for Spanned<T>
141where
142    T: std::fmt::Debug,
143{
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        self.value.fmt(f)
146    }
147}
148
149impl<T> Clone for Spanned<T>
150where
151    T: Clone,
152{
153    fn clone(&self) -> Self {
154        Self {
155            value: self.value.clone(),
156            span: self.span,
157        }
158    }
159}
160
161impl<T> PartialOrd for Spanned<T>
162where
163    T: PartialOrd,
164{
165    fn partial_cmp(&self, o: &Spanned<T>) -> Option<std::cmp::Ordering> {
166        self.value.partial_cmp(&o.value)
167    }
168}
169
170impl<T> Ord for Spanned<T>
171where
172    T: Ord,
173{
174    fn cmp(&self, o: &Spanned<T>) -> std::cmp::Ordering {
175        self.value.cmp(&o.value)
176    }
177}
178
179impl<T> PartialEq for Spanned<T>
180where
181    T: PartialEq,
182{
183    fn eq(&self, o: &Spanned<T>) -> bool {
184        self.value == o.value
185    }
186}
187
188impl<T> Eq for Spanned<T> where T: Eq {}
189
190impl<T> PartialEq<T> for Spanned<T>
191where
192    T: PartialEq,
193{
194    fn eq(&self, o: &T) -> bool {
195        &self.value == o
196    }
197}
198
199impl<'de, T> crate::Deserialize<'de> for Spanned<T>
200where
201    T: crate::Deserialize<'de>,
202{
203    #[inline]
204    fn deserialize(value: &mut crate::value::Item<'de>) -> Result<Self, crate::Error> {
205        let span = value.span();
206        let value = T::deserialize(value)?;
207        Ok(Self { span, value })
208    }
209}