Skip to main content

lib_humus/language/
unicode_language_identifier.rs

1// SPDX-FileCopyrightText: 2026 Slatian <baschdel@disroot.org>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5use std::{
6	fmt::Display,
7	ops::{Deref, DerefMut},
8	str::FromStr,
9};
10
11use serde::Deserialize;
12use unic_langid::{LanguageIdentifier, LanguageIdentifierError};
13
14/// A wrapper for [[unic_langid::LanguageIdentifier]] that als implements serde traits.
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct UnicodeLanguageIdentifier(LanguageIdentifier);
17
18impl From<LanguageIdentifier> for UnicodeLanguageIdentifier {
19	fn from(value: LanguageIdentifier) -> Self {
20		Self(value)
21	}
22}
23
24impl From<UnicodeLanguageIdentifier> for LanguageIdentifier {
25	fn from(value: UnicodeLanguageIdentifier) -> Self {
26		value.0
27	}
28}
29
30impl Deref for UnicodeLanguageIdentifier {
31	type Target = LanguageIdentifier;
32
33	fn deref(&self) -> &Self::Target {
34		&self.0
35	}
36}
37
38impl DerefMut for UnicodeLanguageIdentifier {
39	fn deref_mut(&mut self) -> &mut Self::Target {
40		&mut self.0
41	}
42}
43
44impl Display for UnicodeLanguageIdentifier {
45	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46		self.0.fmt(f)
47	}
48}
49
50impl FromStr for UnicodeLanguageIdentifier {
51	type Err = LanguageIdentifierError;
52
53	fn from_str(s: &str) -> Result<Self, Self::Err> {
54		Ok(Self(s.parse()?))
55	}
56}
57
58impl serde::Serialize for UnicodeLanguageIdentifier {
59	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60	where
61		S: serde::Serializer,
62	{
63		serializer.serialize_str(self.0.to_string().as_str())
64	}
65}
66
67impl<'de> Deserialize<'de> for UnicodeLanguageIdentifier {
68	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
69	where
70		D: serde::Deserializer<'de>,
71	{
72		deserializer.deserialize_str(UnicodeLanguageIdentifierVisitor)
73	}
74}
75
76struct UnicodeLanguageIdentifierVisitor;
77
78impl<'de> serde::de::Visitor<'de> for UnicodeLanguageIdentifierVisitor {
79	type Value = UnicodeLanguageIdentifier;
80
81	fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
82		formatter.write_str(
83			"A language Tag as defined in Unicode TR35 (similar to BCP 47 i.e. en-US, zh-TW, de-Latn or fr)",
84		)
85	}
86
87	fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
88	where
89		E: serde::de::Error,
90	{
91		let identifier: LanguageIdentifier = v.parse().map_err(|e| {
92			E::custom(format!(
93				"Unable to parse unicode TR35 language identifier from {v:?}: {e}"
94			))
95		})?;
96		Ok(UnicodeLanguageIdentifier(identifier))
97	}
98}