use crate::{MissingNulError, UCStr, UChar, UStr, UString, WideChar};
use core::borrow::Borrow;
use core::ops::{Deref, Index, RangeFull};
use core::{mem, ptr, slice};
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::{
borrow::{Cow, ToOwned},
boxed::Box,
vec::Vec,
};
#[cfg(feature = "std")]
use std::{
borrow::{Cow, ToOwned},
boxed::Box,
convert::TryFrom,
str::FromStr,
vec::Vec,
};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UCString<C: UChar> {
pub(crate) inner: Box<[C]>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NulError<C: UChar>(usize, Vec<C>);
impl<C: UChar> UCString<C> {
pub fn new(v: impl Into<Vec<C>>) -> Result<Self, NulError<C>> {
let v = v.into();
match v.iter().position(|&val| val == UChar::NUL) {
None => Ok(unsafe { UCString::from_vec_unchecked(v) }),
Some(pos) => Err(NulError(pos, v)),
}
}
pub fn from_vec_with_nul(v: impl Into<Vec<C>>) -> Result<Self, MissingNulError<C>> {
let mut v = v.into();
match v.iter().position(|&val| val == UChar::NUL) {
None => Err(MissingNulError { inner: Some(v) }),
Some(pos) => {
v.truncate(pos + 1);
Ok(unsafe { UCString::from_vec_with_nul_unchecked(v) })
}
}
}
pub unsafe fn from_vec_unchecked(v: impl Into<Vec<C>>) -> Self {
let mut v = v.into();
match v.last() {
None => v.push(UChar::NUL),
Some(&c) if c != UChar::NUL => v.push(UChar::NUL),
Some(_) => (),
}
UCString::from_vec_with_nul_unchecked(v)
}
pub unsafe fn from_vec_with_nul_unchecked(v: impl Into<Vec<C>>) -> Self {
UCString {
inner: v.into().into_boxed_slice(),
}
}
pub fn from_ustr(s: impl AsRef<UStr<C>>) -> Result<Self, NulError<C>> {
UCString::new(s.as_ref().as_slice())
}
pub unsafe fn from_ustr_unchecked(s: impl AsRef<UStr<C>>) -> Self {
UCString::from_vec_unchecked(s.as_ref().as_slice())
}
pub fn from_ustr_with_nul(s: impl AsRef<UStr<C>>) -> Result<Self, MissingNulError<C>> {
UCString::from_vec_with_nul(s.as_ref().as_slice())
}
pub unsafe fn from_ustr_with_nul_unchecked(s: impl AsRef<UStr<C>>) -> Self {
UCString::from_vec_with_nul_unchecked(s.as_ref().as_slice())
}
pub unsafe fn from_ptr_str(p: *const C) -> Self {
assert!(!p.is_null());
let mut i: isize = 0;
while *p.offset(i) != UChar::NUL {
i += 1;
}
let slice = slice::from_raw_parts(p, i as usize + 1);
UCString::from_vec_with_nul_unchecked(slice)
}
pub fn as_ucstr(&self) -> &UCStr<C> {
self
}
pub fn into_vec(self) -> Vec<C> {
let mut v = self.into_inner().into_vec();
v.pop();
v
}
pub fn into_vec_with_nul(self) -> Vec<C> {
self.into_inner().into_vec()
}
pub fn into_raw(self) -> *mut C {
Box::into_raw(self.into_inner()) as *mut C
}
pub unsafe fn from_raw(p: *mut C) -> Self {
assert!(!p.is_null());
let mut i: isize = 0;
while *p.offset(i) != UChar::NUL {
i += 1;
}
let slice: *mut [C] = slice::from_raw_parts_mut(p, i as usize + 1);
UCString {
inner: Box::from_raw(slice),
}
}
pub fn into_boxed_ucstr(self) -> Box<UCStr<C>> {
unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut UCStr<C>) }
}
fn into_inner(self) -> Box<[C]> {
unsafe {
let result = ptr::read(&self.inner);
mem::forget(self);
result
}
}
}
impl FromStr for UCString<u16> {
type Err = NulError<u16>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_str(s)
}
}
impl UCString<u16> {
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: impl AsRef<str>) -> Result<Self, NulError<u16>> {
let v: Vec<u16> = s.as_ref().encode_utf16().collect();
UCString::new(v)
}
pub unsafe fn from_str_unchecked(s: impl AsRef<str>) -> Self {
let v: Vec<u16> = s.as_ref().encode_utf16().collect();
UCString::from_vec_unchecked(v)
}
pub fn from_str_with_nul(s: impl AsRef<str>) -> Result<Self, MissingNulError<u16>> {
let v: Vec<u16> = s.as_ref().encode_utf16().collect();
UCString::from_vec_with_nul(v)
}
pub unsafe fn from_str_with_nul_unchecked(s: impl AsRef<str>) -> Self {
let v: Vec<u16> = s.as_ref().encode_utf16().collect();
UCString::from_vec_with_nul_unchecked(v)
}
pub unsafe fn from_ptr(p: *const u16, len: usize) -> Result<Self, NulError<u16>> {
if len == 0 {
return Ok(UCString::default());
}
assert!(!p.is_null());
let slice = slice::from_raw_parts(p, len);
UCString::new(slice)
}
pub unsafe fn from_ptr_unchecked(p: *const u16, len: usize) -> Self {
if len == 0 {
return UCString::default();
}
assert!(!p.is_null());
let slice = slice::from_raw_parts(p, len);
UCString::from_vec_unchecked(slice)
}
pub unsafe fn from_ptr_with_nul(
p: *const u16,
len: usize,
) -> Result<Self, MissingNulError<u16>> {
if len == 0 {
return Ok(UCString::default());
}
assert!(!p.is_null());
let slice = slice::from_raw_parts(p, len);
UCString::from_vec_with_nul(slice)
}
pub unsafe fn from_ptr_with_nul_unchecked(p: *const u16, len: usize) -> Self {
if len == 0 {
return UCString::default();
}
assert!(!p.is_null());
let slice = slice::from_raw_parts(p, len);
UCString::from_vec_with_nul_unchecked(slice)
}
#[cfg(feature = "std")]
pub fn from_os_str(s: impl AsRef<std::ffi::OsStr>) -> Result<Self, NulError<u16>> {
let v = crate::platform::os_to_wide(s.as_ref());
UCString::new(v)
}
#[cfg(feature = "std")]
pub unsafe fn from_os_str_unchecked(s: impl AsRef<std::ffi::OsStr>) -> Self {
let v = crate::platform::os_to_wide(s.as_ref());
UCString::from_vec_unchecked(v)
}
#[cfg(feature = "std")]
pub fn from_os_str_with_nul(
s: impl AsRef<std::ffi::OsStr>,
) -> Result<Self, MissingNulError<u16>> {
let v = crate::platform::os_to_wide(s.as_ref());
UCString::from_vec_with_nul(v)
}
#[cfg(feature = "std")]
pub unsafe fn from_os_str_with_nul_unchecked(s: impl AsRef<std::ffi::OsStr>) -> Self {
let v = crate::platform::os_to_wide(s.as_ref());
UCString::from_vec_with_nul_unchecked(v)
}
}
impl FromStr for UCString<u32> {
type Err = NulError<u32>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_str(s)
}
}
impl UCString<u32> {
pub fn from_chars(v: Vec<char>) -> Result<Self, NulError<u32>> {
UCString::new(v.into_iter().map(u32::from).collect::<Vec<_>>())
}
pub fn from_chars_with_nul(v: Vec<char>) -> Result<Self, MissingNulError<u32>> {
let v = v.into_iter().map(u32::from).collect::<Vec<_>>();
UCString::from_vec_with_nul(v)
}
pub unsafe fn from_chars_unchecked(v: Vec<char>) -> Self {
let v = v.into_iter().map(u32::from).collect::<Vec<_>>();
UCString::from_vec_unchecked(v)
}
pub unsafe fn from_chars_with_nul_unchecked(v: Vec<char>) -> Self {
let v = v.into_iter().map(u32::from).collect::<Vec<_>>();
UCString::from_vec_with_nul_unchecked(v)
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: impl AsRef<str>) -> Result<Self, NulError<u32>> {
let v: Vec<char> = s.as_ref().chars().collect();
UCString::from_chars(v)
}
pub unsafe fn from_str_unchecked(s: impl AsRef<str>) -> Self {
let v: Vec<char> = s.as_ref().chars().collect();
UCString::from_chars_unchecked(v)
}
pub fn from_str_with_nul(s: impl AsRef<str>) -> Result<Self, MissingNulError<u32>> {
let v: Vec<char> = s.as_ref().chars().collect();
UCString::from_chars_with_nul(v)
}
pub unsafe fn from_str_with_nul_unchecked(s: impl AsRef<str>) -> Self {
let v: Vec<char> = s.as_ref().chars().collect();
UCString::from_chars_with_nul_unchecked(v)
}
pub unsafe fn from_ptr(p: *const u32, len: usize) -> Result<Self, NulError<u32>> {
if len == 0 {
return Ok(UCString::default());
}
assert!(!p.is_null());
let slice = slice::from_raw_parts(p, len);
UCString::new(slice)
}
pub unsafe fn from_ptr_unchecked(p: *const u32, len: usize) -> Self {
if len == 0 {
return UCString::default();
}
assert!(!p.is_null());
let slice = slice::from_raw_parts(p, len);
UCString::from_vec_unchecked(slice)
}
pub unsafe fn from_ptr_with_nul(
p: *const u32,
len: usize,
) -> Result<Self, MissingNulError<u32>> {
if len == 0 {
return Ok(UCString::default());
}
assert!(!p.is_null());
let slice = slice::from_raw_parts(p, len);
UCString::from_vec_with_nul(slice)
}
pub unsafe fn from_ptr_with_nul_unchecked(p: *const u32, len: usize) -> Self {
if len == 0 {
return UCString::default();
}
assert!(!p.is_null());
let slice = slice::from_raw_parts(p, len);
UCString::from_vec_with_nul_unchecked(slice)
}
pub unsafe fn from_char_ptr(p: *const char, len: usize) -> Result<Self, NulError<u32>> {
UCString::<u32>::from_ptr(p as *const u32, len)
}
pub unsafe fn from_char_ptr_unchecked(p: *const char, len: usize) -> Self {
UCString::<u32>::from_ptr_unchecked(p as *const u32, len)
}
pub unsafe fn from_char_ptr_with_nul(
p: *const char,
len: usize,
) -> Result<Self, MissingNulError<u32>> {
UCString::<u32>::from_ptr_with_nul(p as *const u32, len)
}
pub unsafe fn from_char_ptr_with_nul_unchecked(p: *const char, len: usize) -> Self {
UCString::<u32>::from_ptr_with_nul_unchecked(p as *const u32, len)
}
#[cfg(feature = "std")]
pub fn from_os_str(s: impl AsRef<std::ffi::OsStr>) -> Result<Self, NulError<u32>> {
let v: Vec<char> = s.as_ref().to_string_lossy().chars().collect();
UCString::from_chars(v)
}
#[cfg(feature = "std")]
pub unsafe fn from_os_str_unchecked(s: impl AsRef<std::ffi::OsStr>) -> Self {
let v: Vec<char> = s.as_ref().to_string_lossy().chars().collect();
UCString::from_chars_unchecked(v)
}
#[cfg(feature = "std")]
pub fn from_os_str_with_nul(
s: impl AsRef<std::ffi::OsStr>,
) -> Result<Self, MissingNulError<u32>> {
let v: Vec<char> = s.as_ref().to_string_lossy().chars().collect();
UCString::from_chars_with_nul(v)
}
#[cfg(feature = "std")]
pub unsafe fn from_os_str_with_nul_unchecked(s: impl AsRef<std::ffi::OsStr>) -> Self {
let v: Vec<char> = s.as_ref().to_string_lossy().chars().collect();
UCString::from_chars_with_nul_unchecked(v)
}
}
impl<C: UChar> Into<Vec<C>> for UCString<C> {
fn into(self) -> Vec<C> {
self.into_vec()
}
}
impl TryFrom<String> for UCString<u16> {
type Error = NulError<u16>;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl TryFrom<String> for UCString<u32> {
type Error = NulError<u32>;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl TryFrom<&'_ str> for UCString<u16> {
type Error = NulError<u16>;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl TryFrom<&'_ str> for UCString<u32> {
type Error = NulError<u32>;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl TryFrom<&'_ String> for UCString<u16> {
type Error = NulError<u16>;
fn try_from(value: &String) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl TryFrom<&'_ String> for UCString<u32> {
type Error = NulError<u32>;
fn try_from(value: &String) -> Result<Self, Self::Error> {
Self::from_str(value)
}
}
impl<'a> From<UCString<u16>> for Cow<'a, UCStr<u16>> {
fn from(s: UCString<u16>) -> Cow<'a, UCStr<u16>> {
Cow::Owned(s)
}
}
impl<'a> From<UCString<u32>> for Cow<'a, UCStr<u32>> {
fn from(s: UCString<u32>) -> Cow<'a, UCStr<u32>> {
Cow::Owned(s)
}
}
#[cfg(feature = "std")]
impl From<UCString<u16>> for std::ffi::OsString {
fn from(s: UCString<u16>) -> std::ffi::OsString {
s.to_os_string()
}
}
#[cfg(feature = "std")]
impl From<UCString<u32>> for std::ffi::OsString {
fn from(s: UCString<u32>) -> std::ffi::OsString {
s.to_os_string()
}
}
impl<C: UChar> From<UCString<C>> for UString<C> {
fn from(s: UCString<C>) -> Self {
s.to_ustring()
}
}
impl<C: UChar, T: ?Sized + AsRef<UCStr<C>>> From<&'_ T> for UCString<C> {
fn from(s: &T) -> Self {
s.as_ref().to_ucstring()
}
}
impl<C: UChar> Index<RangeFull> for UCString<C> {
type Output = UCStr<C>;
#[inline]
fn index(&self, _index: RangeFull) -> &UCStr<C> {
UCStr::from_inner(&self.inner)
}
}
impl<C: UChar> Deref for UCString<C> {
type Target = UCStr<C>;
#[inline]
fn deref(&self) -> &UCStr<C> {
&self[..]
}
}
impl<'a> Default for &'a UCStr<u16> {
fn default() -> Self {
const SLICE: &[u16] = &[UChar::NUL];
unsafe { UCStr::from_slice_with_nul_unchecked(SLICE) }
}
}
impl<'a> Default for &'a UCStr<u32> {
fn default() -> Self {
const SLICE: &[u32] = &[UChar::NUL];
unsafe { UCStr::from_slice_with_nul_unchecked(SLICE) }
}
}
impl Default for UCString<u16> {
fn default() -> Self {
let def: &UCStr<u16> = Default::default();
def.to_ucstring()
}
}
impl Default for UCString<u32> {
fn default() -> Self {
let def: &UCStr<u32> = Default::default();
def.to_ucstring()
}
}
impl<C: UChar> Drop for UCString<C> {
#[inline]
fn drop(&mut self) {
unsafe {
*self.inner.get_unchecked_mut(0) = UChar::NUL;
}
}
}
impl<C: UChar> Borrow<UCStr<C>> for UCString<C> {
fn borrow(&self) -> &UCStr<C> {
&self[..]
}
}
impl<C: UChar> ToOwned for UCStr<C> {
type Owned = UCString<C>;
fn to_owned(&self) -> UCString<C> {
self.to_ucstring()
}
}
impl<'a> From<&'a UCStr<u16>> for Cow<'a, UCStr<u16>> {
fn from(s: &'a UCStr<u16>) -> Cow<'a, UCStr<u16>> {
Cow::Borrowed(s)
}
}
impl<'a> From<&'a UCStr<u32>> for Cow<'a, UCStr<u32>> {
fn from(s: &'a UCStr<u32>) -> Cow<'a, UCStr<u32>> {
Cow::Borrowed(s)
}
}
impl<C: UChar> AsRef<UCStr<C>> for UCStr<C> {
fn as_ref(&self) -> &Self {
self
}
}
impl<C: UChar> AsRef<UCStr<C>> for UCString<C> {
fn as_ref(&self) -> &UCStr<C> {
self
}
}
impl<C: UChar> AsRef<[C]> for UCStr<C> {
fn as_ref(&self) -> &[C] {
self.as_slice()
}
}
impl<C: UChar> AsRef<[C]> for UCString<C> {
fn as_ref(&self) -> &[C] {
self.as_slice()
}
}
impl<'a, C: UChar> From<&'a UCStr<C>> for Box<UCStr<C>> {
fn from(s: &'a UCStr<C>) -> Box<UCStr<C>> {
let boxed: Box<[C]> = Box::from(s.as_slice_with_nul());
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut UCStr<C>) }
}
}
impl<C: UChar> From<Box<UCStr<C>>> for UCString<C> {
#[inline]
fn from(s: Box<UCStr<C>>) -> Self {
s.into_ucstring()
}
}
impl<C: UChar> From<UCString<C>> for Box<UCStr<C>> {
#[inline]
fn from(s: UCString<C>) -> Box<UCStr<C>> {
s.into_boxed_ucstr()
}
}
impl<C: UChar> Default for Box<UCStr<C>> {
fn default() -> Box<UCStr<C>> {
let boxed: Box<[C]> = Box::from([UChar::NUL]);
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut UCStr<C>) }
}
}
impl<C: UChar> NulError<C> {
pub fn nul_position(&self) -> usize {
self.0
}
pub fn into_vec(self) -> Vec<C> {
self.1
}
}
impl<C: UChar> Into<Vec<C>> for NulError<C> {
fn into(self) -> Vec<C> {
self.into_vec()
}
}
impl<C: UChar> core::fmt::Display for NulError<C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "nul value found at position {}", self.0)
}
}
#[cfg(feature = "std")]
impl<C: UChar> std::error::Error for NulError<C> {
fn description(&self) -> &str {
"nul value found"
}
}
pub type U16CString = UCString<u16>;
pub type U32CString = UCString<u32>;
pub type WideCString = UCString<WideChar>;