xapi_rs/data/
language_tag.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use crate::DataError;
4use core::fmt;
5use language_tags::LanguageTag;
6use serde::{Deserialize, Serialize};
7use serde_with::{serde_as, DisplayFromStr};
8use std::{
9    cmp::Ordering,
10    ops::{Deref, DerefMut},
11    str::FromStr,
12};
13use tracing::error;
14
15/// A wrapper around [LanguageTag] to use it when `Option<T>`, serialization
16/// and deserialization are needed.
17///
18/// Language tags are used to help identify languages, whether spoken, written,
19/// signed, or otherwise signaled, for the purpose of communication. This
20/// includes constructed and artificial languages but excludes languages not
21/// intended primarily for human communication, such as programming languages.
22///
23#[serde_as]
24#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
25pub struct MyLanguageTag(#[serde_as(as = "DisplayFromStr")] LanguageTag);
26
27impl MyLanguageTag {
28    /// Return the language tag as a string reference.
29    pub fn as_str(&self) -> &str {
30        self.0.as_str()
31    }
32}
33
34impl PartialEq<MyLanguageTag> for &MyLanguageTag {
35    fn eq(&self, other: &MyLanguageTag) -> bool {
36        self.0.as_str() == other.0.as_str()
37    }
38}
39
40impl PartialEq<String> for MyLanguageTag {
41    fn eq(&self, other: &String) -> bool {
42        self.0.as_str() == other
43    }
44}
45
46impl PartialEq<str> for MyLanguageTag {
47    fn eq(&self, other: &str) -> bool {
48        self.0.as_str() == other
49    }
50}
51
52impl FromStr for MyLanguageTag {
53    type Err = DataError;
54
55    fn from_str(s: &str) -> Result<Self, Self::Err> {
56        let lt = LanguageTag::parse(s)?.canonicalize()?;
57        // FIXME (rsn) 20240929 - not sure we need this after canonicalize call...
58        lt.validate().map_err(|x| {
59            error!("{}", x);
60            DataError::LTValidationError(x)
61        })?;
62        Ok(MyLanguageTag(lt))
63    }
64}
65
66impl From<LanguageTag> for MyLanguageTag {
67    fn from(value: LanguageTag) -> Self {
68        Self(value)
69    }
70}
71
72impl Ord for MyLanguageTag {
73    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
74        self.0.as_str().cmp(other.0.as_str())
75    }
76}
77
78impl PartialOrd for MyLanguageTag {
79    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
80        Some(self.cmp(other))
81    }
82}
83
84impl fmt::Display for MyLanguageTag {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        write!(f, "{}", self.0)
87    }
88}
89
90impl Deref for MyLanguageTag {
91    type Target = LanguageTag;
92
93    fn deref(&self) -> &Self::Target {
94        &self.0
95    }
96}
97
98impl DerefMut for MyLanguageTag {
99    fn deref_mut(&mut self) -> &mut Self::Target {
100        &mut self.0
101    }
102}