Skip to main content

ruff_python_ast/
name.rs

1use std::borrow::{Borrow, Cow};
2use std::fmt::{Debug, Display, Formatter, Write};
3use std::hash::{Hash, Hasher};
4use std::ops::Deref;
5
6use arrayvec::ArrayVec;
7use char_str::{CharStr, CharString};
8
9use crate::Expr;
10use crate::generated::ExprName;
11
12/// An immutable name.
13///
14/// # Choosing a string representation
15///
16/// On 64-bit targets, [`CharStr`] occupies 16 bytes and stores up to 16 UTF-8 bytes inline. Longer
17/// values use an exactly-sized, reference-counted allocation, so cloning a heap-backed value
18/// reuses its allocation. [`compact_str::CompactString`] occupies 24 bytes, stores up to 24 bytes
19/// inline, and remains mutable; cloning a heap-backed value copies its contents into a new
20/// allocation.
21///
22/// Prefer `CharStr` for immutable text that is retained densely or passed between owners, when
23/// either the smaller handle or structural sharing offsets the extra heap allocations for values
24/// between 17 and 24 bytes. Prefer `CompactString` for uniquely owned text, especially when it is
25/// built incrementally, mutated, or commonly falls in that 17-to-24-byte range.
26///
27/// `Name` uses `CharStr` because names appear throughout the AST and repeated heap-backed parser
28/// names share an allocation. By contrast, [`crate::DebugText`] uses `CompactString` because it
29/// builds a uniquely owned buffer incrementally, and `ty_module_resolver::ModuleName` uses
30/// `CompactString` because module names can be extended in place.
31///
32/// Converting a borrowed `&str` into `CharStr` creates a new value and does not preserve structural
33/// sharing. When an API retains text already held in a `CharStr` (including a `Name`), pass or clone
34/// the owned value rather than converting it through `&str`. This is especially relevant at Salsa
35/// interning boundaries.
36#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
37#[cfg_attr(feature = "salsa", derive(salsa::SalsaValue))]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[cfg_attr(feature = "cache", derive(ruff_macros::CacheKey))]
40#[cfg_attr(feature = "get-size", derive(get_size2::GetSize))]
41#[cfg_attr(
42    feature = "schemars",
43    derive(schemars::JsonSchema),
44    schemars(with = "String")
45)]
46pub struct Name(CharStr);
47
48impl Name {
49    #[inline]
50    pub fn empty() -> Self {
51        Self(CharStr::new())
52    }
53
54    #[inline]
55    pub fn new(name: impl AsRef<str>) -> Self {
56        Self(CharStr::from(name.as_ref()))
57    }
58
59    /// Creates an inline name, returning `None` if `name` does not fit inline.
60    #[inline]
61    pub fn new_inline(name: impl AsRef<str>) -> Option<Self> {
62        CharStr::new_inline(name.as_ref()).map(Self)
63    }
64
65    /// Creates an exactly-sized, heap-allocated name.
66    #[inline]
67    pub fn new_heap(name: impl AsRef<str>) -> Self {
68        Self(CharStr::new_heap(name.as_ref()))
69    }
70
71    #[inline]
72    pub const fn new_static(name: &'static str) -> Self {
73        Self(CharStr::from_static_str(name))
74    }
75
76    /// Creates an exactly-sized name by concatenating string slices.
77    ///
78    /// The combined length is computed up front, so heap storage is allocated at most once.
79    #[inline]
80    pub fn concat<T: AsRef<str>>(slices: &[T]) -> Self {
81        Self(CharStr::concat(slices))
82    }
83
84    /// Creates an exactly-sized name by joining string slices with a separator.
85    ///
86    /// Like [`Name::concat`], this computes the combined length up front, so heap storage is
87    /// allocated at most once. For dynamically formatted names, use [`Name::from`] with
88    /// [`format_char!`](char_str::format_char). If a [`CharStr`] is sufficient, use
89    /// [`format_char_str!`](char_str::format_char_str) instead.
90    #[inline]
91    pub fn join<T: AsRef<str>>(slices: &[T], separator: &str) -> Self {
92        Self(CharStr::join(slices, separator))
93    }
94
95    #[inline]
96    pub fn as_str(&self) -> &str {
97        self.0.as_str()
98    }
99}
100
101impl Debug for Name {
102    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
103        write!(f, "Name({:?})", self.as_str())
104    }
105}
106
107impl AsRef<str> for Name {
108    #[inline]
109    fn as_ref(&self) -> &str {
110        self.as_str()
111    }
112}
113
114impl Deref for Name {
115    type Target = str;
116
117    #[inline]
118    fn deref(&self) -> &Self::Target {
119        self.as_str()
120    }
121}
122
123impl Borrow<str> for Name {
124    #[inline]
125    fn borrow(&self) -> &str {
126        self.as_str()
127    }
128}
129
130impl<'a> From<&'a str> for Name {
131    #[inline]
132    fn from(s: &'a str) -> Self {
133        Name::new(s)
134    }
135}
136
137impl From<String> for Name {
138    #[inline]
139    fn from(s: String) -> Self {
140        Name(s.into())
141    }
142}
143
144impl<'a> From<&'a String> for Name {
145    #[inline]
146    fn from(s: &'a String) -> Self {
147        Name::new(s)
148    }
149}
150
151impl<'a> From<Cow<'a, str>> for Name {
152    #[inline]
153    fn from(cow: Cow<'a, str>) -> Self {
154        Name(cow.into())
155    }
156}
157
158impl From<Box<str>> for Name {
159    #[inline]
160    fn from(b: Box<str>) -> Self {
161        Name(b.into())
162    }
163}
164
165#[cfg(feature = "salsa")]
166impl salsa::Lookup<Name> for &str {
167    #[inline]
168    fn into_owned(self) -> Name {
169        Name::new(self)
170    }
171}
172
173#[cfg(feature = "salsa")]
174impl salsa::HashEqLike<&str> for Name {
175    #[inline]
176    fn hash<H: Hasher>(&self, state: &mut H) {
177        self.as_str().hash(state);
178    }
179
180    #[inline]
181    fn eq(&self, data: &&str) -> bool {
182        self.as_str() == *data
183    }
184}
185
186impl From<Name> for String {
187    #[inline]
188    fn from(name: Name) -> Self {
189        name.0.into()
190    }
191}
192
193impl From<Name> for CharStr {
194    #[inline]
195    fn from(name: Name) -> Self {
196        name.0
197    }
198}
199
200#[cfg(feature = "salsa")]
201impl salsa::Lookup<compact_str::CompactString> for Name {
202    #[inline]
203    fn into_owned(self) -> compact_str::CompactString {
204        compact_str::CompactString::new(self.as_str())
205    }
206}
207
208#[cfg(feature = "salsa")]
209impl salsa::Lookup<compact_str::CompactString> for &Name {
210    #[inline]
211    fn into_owned(self) -> compact_str::CompactString {
212        compact_str::CompactString::new(self.as_str())
213    }
214}
215
216#[cfg(feature = "salsa")]
217impl salsa::HashEqLike<Name> for compact_str::CompactString {
218    #[inline]
219    fn hash<H: Hasher>(&self, state: &mut H) {
220        Hash::hash(self, state);
221    }
222
223    #[inline]
224    fn eq(&self, data: &Name) -> bool {
225        self.as_str() == data.as_str()
226    }
227}
228
229#[cfg(feature = "salsa")]
230impl salsa::HashEqLike<&Name> for compact_str::CompactString {
231    #[inline]
232    fn hash<H: Hasher>(&self, state: &mut H) {
233        Hash::hash(self, state);
234    }
235
236    #[inline]
237    fn eq(&self, data: &&Name) -> bool {
238        self.as_str() == data.as_str()
239    }
240}
241
242impl From<CharString> for Name {
243    #[inline]
244    fn from(name: CharString) -> Self {
245        Self(name.freeze())
246    }
247}
248
249impl FromIterator<char> for Name {
250    fn from_iter<I: IntoIterator<Item = char>>(iter: I) -> Self {
251        Self(iter.into_iter().collect())
252    }
253}
254
255impl std::fmt::Display for Name {
256    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
257        f.write_str(self.as_str())
258    }
259}
260
261impl PartialEq<str> for Name {
262    #[inline]
263    fn eq(&self, other: &str) -> bool {
264        self.as_str() == other
265    }
266}
267
268impl PartialEq<Name> for str {
269    #[inline]
270    fn eq(&self, other: &Name) -> bool {
271        other == self
272    }
273}
274
275impl PartialEq<&str> for Name {
276    #[inline]
277    fn eq(&self, other: &&str) -> bool {
278        self.as_str() == *other
279    }
280}
281
282impl PartialEq<Name> for &str {
283    #[inline]
284    fn eq(&self, other: &Name) -> bool {
285        other == self
286    }
287}
288
289impl PartialEq<String> for Name {
290    fn eq(&self, other: &String) -> bool {
291        self == other.as_str()
292    }
293}
294
295impl PartialEq<Name> for String {
296    #[inline]
297    fn eq(&self, other: &Name) -> bool {
298        other == self
299    }
300}
301
302impl PartialEq<&String> for Name {
303    #[inline]
304    fn eq(&self, other: &&String) -> bool {
305        self.as_str() == *other
306    }
307}
308
309impl PartialEq<Name> for &String {
310    #[inline]
311    fn eq(&self, other: &Name) -> bool {
312        other == self
313    }
314}
315
316/// A representation of a qualified name, like `typing.List`.
317#[derive(Debug, Clone, PartialEq, Eq, Hash)]
318pub struct QualifiedName<'a>(SegmentsVec<'a>);
319
320impl<'a> QualifiedName<'a> {
321    /// Create a [`QualifiedName`] from a dotted name.
322    ///
323    /// ```rust
324    /// # use ruff_python_ast::name::QualifiedName;
325    ///
326    /// assert_eq!(QualifiedName::from_dotted_name("typing.List").segments(), ["typing", "List"]);
327    /// assert_eq!(QualifiedName::from_dotted_name("list").segments(), ["", "list"]);
328    /// ```
329    #[inline]
330    pub fn from_dotted_name(name: &'a str) -> Self {
331        if let Some(dot) = name.find('.') {
332            let mut builder = QualifiedNameBuilder::default();
333            builder.push(&name[..dot]);
334            builder.extend(name[dot + 1..].split('.'));
335            builder.build()
336        } else {
337            Self::builtin(name)
338        }
339    }
340
341    /// Creates a name that's guaranteed not be a built in
342    #[inline]
343    pub fn user_defined(name: &'a str) -> Self {
344        name.split('.').collect()
345    }
346
347    /// Creates a qualified name for a built in
348    #[inline]
349    pub fn builtin(name: &'a str) -> Self {
350        debug_assert!(!name.contains('.'));
351        Self(SegmentsVec::from_slice(&["", name]))
352    }
353
354    #[inline]
355    pub fn segments(&self) -> &[&'a str] {
356        self.0.as_slice()
357    }
358
359    /// If the first segment is empty, the `CallPath` represents a "builtin binding".
360    ///
361    /// A builtin binding is the binding that a symbol has if it was part of Python's
362    /// global scope without any imports taking place. However, if builtin members are
363    /// accessed explicitly via the `builtins` module, they will not have a
364    /// "builtin binding", so this method will return `false`.
365    ///
366    /// Ex) `["", "bool"]` -> `"bool"`
367    fn is_builtin(&self) -> bool {
368        matches!(self.segments(), ["", ..])
369    }
370
371    /// If the call path is dot-prefixed, it's an unresolved relative import.
372    /// Ex) `[".foo", "bar"]` -> `".foo.bar"`
373    pub fn is_unresolved_import(&self) -> bool {
374        matches!(self.segments(), [".", ..])
375    }
376
377    pub fn starts_with(&self, other: &QualifiedName<'_>) -> bool {
378        self.segments().starts_with(other.segments())
379    }
380
381    /// Appends a member to the qualified name.
382    #[must_use]
383    pub fn append_member(self, member: &'a str) -> Self {
384        let mut inner = self.0;
385        inner.push(member);
386        Self(inner)
387    }
388
389    /// Extends the qualified name using the given members.
390    #[must_use]
391    pub fn extend_members<T: IntoIterator<Item = &'a str>>(self, members: T) -> Self {
392        let mut inner = self.0;
393        inner.extend(members);
394        Self(inner)
395    }
396}
397
398impl Display for QualifiedName<'_> {
399    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
400        let segments = self.segments();
401
402        if self.is_unresolved_import() {
403            let mut iter = segments.iter();
404            for segment in iter.by_ref() {
405                if *segment == "." {
406                    f.write_char('.')?;
407                } else {
408                    f.write_str(segment)?;
409                    break;
410                }
411            }
412            for segment in iter {
413                f.write_char('.')?;
414                f.write_str(segment)?;
415            }
416        } else {
417            let segments = if self.is_builtin() {
418                &segments[1..]
419            } else {
420                segments
421            };
422
423            let mut first = true;
424            for segment in segments {
425                if !first {
426                    f.write_char('.')?;
427                }
428
429                f.write_str(segment)?;
430                first = false;
431            }
432        }
433
434        Ok(())
435    }
436}
437
438impl<'a> FromIterator<&'a str> for QualifiedName<'a> {
439    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
440        Self(SegmentsVec::from_iter(iter))
441    }
442}
443
444#[derive(Debug, Clone, Default)]
445pub struct QualifiedNameBuilder<'a> {
446    segments: SegmentsVec<'a>,
447}
448
449impl<'a> QualifiedNameBuilder<'a> {
450    pub fn with_capacity(capacity: usize) -> Self {
451        Self {
452            segments: SegmentsVec::with_capacity(capacity),
453        }
454    }
455
456    #[inline]
457    pub(crate) fn is_empty(&self) -> bool {
458        self.segments.is_empty()
459    }
460
461    #[inline]
462    pub fn push(&mut self, segment: &'a str) {
463        self.segments.push(segment);
464    }
465
466    #[inline]
467    pub(crate) fn pop(&mut self) {
468        self.segments.pop();
469    }
470
471    #[inline]
472    pub fn extend(&mut self, segments: impl IntoIterator<Item = &'a str>) {
473        self.segments.extend(segments);
474    }
475
476    #[inline]
477    pub(crate) fn extend_from_slice(&mut self, segments: &[&'a str]) {
478        self.segments.extend_from_slice(segments);
479    }
480
481    pub fn build(self) -> QualifiedName<'a> {
482        QualifiedName(self.segments)
483    }
484}
485
486#[derive(Debug, Clone, PartialEq, Eq, Hash)]
487pub struct UnqualifiedName<'a>(SegmentsVec<'a>);
488
489impl<'a> UnqualifiedName<'a> {
490    /// Convert an `Expr` to its [`UnqualifiedName`] (like `["typing", "List"]`).
491    pub fn from_expr(expr: &'a Expr) -> Option<Self> {
492        // Unroll the loop up to eight times, to match the maximum number of expected attributes.
493        // In practice, unrolling appears to give about a 4x speed-up on this hot path.
494        let attr1 = match expr {
495            Expr::Attribute(attr1) => attr1,
496            // Ex) `foo`
497            Expr::Name(ExprName { id, .. }) => return Some(Self::from_slice(&[id.as_str()])),
498            _ => return None,
499        };
500
501        let attr2 = match attr1.value.as_ref() {
502            Expr::Attribute(attr2) => attr2,
503            // Ex) `foo.bar`
504            Expr::Name(ExprName { id, .. }) => {
505                return Some(Self::from_slice(&[id.as_str(), attr1.attr.as_str()]));
506            }
507            _ => return None,
508        };
509
510        let attr3 = match attr2.value.as_ref() {
511            Expr::Attribute(attr3) => attr3,
512            // Ex) `foo.bar.baz`
513            Expr::Name(ExprName { id, .. }) => {
514                return Some(Self::from_slice(&[
515                    id.as_str(),
516                    attr2.attr.as_str(),
517                    attr1.attr.as_str(),
518                ]));
519            }
520            _ => return None,
521        };
522
523        let attr4 = match attr3.value.as_ref() {
524            Expr::Attribute(attr4) => attr4,
525            // Ex) `foo.bar.baz.bop`
526            Expr::Name(ExprName { id, .. }) => {
527                return Some(Self::from_slice(&[
528                    id.as_str(),
529                    attr3.attr.as_str(),
530                    attr2.attr.as_str(),
531                    attr1.attr.as_str(),
532                ]));
533            }
534            _ => return None,
535        };
536
537        let attr5 = match attr4.value.as_ref() {
538            Expr::Attribute(attr5) => attr5,
539            // Ex) `foo.bar.baz.bop.bap`
540            Expr::Name(ExprName { id, .. }) => {
541                return Some(Self::from_slice(&[
542                    id.as_str(),
543                    attr4.attr.as_str(),
544                    attr3.attr.as_str(),
545                    attr2.attr.as_str(),
546                    attr1.attr.as_str(),
547                ]));
548            }
549            _ => return None,
550        };
551
552        let attr6 = match attr5.value.as_ref() {
553            Expr::Attribute(attr6) => attr6,
554            // Ex) `foo.bar.baz.bop.bap.bab`
555            Expr::Name(ExprName { id, .. }) => {
556                return Some(Self::from_slice(&[
557                    id.as_str(),
558                    attr5.attr.as_str(),
559                    attr4.attr.as_str(),
560                    attr3.attr.as_str(),
561                    attr2.attr.as_str(),
562                    attr1.attr.as_str(),
563                ]));
564            }
565            _ => return None,
566        };
567
568        let attr7 = match attr6.value.as_ref() {
569            Expr::Attribute(attr7) => attr7,
570            // Ex) `foo.bar.baz.bop.bap.bab.bob`
571            Expr::Name(ExprName { id, .. }) => {
572                return Some(Self::from_slice(&[
573                    id.as_str(),
574                    attr6.attr.as_str(),
575                    attr5.attr.as_str(),
576                    attr4.attr.as_str(),
577                    attr3.attr.as_str(),
578                    attr2.attr.as_str(),
579                    attr1.attr.as_str(),
580                ]));
581            }
582            _ => return None,
583        };
584
585        let attr8 = match attr7.value.as_ref() {
586            Expr::Attribute(attr8) => attr8,
587            // Ex) `foo.bar.baz.bop.bap.bab.bob.bib`
588            Expr::Name(ExprName { id, .. }) => {
589                return Some(Self(SegmentsVec::from([
590                    id.as_str(),
591                    attr7.attr.as_str(),
592                    attr6.attr.as_str(),
593                    attr5.attr.as_str(),
594                    attr4.attr.as_str(),
595                    attr3.attr.as_str(),
596                    attr2.attr.as_str(),
597                    attr1.attr.as_str(),
598                ])));
599            }
600            _ => return None,
601        };
602
603        let mut segments = Vec::with_capacity(SMALL_LEN * 2);
604
605        let mut current = &*attr8.value;
606
607        loop {
608            current = match current {
609                Expr::Attribute(attr) => {
610                    segments.push(attr.attr.as_str());
611                    &*attr.value
612                }
613                Expr::Name(ExprName { id, .. }) => {
614                    segments.push(id.as_str());
615                    break;
616                }
617                _ => {
618                    return None;
619                }
620            }
621        }
622
623        segments.reverse();
624
625        // Append the attributes we visited before calling into the recursion.
626        segments.extend_from_slice(&[
627            attr8.attr.as_str(),
628            attr7.attr.as_str(),
629            attr6.attr.as_str(),
630            attr5.attr.as_str(),
631            attr4.attr.as_str(),
632            attr3.attr.as_str(),
633            attr2.attr.as_str(),
634            attr1.attr.as_str(),
635        ]);
636
637        Some(Self(SegmentsVec::from(segments)))
638    }
639
640    #[inline]
641    fn from_slice(segments: &[&'a str]) -> Self {
642        Self(SegmentsVec::from_slice(segments))
643    }
644
645    pub fn segments(&self) -> &[&'a str] {
646        self.0.as_slice()
647    }
648}
649
650impl Display for UnqualifiedName<'_> {
651    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
652        let mut first = true;
653        for segment in self.segments() {
654            if !first {
655                f.write_char('.')?;
656            }
657
658            f.write_str(segment)?;
659            first = false;
660        }
661
662        Ok(())
663    }
664}
665
666impl<'a> FromIterator<&'a str> for UnqualifiedName<'a> {
667    #[inline]
668    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
669        Self(iter.into_iter().collect())
670    }
671}
672
673/// A smallvec like storage for qualified and unqualified name segments.
674///
675/// Stores up to 8 segments inline, and falls back to a heap-allocated vector for names with more segments.
676///
677/// ## Note
678/// The inline variant uses `ArrayVec` rather than `SmallVec` v1 because `SmallVec`'s type
679/// definition has a variance problem. The incorrect variance leads lifetime inference in the
680/// `SemanticModel` astray, causing all sorts of "strange" lifetime errors.
681#[derive(Clone)]
682enum SegmentsVec<'a> {
683    Stack(SegmentsStack<'a>),
684    Heap(Vec<&'a str>),
685}
686
687impl<'a> SegmentsVec<'a> {
688    /// Creates an empty segment vec.
689    fn new() -> Self {
690        Self::Stack(SegmentsStack::default())
691    }
692
693    /// Creates a segment vec that has reserved storage for up to `capacity` items.
694    fn with_capacity(capacity: usize) -> Self {
695        if capacity <= SMALL_LEN {
696            Self::new()
697        } else {
698            Self::Heap(Vec::with_capacity(capacity))
699        }
700    }
701
702    #[cfg(test)]
703    const fn is_spilled(&self) -> bool {
704        matches!(self, SegmentsVec::Heap(_))
705    }
706
707    /// Initializes the segments from a slice.
708    #[inline]
709    fn from_slice(slice: &[&'a str]) -> Self {
710        match SegmentsStack::try_from(slice) {
711            Ok(stack) => SegmentsVec::Stack(stack),
712            Err(_) => SegmentsVec::Heap(slice.to_vec()),
713        }
714    }
715
716    /// Returns the segments as a slice.
717    #[inline]
718    fn as_slice(&self) -> &[&'a str] {
719        match self {
720            Self::Stack(stack) => stack.as_slice(),
721            Self::Heap(heap) => heap.as_slice(),
722        }
723    }
724
725    /// Pushes `name` to the end of the segments.
726    ///
727    /// Spills to the heap if the segments are stored on the stack and the 9th segment is pushed.
728    #[inline]
729    fn push(&mut self, name: &'a str) {
730        match self {
731            SegmentsVec::Stack(stack) => {
732                if let Err(error) = stack.try_push(name) {
733                    let mut segments = Vec::with_capacity(stack.len() * 2);
734                    segments.extend(stack.iter().copied());
735                    segments.push(error.element());
736                    *self = SegmentsVec::Heap(segments);
737                }
738            }
739            SegmentsVec::Heap(heap) => {
740                heap.push(name);
741            }
742        }
743    }
744
745    /// Pops the last segment from the end and returns it.
746    ///
747    /// Returns `None` if the vector is empty.
748    #[inline]
749    fn pop(&mut self) -> Option<&'a str> {
750        match self {
751            SegmentsVec::Stack(stack) => stack.pop(),
752            SegmentsVec::Heap(heap) => heap.pop(),
753        }
754    }
755
756    #[inline]
757    fn extend_from_slice(&mut self, slice: &[&'a str]) {
758        match self {
759            SegmentsVec::Stack(stack) => {
760                if stack.try_extend_from_slice(slice).is_err() {
761                    let mut segments = Vec::with_capacity(stack.len() + slice.len());
762                    segments.extend(stack.iter().copied());
763                    segments.extend_from_slice(slice);
764                    *self = SegmentsVec::Heap(segments);
765                }
766            }
767            SegmentsVec::Heap(heap) => heap.extend_from_slice(slice),
768        }
769    }
770}
771
772impl Default for SegmentsVec<'_> {
773    fn default() -> Self {
774        Self::new()
775    }
776}
777
778impl Debug for SegmentsVec<'_> {
779    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
780        f.debug_list().entries(self.as_slice()).finish()
781    }
782}
783
784impl<'a> Deref for SegmentsVec<'a> {
785    type Target = [&'a str];
786    fn deref(&self) -> &Self::Target {
787        self.as_slice()
788    }
789}
790
791impl<'b> PartialEq<SegmentsVec<'b>> for SegmentsVec<'_> {
792    fn eq(&self, other: &SegmentsVec<'b>) -> bool {
793        self.as_slice() == other.as_slice()
794    }
795}
796
797impl Eq for SegmentsVec<'_> {}
798
799impl Hash for SegmentsVec<'_> {
800    fn hash<H: Hasher>(&self, state: &mut H) {
801        self.as_slice().hash(state);
802    }
803}
804
805impl<'a> FromIterator<&'a str> for SegmentsVec<'a> {
806    #[inline]
807    fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
808        let mut segments = SegmentsVec::default();
809        segments.extend(iter);
810        segments
811    }
812}
813
814impl<'a> From<[&'a str; 8]> for SegmentsVec<'a> {
815    #[inline]
816    fn from(segments: [&'a str; 8]) -> Self {
817        SegmentsVec::Stack(SegmentsStack::from(segments))
818    }
819}
820
821impl<'a> From<Vec<&'a str>> for SegmentsVec<'a> {
822    #[inline]
823    fn from(segments: Vec<&'a str>) -> Self {
824        SegmentsVec::Heap(segments)
825    }
826}
827
828impl<'a> Extend<&'a str> for SegmentsVec<'a> {
829    #[inline]
830    fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
831        match self {
832            SegmentsVec::Stack(stack) => {
833                let mut iter = iter.into_iter();
834                let (lower, _) = iter.size_hint();
835
836                if lower > stack.remaining_capacity() {
837                    let mut segments = Vec::with_capacity(stack.len() + lower);
838                    segments.extend(stack.iter().copied());
839                    segments.extend(iter);
840                    *self = SegmentsVec::Heap(segments);
841                    return;
842                }
843
844                while let Some(name) = iter.next() {
845                    if let Err(error) = stack.try_push(name) {
846                        let mut segments = Vec::with_capacity(stack.len() * 2);
847                        segments.extend(stack.iter().copied());
848                        segments.push(error.element());
849                        segments.extend(iter);
850                        *self = SegmentsVec::Heap(segments);
851                        return;
852                    }
853                }
854            }
855            SegmentsVec::Heap(heap) => {
856                heap.extend(iter);
857            }
858        }
859    }
860}
861
862const SMALL_LEN: usize = 8;
863type SegmentsStack<'a> = ArrayVec<&'a str, SMALL_LEN>;
864
865#[cfg(test)]
866mod tests {
867    #[cfg(feature = "salsa")]
868    use std::hash::{DefaultHasher, Hash, Hasher};
869
870    #[cfg(feature = "salsa")]
871    use crate::name::Name;
872    use crate::name::SegmentsVec;
873
874    #[cfg(feature = "salsa")]
875    #[test]
876    fn salsa_lookup_name_from_str() {
877        let name = Name::new("member");
878        let lookup = "member";
879
880        let mut name_hasher = DefaultHasher::new();
881        salsa::HashEqLike::<&str>::hash(&name, &mut name_hasher);
882        let mut lookup_hasher = DefaultHasher::new();
883        lookup.hash(&mut lookup_hasher);
884
885        assert_eq!(name_hasher.finish(), lookup_hasher.finish());
886        assert!(salsa::HashEqLike::<&str>::eq(&name, &lookup));
887        assert_eq!(salsa::Lookup::<Name>::into_owned(lookup), name);
888    }
889
890    #[test]
891    fn empty_vec() {
892        let empty = SegmentsVec::new();
893        assert_eq!(empty.as_slice(), &[] as &[&str]);
894        assert!(!empty.is_spilled());
895    }
896
897    #[test]
898    fn from_slice_stack() {
899        let stack = SegmentsVec::from_slice(&["a", "b", "c"]);
900
901        assert_eq!(stack.as_slice(), &["a", "b", "c"]);
902        assert!(!stack.is_spilled());
903    }
904
905    #[test]
906    fn from_slice_stack_capacity() {
907        let stack = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h"]);
908
909        assert_eq!(stack.as_slice(), &["a", "b", "c", "d", "e", "f", "g", "h"]);
910        assert!(!stack.is_spilled());
911    }
912
913    #[test]
914    fn from_slice_heap() {
915        let heap = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h", "i"]);
916
917        assert_eq!(
918            heap.as_slice(),
919            &["a", "b", "c", "d", "e", "f", "g", "h", "i"]
920        );
921        assert!(heap.is_spilled());
922    }
923
924    #[test]
925    fn push_stack() {
926        let mut stack = SegmentsVec::from_slice(&["a", "b", "c"]);
927        stack.push("d");
928        stack.push("e");
929
930        assert_eq!(stack.as_slice(), &["a", "b", "c", "d", "e"]);
931        assert!(!stack.is_spilled());
932    }
933
934    #[test]
935    fn push_stack_spill() {
936        let mut stack = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g"]);
937        stack.push("h");
938
939        assert!(!stack.is_spilled());
940
941        stack.push("i");
942
943        assert_eq!(
944            stack.as_slice(),
945            &["a", "b", "c", "d", "e", "f", "g", "h", "i"]
946        );
947        assert!(stack.is_spilled());
948    }
949
950    #[test]
951    fn pop_stack() {
952        let mut stack = SegmentsVec::from_slice(&["a", "b", "c", "d", "e"]);
953        assert_eq!(stack.pop(), Some("e"));
954        assert_eq!(stack.pop(), Some("d"));
955        assert_eq!(stack.pop(), Some("c"));
956        assert_eq!(stack.pop(), Some("b"));
957        assert_eq!(stack.pop(), Some("a"));
958        assert_eq!(stack.pop(), None);
959
960        assert!(!stack.is_spilled());
961    }
962
963    #[test]
964    fn pop_heap() {
965        let mut heap = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h", "i"]);
966
967        assert_eq!(heap.pop(), Some("i"));
968        assert_eq!(heap.pop(), Some("h"));
969        assert_eq!(heap.pop(), Some("g"));
970
971        assert!(heap.is_spilled());
972    }
973
974    #[test]
975    fn extend_from_slice_stack() {
976        let mut stack = SegmentsVec::from_slice(&["a", "b", "c"]);
977        stack.extend_from_slice(&["d", "e", "f"]);
978
979        assert_eq!(stack.as_slice(), &["a", "b", "c", "d", "e", "f"]);
980        assert!(!stack.is_spilled());
981    }
982
983    #[test]
984    fn extend_from_slice_stack_spill() {
985        let mut spilled = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f"]);
986        spilled.extend_from_slice(&["g", "h", "i", "j"]);
987
988        assert_eq!(
989            spilled.as_slice(),
990            &["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
991        );
992        assert!(spilled.is_spilled());
993    }
994
995    #[test]
996    fn extend_from_slice_heap() {
997        let mut heap = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h", "i"]);
998        assert!(heap.is_spilled());
999
1000        heap.extend_from_slice(&["j", "k", "l"]);
1001
1002        assert_eq!(
1003            heap.as_slice(),
1004            &["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
1005        );
1006    }
1007
1008    #[test]
1009    fn extend_stack() {
1010        let mut stack = SegmentsVec::from_slice(&["a", "b", "c"]);
1011        stack.extend(["d", "e", "f"]);
1012
1013        assert_eq!(stack.as_slice(), &["a", "b", "c", "d", "e", "f"]);
1014        assert!(!stack.is_spilled());
1015    }
1016
1017    #[test]
1018    fn extend_stack_spilled() {
1019        let mut stack = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f"]);
1020        stack.extend(["g", "h", "i", "j"]);
1021
1022        assert_eq!(
1023            stack.as_slice(),
1024            &["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
1025        );
1026        assert!(stack.is_spilled());
1027    }
1028
1029    #[test]
1030    fn extend_heap() {
1031        let mut heap = SegmentsVec::from_slice(&["a", "b", "c", "d", "e", "f", "g", "h", "i"]);
1032        assert!(heap.is_spilled());
1033
1034        heap.extend(["j", "k", "l"]);
1035
1036        assert_eq!(
1037            heap.as_slice(),
1038            &["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
1039        );
1040    }
1041}