#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use core::cmp::Ordering;
use core::fmt;
use core::ops::Deref;
#[repr(transparent)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] pub struct PotentialUtf8(pub [u8]);
impl fmt::Debug for PotentialUtf8 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_as_str() {
Ok(s) => fmt::Debug::fmt(s, f),
Err(_) => fmt::Debug::fmt(&self.0, f),
}
}
}
impl PotentialUtf8 {
#[inline]
pub const fn from_bytes(other: &[u8]) -> &Self {
unsafe { &*(other as *const [u8] as *const Self) }
}
#[inline]
pub const fn from_str(s: &str) -> &Self {
Self::from_bytes(s.as_bytes())
}
#[inline]
#[cfg(feature = "alloc")]
pub fn from_boxed_bytes(other: Box<[u8]>) -> Box<Self> {
unsafe { core::mem::transmute::<Box<[u8]>, Box<Self>>(other) }
}
#[inline]
#[cfg(feature = "alloc")]
pub fn from_boxed_str(other: Box<str>) -> Box<Self> {
Self::from_boxed_bytes(other.into_boxed_bytes())
}
#[inline]
pub const fn as_bytes(&self) -> &[u8] {
&self.0
}
#[inline]
pub fn try_as_str(&self) -> Result<&str, core::str::Utf8Error> {
core::str::from_utf8(&self.0)
}
}
impl<'a> From<&'a str> for &'a PotentialUtf8 {
#[inline]
fn from(other: &'a str) -> Self {
PotentialUtf8::from_str(other)
}
}
impl PartialEq<str> for PotentialUtf8 {
fn eq(&self, other: &str) -> bool {
self.eq(Self::from_str(other))
}
}
impl PartialOrd<str> for PotentialUtf8 {
fn partial_cmp(&self, other: &str) -> Option<Ordering> {
self.partial_cmp(Self::from_str(other))
}
}
impl PartialEq<PotentialUtf8> for str {
fn eq(&self, other: &PotentialUtf8) -> bool {
PotentialUtf8::from_str(self).eq(other)
}
}
impl PartialOrd<PotentialUtf8> for str {
fn partial_cmp(&self, other: &PotentialUtf8) -> Option<Ordering> {
PotentialUtf8::from_str(self).partial_cmp(other)
}
}
#[cfg(feature = "alloc")]
impl From<Box<str>> for Box<PotentialUtf8> {
#[inline]
fn from(other: Box<str>) -> Self {
PotentialUtf8::from_boxed_str(other)
}
}
impl Deref for PotentialUtf8 {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(all(feature = "zerovec", feature = "alloc"))]
impl<'a> zerovec::maps::ZeroMapKV<'a> for PotentialUtf8 {
type Container = zerovec::VarZeroVec<'a, PotentialUtf8>;
type Slice = zerovec::VarZeroSlice<PotentialUtf8>;
type GetType = PotentialUtf8;
type OwnedType = Box<PotentialUtf8>;
}
#[cfg(feature = "zerovec")]
unsafe impl zerovec::ule::VarULE for PotentialUtf8 {
#[inline]
fn validate_bytes(_: &[u8]) -> Result<(), zerovec::ule::UleError> {
Ok(())
}
#[inline]
unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
PotentialUtf8::from_bytes(bytes)
}
}
#[cfg(feature = "serde")]
impl serde_core::Serialize for PotentialUtf8 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde_core::Serializer,
{
use serde_core::ser::Error;
let s = self
.try_as_str()
.map_err(|_| S::Error::custom("invalid UTF-8 in PotentialUtf8"))?;
if serializer.is_human_readable() {
serializer.serialize_str(s)
} else {
serializer.serialize_bytes(s.as_bytes())
}
}
}
#[cfg(all(feature = "serde", feature = "alloc"))]
impl<'de> serde_core::Deserialize<'de> for Box<PotentialUtf8> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde_core::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let boxed_str = Box::<str>::deserialize(deserializer)?;
Ok(PotentialUtf8::from_boxed_str(boxed_str))
} else {
let boxed_bytes = Box::<[u8]>::deserialize(deserializer)?;
Ok(PotentialUtf8::from_boxed_bytes(boxed_bytes))
}
}
}
#[cfg(feature = "serde")]
impl<'de, 'a> serde_core::Deserialize<'de> for &'a PotentialUtf8
where
'de: 'a,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde_core::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = <&str>::deserialize(deserializer)?;
Ok(PotentialUtf8::from_str(s))
} else {
let bytes = <&[u8]>::deserialize(deserializer)?;
Ok(PotentialUtf8::from_bytes(bytes))
}
}
}
#[repr(transparent)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
#[allow(clippy::exhaustive_structs)] pub struct PotentialUtf16(pub [u16]);
impl fmt::Debug for PotentialUtf16 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for c in char::decode_utf16(self.0.iter().copied()) {
match c {
Ok(c) => write!(f, "{c}")?,
Err(e) => write!(f, "\\0x{:x}", e.unpaired_surrogate())?,
}
}
Ok(())
}
}
impl PotentialUtf16 {
#[inline]
pub const fn from_slice(other: &[u16]) -> &Self {
unsafe { &*(other as *const [u16] as *const Self) }
}
pub fn chars(&self) -> impl Iterator<Item = char> + '_ {
char::decode_utf16(self.0.iter().copied()).map(|c| c.unwrap_or(char::REPLACEMENT_CHARACTER))
}
}