use core::str::FromStr;
use smallvec::SmallVec;
type SmallVecLanguage = SmallVec<[u8; 8]>;
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Language(SmallVecLanguage);
impl Language {
#[inline]
pub fn new(bytes: impl AsRef<[u8]>) -> Option<Self> {
let bytes = bytes.as_ref();
(!bytes.is_empty()).then(|| Language::from_bytes(bytes))
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
#[inline]
pub fn as_str(&self) -> &str {
core::str::from_utf8(&self.0).unwrap_or_default()
}
fn from_bytes(bytes: &[u8]) -> Self {
if bytes.is_empty() {
Language(SmallVec::new())
} else {
let mut bytes = SmallVecLanguage::from_slice(bytes);
for b in &mut bytes.iter_mut() {
if b.is_ascii_uppercase() {
*b = b.to_ascii_lowercase();
} else if *b == b'_' {
*b = b'-';
}
}
Language(bytes)
}
}
}
impl FromStr for Language {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if !s.is_empty() {
Ok(Language::from_bytes(s.as_bytes()))
} else {
Err("invalid language")
}
}
}
#[cfg(test)]
mod tests_language {
use super::*;
use alloc::string::String;
use alloc::vec::Vec;
#[test]
fn new_empty() {
assert_eq!(Language::new(""), None);
}
#[test]
fn new_basic() {
let lang = Language::new("en").unwrap();
assert_eq!(lang.as_str(), "en");
assert_eq!(lang.as_bytes(), b"en");
}
#[test]
fn new_lowercases() {
let lang = Language::new("EN-US").unwrap();
assert_eq!(lang.as_str(), "en-us");
assert_eq!(lang.as_bytes(), b"en-us");
}
#[test]
fn new_replaces_underscore() {
let lang = Language::new("en_US").unwrap();
assert_eq!(lang.as_str(), "en-us");
assert_eq!(lang.as_bytes(), b"en-us");
}
#[test]
fn new_accepts_str() {
let lang = Language::new("zh-Hant").unwrap();
assert_eq!(lang.as_str(), "zh-hant");
}
#[test]
fn new_accepts_byte_slice() {
let lang = Language::new(b"zh-Hant" as &[u8]).unwrap();
assert_eq!(lang.as_str(), "zh-hant");
}
#[test]
fn new_accepts_byte_array() {
let lang = Language::new(*b"zh-Hant").unwrap();
assert_eq!(lang.as_str(), "zh-hant");
}
#[test]
fn new_accepts_string() {
let lang = Language::new(String::from("zh-Hant")).unwrap();
assert_eq!(lang.as_str(), "zh-hant");
}
#[test]
fn new_accepts_vec() {
let lang = Language::new(Vec::from(b"zh-Hant" as &[u8])).unwrap();
assert_eq!(lang.as_str(), "zh-hant");
}
#[test]
fn new_matches_from_str() {
assert_eq!(
Language::new("en-US"),
Some(Language::from_str("en-US").unwrap())
);
assert_eq!(
Language::new("zh_Hant_TW"),
Some(Language::from_str("zh_Hant_TW").unwrap())
);
}
#[test]
fn new_empty_matches_from_str() {
assert_eq!(Language::new(""), None);
assert!(Language::from_str("").is_err());
}
}