1 use std::cmp::{Ord, Ordering, PartialOrd};
2 use std::str;
3
4 #[derive(Copy, Clone, Eq, PartialEq)]
5 pub enum Atom<'a> {
6 /// A sequence of underscores.
7 Underscore(usize),
8 /// A sequence of digits.
9 Number(&'a str),
10 /// A sequence of characters.
11 Chars(&'a str),
12 }
13
14 impl Atom<'_> {
underscores(&self) -> usize15 pub fn underscores(&self) -> usize {
16 match *self {
17 Atom::Underscore(n) => n,
18 _ => 0,
19 }
20 }
21 }
22
23 impl PartialOrd for Atom<'_> {
partial_cmp(&self, other: &Self) -> Option<Ordering>24 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
25 Some(self.cmp(other))
26 }
27 }
28
29 impl Ord for Atom<'_> {
cmp(&self, other: &Self) -> Ordering30 fn cmp(&self, other: &Self) -> Ordering {
31 use self::Atom::*;
32
33 match (self, other) {
34 (Underscore(l), Underscore(r)) => l.cmp(r),
35 (Underscore(_), _) => Ordering::Less,
36 (_, Underscore(_)) => Ordering::Greater,
37 (Number(l), Number(r)) => cmp_numeric(l, r),
38 (Number(_), Chars(_)) => Ordering::Less,
39 (Chars(_), Number(_)) => Ordering::Greater,
40 (Chars(l), Chars(r)) => cmp_ignore_case(l, r),
41 }
42 }
43 }
44
cmp_numeric(l: &str, r: &str) -> Ordering45 fn cmp_numeric(l: &str, r: &str) -> Ordering {
46 // Trim leading zeros.
47 let l = l.trim_start_matches('0');
48 let r = r.trim_start_matches('0');
49
50 match l.len().cmp(&r.len()) {
51 Ordering::Equal => l.cmp(r),
52 non_eq => non_eq,
53 }
54 }
55
cmp_ignore_case(l: &str, r: &str) -> Ordering56 fn cmp_ignore_case(l: &str, r: &str) -> Ordering {
57 for (a, b) in l.bytes().zip(r.bytes()) {
58 match a.to_ascii_lowercase().cmp(&b.to_ascii_lowercase()) {
59 Ordering::Equal => match a.cmp(&b) {
60 Ordering::Equal => {}
61 non_eq => return non_eq,
62 },
63 non_eq => return non_eq,
64 }
65 }
66
67 l.len().cmp(&r.len())
68 }
69
iter_atoms(string: &str) -> AtomIter70 pub fn iter_atoms(string: &str) -> AtomIter {
71 AtomIter {
72 bytes: string.as_bytes(),
73 offset: 0,
74 }
75 }
76
77 pub struct AtomIter<'a> {
78 bytes: &'a [u8],
79 offset: usize,
80 }
81
82 impl<'a> Iterator for AtomIter<'a> {
83 type Item = Atom<'a>;
84
next(&mut self) -> Option<Atom<'a>>85 fn next(&mut self) -> Option<Atom<'a>> {
86 if self.offset >= self.bytes.len() {
87 return None;
88 }
89
90 let x = self.bytes[self.offset];
91
92 match x {
93 b'_' => {
94 self.offset += 1;
95
96 let mut n = 1;
97 while self.offset < self.bytes.len() {
98 match self.bytes[self.offset] {
99 b'_' => {
100 self.offset += 1;
101 n += 1;
102 }
103 _ => break,
104 }
105 }
106
107 Some(Atom::Underscore(n))
108 }
109 b'0'..=b'9' => {
110 let start = self.offset;
111
112 self.offset += 1;
113 while self.offset < self.bytes.len() {
114 match self.bytes[self.offset] {
115 b'0'..=b'9' => self.offset += 1,
116 _ => break,
117 }
118 }
119
120 let bytes = &self.bytes[start..self.offset];
121 let number = str::from_utf8(bytes).expect("valid utf8");
122 Some(Atom::Number(number))
123 }
124 _ => {
125 let start = self.offset;
126
127 self.offset += 1;
128 while self.offset < self.bytes.len() {
129 match self.bytes[self.offset] {
130 b'_' | b'0'..=b'9' => break,
131 _ => self.offset += 1,
132 }
133 }
134
135 let bytes = &self.bytes[start..self.offset];
136 let chars = str::from_utf8(bytes).expect("valid utf8");
137 Some(Atom::Chars(chars))
138 }
139 }
140 }
141 }
142