1pub use langtag::{InvalidLangTag, LangTag, LangTagBuf};
2use std::{borrow::Borrow, fmt, hash::Hash, ops::Deref};
3
4use crate::utils::{case_insensitive_cmp, case_insensitive_eq, case_insensitive_hash};
5
6#[derive(Debug)]
14#[repr(transparent)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize))]
16#[cfg_attr(feature = "serde", serde(transparent))]
17pub struct LenientLangTag(str);
18
19impl LenientLangTag {
20 #[must_use]
23 pub fn new(s: &str) -> (&Self, Option<InvalidLangTag<&str>>) {
24 let err = LangTag::new(s).err();
25 (unsafe { &*(std::ptr::from_ref::<str>(s) as *const Self) }, err)
28 }
29
30 #[must_use]
32 pub fn as_bytes(&self) -> &[u8] {
33 self.0.as_bytes()
34 }
35
36 #[must_use]
38 pub fn as_str(&self) -> &str {
39 &self.0
40 }
41
42 #[must_use]
44 pub fn is_well_formed(&self) -> bool {
45 LangTag::new(self.as_str()).is_ok()
46 }
47
48 #[must_use]
51 pub fn as_well_formed(&self) -> Option<&LangTag> {
52 LangTag::new(self.as_str()).ok()
53 }
54}
55
56impl PartialEq for LenientLangTag {
57 fn eq(&self, other: &Self) -> bool {
58 case_insensitive_eq(self.as_bytes(), other.as_bytes())
59 }
60}
61
62impl Eq for LenientLangTag {}
63
64impl PartialOrd for LenientLangTag {
65 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
66 Some(self.cmp(other))
67 }
68}
69
70impl Ord for LenientLangTag {
71 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
72 case_insensitive_cmp(self.as_bytes(), other.as_bytes())
73 }
74}
75
76impl Hash for LenientLangTag {
77 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
78 case_insensitive_hash(self.as_bytes(), state);
79 }
80}
81
82impl ToOwned for LenientLangTag {
83 type Owned = LenientLangTagBuf;
84
85 fn to_owned(&self) -> Self::Owned {
86 LenientLangTagBuf(self.0.to_owned())
87 }
88}
89
90impl AsRef<str> for LenientLangTag {
94 fn as_ref(&self) -> &str {
95 self.as_str()
96 }
97}
98
99impl fmt::Display for LenientLangTag {
100 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
101 self.0.fmt(f)
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct LenientLangTagBuf(String);
108
109impl LenientLangTagBuf {
110 #[must_use]
113 pub fn new(s: String) -> (Self, Option<InvalidLangTag<String>>) {
114 let err = LangTag::new(s.as_str()).err().map(|InvalidLangTag(s)| InvalidLangTag(s.to_owned()));
115 (Self(s), err)
116 }
117
118 #[must_use]
120 pub fn as_lenient_lang_tag_ref(&self) -> &LenientLangTag {
121 unsafe { &*(std::ptr::from_ref::<str>(self.0.as_str()) as *const LenientLangTag) }
123 }
124
125 #[must_use]
127 pub fn into_string(self) -> String {
128 self.0
129 }
130
131 pub fn into_well_formed(self) -> Result<LangTagBuf, InvalidLangTag<String>> {
138 LangTagBuf::new(self.0)
139 }
140}
141
142impl Deref for LenientLangTagBuf {
143 type Target = LenientLangTag;
144
145 fn deref(&self) -> &Self::Target {
146 self.as_lenient_lang_tag_ref()
147 }
148}
149
150impl PartialEq for LenientLangTagBuf {
151 fn eq(&self, other: &Self) -> bool {
152 self.as_lenient_lang_tag_ref().eq(other.as_lenient_lang_tag_ref())
153 }
154}
155
156impl Eq for LenientLangTagBuf {}
157
158impl PartialOrd for LenientLangTagBuf {
159 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
160 Some(self.cmp(other))
161 }
162}
163
164impl Ord for LenientLangTagBuf {
165 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
166 self.as_lenient_lang_tag_ref().cmp(other.as_lenient_lang_tag_ref())
167 }
168}
169
170impl Hash for LenientLangTagBuf {
171 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
172 self.as_lenient_lang_tag_ref().hash(state);
173 }
174}
175
176impl Borrow<LenientLangTag> for LenientLangTagBuf {
177 fn borrow(&self) -> &LenientLangTag {
178 self.as_lenient_lang_tag_ref()
179 }
180}
181
182impl AsRef<LenientLangTag> for LenientLangTagBuf {
183 fn as_ref(&self) -> &LenientLangTag {
184 self.as_lenient_lang_tag_ref()
185 }
186}
187
188impl From<LangTagBuf> for LenientLangTagBuf {
189 fn from(tag: LangTagBuf) -> Self {
190 Self(tag.into_string())
191 }
192}
193
194impl From<String> for LenientLangTagBuf {
195 fn from(tag: String) -> Self {
196 Self(tag)
197 }
198}
199
200impl fmt::Display for LenientLangTagBuf {
201 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202 self.0.fmt(f)
203 }
204}
205
206#[cfg(feature = "serde")]
207impl serde::Serialize for LenientLangTagBuf {
208 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
209 where
210 S: serde::Serializer,
211 {
212 self.as_str().serialize(serializer)
213 }
214}
215
216#[cfg(feature = "serde")]
217impl<'de> serde::Deserialize<'de> for LenientLangTagBuf {
218 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
219 where
220 D: serde::Deserializer<'de>,
221 {
222 Ok(Self(String::deserialize(deserializer)?))
223 }
224}