1#[cfg(feature = "alloc")]
6use alloc::boxed::Box;
7use core::cmp::Ordering;
8use core::fmt;
9use core::ops::Deref;
10
11#[repr(transparent)]
45#[derive(PartialEq, Eq, PartialOrd, Ord)]
46#[allow(clippy::exhaustive_structs)] pub struct PotentialUtf8(pub [u8]);
48
49impl fmt::Debug for PotentialUtf8 {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self.try_as_str() {
53 Ok(s) => fmt::Debug::fmt(s, f),
54 Err(_) => fmt::Debug::fmt(&self.0, f),
55 }
56 }
57}
58
59impl PotentialUtf8 {
60 #[inline]
62 pub const fn from_bytes(other: &[u8]) -> &Self {
63 unsafe { &*(other as *const [u8] as *const Self) }
65 }
66
67 #[inline]
69 pub const fn from_str(s: &str) -> &Self {
70 Self::from_bytes(s.as_bytes())
71 }
72
73 #[inline]
77 #[cfg(feature = "alloc")]
78 pub fn from_boxed_bytes(other: Box<[u8]>) -> Box<Self> {
79 unsafe { Box::from_raw(Box::into_raw(other) as *mut Self) }
91 }
92
93 #[inline]
97 #[cfg(feature = "alloc")]
98 pub fn from_boxed_str(other: Box<str>) -> Box<Self> {
99 Self::from_boxed_bytes(other.into_boxed_bytes())
100 }
101
102 #[inline]
104 pub const fn as_bytes(&self) -> &[u8] {
105 &self.0
106 }
107
108 #[inline]
122 pub fn try_as_str(&self) -> Result<&str, core::str::Utf8Error> {
123 core::str::from_utf8(&self.0)
124 }
125}
126
127impl<'a> From<&'a str> for &'a PotentialUtf8 {
128 #[inline]
129 fn from(other: &'a str) -> Self {
130 PotentialUtf8::from_str(other)
131 }
132}
133
134impl PartialEq<str> for PotentialUtf8 {
135 fn eq(&self, other: &str) -> bool {
136 self.eq(Self::from_str(other))
137 }
138}
139
140impl PartialOrd<str> for PotentialUtf8 {
141 fn partial_cmp(&self, other: &str) -> Option<Ordering> {
142 self.partial_cmp(Self::from_str(other))
143 }
144}
145
146impl PartialEq<PotentialUtf8> for str {
147 fn eq(&self, other: &PotentialUtf8) -> bool {
148 PotentialUtf8::from_str(self).eq(other)
149 }
150}
151
152impl PartialOrd<PotentialUtf8> for str {
153 fn partial_cmp(&self, other: &PotentialUtf8) -> Option<Ordering> {
154 PotentialUtf8::from_str(self).partial_cmp(other)
155 }
156}
157
158#[cfg(feature = "alloc")]
159impl From<Box<str>> for Box<PotentialUtf8> {
160 #[inline]
161 fn from(other: Box<str>) -> Self {
162 PotentialUtf8::from_boxed_str(other)
163 }
164}
165
166impl Deref for PotentialUtf8 {
167 type Target = [u8];
168 fn deref(&self) -> &Self::Target {
169 &self.0
170 }
171}
172
173#[cfg(all(feature = "zerovec", feature = "alloc"))]
175impl<'a> zerovec::maps::ZeroMapKV<'a> for PotentialUtf8 {
176 type Container = zerovec::VarZeroVec<'a, PotentialUtf8>;
177 type Slice = zerovec::VarZeroSlice<PotentialUtf8>;
178 type GetType = PotentialUtf8;
179 type OwnedType = Box<PotentialUtf8>;
180}
181
182#[cfg(feature = "zerovec")]
192unsafe impl zerovec::ule::VarULE for PotentialUtf8 {
193 #[inline]
194 fn validate_bytes(_: &[u8]) -> Result<(), zerovec::ule::UleError> {
195 Ok(())
196 }
197 #[inline]
198 unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
199 PotentialUtf8::from_bytes(bytes)
200 }
201}
202
203#[cfg(feature = "serde")]
205impl serde_core::Serialize for PotentialUtf8 {
206 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
207 where
208 S: serde_core::Serializer,
209 {
210 use serde_core::ser::Error;
211 let s = self
212 .try_as_str()
213 .map_err(|_| S::Error::custom("invalid UTF-8 in PotentialUtf8"))?;
214 if serializer.is_human_readable() {
215 serializer.serialize_str(s)
216 } else {
217 serializer.serialize_bytes(s.as_bytes())
218 }
219 }
220}
221
222#[cfg(all(feature = "serde", feature = "alloc"))]
224impl<'de> serde_core::Deserialize<'de> for Box<PotentialUtf8> {
225 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
226 where
227 D: serde_core::Deserializer<'de>,
228 {
229 if deserializer.is_human_readable() {
230 let boxed_str = Box::<str>::deserialize(deserializer)?;
231 Ok(PotentialUtf8::from_boxed_str(boxed_str))
232 } else {
233 let boxed_bytes = Box::<[u8]>::deserialize(deserializer)?;
234 Ok(PotentialUtf8::from_boxed_bytes(boxed_bytes))
235 }
236 }
237}
238
239#[cfg(feature = "serde")]
241impl<'de, 'a> serde_core::Deserialize<'de> for &'a PotentialUtf8
242where
243 'de: 'a,
244{
245 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
246 where
247 D: serde_core::Deserializer<'de>,
248 {
249 if deserializer.is_human_readable() {
250 let s = <&str>::deserialize(deserializer)?;
251 Ok(PotentialUtf8::from_str(s))
252 } else {
253 let bytes = <&[u8]>::deserialize(deserializer)?;
254 Ok(PotentialUtf8::from_bytes(bytes))
255 }
256 }
257}
258
259#[repr(transparent)]
263#[derive(PartialEq, Eq, PartialOrd, Ord)]
264#[allow(clippy::exhaustive_structs)] pub struct PotentialUtf16(pub [u16]);
266
267impl fmt::Debug for PotentialUtf16 {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 for c in char::decode_utf16(self.0.iter().copied()) {
271 match c {
272 Ok(c) => write!(f, "{c}")?,
273 Err(e) => write!(f, "\\0x{:x}", e.unpaired_surrogate())?,
274 }
275 }
276 Ok(())
277 }
278}
279
280impl PotentialUtf16 {
281 #[inline]
283 pub const fn from_slice(other: &[u16]) -> &Self {
284 unsafe { &*(other as *const [u16] as *const Self) }
286 }
287
288 pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
292 char::decode_utf16(self.0.iter().copied()).map(|c| c.unwrap_or(char::REPLACEMENT_CHARACTER))
293 }
294}