unicode_locale_parser/
extensions.rs1pub mod other;
2pub mod pu;
3pub mod transformed;
4pub mod unicode_locale;
5
6use other::{parse_other_extensions, OtherExtensions};
7use pu::{parse_pu_extensions, PuExtensions};
8use transformed::{parse_transformed_extensions, TransformedExtensions};
9use unicode_locale::{parse_unicode_locale_extensions, UnicodeLocaleExtensions};
10
11use crate::constants::SEP;
12use crate::errors::ParserError;
13use crate::shared::split_str;
14
15use std::fmt::{self, Write};
16use std::iter::Peekable;
17
18#[derive(Debug, PartialEq)]
19enum ExtensionKind {
20 UnicodeLocale,
21 Transformed,
22 Pu,
23 Other(char),
24}
25
26impl ExtensionKind {
27 fn from_byte(key: u8) -> Result<Self, ParserError> {
28 let key = key.to_ascii_lowercase();
29 match key {
30 b'u' => Ok(ExtensionKind::UnicodeLocale),
31 b't' => Ok(ExtensionKind::Transformed),
32 b'x' => Ok(ExtensionKind::Pu),
33 other if other.is_ascii_alphanumeric() => Ok(ExtensionKind::Other(char::from(other))),
34 _ => Err(ParserError::InvalidExtension),
35 }
36 }
37}
38
39impl fmt::Display for ExtensionKind {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 let c = match self {
42 ExtensionKind::UnicodeLocale => 'u',
43 ExtensionKind::Transformed => 't',
44 ExtensionKind::Pu => 'x',
45 ExtensionKind::Other(c) => *c,
46 };
47 f.write_char(c)
48 }
49}
50
51#[derive(Debug)]
52pub struct Extensions {
53 pub unicode_locale: Option<Vec<UnicodeLocaleExtensions>>,
54 pub transformed: Option<Vec<TransformedExtensions>>,
55 pub other: Option<Vec<OtherExtensions>>,
56 pub pu: Option<PuExtensions>,
57}
58
59impl fmt::Display for Extensions {
60 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61 let mut messages = vec![];
62 if let Some(unicode_locale) = &self.unicode_locale {
63 for u in unicode_locale {
64 messages.push(format!("{}", u));
65 }
66 }
67 if let Some(transformed) = &self.transformed {
68 for t in transformed {
69 messages.push(format!("{}", t));
70 }
71 }
72 if let Some(other) = &self.other {
73 for o in other {
74 messages.push(format!("{}", o));
75 }
76 }
77 if let Some(pu) = &self.pu {
78 messages.push(format!("{}", pu));
79 }
80
81 if !messages.is_empty() {
82 f.write_str(&messages.join(&SEP.to_string()))?;
83 }
84 Ok(())
85 }
86}
87
88#[allow(dead_code)]
89pub fn parse_extensions(chunk: &str) -> Result<Extensions, ParserError> {
90 if chunk.is_empty() {
92 return Err(ParserError::Missing);
93 }
94
95 parse_extensions_from_iter(&mut split_str(chunk).peekable())
96}
97
98pub fn parse_extensions_from_iter<'a>(
99 iter: &mut Peekable<impl Iterator<Item = &'a str>>,
100) -> Result<Extensions, ParserError> {
101 let mut unicode_locale = vec![];
102 let mut transformed = vec![];
103 let mut other = vec![];
104 let mut pu = None;
105
106 let mut chunk = iter.next();
107 while let Some(subtag) = chunk {
108 match subtag
109 .as_bytes()
110 .first()
111 .map(|c| ExtensionKind::from_byte(*c))
112 {
113 Some(Ok(ExtensionKind::UnicodeLocale)) => {
114 unicode_locale.push(parse_unicode_locale_extensions(iter)?);
115 }
116 Some(Ok(ExtensionKind::Transformed)) => {
117 transformed.push(parse_transformed_extensions(iter)?);
118 }
119 Some(Ok(ExtensionKind::Pu)) => {
120 if pu.is_some() {
121 return Err(ParserError::Unexpected);
122 }
123 pu = Some(parse_pu_extensions(iter)?);
124 }
125 Some(Ok(ExtensionKind::Other(c))) => {
126 other.push(parse_other_extensions(iter, c)?);
127 }
128 None => {}
129 _ => unreachable!(),
130 }
131
132 chunk = iter.next();
133 }
134
135 let unicode_locale = if unicode_locale.is_empty() {
137 None
138 } else {
139 Some(unicode_locale)
140 };
141
142 let transformed = if transformed.is_empty() {
144 None
145 } else {
146 Some(transformed)
147 };
148
149 let other = if other.is_empty() { None } else { Some(other) };
151
152 Ok(Extensions {
153 unicode_locale,
154 transformed,
155 pu,
156 other,
157 })
158}
159
160#[test]
165fn success_parse_extensions() {
166 let extensions = parse_extensions(
168 "U-attr1-kz-value2-t-en-Latn-US-macos-t1-value1-value2-a-vue-rust-x-foo-123",
169 )
170 .unwrap();
171 let unicode_locale = extensions.unicode_locale.unwrap();
172 assert_eq!(
173 ["u-attr1-kz-value2"],
174 unicode_locale
175 .iter()
176 .map(|u| format!("{}", u))
177 .collect::<Vec<String>>()
178 .as_slice()
179 );
180 let transformed = extensions.transformed.unwrap();
181 assert_eq!(
182 ["t-en-Latn-US-macos-t1-value1-value2"],
183 transformed
184 .iter()
185 .map(|t| format!("{}", t))
186 .collect::<Vec<String>>()
187 .as_slice()
188 );
189 let other = extensions.other.unwrap();
190 assert_eq!(
191 ["a-vue-rust"],
192 other
193 .iter()
194 .map(|o| format!("{}", o))
195 .collect::<Vec<String>>()
196 .as_slice()
197 );
198 let pu = extensions.pu.unwrap();
199 assert_eq!("x-foo-123", format!("{}", pu));
200
201 assert_eq!(
203 "u-attr1-kz-value2-t-en-Latn-US-macos-t1-value1-value2-a-vue-rust-x-foo-123",
204 format!(
205 "{}",
206 parse_extensions(
207 "U-attr1-kz-value2-t-en-Latn-US-macos-t1-value1-value2-a-vue-rust-x-foo-123",
208 )
209 .unwrap()
210 )
211 );
212}
213
214#[test]
219fn fail_parse_unicode_extensions() {
220 assert_eq!(ParserError::Missing, parse_extensions("").unwrap_err());
222}
223
224#[test]
225fn success_extension_kind_from_byte() {
226 assert_eq!(
227 ExtensionKind::UnicodeLocale,
228 ExtensionKind::from_byte(b'u').unwrap()
229 );
230 assert_eq!(
231 ExtensionKind::Transformed,
232 ExtensionKind::from_byte(b't').unwrap()
233 );
234 assert_eq!(
235 ExtensionKind::Transformed,
236 ExtensionKind::from_byte(b'T').unwrap()
237 );
238 assert_eq!(ExtensionKind::Pu, ExtensionKind::from_byte(b'x').unwrap());
239 assert_eq!(
240 ExtensionKind::Other('a'),
241 ExtensionKind::from_byte(b'a').unwrap()
242 );
243 assert_eq!(
244 ExtensionKind::Other('1'),
245 ExtensionKind::from_byte(b'1').unwrap()
246 );
247}
248
249#[test]
254fn fail_extension_kind_from_byte() {
255 assert_eq!(
256 ParserError::InvalidExtension,
257 ExtensionKind::from_byte(b'!').unwrap_err()
258 );
259 assert_eq!(
260 ParserError::InvalidExtension,
261 ExtensionKind::from_byte(b' ').unwrap_err()
262 );
263}
264
265#[test]
266fn extention_kind_display() {
267 assert_eq!("u", format!("{}", ExtensionKind::UnicodeLocale));
268 assert_eq!("t", format!("{}", ExtensionKind::Transformed));
269 assert_eq!("x", format!("{}", ExtensionKind::Pu));
270 assert_eq!("a", format!("{}", ExtensionKind::Other('a')));
271}