icu_locale_core/parser/
mod.rs1pub mod errors;
6mod langid;
7mod locale;
8
9pub use errors::ParseError;
10pub use langid::*;
11
12pub use locale::*;
13
14const fn skip_before_separator(slice: &[u8]) -> &[u8] {
16 let mut end = 0;
17 #[allow(clippy::indexing_slicing)] while end < slice.len() && !matches!(slice[end], b'-') {
21 end += 1;
24 }
26
27 unsafe { slice.split_at_unchecked(end).0 }
31}
32
33#[derive(Copy, Clone, Debug)]
46pub struct SubtagIterator<'a> {
47 remaining: &'a [u8],
48 current: Option<&'a [u8]>,
50}
51
52impl<'a> SubtagIterator<'a> {
53 pub const fn new(rest: &'a [u8]) -> Self {
54 Self {
55 remaining: rest,
56 current: Some(skip_before_separator(rest)),
58 }
59 }
60
61 pub const fn next_const(mut self) -> (Self, Option<&'a [u8]>) {
62 let Some(result) = self.current else {
63 return (self, None);
64 };
65
66 self.current = if result.len() < self.remaining.len() {
67 self.remaining = unsafe { self.remaining.split_at_unchecked(result.len() + 1).1 };
70 Some(skip_before_separator(self.remaining))
73 } else {
74 None
75 };
76 (self, Some(result))
77 }
78
79 pub const fn peek(&self) -> Option<&'a [u8]> {
80 self.current
81 }
82}
83
84impl<'a> Iterator for SubtagIterator<'a> {
85 type Item = &'a [u8];
86
87 fn next(&mut self) -> Option<Self::Item> {
88 let (s, res) = self.next_const();
89 *self = s;
90 res
91 }
92}
93
94#[cfg(test)]
95mod test {
96 use super::*;
97
98 fn slice_to_str(input: &[u8]) -> &str {
99 std::str::from_utf8(input).unwrap()
100 }
101
102 #[test]
103 fn subtag_iterator_peek_test() {
104 let slice = "de-at-u-ca-foobar";
105 let mut si = SubtagIterator::new(slice.as_bytes());
106
107 assert_eq!(si.peek().map(slice_to_str), Some("de"));
108 assert_eq!(si.peek().map(slice_to_str), Some("de"));
109 assert_eq!(si.next().map(slice_to_str), Some("de"));
110
111 assert_eq!(si.peek().map(slice_to_str), Some("at"));
112 assert_eq!(si.peek().map(slice_to_str), Some("at"));
113 assert_eq!(si.next().map(slice_to_str), Some("at"));
114 }
115
116 #[test]
117 fn subtag_iterator_test() {
118 let slice = "";
119 let mut si = SubtagIterator::new(slice.as_bytes());
120 assert_eq!(si.next().map(slice_to_str), Some(""));
121
122 let slice = "-";
123 let mut si = SubtagIterator::new(slice.as_bytes());
124 assert_eq!(si.next().map(slice_to_str), Some(""));
125
126 let slice = "-en";
127 let mut si = SubtagIterator::new(slice.as_bytes());
128 assert_eq!(si.next().map(slice_to_str), Some(""));
129 assert_eq!(si.next().map(slice_to_str), Some("en"));
130 assert_eq!(si.next(), None);
131
132 let slice = "en";
133 let si = SubtagIterator::new(slice.as_bytes());
134 assert_eq!(si.map(slice_to_str).collect::<Vec<_>>(), vec!["en",]);
135
136 let slice = "en-";
137 let si = SubtagIterator::new(slice.as_bytes());
138 assert_eq!(si.map(slice_to_str).collect::<Vec<_>>(), vec!["en", "",]);
139
140 let slice = "--";
141 let mut si = SubtagIterator::new(slice.as_bytes());
142 assert_eq!(si.next().map(slice_to_str), Some(""));
143 assert_eq!(si.next().map(slice_to_str), Some(""));
144 assert_eq!(si.next().map(slice_to_str), Some(""));
145 assert_eq!(si.next(), None);
146
147 let slice = "-en-";
148 let mut si = SubtagIterator::new(slice.as_bytes());
149 assert_eq!(si.next().map(slice_to_str), Some(""));
150 assert_eq!(si.next().map(slice_to_str), Some("en"));
151 assert_eq!(si.next().map(slice_to_str), Some(""));
152 assert_eq!(si.next(), None);
153
154 let slice = "de-at-u-ca-foobar";
155 let si = SubtagIterator::new(slice.as_bytes());
156 assert_eq!(
157 si.map(slice_to_str).collect::<Vec<_>>(),
158 vec!["de", "at", "u", "ca", "foobar",]
159 );
160 }
161
162 #[test]
163 fn skip_before_separator_test() {
164 let current = skip_before_separator(b"");
165 assert_eq!(current, b"");
166
167 let current = skip_before_separator(b"en");
168 assert_eq!(current, b"en");
169
170 let current = skip_before_separator(b"en-");
171 assert_eq!(current, b"en");
172
173 let current = skip_before_separator(b"en--US");
174 assert_eq!(current, b"en");
175
176 let current = skip_before_separator(b"-US");
177 assert_eq!(current, b"");
178
179 let current = skip_before_separator(b"US");
180 assert_eq!(current, b"US");
181
182 let current = skip_before_separator(b"-");
183 assert_eq!(current, b"");
184 }
185}