use crate::{UChar, WideChar};
use core::slice;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::{
borrow::ToOwned,
boxed::Box,
string::{FromUtf16Error, String},
vec::Vec,
};
#[cfg(feature = "std")]
use std::{
borrow::ToOwned,
boxed::Box,
string::{FromUtf16Error, String},
vec::Vec,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MissingNulError<C> {
#[cfg(feature = "alloc")]
pub(crate) inner: Option<Vec<C>>,
#[cfg(not(feature = "alloc"))]
_p: core::marker::PhantomData<C>,
}
impl<C: UChar> MissingNulError<C> {
#[cfg(feature = "alloc")]
fn empty() -> Self {
Self { inner: None }
}
#[cfg(not(feature = "alloc"))]
fn empty() -> Self {
Self {
_p: core::marker::PhantomData,
}
}
#[cfg(feature = "alloc")]
pub fn into_vec(self) -> Option<Vec<C>> {
self.inner
}
}
impl<C: UChar> core::fmt::Display for MissingNulError<C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "missing terminating nul value")
}
}
#[cfg(feature = "std")]
impl<C: UChar> std::error::Error for MissingNulError<C> {
fn description(&self) -> &str {
"missing terminating nul value"
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UCStr<C: UChar> {
inner: [C],
}
impl<C: UChar> UCStr<C> {
pub fn new<S: AsRef<UCStr<C>> + ?Sized>(s: &S) -> &Self {
s.as_ref()
}
pub unsafe fn from_ptr_str<'a>(p: *const C) -> &'a Self {
assert!(!p.is_null());
let mut i: isize = 0;
while *p.offset(i) != UChar::NUL {
i += 1;
}
let slice: *const [C] = slice::from_raw_parts(p, i as usize + 1);
&*(slice as *const UCStr<C>)
}
pub unsafe fn from_ptr_with_nul<'a>(p: *const C, len: usize) -> &'a Self {
assert!(*p.add(len) == UChar::NUL);
let slice: *const [C] = slice::from_raw_parts(p, len + 1);
&*(slice as *const UCStr<C>)
}
pub fn from_slice_with_nul(slice: &[C]) -> Result<&Self, MissingNulError<C>> {
match slice.iter().position(|x| *x == UChar::NUL) {
None => Err(MissingNulError::empty()),
Some(i) => Ok(unsafe { UCStr::from_slice_with_nul_unchecked(&slice[..i + 1]) }),
}
}
pub unsafe fn from_slice_with_nul_unchecked(slice: &[C]) -> &Self {
let slice: *const [C] = slice;
&*(slice as *const UCStr<C>)
}
#[cfg(feature = "alloc")]
pub fn to_ucstring(&self) -> crate::UCString<C> {
unsafe { crate::UCString::from_vec_with_nul_unchecked(self.inner.to_owned()) }
}
#[cfg(feature = "alloc")]
pub fn to_ustring(&self) -> crate::UString<C> {
crate::UString::from_vec(self.as_slice())
}
pub fn as_slice(&self) -> &[C] {
&self.inner[..self.len()]
}
pub fn as_slice_with_nul(&self) -> &[C] {
&self.inner
}
pub fn as_ptr(&self) -> *const C {
self.inner.as_ptr()
}
pub fn len(&self) -> usize {
self.inner.len() - 1
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[cfg(feature = "alloc")]
pub fn into_ucstring(self: Box<Self>) -> crate::UCString<C> {
let raw = Box::into_raw(self) as *mut [C];
crate::UCString {
inner: unsafe { Box::from_raw(raw) },
}
}
#[cfg(feature = "alloc")]
pub(crate) fn from_inner(slice: &[C]) -> &UCStr<C> {
let slice: *const [C] = slice;
unsafe { &*(slice as *const UCStr<C>) }
}
}
impl UCStr<u16> {
#[cfg(feature = "std")]
pub fn to_os_string(&self) -> std::ffi::OsString {
crate::platform::os_from_wide(self.as_slice())
}
#[cfg(feature = "alloc")]
pub fn to_string(&self) -> Result<String, FromUtf16Error> {
String::from_utf16(self.as_slice())
}
#[cfg(feature = "alloc")]
pub fn to_string_lossy(&self) -> String {
String::from_utf16_lossy(self.as_slice())
}
}
impl UCStr<u32> {
pub unsafe fn from_char_ptr_str<'a>(p: *const char) -> &'a Self {
UCStr::from_ptr_str(p as *const u32)
}
pub unsafe fn from_char_ptr_with_nul<'a>(p: *const char, len: usize) -> &'a Self {
UCStr::from_ptr_with_nul(p as *const u32, len)
}
pub fn from_char_slice_with_nul(slice: &[char]) -> Result<&Self, MissingNulError<u32>> {
let slice: *const [char] = slice;
UCStr::from_slice_with_nul(unsafe { &*(slice as *const [u32]) })
}
pub unsafe fn from_char_slice_with_nul_unchecked(slice: &[char]) -> &Self {
let slice: *const [char] = slice;
UCStr::from_slice_with_nul_unchecked(&*(slice as *const [u32]))
}
#[cfg(feature = "std")]
pub fn to_os_string(&self) -> std::ffi::OsString {
self.to_ustring().to_os_string()
}
#[cfg(feature = "alloc")]
pub fn to_string(&self) -> Result<String, crate::FromUtf32Error> {
self.to_ustring().to_string()
}
#[cfg(feature = "alloc")]
pub fn to_string_lossy(&self) -> String {
self.to_ustring().to_string_lossy()
}
}
pub type U16CStr = UCStr<u16>;
pub type U32CStr = UCStr<u32>;
pub type WideCStr = UCStr<WideChar>;