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 root = toml_spanner::parse("name = \"hello\"", &arena)?;
75/// let name: Spanned<String> = {
76///     let mut helper = root.helper();
77///     helper.required("name").ok().unwrap()
78/// };
79/// assert_eq!(name.value, "hello");
80/// assert!(name.span.start < name.span.end);
81/// # Ok::<(), toml_spanner::Error>(())
82/// ```
83pub struct Spanned<T> {
84    /// The deserialized value.
85    pub value: T,
86    /// The byte-offset span in the source document.
87    pub span: Span,
88}
89
90impl<T> Spanned<T> {
91    /// Creates a [`Spanned`] with the given value and a zero span.
92    #[inline]
93    pub const fn new(value: T) -> Self {
94        Self {
95            value,
96            span: Span { start: 0, end: 0 },
97        }
98    }
99
100    /// Creates a [`Spanned`] from a value and a [`Span`].
101    #[inline]
102    pub const fn with_span(value: T, span: Span) -> Self {
103        Self { value, span }
104    }
105
106    /// Consumes the wrapper, returning the inner value.
107    #[inline]
108    pub fn take(self) -> T {
109        self.value
110    }
111
112    /// Maps the inner value via [`From`], preserving the span.
113    #[inline]
114    pub fn map<V>(self) -> Spanned<V>
115    where
116        V: From<T>,
117    {
118        Spanned {
119            value: self.value.into(),
120            span: self.span,
121        }
122    }
123}
124
125impl<T> Default for Spanned<T>
126where
127    T: Default,
128{
129    fn default() -> Self {
130        Self {
131            value: Default::default(),
132            span: Span::default(),
133        }
134    }
135}
136
137impl<T> AsRef<T> for Spanned<T> {
138    fn as_ref(&self) -> &T {
139        &self.value
140    }
141}
142
143impl<T> std::fmt::Debug for Spanned<T>
144where
145    T: std::fmt::Debug,
146{
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        self.value.fmt(f)
149    }
150}
151
152impl<T> Clone for Spanned<T>
153where
154    T: Clone,
155{
156    fn clone(&self) -> Self {
157        Self {
158            value: self.value.clone(),
159            span: self.span,
160        }
161    }
162}
163
164impl<T> PartialOrd for Spanned<T>
165where
166    T: PartialOrd,
167{
168    fn partial_cmp(&self, o: &Spanned<T>) -> Option<std::cmp::Ordering> {
169        self.value.partial_cmp(&o.value)
170    }
171}
172
173impl<T> Ord for Spanned<T>
174where
175    T: Ord,
176{
177    fn cmp(&self, o: &Spanned<T>) -> std::cmp::Ordering {
178        self.value.cmp(&o.value)
179    }
180}
181
182impl<T> PartialEq for Spanned<T>
183where
184    T: PartialEq,
185{
186    fn eq(&self, o: &Spanned<T>) -> bool {
187        self.value == o.value
188    }
189}
190
191impl<T> Eq for Spanned<T> where T: Eq {}
192
193impl<T> PartialEq<T> for Spanned<T>
194where
195    T: PartialEq,
196{
197    fn eq(&self, o: &T) -> bool {
198        &self.value == o
199    }
200}
201
202#[cfg(feature = "deserialization")]
203impl<'de, T> crate::de::Deserialize<'de> for Spanned<T>
204where
205    T: crate::de::Deserialize<'de>,
206{
207    #[inline]
208    fn deserialize(
209        ctx: &mut crate::de::Context<'de>,
210        value: &crate::value::Item<'de>,
211    ) -> Result<Self, crate::de::Failed> {
212        let span = value.span();
213        let inner = T::deserialize(ctx, value)?;
214        Ok(Self { span, value: inner })
215    }
216}