use alloc::borrow::ToOwned;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use core::borrow::Borrow;
use core::cmp::Ordering;
use core::fmt::{self, Debug, Display, Formatter};
use core::hash::{Hash, Hasher};
use core::ops::Deref;
use crate::encoding::{Wtf8, Wtf16, WtfEncoding};
#[repr(transparent)]
pub struct WtfStr<E: WtfEncoding> {
units: [E::Unit],
}
impl<E: WtfEncoding> WtfStr<E> {
#[must_use]
pub fn from_units(units: &[E::Unit]) -> &WtfStr<E> {
unsafe { &*(units as *const [E::Unit] as *const WtfStr<E>) }
}
#[must_use]
pub fn as_units(&self) -> &[E::Unit] {
&self.units
}
#[must_use]
pub fn len(&self) -> usize {
self.units.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.units.is_empty()
}
#[must_use]
pub fn has_interior_nul(&self) -> bool {
self.units.contains(&E::NUL)
}
#[must_use]
pub fn to_string_checked(&self) -> Option<String> {
E::decode(self.as_units())
}
#[must_use]
pub fn to_string_lossy(&self) -> String {
E::decode_lossy(self.as_units())
}
}
pub struct WtfString<E: WtfEncoding> {
units: Vec<E::Unit>,
}
impl<E: WtfEncoding> WtfString<E> {
#[must_use]
pub fn new() -> Self {
WtfString {
units: vec![E::NUL],
}
}
#[must_use]
pub fn from_units(units: &[E::Unit]) -> Self {
let capacity = units.len().checked_add(1).expect("capacity overflow");
let mut buf = Vec::with_capacity(capacity);
buf.extend_from_slice(units);
buf.push(E::NUL);
WtfString { units: buf }
}
fn content(&self) -> &[E::Unit] {
&self.units[..self.units.len() - 1]
}
fn from_encoded(mut units: Vec<E::Unit>) -> Self {
units.push(E::NUL);
WtfString { units }
}
pub fn into_string(self) -> Result<String, Self> {
match E::decode(self.content()) {
Some(s) => Ok(s),
None => Err(self),
}
}
pub fn push<S: AsRef<WtfStr<E>>>(&mut self, s: S) {
let new_units = s.as_ref().as_units();
self.units.reserve(new_units.len());
self.units.pop();
self.units.extend_from_slice(new_units);
self.units.push(E::NUL);
}
pub fn push_str(&mut self, s: &str) {
self.push(WtfStr::from_units(&E::encode_str(s)));
}
pub fn clear(&mut self) {
self.units.clear();
self.units.push(E::NUL);
}
#[must_use]
pub fn capacity(&self) -> usize {
self.units.capacity() - 1
}
pub fn reserve(&mut self, additional: usize) {
self.units.reserve(additional);
}
pub fn reserve_exact(&mut self, additional: usize) {
self.units.reserve_exact(additional);
}
pub fn shrink_to_fit(&mut self) {
self.units.shrink_to_fit();
}
pub fn shrink_to(&mut self, min_capacity: usize) {
let min_capacity = min_capacity.checked_add(1).expect("capacity overflow");
self.units.shrink_to(min_capacity);
}
}
impl<E: WtfEncoding> Deref for WtfString<E> {
type Target = WtfStr<E>;
fn deref(&self) -> &WtfStr<E> {
WtfStr::from_units(self.content())
}
}
impl<E: WtfEncoding> AsRef<WtfStr<E>> for WtfString<E> {
fn as_ref(&self) -> &WtfStr<E> {
self
}
}
impl<E: WtfEncoding> AsRef<WtfStr<E>> for WtfStr<E> {
fn as_ref(&self) -> &WtfStr<E> {
self
}
}
impl<E: WtfEncoding> Borrow<WtfStr<E>> for WtfString<E> {
fn borrow(&self) -> &WtfStr<E> {
self
}
}
impl<E: WtfEncoding> ToOwned for WtfStr<E> {
type Owned = WtfString<E>;
fn to_owned(&self) -> WtfString<E> {
WtfString::from_units(self.as_units())
}
}
impl<E: WtfEncoding> Default for WtfString<E> {
fn default() -> Self {
Self::new()
}
}
impl<E: WtfEncoding> Clone for WtfString<E> {
fn clone(&self) -> Self {
WtfString {
units: self.units.clone(),
}
}
}
impl<E: WtfEncoding> From<&str> for WtfString<E> {
fn from(s: &str) -> Self {
Self::from_encoded(E::encode_str(s))
}
}
impl<E: WtfEncoding> From<String> for WtfString<E> {
fn from(s: String) -> Self {
Self::from_encoded(E::encode_str(&s))
}
}
impl<E: WtfEncoding> Display for WtfStr<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.to_string_lossy(), f)
}
}
impl<E: WtfEncoding> Display for WtfString<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&**self, f)
}
}
impl<E: WtfEncoding> Debug for WtfStr<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
E::debug_fmt(self.as_units(), f)
}
}
impl<E: WtfEncoding> Debug for WtfString<E> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&**self, f)
}
}
impl<E: WtfEncoding> PartialEq for WtfStr<E> {
fn eq(&self, other: &Self) -> bool {
self.units == other.units
}
}
impl<E: WtfEncoding> Eq for WtfStr<E> {}
impl<E: WtfEncoding> Ord for WtfStr<E> {
fn cmp(&self, other: &Self) -> Ordering {
self.units.cmp(&other.units)
}
}
impl<E: WtfEncoding> PartialOrd for WtfStr<E> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<E: WtfEncoding> Hash for WtfStr<E> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.units.hash(state);
}
}
impl<E: WtfEncoding> PartialEq for WtfString<E> {
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<E: WtfEncoding> Eq for WtfString<E> {}
impl<E: WtfEncoding> Ord for WtfString<E> {
fn cmp(&self, other: &Self) -> Ordering {
(**self).cmp(&**other)
}
}
impl<E: WtfEncoding> PartialOrd for WtfString<E> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<E: WtfEncoding> Hash for WtfString<E> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl<E: WtfEncoding> PartialEq<str> for WtfStr<E> {
fn eq(&self, other: &str) -> bool {
E::eq_str(self.as_units(), other)
}
}
impl<E: WtfEncoding> PartialEq<&str> for WtfStr<E> {
fn eq(&self, other: &&str) -> bool {
*self == **other
}
}
impl<E: WtfEncoding> PartialEq<str> for WtfString<E> {
fn eq(&self, other: &str) -> bool {
**self == *other
}
}
impl<E: WtfEncoding> PartialEq<&str> for WtfString<E> {
fn eq(&self, other: &&str) -> bool {
**self == **other
}
}
pub type Wtf16String = WtfString<Wtf16>;
pub type Wtf16Str = WtfStr<Wtf16>;
pub type Wtf8String = WtfString<Wtf8>;
pub type Wtf8Str = WtfStr<Wtf8>;
impl Wtf16Str {
#[must_use]
pub fn as_ptr(&self) -> *const u16 {
self.as_units().as_ptr()
}
}
impl Wtf16String {
#[must_use]
pub fn as_terminated_ptr(&self) -> *const u16 {
self.units.as_ptr()
}
#[must_use]
pub fn with_capacity(units: usize) -> Self {
let capacity = units.checked_add(1).expect("capacity overflow");
let mut buf = Vec::with_capacity(capacity);
buf.push(Wtf16::NUL);
WtfString { units: buf }
}
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut u16 {
self.units.as_mut_ptr()
}
pub unsafe fn set_len_from_ffi(&mut self, content_units: usize) {
debug_assert!(
content_units < self.units.capacity(),
"set_len_from_ffi content length leaves no room for the terminator"
);
unsafe { self.units.set_len(content_units) };
self.units.push(Wtf16::NUL);
}
#[must_use]
pub unsafe fn from_wide_ptr(ptr: *const u16, len: usize) -> Self {
if len == 0 {
return Self::new();
}
let content = unsafe { core::slice::from_raw_parts(ptr, len) };
Self::from_units(content)
}
}
#[cfg(all(windows, feature = "std"))]
mod os_str;
#[cfg(feature = "windows-core")]
mod param;
#[cfg(test)]
mod tests;