1 use proc_macro2::Ident;
2 use std::cmp::Ordering;
3
4 use crate::atom::iter_atoms;
5
6 #[derive(Copy, Clone, PartialEq)]
7 pub enum UnderscoreOrder {
8 First,
9 Last,
10 }
11
12 pub struct Path {
13 pub segments: Vec<Ident>,
14 }
15
cmp(lhs: &Path, rhs: &Path, mode: UnderscoreOrder) -> Ordering16 pub fn cmp(lhs: &Path, rhs: &Path, mode: UnderscoreOrder) -> Ordering {
17 // Lexicographic ordering across path segments.
18 for (lhs, rhs) in lhs.segments.iter().zip(&rhs.segments) {
19 match cmp_segment(&lhs.to_string(), &rhs.to_string(), mode) {
20 Ordering::Equal => {}
21 non_eq => return non_eq,
22 }
23 }
24
25 lhs.segments.len().cmp(&rhs.segments.len())
26 }
27
cmp_segment(lhs: &str, rhs: &str, mode: UnderscoreOrder) -> Ordering28 fn cmp_segment(lhs: &str, rhs: &str, mode: UnderscoreOrder) -> Ordering {
29 // Sort `_` last.
30 match (lhs, rhs) {
31 ("_", "_") => return Ordering::Equal,
32 ("_", _) => return Ordering::Greater,
33 (_, "_") => return Ordering::Less,
34 (_, _) => {}
35 }
36
37 let mut lhs_atoms = iter_atoms(lhs);
38 let mut rhs_atoms = iter_atoms(rhs);
39
40 // Path segments can't be empty.
41 let mut left = lhs_atoms.next().unwrap();
42 let mut right = rhs_atoms.next().unwrap();
43
44 if mode == UnderscoreOrder::Last {
45 // Compare leading underscores.
46 match left.underscores().cmp(&right.underscores()) {
47 Ordering::Equal => {}
48 non_eq => return non_eq,
49 }
50 }
51
52 loop {
53 match left.cmp(&right) {
54 Ordering::Equal => {}
55 non_eq => return non_eq,
56 }
57
58 match (lhs_atoms.next(), rhs_atoms.next()) {
59 (None, None) => return Ordering::Equal,
60 (None, Some(_)) => return Ordering::Less,
61 (Some(_), None) => return Ordering::Greater,
62 (Some(nextl), Some(nextr)) => {
63 left = nextl;
64 right = nextr;
65 }
66 }
67 }
68 }
69