1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
use crate::errors::LocaleError; use crate::parser::ParserError; use unic_langid_impl::LanguageIdentifier; use std::collections::BTreeMap; use std::iter::Peekable; use tinystr::{TinyStr4, TinyStr8}; #[derive(Clone, PartialEq, Eq, Debug, Default)] pub struct TransformExtensionList { tlang: Option<LanguageIdentifier>, tfields: BTreeMap<TinyStr4, Vec<TinyStr8>>, } fn parse_tkey(key: &str) -> Result<TinyStr4, ParserError> { if key.len() != 2 || !key.as_bytes()[0].is_ascii_alphabetic() || !key.as_bytes()[1].is_ascii_digit() { return Err(ParserError::InvalidSubtag); } let tkey: TinyStr4 = key.parse().map_err(|_| ParserError::InvalidSubtag)?; Ok(tkey.to_ascii_lowercase()) } fn parse_tvalue(t: &str) -> Result<TinyStr8, ParserError> { let s: TinyStr8 = t.parse().map_err(|_| ParserError::InvalidSubtag)?; if t.len() < 3 || t.len() > 8 || !s.is_ascii_alphanumeric() { return Err(ParserError::InvalidSubtag); } Ok(s.to_ascii_lowercase()) } fn is_language_subtag(t: &str) -> bool { let slen = t.len(); (slen >= 2 && slen <= 8 || slen == 4) && !t.contains(|c: char| !c.is_ascii_alphabetic()) } impl TransformExtensionList { pub fn is_empty(&self) -> bool { self.tlang.is_none() && self.tfields.is_empty() } pub fn set_tlang(&mut self, tlang: LanguageIdentifier) -> Result<(), LocaleError> { self.tlang = Some(tlang); Ok(()) } pub fn set_tfield(&mut self, tkey: &str, tvalue: Vec<&str>) -> Result<(), LocaleError> { let tkey = parse_tkey(tkey)?; let mut t = Vec::with_capacity(tvalue.len()); for val in tvalue { t.push(parse_tvalue(val)?); } self.tfields.insert(tkey, t); Ok(()) } pub fn try_from_iter<'a>( mut iter: &mut Peekable<impl Iterator<Item = &'a str>>, ) -> Result<Self, ParserError> { let mut text = Self::default(); let mut st_peek = iter.peek(); let mut current_tkey = None; let mut current_tvalue = vec![]; while let Some(subtag) = st_peek { let slen = subtag.len(); if slen == 2 && subtag.as_bytes()[0].is_ascii_alphabetic() && subtag.as_bytes()[1].is_ascii_digit() { let tkey: TinyStr4 = subtag.parse().map_err(|_| ParserError::InvalidSubtag)?; current_tkey = Some(tkey); iter.next(); } else if current_tkey.is_some() { current_tvalue.push(parse_tvalue(subtag)?); } else if is_language_subtag(subtag) { text.tlang = Some( LanguageIdentifier::try_from_iter(&mut iter, true) .map_err(|_| ParserError::InvalidLanguage)?, ); } else { break; } st_peek = iter.peek(); } Ok(text) } } impl std::fmt::Display for TransformExtensionList { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { if self.is_empty() { return Ok(()); } f.write_str("-t")?; if let Some(tlang) = &self.tlang { write!(f, "-{}", tlang)?; } for (k, t) in &self.tfields { write!(f, "-{}", k)?; for v in t { write!(f, "-{}", v)?; } } Ok(()) } }