use language_tags::{LanguageTag, ValidationError};
use std::cmp::Ordering;
use std::vec::Vec;
#[allow(clippy::cast_possible_truncation)]
pub fn parse_language_weight<S: AsRef<str>>(weight: S) -> Option<u16> {
let weight = weight.as_ref();
if weight.len() == 2 {
None
} else if let Some(position) = weight.find('.') {
if position != 1 {
None
} else if let Some(exponent) = 5usize.checked_sub(weight.len()) {
if exponent <= 5 {
let result = weight.replace('.', "").parse::<u16>().ok();
if exponent > 0 {
result.map(|number| {
number * 10u16.pow(exponent as u32)
})
} else {
result
}
} else {
None
}
} else {
None
}
} else {
match weight {
"1" => Some(1000),
"0" => Some(0),
_ => None
}
}
}
#[test]
pub fn test_parse_http_header_weight() {
assert_eq!(parse_language_weight("0.5"), Some(500));
assert_eq!(parse_language_weight("1.000"), Some(1000));
assert_eq!(parse_language_weight("0.000"), Some(0));
assert_eq!(parse_language_weight("0.001"), Some(1));
assert_eq!(parse_language_weight("1"), Some(1000));
assert_eq!(parse_language_weight("0"), Some(0));
assert_eq!(parse_language_weight(".0"), None);
assert_eq!(parse_language_weight("0."), None);
assert_eq!(parse_language_weight("1."), None);
assert_eq!(parse_language_weight("1.0"), Some(1000));
assert_eq!(parse_language_weight("0.0"), Some(0));
assert_eq!(parse_language_weight("a"), None);
}
#[derive(Debug, PartialEq, Eq)]
pub struct WeightedLanguage(pub LanguageTag, pub u16);
impl PartialOrd<Self> for WeightedLanguage {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for WeightedLanguage {
fn cmp(&self, other: &Self) -> Ordering {
self.1.cmp(&other.1)
}
}
pub type WeightedLanguages = Vec<WeightedLanguage>;
pub fn parse_accept_language_header<S: AsRef<str>>(tag: S) -> Option<WeightedLanguages> {
tag.as_ref().split(',')
.map(str::trim)
.map(|language| {
language.split(';')
})
.map(|mut language| {
Some((
language.next()?,
if let Some(weight) = language.next() {
Some(weight.strip_prefix("q=")?)
} else {
None
}
))
})
.map(|language| {
if let Some(language) = language {
Some(WeightedLanguage(LanguageTag::parse(language.0).ok()?, if let Some(weight) = language.1 {
parse_language_weight(weight)?
} else {
1000
}))
} else {
None
}
})
.collect()
}
pub type CanonicalizedWeightedLanguages = WeightedLanguages;
pub fn canonicalize_weighted_languages(weighted_languages: WeightedLanguages) -> Result<CanonicalizedWeightedLanguages, ValidationError> {
weighted_languages.into_iter()
.map(|mut language| {
language.0 = language.0.canonicalize()?;
Ok(language)
}).collect::<Result<Vec<_>, _>>()
}
#[test]
pub fn test_parse_accept_language_header() {
assert_eq!(parse_accept_language_header("en-GB;q=1, en-US;q=0.9, zh-CN"), Some(vec![
WeightedLanguage(LanguageTag::parse("en-GB").unwrap(), 1000),
WeightedLanguage(LanguageTag::parse("en-US").unwrap(), 900),
WeightedLanguage(LanguageTag::parse("zh-CN").unwrap(), 1000),
]))
}