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)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize))]
9#[cfg_attr(feature = "serde", serde(transparent))]
10pub struct LenientLangTag(str);
11
12impl LenientLangTag {
13 pub fn new(s: &str) -> (&Self, Option<InvalidLangTag<&str>>) {
14 let err = LangTag::new(s).err();
15 (unsafe { std::mem::transmute::<&str, &Self>(s) }, err)
16 }
17
18 pub fn as_bytes(&self) -> &[u8] {
19 self.0.as_bytes()
20 }
21
22 pub fn as_str(&self) -> &str {
23 &self.0
24 }
25
26 pub fn is_well_formed(&self) -> bool {
27 LangTag::new(self.as_str()).is_ok()
28 }
29
30 pub fn as_well_formed(&self) -> Option<&LangTag> {
31 LangTag::new(self.as_str()).ok()
32 }
33}
34
35impl PartialEq for LenientLangTag {
36 fn eq(&self, other: &Self) -> bool {
37 case_insensitive_eq(self.as_bytes(), other.as_bytes())
38 }
39}
40
41impl Eq for LenientLangTag {}
42
43impl PartialOrd for LenientLangTag {
44 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
45 Some(self.cmp(other))
46 }
47}
48
49impl Ord for LenientLangTag {
50 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
51 case_insensitive_cmp(self.as_bytes(), other.as_bytes())
52 }
53}
54
55impl Hash for LenientLangTag {
56 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
57 case_insensitive_hash(self.as_bytes(), state)
58 }
59}
60
61impl ToOwned for LenientLangTag {
62 type Owned = LenientLangTagBuf;
63
64 fn to_owned(&self) -> Self::Owned {
65 LenientLangTagBuf(self.0.to_owned())
66 }
67}
68
69impl Borrow<str> for LenientLangTag {
70 fn borrow(&self) -> &str {
71 self.as_str()
72 }
73}
74
75impl AsRef<str> for LenientLangTag {
76 fn as_ref(&self) -> &str {
77 self.as_str()
78 }
79}
80
81impl fmt::Display for LenientLangTag {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 self.0.fmt(f)
84 }
85}
86
87#[derive(Debug, Clone)]
89pub struct LenientLangTagBuf(String);
90
91impl LenientLangTagBuf {
92 pub fn new(s: String) -> (Self, Option<InvalidLangTag<String>>) {
93 let err = LangTag::new(s.as_str())
94 .err()
95 .map(|InvalidLangTag(s)| InvalidLangTag(s.to_owned()));
96 (Self(s), err)
97 }
98
99 pub fn as_lenient_lang_tag_ref(&self) -> &LenientLangTag {
100 unsafe { std::mem::transmute(self.0.as_str()) }
101 }
102
103 pub fn into_string(self) -> String {
104 self.0
105 }
106
107 pub fn into_well_formed(self) -> Result<LangTagBuf, InvalidLangTag<String>> {
108 LangTagBuf::new(self.0)
109 }
110}
111
112impl Deref for LenientLangTagBuf {
113 type Target = LenientLangTag;
114
115 fn deref(&self) -> &Self::Target {
116 self.as_lenient_lang_tag_ref()
117 }
118}
119
120impl PartialEq for LenientLangTagBuf {
121 fn eq(&self, other: &Self) -> bool {
122 self.as_lenient_lang_tag_ref()
123 .eq(other.as_lenient_lang_tag_ref())
124 }
125}
126
127impl Eq for LenientLangTagBuf {}
128
129impl PartialOrd for LenientLangTagBuf {
130 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
131 Some(self.cmp(other))
132 }
133}
134
135impl Ord for LenientLangTagBuf {
136 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
137 self.as_lenient_lang_tag_ref()
138 .cmp(other.as_lenient_lang_tag_ref())
139 }
140}
141
142impl Hash for LenientLangTagBuf {
143 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
144 self.as_lenient_lang_tag_ref().hash(state)
145 }
146}
147
148impl Borrow<LenientLangTag> for LenientLangTagBuf {
149 fn borrow(&self) -> &LenientLangTag {
150 self.as_lenient_lang_tag_ref()
151 }
152}
153
154impl AsRef<LenientLangTag> for LenientLangTagBuf {
155 fn as_ref(&self) -> &LenientLangTag {
156 self.as_lenient_lang_tag_ref()
157 }
158}
159
160impl From<LangTagBuf> for LenientLangTagBuf {
161 fn from(tag: LangTagBuf) -> Self {
162 Self(tag.into_string())
163 }
164}
165
166impl From<String> for LenientLangTagBuf {
167 fn from(tag: String) -> Self {
168 Self(tag)
169 }
170}
171
172impl fmt::Display for LenientLangTagBuf {
173 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174 self.0.fmt(f)
175 }
176}
177
178#[cfg(feature = "serde")]
179impl serde::Serialize for LenientLangTagBuf {
180 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
181 where
182 S: serde::Serializer,
183 {
184 self.as_str().serialize(serializer)
185 }
186}
187
188#[cfg(feature = "serde")]
189impl<'de> serde::Deserialize<'de> for LenientLangTagBuf {
190 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
191 where
192 D: serde::Deserializer<'de>,
193 {
194 Ok(Self(String::deserialize(deserializer)?))
195 }
196}