potential_utf/
ustr.rs
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 { core::mem::transmute(other) }
65 }
66
67 #[inline]
69 pub const fn from_str(s: &str) -> &Self {
70 Self::from_bytes(s.as_bytes())
71 }
72
73 #[inline]
75 #[cfg(feature = "alloc")]
76 pub fn from_boxed_bytes(other: Box<[u8]>) -> Box<Self> {
77 unsafe { core::mem::transmute(other) }
79 }
80
81 #[inline]
83 #[cfg(feature = "alloc")]
84 pub fn from_boxed_str(other: Box<str>) -> Box<Self> {
85 Self::from_boxed_bytes(other.into_boxed_bytes())
86 }
87
88 #[inline]
90 pub const fn as_bytes(&self) -> &[u8] {
91 &self.0
92 }
93
94 #[inline]
108 pub fn try_as_str(&self) -> Result<&str, core::str::Utf8Error> {
109 core::str::from_utf8(&self.0)
110 }
111}
112
113impl<'a> From<&'a str> for &'a PotentialUtf8 {
114 #[inline]
115 fn from(other: &'a str) -> Self {
116 PotentialUtf8::from_str(other)
117 }
118}
119
120impl PartialEq<str> for PotentialUtf8 {
121 fn eq(&self, other: &str) -> bool {
122 self.eq(Self::from_str(other))
123 }
124}
125
126impl PartialOrd<str> for PotentialUtf8 {
127 fn partial_cmp(&self, other: &str) -> Option<Ordering> {
128 self.partial_cmp(Self::from_str(other))
129 }
130}
131
132impl PartialEq<PotentialUtf8> for str {
133 fn eq(&self, other: &PotentialUtf8) -> bool {
134 PotentialUtf8::from_str(self).eq(other)
135 }
136}
137
138impl PartialOrd<PotentialUtf8> for str {
139 fn partial_cmp(&self, other: &PotentialUtf8) -> Option<Ordering> {
140 PotentialUtf8::from_str(self).partial_cmp(other)
141 }
142}
143
144#[cfg(feature = "alloc")]
145impl From<Box<str>> for Box<PotentialUtf8> {
146 #[inline]
147 fn from(other: Box<str>) -> Self {
148 PotentialUtf8::from_boxed_str(other)
149 }
150}
151
152impl Deref for PotentialUtf8 {
153 type Target = [u8];
154 fn deref(&self) -> &Self::Target {
155 &self.0
156 }
157}
158
159#[cfg(all(feature = "zerovec", feature = "alloc"))]
161impl<'a> zerovec::maps::ZeroMapKV<'a> for PotentialUtf8 {
162 type Container = zerovec::VarZeroVec<'a, PotentialUtf8>;
163 type Slice = zerovec::VarZeroSlice<PotentialUtf8>;
164 type GetType = PotentialUtf8;
165 type OwnedType = Box<PotentialUtf8>;
166}
167
168#[cfg(feature = "zerovec")]
178unsafe impl zerovec::ule::VarULE for PotentialUtf8 {
179 #[inline]
180 fn validate_bytes(_: &[u8]) -> Result<(), zerovec::ule::UleError> {
181 Ok(())
182 }
183 #[inline]
184 unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
185 PotentialUtf8::from_bytes(bytes)
186 }
187}
188
189#[cfg(feature = "serde")]
191impl serde::Serialize for PotentialUtf8 {
192 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
193 where
194 S: serde::Serializer,
195 {
196 use serde::ser::Error;
197 let s = self
198 .try_as_str()
199 .map_err(|_| S::Error::custom("invalid UTF-8 in PotentialUtf8"))?;
200 if serializer.is_human_readable() {
201 serializer.serialize_str(s)
202 } else {
203 serializer.serialize_bytes(s.as_bytes())
204 }
205 }
206}
207
208#[cfg(all(feature = "serde", feature = "alloc"))]
210impl<'de> serde::Deserialize<'de> for Box<PotentialUtf8> {
211 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
212 where
213 D: serde::Deserializer<'de>,
214 {
215 if deserializer.is_human_readable() {
216 let boxed_str = Box::<str>::deserialize(deserializer)?;
217 Ok(PotentialUtf8::from_boxed_str(boxed_str))
218 } else {
219 let boxed_bytes = Box::<[u8]>::deserialize(deserializer)?;
220 Ok(PotentialUtf8::from_boxed_bytes(boxed_bytes))
221 }
222 }
223}
224
225#[cfg(feature = "serde")]
227impl<'de, 'a> serde::Deserialize<'de> for &'a PotentialUtf8
228where
229 'de: 'a,
230{
231 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
232 where
233 D: serde::Deserializer<'de>,
234 {
235 if deserializer.is_human_readable() {
236 let s = <&str>::deserialize(deserializer)?;
237 Ok(PotentialUtf8::from_str(s))
238 } else {
239 let bytes = <&[u8]>::deserialize(deserializer)?;
240 Ok(PotentialUtf8::from_bytes(bytes))
241 }
242 }
243}
244
245#[repr(transparent)]
246#[derive(PartialEq, Eq, PartialOrd, Ord)]
247#[allow(clippy::exhaustive_structs)] pub struct PotentialUtf16(pub [u16]);
249
250impl fmt::Debug for PotentialUtf16 {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 for c in char::decode_utf16(self.0.iter().copied()) {
254 match c {
255 Ok(c) => write!(f, "{c}")?,
256 Err(e) => write!(f, "\\0x{:x}", e.unpaired_surrogate())?,
257 }
258 }
259 Ok(())
260 }
261}
262
263impl PotentialUtf16 {
264 #[inline]
266 pub const fn from_slice(other: &[u16]) -> &Self {
267 unsafe { core::mem::transmute(other) }
269 }
270}