1use std::hash::Hash;
2
3use oxilangtag::LanguageTag;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7#[derive(Eq, Debug, Serialize, Deserialize, Clone)]
8pub struct Lang {
9 lang: LanguageTag<String>,
10}
11
12impl Lang {
13 pub fn new(lang: impl Into<String>) -> Result<Lang, LangParseError> {
14 let lang = oxilangtag::LanguageTag::parse_and_normalize(&lang.into())?;
15 Ok(Lang { lang })
16 }
17
18 pub fn new_unchecked(lang: impl Into<String>) -> Lang {
19 let str: String = lang.into();
20 let lang = match oxilangtag::LanguageTag::parse_and_normalize(str.as_str()) {
21 Ok(lang) => lang,
22 Err(e) => panic!("Invalid language tag {str}: {e}"),
23 };
24 Lang { lang }
25 }
26}
27
28impl PartialEq for Lang {
29 fn eq(&self, other: &Self) -> bool {
30 if self.lang.primary_language() == other.lang.primary_language() {
31 let l1 = self.lang.extended_language();
32 let l2 = other.lang.extended_language();
33 match (l1, l2) {
34 (Some(l1), Some(l2)) => l1 == l2,
35 _ => true,
36 }
37 } else {
38 false
39 }
40 }
41}
42
43impl Hash for Lang {
44 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
45 self.lang.hash(state);
46 }
47}
48
49impl std::fmt::Display for Lang {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "{}", self.lang)
52 }
53}
54
55#[derive(Error, Debug)]
56pub enum LangParseError {
57 #[error("Invalid language tag: {0}")]
58 InvalidLangTag(#[from] oxilangtag::LanguageTagParseError),
59}