use crate::{UChar, WideChar};
use core::{char, slice};
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::{
boxed::Box,
string::{FromUtf16Error, String},
vec::Vec,
};
#[cfg(feature = "std")]
use std::{
boxed::Box,
string::{FromUtf16Error, String},
vec::Vec,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FromUtf32Error();
impl core::fmt::Display for FromUtf32Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "error converting from UTF-32 to UTF-8")
}
}
#[cfg(feature = "std")]
impl std::error::Error for FromUtf32Error {
fn description(&self) -> &str {
"error converting from UTF-32 to UTF-8"
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct UStr<C: UChar> {
pub(crate) inner: [C],
}
impl<C: UChar> UStr<C> {
pub fn new<S: AsRef<Self> + ?Sized>(s: &S) -> &Self {
s.as_ref()
}
pub unsafe fn from_ptr<'a>(p: *const C, len: usize) -> &'a Self {
assert!(!p.is_null());
let slice: *const [C] = slice::from_raw_parts(p, len);
&*(slice as *const UStr<C>)
}
pub fn from_slice(slice: &[C]) -> &Self {
let v: *const [C] = slice;
unsafe { &*(v as *const UStr<C>) }
}
#[cfg(feature = "alloc")]
pub fn to_ustring(&self) -> crate::UString<C> {
crate::UString::from_vec(&self.inner)
}
pub fn as_slice(&self) -> &[C] {
&self.inner
}
pub fn as_ptr(&self) -> *const C {
self.inner.as_ptr()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
#[cfg(feature = "alloc")]
pub fn into_ustring(self: Box<Self>) -> crate::UString<C> {
let boxed = unsafe { Box::from_raw(Box::into_raw(self) as *mut [C]) };
crate::UString {
inner: boxed.into_vec(),
}
}
}
impl UStr<u16> {
#[cfg(feature = "std")]
pub fn to_os_string(&self) -> std::ffi::OsString {
crate::platform::os_from_wide(&self.inner)
}
#[cfg(feature = "alloc")]
pub fn to_string(&self) -> Result<String, FromUtf16Error> {
String::from_utf16(&self.inner)
}
#[cfg(feature = "alloc")]
pub fn to_string_lossy(&self) -> String {
String::from_utf16_lossy(&self.inner)
}
}
impl UStr<u32> {
pub unsafe fn from_char_ptr<'a>(p: *const char, len: usize) -> &'a Self {
UStr::from_ptr(p as *const u32, len)
}
pub fn from_char_slice(slice: &[char]) -> &Self {
let slice: *const [char] = slice;
unsafe { &*(slice as *const UStr<u32>) }
}
#[cfg(feature = "std")]
pub fn to_os_string(&self) -> std::ffi::OsString {
self.to_string_lossy().into()
}
#[cfg(feature = "alloc")]
pub fn to_string(&self) -> Result<String, FromUtf32Error> {
let chars: Vec<Option<char>> = self.inner.iter().map(|c| char::from_u32(*c)).collect();
if chars.iter().any(|c| c.is_none()) {
return Err(FromUtf32Error());
}
let size = chars.iter().filter_map(|o| o.map(|c| c.len_utf8())).sum();
let mut vec = Vec::with_capacity(size);
unsafe { vec.set_len(size) };
let mut i = 0;
for c in chars.iter().filter_map(|&o| o) {
c.encode_utf8(&mut vec[i..]);
i += c.len_utf8();
}
Ok(unsafe { String::from_utf8_unchecked(vec) })
}
#[cfg(feature = "alloc")]
pub fn to_string_lossy(&self) -> String {
let chars: Vec<char> = self
.inner
.iter()
.map(|&c| char::from_u32(c).unwrap_or(char::REPLACEMENT_CHARACTER))
.collect();
let size = chars.iter().map(|c| c.len_utf8()).sum();
let mut vec = Vec::with_capacity(size);
unsafe { vec.set_len(size) };
let mut i = 0;
for c in chars {
c.encode_utf8(&mut vec[i..]);
i += c.len_utf8();
}
unsafe { String::from_utf8_unchecked(vec) }
}
}
pub type U16Str = UStr<u16>;
pub type U32Str = UStr<u32>;
pub type WideStr = UStr<WideChar>;