use alloc::borrow::Cow;
use alloc::collections::TryReserveError;
use alloc::string::FromUtf8Error;
use core::borrow::Borrow;
use core::hash::{Hash, Hasher};
use core::iter::{Extend, FromIterator};
use core::marker::PhantomData;
use core::ops::Deref;
use core::str::FromStr;
use core::{cmp, fmt};
use crate::no_std_compat::*;
use crate::{CheckedPathError, Encoding, PathBuf, Utf8Encoding, Utf8Iter, Utf8Path};
pub struct Utf8PathBuf<T>
where
T: Utf8Encoding,
{
pub(crate) _encoding: PhantomData<T>,
pub(crate) inner: String,
}
impl<T> Utf8PathBuf<T>
where
T: Utf8Encoding,
{
pub fn new() -> Self {
Utf8PathBuf {
_encoding: PhantomData,
inner: String::new(),
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Utf8PathBuf {
_encoding: PhantomData,
inner: String::with_capacity(capacity),
}
}
#[inline]
pub fn as_path(&self) -> &Utf8Path<T> {
self
}
pub fn push<P: AsRef<Utf8Path<T>>>(&mut self, path: P) {
T::push(&mut self.inner, path.as_ref().as_str());
}
pub fn push_checked<P: AsRef<Utf8Path<T>>>(&mut self, path: P) -> Result<(), CheckedPathError> {
T::push_checked(&mut self.inner, path.as_ref().as_str())
}
pub fn pop(&mut self) -> bool {
match self.parent().map(|p| p.as_str().len()) {
Some(len) => {
self.inner.truncate(len);
true
}
None => false,
}
}
pub fn set_file_name<S: AsRef<str>>(&mut self, file_name: S) {
self._set_file_name(file_name.as_ref())
}
fn _set_file_name(&mut self, file_name: &str) {
if self.file_name().is_some() {
let popped = self.pop();
debug_assert!(popped);
}
self.push(file_name);
}
pub fn set_extension<S: AsRef<str>>(&mut self, extension: S) -> bool {
self._set_extension(extension.as_ref())
}
fn _set_extension(&mut self, extension: &str) -> bool {
if self.file_stem().is_none() {
return false;
}
let old_ext_len = self.extension().map(|ext| ext.len()).unwrap_or(0);
if old_ext_len > 0 {
self.inner.truncate(self.inner.len() - old_ext_len);
if self.inner.ends_with('.') {
self.inner.pop();
}
}
if !extension.is_empty() {
if !self.inner.ends_with('.') {
self.inner.push('.');
}
self.inner.push_str(extension);
}
true
}
#[inline]
pub fn into_string(self) -> String {
self.inner
}
#[inline]
pub fn into_boxed_path(self) -> Box<Utf8Path<T>> {
let rw = Box::into_raw(self.inner.into_boxed_str()) as *mut Utf8Path<T>;
unsafe { Box::from_raw(rw) }
}
#[inline]
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
#[inline]
pub fn clear(&mut self) {
self.inner.clear()
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.inner.reserve(additional)
}
#[inline]
pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.inner.try_reserve(additional)
}
#[inline]
pub fn reserve_exact(&mut self, additional: usize) {
self.inner.reserve_exact(additional)
}
#[inline]
pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
self.inner.try_reserve_exact(additional)
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.inner.shrink_to_fit()
}
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.inner.shrink_to(min_capacity)
}
pub fn from_bytes_path_buf<U>(path_buf: PathBuf<U>) -> Result<Self, FromUtf8Error>
where
U: Encoding,
{
Ok(Self {
_encoding: PhantomData,
inner: String::from_utf8(path_buf.inner)?,
})
}
pub unsafe fn from_bytes_path_buf_unchecked<U>(path_buf: PathBuf<U>) -> Self
where
U: Encoding,
{
Self {
_encoding: PhantomData,
inner: String::from_utf8_unchecked(path_buf.inner),
}
}
pub fn into_bytes_path_buf<U>(self) -> PathBuf<U>
where
U: Encoding,
{
PathBuf {
_encoding: PhantomData,
inner: self.inner.into_bytes(),
}
}
}
impl<T> Clone for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn clone(&self) -> Self {
Self {
_encoding: self._encoding,
inner: self.inner.clone(),
}
}
}
impl<T> fmt::Debug for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Utf8PathBuf")
.field("_encoding", &T::label())
.field("inner", &self.inner)
.finish()
}
}
impl<T> AsRef<[u8]> for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_str().as_bytes()
}
}
impl<T> AsRef<str> for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl<T> AsRef<Utf8Path<T>> for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn as_ref(&self) -> &Utf8Path<T> {
self
}
}
impl<T> Borrow<Utf8Path<T>> for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn borrow(&self) -> &Utf8Path<T> {
self.deref()
}
}
impl<T> Default for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn default() -> Utf8PathBuf<T> {
Utf8PathBuf::new()
}
}
impl<T> fmt::Display for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.inner, f)
}
}
impl<T> Deref for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
type Target = Utf8Path<T>;
#[inline]
fn deref(&self) -> &Utf8Path<T> {
Utf8Path::new(&self.inner)
}
}
impl<T> Eq for Utf8PathBuf<T> where T: Utf8Encoding {}
impl<T> PartialEq for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
fn eq(&self, other: &Self) -> bool {
self.components() == other.components()
}
}
impl<T, P> Extend<P> for Utf8PathBuf<T>
where
T: Utf8Encoding,
P: AsRef<Utf8Path<T>>,
{
fn extend<I: IntoIterator<Item = P>>(&mut self, iter: I) {
iter.into_iter().for_each(move |p| self.push(p.as_ref()));
}
}
impl<T> From<Box<Utf8Path<T>>> for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
fn from(boxed: Box<Utf8Path<T>>) -> Self {
boxed.into_path_buf()
}
}
impl<T, V> From<&V> for Utf8PathBuf<T>
where
T: Utf8Encoding,
V: ?Sized + AsRef<str>,
{
#[inline]
fn from(s: &V) -> Self {
Utf8PathBuf::from(s.as_ref().to_string())
}
}
impl<T> From<String> for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn from(inner: String) -> Self {
Utf8PathBuf {
_encoding: PhantomData,
inner,
}
}
}
impl<T> From<Utf8PathBuf<T>> for String
where
T: Utf8Encoding,
{
#[inline]
fn from(path_buf: Utf8PathBuf<T>) -> Self {
path_buf.inner
}
}
impl<T> FromStr for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
type Err = core::convert::Infallible;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Utf8PathBuf::from(s))
}
}
impl<'a, T> From<Cow<'a, Utf8Path<T>>> for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn from(p: Cow<'a, Utf8Path<T>>) -> Self {
p.into_owned()
}
}
impl<T, P> FromIterator<P> for Utf8PathBuf<T>
where
T: Utf8Encoding,
P: AsRef<Utf8Path<T>>,
{
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
let mut buf = Utf8PathBuf::new();
buf.extend(iter);
buf
}
}
impl<T> Hash for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
fn hash<H: Hasher>(&self, h: &mut H) {
self.as_path().hash(h)
}
}
impl<'a, T> IntoIterator for &'a Utf8PathBuf<T>
where
T: Utf8Encoding,
{
type IntoIter = Utf8Iter<'a, T>;
type Item = &'a str;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<T> cmp::PartialOrd for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T> cmp::Ord for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.components().cmp(other.components())
}
}
#[cfg(any(
unix,
all(target_vendor = "fortanix", target_env = "sgx"),
target_os = "solid_asp3",
target_os = "hermit",
target_os = "wasi"
))]
#[cfg(feature = "std")]
mod std_conversions {
use std::ffi::{OsStr, OsString};
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
use std::os::fortanix_sgx as os;
#[cfg(target_os = "solid_asp3")]
use std::os::solid as os;
#[cfg(any(target_os = "hermit", unix))]
use std::os::unix as os;
#[cfg(target_os = "wasi")]
use std::os::wasi as os;
use os::ffi::{OsStrExt, OsStringExt};
use super::*;
impl<T> From<Utf8PathBuf<T>> for OsString
where
T: Utf8Encoding,
{
#[inline]
fn from(path_buf: Utf8PathBuf<T>) -> Self {
OsStringExt::from_vec(path_buf.into_string().into_bytes())
}
}
impl<T> AsRef<OsStr> for Utf8PathBuf<T>
where
T: Utf8Encoding,
{
#[inline]
fn as_ref(&self) -> &OsStr {
OsStrExt::from_bytes(self.as_str().as_bytes())
}
}
}