unic_locale_impl/extensions/
mod.rs

1//! Unicode Extensions provide a mechanism to extend the `LanguageIdentifier` with
2//! additional bits of information.
3//!
4//! There are four types of extensions:
5//!
6//!  * Unicode Extensions - marked as `u`.
7//!  * Transform Extensions - marked as `t`.
8//!  * Private Use Extensions - marked as `x`.
9//!  * Other extensions - marked as any `a-z` except of `u`, `t` and `x`.
10mod private;
11mod transform;
12mod unicode;
13
14pub use private::PrivateExtensionList;
15pub use transform::TransformExtensionList;
16pub use unicode::UnicodeExtensionList;
17
18use std::collections::BTreeMap;
19use std::fmt::Write;
20use std::iter::Peekable;
21use std::str::FromStr;
22
23use tinystr::TinyStr8;
24
25use crate::parser::ParserError;
26
27/// Defines the type of extension.
28#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash, PartialOrd, Ord)]
29pub enum ExtensionType {
30    /// Transform Extension Type marked as `t`.
31    Transform,
32    /// Unicode Extension Type marked as `u`.
33    Unicode,
34    /// Private Extension Type marked as `x`.
35    Private,
36    /// Other Extension Type marked as `a-z` except of `t`, `u` and `x`.
37    Other(char),
38}
39
40impl ExtensionType {
41    pub fn from_byte(key: u8) -> Result<Self, ParserError> {
42        let key = key.to_ascii_lowercase();
43        match key {
44            b'u' => Ok(ExtensionType::Unicode),
45            b't' => Ok(ExtensionType::Transform),
46            b'x' => Ok(ExtensionType::Private),
47            sign if sign.is_ascii_alphanumeric() => Ok(ExtensionType::Other(char::from(sign))),
48            _ => Err(ParserError::InvalidExtension),
49        }
50    }
51}
52
53impl std::fmt::Display for ExtensionType {
54    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
55        let ch = match self {
56            ExtensionType::Unicode => 'u',
57            ExtensionType::Transform => 't',
58            ExtensionType::Other(n) => *n,
59            ExtensionType::Private => 'x',
60        };
61        f.write_char(ch)
62    }
63}
64
65/// A map of extensions associated with a given `Locale.
66#[derive(Debug, Default, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
67pub struct ExtensionsMap {
68    pub unicode: UnicodeExtensionList,
69    pub transform: TransformExtensionList,
70    pub other: BTreeMap<char, Vec<TinyStr8>>,
71    pub private: PrivateExtensionList,
72}
73
74impl ExtensionsMap {
75    pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParserError> {
76        let mut iterator = bytes.split(|c| *c == b'-' || *c == b'_').peekable();
77        Self::try_from_iter(&mut iterator)
78    }
79
80    pub(crate) fn try_from_iter<'a>(
81        iter: &mut Peekable<impl Iterator<Item = &'a [u8]>>,
82    ) -> Result<Self, ParserError> {
83        let mut result = ExtensionsMap::default();
84
85        let mut st = iter.next();
86        while let Some(subtag) = st {
87            match subtag.first().map(|b| ExtensionType::from_byte(*b)) {
88                Some(Ok(ExtensionType::Unicode)) => {
89                    result.unicode = UnicodeExtensionList::try_from_iter(iter)?;
90                }
91                Some(Ok(ExtensionType::Transform)) => {
92                    result.transform = TransformExtensionList::try_from_iter(iter)?;
93                }
94                Some(Ok(ExtensionType::Private)) => {
95                    result.private = PrivateExtensionList::try_from_iter(iter)?;
96                }
97                None => {}
98                _ => unimplemented!(),
99            }
100
101            st = iter.next();
102        }
103
104        Ok(result)
105    }
106
107    pub fn is_empty(&self) -> bool {
108        self.unicode.is_empty() && self.transform.is_empty() && self.private.is_empty()
109    }
110}
111
112impl FromStr for ExtensionsMap {
113    type Err = ParserError;
114
115    fn from_str(source: &str) -> Result<Self, Self::Err> {
116        Self::from_bytes(source.as_bytes())
117    }
118}
119
120impl std::fmt::Display for ExtensionsMap {
121    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
122        // Alphabetic by singleton (t, u, x)
123        write!(f, "{}{}{}", self.transform, self.unicode, self.private)?;
124
125        Ok(())
126    }
127}