solar_ast/ast/
path.rs

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