use std::borrow::Borrow;
use std::borrow::Cow;
use std::borrow::ToOwned;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fmt;
use std::fmt::Debug;
use std::fmt::Formatter;
use std::mem;
use std::ops::Deref;
use std::ops::Index;
use std::result;
use std::str;
use super::ext;
use super::ext::SliceIndex;
use super::iter::RawRSplit;
use super::iter::RawSplit;
use super::iter::Utf8Chunks;
use super::private;
use super::OsStrBytesExt;
use super::Pattern;
if_checked_conversions! {
use super::Result;
}
if_conversions! {
use super::OsStrBytes;
use super::OsStringBytes;
}
#[allow(clippy::missing_safety_doc)]
unsafe trait TransmuteBox {
fn transmute_box<R>(self: Box<Self>) -> Box<R>
where
R: ?Sized + TransmuteBox,
{
let value = Box::into_raw(self);
unsafe { Box::from_raw(mem::transmute_copy(&value)) }
}
}
unsafe impl TransmuteBox for RawOsStr {}
unsafe impl TransmuteBox for [u8] {}
#[derive(Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "raw_os_str")))]
#[repr(transparent)]
pub struct RawOsStr([u8]);
impl RawOsStr {
#[inline]
#[must_use]
pub fn new<S>(string: &S) -> &Self
where
S: AsRef<OsStr> + ?Sized,
{
let string = string.as_ref().as_encoded_bytes();
unsafe { Self::from_encoded_bytes_unchecked(string) }
}
fn from_tuple<'a, 'b>(
(prefix, suffix): (&'a OsStr, &'b OsStr),
) -> (&'a Self, &'b Self) {
(Self::new(prefix), Self::new(suffix))
}
#[deprecated(since = "7.0.0", note = "use `new` instead")]
#[inline]
#[must_use]
pub fn from_os_str(string: &OsStr) -> &Self {
Self::new(string)
}
#[allow(clippy::should_implement_trait)]
#[deprecated(since = "7.0.0", note = "use `new` instead")]
#[inline]
#[must_use]
pub fn from_str(string: &str) -> &Self {
Self::new(string)
}
#[allow(clippy::missing_safety_doc)]
#[inline]
#[must_use]
pub unsafe fn from_encoded_bytes_unchecked(string: &[u8]) -> &Self {
unsafe { mem::transmute(string) }
}
if_conversions! {
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
#[inline]
#[must_use = "method should not be used for validation"]
#[track_caller]
pub fn assert_cow_from_raw_bytes(string: &[u8]) -> Cow<'_, Self> {
Cow::from_os_str(OsStr::assert_from_raw_bytes(string))
}
}
if_checked_conversions! {
#[cfg_attr(
os_str_bytes_docs_rs,
doc(cfg(feature = "checked_conversions"))
)]
#[inline]
pub fn cow_from_raw_bytes(string: &[u8]) -> Result<Cow<'_, Self>> {
OsStr::from_raw_bytes(string).map(Cow::from_os_str)
}
}
if_conversions! {
#[deprecated(
since = "6.6.0",
note = "use `assert_cow_from_raw_bytes` or
`from_encoded_bytes_unchecked` instead",
)]
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
#[inline]
#[must_use]
#[track_caller]
pub unsafe fn cow_from_raw_bytes_unchecked(
string: &[u8],
) -> Cow<'_, Self> {
Self::assert_cow_from_raw_bytes(string)
}
}
#[inline]
#[must_use]
pub fn as_encoded_bytes(&self) -> &[u8] {
&self.0
}
#[inline]
#[must_use]
pub fn as_os_str(&self) -> &OsStr {
unsafe { ext::os_str(&self.0) }
}
#[inline]
#[must_use]
pub fn contains<P>(&self, pat: P) -> bool
where
P: Pattern,
{
self.as_os_str().contains(pat)
}
#[inline]
#[must_use]
pub fn ends_with<P>(&self, pat: P) -> bool
where
P: Pattern,
{
self.as_os_str().ends_with(pat)
}
if_conversions! {
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
#[inline]
#[must_use]
pub fn ends_with_os(&self, pat: &Self) -> bool {
self.as_os_str().ends_with_os(pat.as_os_str())
}
}
#[inline]
#[must_use]
pub fn find<P>(&self, pat: P) -> Option<usize>
where
P: Pattern,
{
self.as_os_str().find(pat)
}
#[allow(clippy::missing_safety_doc)]
#[inline]
#[must_use]
pub unsafe fn get_unchecked<I>(&self, index: I) -> &Self
where
I: SliceIndex,
{
let string = self.as_os_str();
Self::new(unsafe { string.get_unchecked(index) })
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.as_os_str().is_empty()
}
#[inline]
#[must_use]
pub fn repeat(&self, n: usize) -> RawOsString {
RawOsString::new(self.as_os_str().repeat(n))
}
#[inline]
#[must_use]
pub fn rfind<P>(&self, pat: P) -> Option<usize>
where
P: Pattern,
{
self.as_os_str().rfind(pat)
}
#[inline]
#[track_caller]
pub fn rsplit<P>(&self, pat: P) -> RawRSplit<'_, P>
where
P: Pattern,
{
RawRSplit::new(self, pat)
}
#[inline]
#[must_use]
pub fn rsplit_once<P>(&self, pat: P) -> Option<(&Self, &Self)>
where
P: Pattern,
{
self.as_os_str().rsplit_once(pat).map(Self::from_tuple)
}
#[inline]
#[track_caller]
pub fn split<P>(&self, pat: P) -> RawSplit<'_, P>
where
P: Pattern,
{
RawSplit::new(self, pat)
}
#[inline]
#[must_use]
#[track_caller]
pub fn split_at(&self, mid: usize) -> (&Self, &Self) {
Self::from_tuple(self.as_os_str().split_at(mid))
}
#[inline]
#[must_use]
pub fn split_once<P>(&self, pat: P) -> Option<(&Self, &Self)>
where
P: Pattern,
{
self.as_os_str().split_once(pat).map(Self::from_tuple)
}
#[inline]
#[must_use]
pub fn starts_with<P>(&self, pat: P) -> bool
where
P: Pattern,
{
self.as_os_str().starts_with(pat)
}
if_conversions! {
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
#[inline]
#[must_use]
pub fn starts_with_os(&self, pat: &Self) -> bool {
self.as_os_str().starts_with_os(pat.as_os_str())
}
}
#[inline]
#[must_use]
pub fn strip_prefix<P>(&self, pat: P) -> Option<&Self>
where
P: Pattern,
{
self.as_os_str().strip_prefix(pat).map(Self::new)
}
#[inline]
#[must_use]
pub fn strip_suffix<P>(&self, pat: P) -> Option<&Self>
where
P: Pattern,
{
self.as_os_str().strip_suffix(pat).map(Self::new)
}
#[deprecated(since = "6.6.0", note = "use `as_os_str` instead")]
#[inline]
#[must_use]
pub fn to_os_str(&self) -> Cow<'_, OsStr> {
Cow::Borrowed(self.as_os_str())
}
if_conversions! {
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
#[inline]
#[must_use]
pub fn to_raw_bytes(&self) -> Cow<'_, [u8]> {
self.as_os_str().to_raw_bytes()
}
}
#[inline]
#[must_use]
pub fn to_str(&self) -> Option<&str> {
self.as_os_str().to_str()
}
#[inline]
#[must_use]
pub fn to_str_lossy(&self) -> Cow<'_, str> {
self.as_os_str().to_string_lossy()
}
#[inline]
#[must_use]
pub fn trim_end_matches<P>(&self, pat: P) -> &Self
where
P: Pattern,
{
Self::new(self.as_os_str().trim_end_matches(pat))
}
#[inline]
#[must_use]
pub fn trim_matches<P>(&self, pat: P) -> &Self
where
P: Pattern,
{
Self::new(self.as_os_str().trim_matches(pat))
}
#[inline]
#[must_use]
pub fn trim_start_matches<P>(&self, pat: P) -> &Self
where
P: Pattern,
{
Self::new(self.as_os_str().trim_start_matches(pat))
}
#[inline]
pub fn utf8_chunks(&self) -> Utf8Chunks<'_> {
Utf8Chunks::new(self.as_os_str())
}
}
impl AsRef<Self> for RawOsStr {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
impl AsRef<OsStr> for RawOsStr {
#[inline]
fn as_ref(&self) -> &OsStr {
self.as_os_str()
}
}
impl AsRef<RawOsStr> for OsStr {
#[inline]
fn as_ref(&self) -> &RawOsStr {
RawOsStr::new(self)
}
}
impl AsRef<RawOsStr> for OsString {
#[inline]
fn as_ref(&self) -> &RawOsStr {
(**self).as_ref()
}
}
impl AsRef<RawOsStr> for str {
#[inline]
fn as_ref(&self) -> &RawOsStr {
RawOsStr::new(self)
}
}
impl AsRef<RawOsStr> for String {
#[inline]
fn as_ref(&self) -> &RawOsStr {
(**self).as_ref()
}
}
impl Default for &RawOsStr {
#[inline]
fn default() -> Self {
RawOsStr::new("")
}
}
impl<'a> From<&'a RawOsStr> for Cow<'a, RawOsStr> {
#[inline]
fn from(value: &'a RawOsStr) -> Self {
Cow::Borrowed(value)
}
}
impl From<Box<str>> for Box<RawOsStr> {
#[inline]
fn from(value: Box<str>) -> Self {
value.into_boxed_bytes().transmute_box()
}
}
impl<Idx> Index<Idx> for RawOsStr
where
Idx: SliceIndex,
{
type Output = Self;
#[inline]
fn index(&self, index: Idx) -> &Self::Output {
Self::new(self.as_os_str().index(index))
}
}
impl ToOwned for RawOsStr {
type Owned = RawOsString;
#[inline]
fn to_owned(&self) -> Self::Owned {
RawOsString(self.0.to_owned())
}
}
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "raw_os_str")))]
pub trait RawOsStrCow<'a>: private::Sealed {
#[must_use]
fn from_os_str(string: Cow<'a, OsStr>) -> Self;
#[must_use]
fn into_os_str(self) -> Cow<'a, OsStr>;
}
impl<'a> RawOsStrCow<'a> for Cow<'a, RawOsStr> {
#[inline]
fn from_os_str(string: Cow<'a, OsStr>) -> Self {
match string {
Cow::Borrowed(string) => Cow::Borrowed(RawOsStr::new(string)),
Cow::Owned(string) => Cow::Owned(RawOsString::new(string)),
}
}
#[inline]
fn into_os_str(self) -> Cow<'a, OsStr> {
match self {
Cow::Borrowed(string) => Cow::Borrowed(string.as_os_str()),
Cow::Owned(string) => Cow::Owned(string.into_os_string()),
}
}
}
#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "raw_os_str")))]
pub struct RawOsString(Vec<u8>);
impl RawOsString {
#[inline]
#[must_use]
pub fn new<S>(string: S) -> Self
where
S: Into<OsString>,
{
Self(string.into().into_encoded_bytes())
}
#[deprecated(since = "7.0.0", note = "use `new` instead")]
#[inline]
#[must_use]
pub fn from_string(string: String) -> Self {
Self(string.into_bytes())
}
#[allow(clippy::missing_safety_doc)]
#[inline]
#[must_use]
pub unsafe fn from_encoded_vec_unchecked(string: Vec<u8>) -> Self {
Self(string)
}
if_conversions! {
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
#[inline]
#[must_use = "method should not be used for validation"]
#[track_caller]
pub fn assert_from_raw_vec(string: Vec<u8>) -> Self {
Self::new(OsString::assert_from_raw_vec(string))
}
}
if_checked_conversions! {
#[cfg_attr(
os_str_bytes_docs_rs,
doc(cfg(feature = "checked_conversions"))
)]
#[inline]
pub fn from_raw_vec(string: Vec<u8>) -> Result<Self> {
OsString::from_raw_vec(string).map(Self::new)
}
}
if_conversions! {
#[deprecated(
since = "6.6.0",
note = "use `assert_from_raw_vec` or
`from_encoded_vec_unchecked` instead",
)]
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
#[inline]
#[must_use]
#[track_caller]
pub unsafe fn from_raw_vec_unchecked(string: Vec<u8>) -> Self {
Self::assert_from_raw_vec(string)
}
}
#[inline]
pub fn clear(&mut self) {
self.0.clear();
}
#[inline]
#[must_use]
pub fn into_box(self) -> Box<RawOsStr> {
self.0.into_boxed_slice().transmute_box()
}
#[inline]
#[must_use]
pub fn into_encoded_vec(self) -> Vec<u8> {
self.0
}
#[inline]
#[must_use]
pub fn into_os_string(self) -> OsString {
unsafe { OsString::from_encoded_bytes_unchecked(self.0) }
}
if_conversions! {
#[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
#[inline]
#[must_use]
pub fn into_raw_vec(self) -> Vec<u8> {
self.into_os_string().into_raw_vec()
}
}
#[inline]
pub fn into_string(self) -> result::Result<String, Self> {
String::from_utf8(self.0).map_err(|x| Self(x.into_bytes()))
}
#[inline]
pub fn shrink_to_fit(&mut self) {
self.0.shrink_to_fit();
}
#[track_caller]
fn check_bound(&self, index: usize) {
ext::check_bound(self.as_os_str(), index);
}
#[inline]
#[must_use]
#[track_caller]
pub fn split_off(&mut self, at: usize) -> Self {
self.check_bound(at);
Self(self.0.split_off(at))
}
#[inline]
#[track_caller]
pub fn truncate(&mut self, new_len: usize) {
self.check_bound(new_len);
self.0.truncate(new_len);
}
}
impl AsRef<OsStr> for RawOsString {
#[inline]
fn as_ref(&self) -> &OsStr {
(**self).as_ref()
}
}
impl AsRef<RawOsStr> for RawOsString {
#[inline]
fn as_ref(&self) -> &RawOsStr {
self
}
}
impl Borrow<RawOsStr> for RawOsString {
#[inline]
fn borrow(&self) -> &RawOsStr {
self
}
}
impl Deref for RawOsString {
type Target = RawOsStr;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { RawOsStr::from_encoded_bytes_unchecked(&self.0) }
}
}
impl From<RawOsString> for Box<RawOsStr> {
#[inline]
fn from(value: RawOsString) -> Self {
value.into_box()
}
}
impl From<Box<RawOsStr>> for RawOsString {
#[inline]
fn from(value: Box<RawOsStr>) -> Self {
Self(value.transmute_box::<[_]>().into_vec())
}
}
impl From<RawOsString> for Cow<'_, RawOsStr> {
#[inline]
fn from(value: RawOsString) -> Self {
Cow::Owned(value)
}
}
impl From<OsString> for RawOsString {
#[inline]
fn from(value: OsString) -> Self {
Self::new(value)
}
}
impl From<RawOsString> for OsString {
#[inline]
fn from(value: RawOsString) -> Self {
value.into_os_string()
}
}
impl From<String> for RawOsString {
#[inline]
fn from(value: String) -> Self {
Self::new(value)
}
}
macro_rules! r#impl {
( $type:ty ) => {
impl Debug for $type {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple(stringify!($type))
.field(&self.as_os_str())
.finish()
}
}
};
}
r#impl!(RawOsStr);
r#impl!(RawOsString);
impl<Idx> Index<Idx> for RawOsString
where
Idx: SliceIndex,
{
type Output = <RawOsStr as Index<Idx>>::Output;
#[inline]
fn index(&self, index: Idx) -> &Self::Output {
&(**self)[index]
}
}
macro_rules! r#impl {
( $type:ty , $other_type:ty ) => {
impl PartialEq<$other_type> for $type {
#[inline]
fn eq(&self, other: &$other_type) -> bool {
let raw: &OsStr = self.as_ref();
let other: &OsStr = other.as_ref();
raw == other
}
}
impl PartialEq<$type> for $other_type {
#[inline]
fn eq(&self, other: &$type) -> bool {
other == self
}
}
};
}
r#impl!(RawOsStr, OsStr);
r#impl!(RawOsStr, OsString);
r#impl!(RawOsStr, RawOsString);
r#impl!(RawOsStr, str);
r#impl!(RawOsStr, String);
r#impl!(&RawOsStr, OsString);
r#impl!(&RawOsStr, RawOsString);
r#impl!(&RawOsStr, String);
r#impl!(RawOsString, OsStr);
r#impl!(RawOsString, &OsStr);
r#impl!(RawOsString, OsString);
r#impl!(RawOsString, str);
r#impl!(RawOsString, &str);
r#impl!(RawOsString, String);