solar_ast/ast/
path.rs

1use super::Box;
2use solar_data_structures::smallvec::SmallVec;
3use solar_interface::{Ident, Span, Symbol};
4use std::fmt;
5
6/// A boxed [`PathSlice`].
7pub type AstPath<'ast> = Box<'ast, PathSlice>;
8
9/// A qualified identifier: `foo.bar.baz`.
10///
11/// This is a list of identifiers, and is never empty.
12#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
13#[repr(transparent)]
14pub struct PathSlice([Ident]);
15
16impl ToOwned for PathSlice {
17    type Owned = Path;
18
19    #[inline]
20    fn to_owned(&self) -> Self::Owned {
21        Path::new(&self.0)
22    }
23}
24
25impl fmt::Debug for PathSlice {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        fmt::Display::fmt(self, f)
28    }
29}
30
31impl fmt::Display for PathSlice {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        for (i, ident) in self.segments().iter().enumerate() {
34            if i != 0 {
35                f.write_str(".")?;
36            }
37            write!(f, "{ident}")?;
38        }
39        Ok(())
40    }
41}
42
43impl PartialEq<Ident> for PathSlice {
44    fn eq(&self, other: &Ident) -> bool {
45        match self.get_ident() {
46            Some(ident) => ident == other,
47            None => false,
48        }
49    }
50}
51
52impl PartialEq<Symbol> for PathSlice {
53    fn eq(&self, other: &Symbol) -> bool {
54        match self.get_ident() {
55            Some(ident) => ident.name == *other,
56            None => false,
57        }
58    }
59}
60
61impl PathSlice {
62    /// Creates a new path from a single ident.
63    #[inline]
64    pub fn from_ref(segment: &Ident) -> &Self {
65        unsafe { Self::from_slice_unchecked(std::slice::from_ref(segment)) }
66    }
67
68    /// Creates a new path from a single ident.
69    #[inline]
70    pub fn from_mut(segment: &mut Ident) -> &mut Self {
71        unsafe { Self::from_mut_slice_unchecked(std::slice::from_mut(segment)) }
72    }
73
74    /// Creates a new path from a slice of segments.
75    ///
76    /// # Panics
77    ///
78    /// Panics if `segments` is empty.
79    #[inline]
80    pub fn from_slice(segments: &[Ident]) -> &Self {
81        if segments.is_empty() {
82            panic_empty_path();
83        }
84        // SAFETY: `segments` is not empty.
85        unsafe { Self::from_slice_unchecked(segments) }
86    }
87
88    /// Creates a new path from a slice of segments.
89    ///
90    /// # Safety
91    ///
92    /// The caller must ensure that `segments` is not empty.
93    #[inline]
94    pub unsafe fn from_slice_unchecked(segments: &[Ident]) -> &Self {
95        debug_assert!(!segments.is_empty());
96        // SAFETY: We're just a wrapper around a slice `[Ident]`.
97        unsafe { &*(segments as *const _ as *const Self) }
98    }
99
100    /// Creates a new path from a mutable slice of segments.
101    ///
102    /// # Panics
103    ///
104    /// Panics if `segments` is empty.
105    #[inline]
106    pub fn from_mut_slice(segments: &mut [Ident]) -> &mut Self {
107        assert!(!segments.is_empty());
108        // SAFETY: `segments` is not empty.
109        unsafe { Self::from_mut_slice_unchecked(segments) }
110    }
111
112    /// Creates a new path from a mutable slice of segments.
113    ///
114    /// # Safety
115    ///
116    /// The caller must ensure that `segments` is not empty.
117    #[inline]
118    pub unsafe fn from_mut_slice_unchecked(segments: &mut [Ident]) -> &mut Self {
119        // SAFETY: We're just a wrapper around a slice `[Ident]`.
120        unsafe { &mut *(segments as *mut _ as *mut Self) }
121    }
122
123    /// Returns the path's span.
124    #[inline]
125    #[cfg_attr(debug_assertions, track_caller)]
126    pub fn span(&self) -> Span {
127        match self.segments() {
128            [] => panic_empty_path(),
129            [ident] => ident.span,
130            [first, .., last] => first.span.with_hi(last.span.hi()),
131        }
132    }
133
134    /// Returns the path's segments.
135    #[inline]
136    pub fn segments(&self) -> &[Ident] {
137        &self.0
138    }
139
140    /// Returns the path's segments.
141    #[inline]
142    pub fn segments_mut(&mut self) -> &mut [Ident] {
143        &mut self.0
144    }
145
146    /// If this path consists of a single ident, returns the ident.
147    #[inline]
148    pub fn get_ident(&self) -> Option<&Ident> {
149        match self.segments() {
150            [ident] => Some(ident),
151            _ => None,
152        }
153    }
154
155    /// If this path consists of a single ident, returns the ident.
156    #[inline]
157    pub fn get_ident_mut(&mut self) -> Option<&mut Ident> {
158        match self.segments_mut() {
159            [ident] => Some(ident),
160            _ => None,
161        }
162    }
163
164    /// Returns the first segment of the path.
165    #[inline]
166    #[cfg_attr(debug_assertions, track_caller)]
167    pub fn first(&self) -> &Ident {
168        self.segments().first().unwrap_or_else(|| panic_empty_path())
169    }
170
171    /// Returns the first segment of the path.
172    #[inline]
173    #[cfg_attr(debug_assertions, track_caller)]
174    pub fn first_mut(&mut self) -> &mut Ident {
175        self.segments_mut().first_mut().unwrap_or_else(|| panic_empty_path())
176    }
177
178    /// Returns the last segment of the path.
179    #[inline]
180    #[cfg_attr(debug_assertions, track_caller)]
181    pub fn last(&self) -> &Ident {
182        self.segments().last().unwrap_or_else(|| panic_empty_path())
183    }
184
185    /// Returns the last segment of the path.
186    #[inline]
187    #[cfg_attr(debug_assertions, track_caller)]
188    pub fn last_mut(&mut self) -> &mut Ident {
189        self.segments_mut().last_mut().unwrap_or_else(|| panic_empty_path())
190    }
191}
192
193#[inline(never)]
194#[cold]
195#[cfg_attr(debug_assertions, track_caller)]
196fn panic_empty_path() -> ! {
197    panic!("paths cannot be empty");
198}
199
200/// A qualified identifier: `foo.bar.baz`.
201///
202/// This is a list of identifiers, and is never empty.
203#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
204pub struct Path(SmallVec<[Ident; 1]>);
205
206impl fmt::Debug for Path {
207    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208        fmt::Debug::fmt(self.as_slice(), f)
209    }
210}
211
212impl fmt::Display for Path {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        fmt::Display::fmt(self.as_slice(), f)
215    }
216}
217
218impl PartialEq<Ident> for Path {
219    fn eq(&self, other: &Ident) -> bool {
220        PartialEq::eq(self.as_slice(), other)
221    }
222}
223
224impl PartialEq<Symbol> for Path {
225    fn eq(&self, other: &Symbol) -> bool {
226        PartialEq::eq(self.as_slice(), other)
227    }
228}
229
230impl FromIterator<Ident> for Path {
231    /// Creates a path from an iterator of idents.
232    ///
233    /// # Panics
234    ///
235    /// Panics if the iterator is empty.
236    fn from_iter<T: IntoIterator<Item = Ident>>(iter: T) -> Self {
237        let mut iter = iter.into_iter();
238        let first = iter.next().expect("paths cannot be empty");
239        match iter.next() {
240            Some(second) => Self(SmallVec::from_iter([first, second].into_iter().chain(iter))),
241            None => Self::new_single(first),
242        }
243    }
244}
245
246impl std::ops::Deref for Path {
247    type Target = PathSlice;
248
249    #[inline]
250    fn deref(&self) -> &Self::Target {
251        self.as_slice()
252    }
253}
254
255impl std::ops::DerefMut for Path {
256    #[inline]
257    fn deref_mut(&mut self) -> &mut Self::Target {
258        self.as_mut_slice()
259    }
260}
261
262impl std::borrow::Borrow<PathSlice> for Path {
263    #[inline]
264    fn borrow(&self) -> &PathSlice {
265        self.as_slice()
266    }
267}
268
269impl Path {
270    /// Creates a new path from a slice of segments.
271    ///
272    /// # Panics
273    ///
274    /// Panics if `segments` is empty.
275    #[inline]
276    pub fn new(segments: &[Ident]) -> Self {
277        assert!(!segments.is_empty());
278        Self(SmallVec::from_slice(segments))
279    }
280
281    /// Creates a new path from a list of segments.
282    ///
283    /// # Panics
284    ///
285    /// Panics if `segments` is empty.
286    #[inline]
287    pub fn from_vec(segments: Vec<Ident>) -> Self {
288        assert!(!segments.is_empty());
289        Self(SmallVec::from_vec(segments))
290    }
291
292    /// Creates a new path from a list of segments.
293    ///
294    /// # Panics
295    ///
296    /// Panics if `segments` is empty.
297    #[inline]
298    pub fn from_ast(ast: AstPath<'_>) -> Self {
299        Self::new(ast.segments())
300    }
301
302    /// Creates a new path from a single ident.
303    #[inline]
304    pub fn new_single(ident: Ident) -> Self {
305        Self(SmallVec::from_buf_and_len([ident], 1))
306    }
307
308    /// Converts the path into a slice.
309    #[inline]
310    pub fn as_slice(&self) -> &PathSlice {
311        unsafe { PathSlice::from_slice_unchecked(&self.0) }
312    }
313
314    /// Converts the path into a mutable slice.
315    #[inline]
316    pub fn as_mut_slice(&mut self) -> &mut PathSlice {
317        unsafe { PathSlice::from_mut_slice_unchecked(&mut self.0) }
318    }
319}