use crate::{c::c_char, Bstr, Ustring};
use std::{
ffi::{CStr, FromBytesWithNulError, OsStr},
fmt,
fmt::{Debug, Formatter},
ops::{Deref, DerefMut},
os::unix::ffi::OsStrExt,
path::Path,
ptr, slice,
};
#[repr(transparent)]
#[derive(Eq, Ord, PartialOrd, PartialEq, Hash)]
pub struct Ustr {
bytes: [u8],
}
static NULL: [u8; 1] = [0];
impl Ustr {
pub fn empty() -> &'static Self {
static E: [u8; 1] = [0];
unsafe { Self::from_bytes_unchecked(&E[..]) }
}
pub fn null() -> &'static Self {
unsafe { Self::from_bytes_unchecked(&NULL[..]) }
}
pub fn is_null(&self) -> bool {
self.bytes.as_ptr() == NULL.as_ptr()
}
pub unsafe fn from_bytes_unchecked(s: &[u8]) -> &Self {
&*(s as *const _ as *const _)
}
pub unsafe fn from_bytes_unchecked_mut(s: &mut [u8]) -> &mut Self {
&mut *(s as *mut _ as *mut _)
}
pub fn from_bytes(s: &[u8]) -> Option<&Self> {
if s.is_empty() || s[s.len() - 1] != 0 {
return None;
}
Some(unsafe { Self::from_bytes_unchecked(s) })
}
pub fn from_bytes_mut(s: &mut [u8]) -> Option<&mut Self> {
if s.is_empty() || s[s.len() - 1] != 0 {
return None;
}
Some(unsafe { Self::from_bytes_unchecked_mut(s) })
}
pub fn as_bytes_with_nul(&self) -> &[u8] {
&self.bytes
}
#[allow(clippy::should_implement_trait)] pub fn from_str(s: &str) -> Option<&Self> {
Self::from_bytes(s.as_bytes())
}
pub fn from_c_str(s: &CStr) -> &Self {
unsafe { Self::from_bytes_unchecked(s.to_bytes_with_nul()) }
}
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a Self {
Self::from_c_str(CStr::from_ptr(ptr))
}
pub unsafe fn from_ptr_mut<'a>(ptr: *mut c_char) -> &'a mut Self {
let len = Self::from_ptr(ptr).len_with_nul();
Self::from_bytes_unchecked_mut(slice::from_raw_parts_mut(ptr as *mut _, len))
}
pub fn as_c_str(&self) -> Result<&CStr, FromBytesWithNulError> {
CStr::from_bytes_with_nul(&self.bytes)
}
pub fn from_os_str(s: &OsStr) -> Option<&Self> {
Self::from_bytes(s.as_bytes())
}
pub fn as_os_str_with_nul(&self) -> &OsStr {
OsStr::from_bytes(self.as_bytes_with_nul())
}
pub fn from_path(s: &Path) -> Option<&Self> {
Self::from_os_str(s.as_os_str())
}
pub fn as_path_with_nul(&self) -> &Path {
Path::new(self.as_os_str_with_nul())
}
pub fn len_with_nul(&self) -> usize {
self.bytes.len()
}
pub fn as_bstr(&self) -> &Bstr {
Bstr::from_bytes(&self.bytes[..self.bytes.len() - 1])
}
pub fn as_bstr_mut(&mut self) -> &mut Bstr {
let len = self.bytes.len();
Bstr::from_bytes_mut(&mut self.bytes[..len - 1])
}
pub fn as_ptr_null(&self) -> *const c_char {
if self.is_null() {
ptr::null()
} else {
self.as_ptr()
}
}
}
impl ToOwned for Ustr {
type Owned = Ustring;
fn to_owned(&self) -> Self::Owned {
self.to_ustring()
}
}
impl Deref for Ustr {
type Target = Bstr;
fn deref(&self) -> &Self::Target {
self.as_bstr()
}
}
impl DerefMut for Ustr {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_bstr_mut()
}
}
impl AsRef<[u8]> for Ustr {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsMut<[u8]> for Ustr {
fn as_mut(&mut self) -> &mut [u8] {
self.as_bytes_mut()
}
}
impl AsRef<Ustr> for CStr {
fn as_ref(&self) -> &Ustr {
Ustr::from_c_str(self)
}
}
impl AsRef<OsStr> for Ustr {
fn as_ref(&self) -> &OsStr {
self.as_os_str()
}
}
impl AsRef<Path> for Ustr {
fn as_ref(&self) -> &Path {
self.as_path()
}
}
impl Debug for Ustr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(self.as_os_str(), f)
}
}