Skip to main content

dbt_yaml/spanned/
span.rs

1use std::fmt::{self, Debug, Display};
2use std::ops::Range;
3#[cfg(feature = "filename")]
4use std::path::PathBuf;
5#[cfg(feature = "filename")]
6use std::sync::Arc;
7
8use crate::libyaml::error::Mark;
9
10/// A source span.
11#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
12pub struct Span {
13    /// The start of the span.
14    pub start: Marker,
15
16    /// The end of the span.
17    pub end: Marker,
18
19    #[cfg(feature = "filename")]
20    /// An optional filename.
21    pub filename: Option<Arc<PathBuf>>,
22}
23
24impl Span {
25    /// Create a new span.
26    pub fn new(start: Marker, end: Marker) -> Self {
27        Span {
28            start,
29            end,
30            #[cfg(feature = "filename")]
31            filename: None,
32        }
33    }
34
35    /// True if this span is valid.
36    pub fn is_valid(&self) -> bool {
37        self.start.index <= self.end.index
38            && self.start.line > 0
39            && self.start.column > 0
40            && self.end.line > 0
41            && self.end.column > 0
42    }
43
44    /// Construct an empty (invalid) span.
45    pub const fn zero() -> Self {
46        Span {
47            start: Marker::zero(),
48            end: Marker::zero(),
49            #[cfg(feature = "filename")]
50            filename: None,
51        }
52    }
53}
54
55#[cfg(feature = "filename")]
56impl Span {
57    /// Create a new span with the specified filename.
58    pub fn new_with_filename(
59        start: impl Into<Marker>,
60        end: impl Into<Marker>,
61        filename: impl Into<Arc<PathBuf>>,
62    ) -> Self {
63        Span {
64            start: start.into(),
65            end: end.into(),
66            filename: Some(filename.into()),
67        }
68    }
69
70    /// Replace the filename in this span with the given filename.
71    pub fn with_filename(self, filename: impl Into<Arc<PathBuf>>) -> Self {
72        Span {
73            filename: Some(filename.into()),
74            ..self
75        }
76    }
77
78    /// Get the filename in this span.
79    pub fn get_filename(&self) -> Option<&std::path::Path> {
80        self.filename.as_deref().map(|f| f.as_ref())
81    }
82
83    pub(crate) fn maybe_capture_filename(self) -> Self {
84        if let Some(filename) = crate::spanned::get_filename() {
85            Self {
86                filename: Some(filename),
87                ..self
88            }
89        } else {
90            self
91        }
92    }
93}
94
95impl Default for Span {
96    fn default() -> Self {
97        Span::zero()
98    }
99}
100
101impl Debug for Span {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        write!(f, "{:?}..{:?}", self.start, self.end)
104    }
105}
106
107impl Display for Span {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        write!(f, "{}..{}", self.start, self.end)
110    }
111}
112
113impl From<(Marker, Marker)> for Span {
114    fn from((start, end): (Marker, Marker)) -> Self {
115        Span::new(start, end)
116    }
117}
118
119impl From<Range<Option<Marker>>> for Span {
120    fn from(range: Range<Option<Marker>>) -> Self {
121        let start = range.start.unwrap_or_default();
122        let end = range.end.unwrap_or_default();
123        Span::new(start, end)
124    }
125}
126
127impl From<Marker> for Span {
128    fn from(marker: Marker) -> Self {
129        Span::new(marker, marker)
130    }
131}
132
133/// A location in the source string.
134#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
135pub struct Marker {
136    /// Offset in bytes from the start of the source string.
137    pub index: usize,
138
139    /// Line number in the source string.
140    pub line: usize,
141
142    /// Column number in the source string.
143    pub column: usize,
144}
145
146impl Marker {
147    /// Create a new location.
148    pub fn new(index: usize, line: usize, column: usize) -> Self {
149        Marker {
150            index,
151            line,
152            column,
153        }
154    }
155
156    /// Create a location pointing to the start of the source string.
157    pub const fn start() -> Self {
158        Marker {
159            index: 0,
160            line: 1,
161            column: 1,
162        }
163    }
164
165    /// Create an empty location.
166    pub const fn zero() -> Self {
167        Marker {
168            index: 0,
169            line: 0,
170            column: 0,
171        }
172    }
173
174    /// Return the line number of this location.
175    pub fn line(&self) -> usize {
176        self.line
177    }
178
179    /// Return the column number of this location.
180    pub fn column(&self) -> usize {
181        self.column
182    }
183
184    /// Return the index of this location.
185    pub fn index(&self) -> usize {
186        self.index
187    }
188}
189
190impl Default for Marker {
191    fn default() -> Self {
192        Marker::zero()
193    }
194}
195
196impl From<Mark> for Marker {
197    fn from(mark: Mark) -> Self {
198        Marker {
199            index: mark.index() as usize,
200            // `line` and `column` returned from libyaml are 0-indexed
201            line: mark.line() as usize + 1,
202            column: mark.column() as usize + 1,
203        }
204    }
205}
206
207impl Debug for Marker {
208    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209        write!(f, "{}:{}[{}]", self.line, self.column, self.index)
210    }
211}
212
213impl Display for Marker {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        write!(f, "line {} column {}", self.line, self.column)
216    }
217}