|  | 
|  | 1 | +use crate::content::Language; | 
|  | 2 | +use crate::ensure; | 
|  | 3 | +use crate::headers::HeaderValue; | 
|  | 4 | +use crate::utils::parse_weight; | 
|  | 5 | + | 
|  | 6 | +use std::cmp::{Ordering, PartialEq}; | 
|  | 7 | +use std::ops::{Deref, DerefMut}; | 
|  | 8 | + | 
|  | 9 | +/// A proposed `Language` in `AcceptLanguage`. | 
|  | 10 | +#[derive(Debug, Clone, Copy, PartialEq)] | 
|  | 11 | +pub struct LanguageProposal { | 
|  | 12 | +    /// The proposed language. | 
|  | 13 | +    pub(crate) language: Language, | 
|  | 14 | + | 
|  | 15 | +    /// The weight of the proposal. | 
|  | 16 | +    /// | 
|  | 17 | +    /// This is a number between 0.0 and 1.0, and is max 3 decimal points. | 
|  | 18 | +    weight: Option<f32>, | 
|  | 19 | +} | 
|  | 20 | + | 
|  | 21 | +impl LanguageProposal { | 
|  | 22 | +    /// Create a new instance of `LanguageProposal`. | 
|  | 23 | +    pub fn new(language: impl Into<Language>, weight: Option<f32>) -> crate::Result<Self> { | 
|  | 24 | +        if let Some(weight) = weight { | 
|  | 25 | +            ensure!( | 
|  | 26 | +                weight.is_sign_positive() && weight <= 1.0, | 
|  | 27 | +                "LanguageProposal should have a weight between 0.0 and 1.0" | 
|  | 28 | +            ) | 
|  | 29 | +        } | 
|  | 30 | + | 
|  | 31 | +        Ok(Self { | 
|  | 32 | +            language: language.into(), | 
|  | 33 | +            weight, | 
|  | 34 | +        }) | 
|  | 35 | +    } | 
|  | 36 | + | 
|  | 37 | +    /// Get the proposed language. | 
|  | 38 | +    pub fn language(&self) -> &Language { | 
|  | 39 | +        &self.language | 
|  | 40 | +    } | 
|  | 41 | + | 
|  | 42 | +    /// Get the weight of the proposal. | 
|  | 43 | +    pub fn weight(&self) -> Option<f32> { | 
|  | 44 | +        self.weight | 
|  | 45 | +    } | 
|  | 46 | + | 
|  | 47 | +    pub(crate) fn from_str(s: &str) -> crate::Result<Option<Self>> { | 
|  | 48 | +        let mut parts = s.split(';'); | 
|  | 49 | +        let language = match Language::from_str(parts.next().unwrap()) { | 
|  | 50 | +            Some(language) => language, | 
|  | 51 | +            None => return Ok(None), | 
|  | 52 | +        }; | 
|  | 53 | +        let weight = parts.next().map(parse_weight).transpose()?; | 
|  | 54 | + | 
|  | 55 | +        Ok(Some(Self::new(language, weight)?)) | 
|  | 56 | +    } | 
|  | 57 | +} | 
|  | 58 | + | 
|  | 59 | +impl From<Language> for LanguageProposal { | 
|  | 60 | +    fn from(language: Language) -> Self { | 
|  | 61 | +        Self { | 
|  | 62 | +            language, | 
|  | 63 | +            weight: None, | 
|  | 64 | +        } | 
|  | 65 | +    } | 
|  | 66 | +} | 
|  | 67 | + | 
|  | 68 | +impl PartialEq<Language> for LanguageProposal { | 
|  | 69 | +    fn eq(&self, other: &Language) -> bool { | 
|  | 70 | +        self.language == *other | 
|  | 71 | +    } | 
|  | 72 | +} | 
|  | 73 | + | 
|  | 74 | +impl PartialEq<Language> for &LanguageProposal { | 
|  | 75 | +    fn eq(&self, other: &Language) -> bool { | 
|  | 76 | +        self.language == *other | 
|  | 77 | +    } | 
|  | 78 | +} | 
|  | 79 | + | 
|  | 80 | +impl Deref for LanguageProposal { | 
|  | 81 | +    type Target = Language; | 
|  | 82 | +    fn deref(&self) -> &Self::Target { | 
|  | 83 | +        &self.language | 
|  | 84 | +    } | 
|  | 85 | +} | 
|  | 86 | + | 
|  | 87 | +impl DerefMut for LanguageProposal { | 
|  | 88 | +    fn deref_mut(&mut self) -> &mut Self::Target { | 
|  | 89 | +        &mut self.language | 
|  | 90 | +    } | 
|  | 91 | +} | 
|  | 92 | + | 
|  | 93 | +impl PartialOrd for LanguageProposal { | 
|  | 94 | +    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | 
|  | 95 | +        match (self.weight, other.weight) { | 
|  | 96 | +            (Some(left), Some(right)) => left.partial_cmp(&right), | 
|  | 97 | +            (Some(_), None) => Some(Ordering::Greater), | 
|  | 98 | +            (None, Some(_)) => Some(Ordering::Less), | 
|  | 99 | +            (None, None) => None, | 
|  | 100 | +        } | 
|  | 101 | +    } | 
|  | 102 | +} | 
|  | 103 | + | 
|  | 104 | +impl From<LanguageProposal> for HeaderValue { | 
|  | 105 | +    fn from(entry: LanguageProposal) -> HeaderValue { | 
|  | 106 | +        let s = match entry.weight { | 
|  | 107 | +            Some(weight) => format!("{};q={:.3}", entry.language, weight), | 
|  | 108 | +            None => entry.language.to_string(), | 
|  | 109 | +        }; | 
|  | 110 | +        unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) } | 
|  | 111 | +    } | 
|  | 112 | +} | 
0 commit comments