//! This crate allows interacting with the data stored by [`OsStr`] and //! [`OsString`], without resorting to panics or corruption for invalid UTF-8. //! Thus, methods can be used that are already defined on [`[u8]`][slice] and //! [`Vec`]. //! //! Typically, the only way to losslessly construct [`OsStr`] or [`OsString`] //! from a byte sequence is to use `OsStr::new(str::from_utf8(bytes)?)`, which //! requires the bytes to be valid in UTF-8. However, since this crate makes //! conversions directly between the platform encoding and raw bytes, even some //! strings invalid in UTF-8 can be converted. //! //! In most cases, [`RawOsStr`] and [`RawOsString`] should be used. //! [`OsStrBytes`] and [`OsStringBytes`] provide lower-level APIs that are //! easier to misuse. //! //! # Encoding //! //! The encoding of bytes returned or accepted by methods of this crate is //! intentionally left unspecified. It may vary for different platforms, so //! defining it would run contrary to the goal of generic string handling. //! However, the following invariants will always be upheld: //! //! - The encoding will be compatible with UTF-8. In particular, splitting an //! encoded byte sequence by a UTF-8–encoded character always produces //! other valid byte sequences. They can be re-encoded without error using //! [`RawOsString::into_os_string`] and similar methods. //! //! - All characters valid in platform strings are representable. [`OsStr`] and //! [`OsString`] can always be losslessly reconstructed from extracted bytes. //! //! Note that the chosen encoding may not match how Rust stores these strings //! internally, which is undocumented. For instance, the result of calling //! [`OsStr::len`] will not necessarily match the number of bytes this crate //! uses to represent the same string. //! //! Additionally, concatenation may yield unexpected results without a UTF-8 //! separator. If two platform strings need to be concatenated, the only safe //! way to do so is using [`OsString::push`]. This limitation also makes it //! undesirable to use the bytes in interchange. //! //! Since this encoding can change between versions and platforms, it should //! not be used for storage. The standard library provides implementations of //! [`OsStrExt`] and [`OsStringExt`] for various platforms, which should be //! preferred for that use case. //! //! # User Input //! //! Traits in this crate should ideally not be used to convert byte sequences //! that did not originate from [`OsStr`] or a related struct. The encoding //! used by this crate is an implementation detail, so it does not make sense //! to expose it to users. //! //! Crate [bstr] offers some useful alternative methods, such as //! [`ByteSlice::to_os_str`] and [`ByteVec::into_os_string`], that are meant //! for user input. But, they reject some byte sequences used to represent //! valid platform strings, which would be undesirable for reliable path //! handling. They are best used only when accepting unknown input. //! //! This crate is meant to help when you already have an instance of [`OsStr`] //! and need to modify the data in a lossless way. //! //! # Features //! //! These features are optional and can be enabled or disabled in a //! "Cargo.toml" file. //! //! ### Default Features //! //! - **memchr** - //! Changes the implementation to use crate [memchr] for better performance. //! This feature is useless when "raw\_os\_str" is disabled. //! //! For more information, see [`RawOsStr`][memchr complexity]. //! //! - **raw\_os\_str** - //! Provides: //! - [`iter`] //! - [`Pattern`] //! - [`OsStrBytesExt`] //! - [`RawOsStr`] //! - [`RawOsStrCow`] //! - [`RawOsString`] //! //! ### Optional Features //! //! - **checked\_conversions** - //! Provides: //! - [`EncodingError`] //! - [`OsStrBytes::from_raw_bytes`] //! - [`OsStringBytes::from_raw_vec`] //! - [`RawOsStr::cow_from_raw_bytes`] //! - [`RawOsString::from_raw_vec`] //! //! Because this feature should not be used in libraries, the //! "OS_STR_BYTES_CHECKED_CONVERSIONS" environment variable must be defined //! during compilation. //! //! - **conversions** - //! Provides methods that require encoding conversion and may be expensive: //! - [`OsStrBytesExt::ends_with_os`] //! - [`OsStrBytesExt::starts_with_os`] //! - [`RawOsStr::assert_cow_from_raw_bytes`] //! - [`RawOsStr::ends_with_os`] //! - [`RawOsStr::starts_with_os`] //! - [`RawOsStr::to_raw_bytes`] //! - [`RawOsString::assert_from_raw_vec`] //! - [`RawOsString::into_raw_vec`] //! - [`OsStrBytes`] //! - [`OsStringBytes`] //! //! - **print\_bytes** - //! Provides implementations of [`print_bytes::ToBytes`] for [`RawOsStr`] and //! [`RawOsString`]. //! //! - **uniquote** - //! Provides implementations of [`uniquote::Quote`] for [`RawOsStr`] and //! [`RawOsString`]. //! //! ### Nightly Features //! //! These features are unstable, since they rely on unstable Rust features. //! //! - **nightly** - //! Changes the implementation to use the ["os\_str\_bytes" nightly //! feature][feature] and provides: //! - [`RawOsStr::as_encoded_bytes`] //! - [`RawOsStr::as_os_str`] //! - [`RawOsStr::from_encoded_bytes_unchecked`] //! - [`RawOsStr::from_os_str`] //! - [`RawOsString::from_encoded_vec_unchecked`] //! - [`RawOsString::into_encoded_vec`] //! - additional trait implementations for [`RawOsStr`] and [`RawOsString`] //! //! When applicable, a "Nightly Notes" section will be added to documentation //! descriptions, indicating differences when this feature is enabled. //! However, it will not cause any breaking changes. //! //! This feature will cause memory leaks for some newly deprecated methods. //! Therefore, it is not recommended to use this feature until the next major //! version, when those methods will be removed. However, it can be used to //! prepare for upgrading and determine impact of the new feature. //! //! Because this feature should not be used in libraries, the //! "OS_STR_BYTES_NIGHTLY" environment variable must be defined during //! compilation. //! //! # Implementation //! //! Some methods return [`Cow`] to account for platform differences. However, //! no guarantee is made that the same variant of that enum will always be //! returned for the same platform. Whichever can be constructed most //! efficiently will be returned. //! //! All traits are [sealed], meaning that they can only be implemented by this //! crate. Otherwise, backward compatibility would be more difficult to //! maintain for new features. //! //! # Complexity //! //! Conversion method complexities will vary based on what functionality is //! available for the platform. At worst, they will all be linear, but some can //! take constant time. For example, [`RawOsString::into_os_string`] might be //! able to reuse its allocation. //! //! # Examples //! //! ``` //! # use std::io; //! # //! # #[cfg(feature = "raw_os_str")] //! # { //! # #[cfg(any())] //! use std::env; //! use std::fs; //! //! use os_str_bytes::RawOsStr; //! //! # mod env { //! # use std::env; //! # use std::ffi::OsString; //! # //! # pub fn args_os() -> impl Iterator { //! # let mut file = env::temp_dir(); //! # file.push("os_str_bytes\u{E9}.txt"); //! # return vec![OsString::new(), file.into_os_string()].into_iter(); //! # } //! # } //! # //! for file in env::args_os().skip(1) { //! if !RawOsStr::new(&file).starts_with('-') { //! let string = "Hello, world!"; //! fs::write(&file, string)?; //! assert_eq!(string, fs::read_to_string(file)?); //! } //! } //! # } //! # //! # Ok::<_, io::Error>(()) //! ``` //! //! [bstr]: https://crates.io/crates/bstr //! [`ByteSlice::to_os_str`]: https://docs.rs/bstr/0.2.12/bstr/trait.ByteSlice.html#method.to_os_str //! [`ByteVec::into_os_string`]: https://docs.rs/bstr/0.2.12/bstr/trait.ByteVec.html#method.into_os_string //! [feature]: https://doc.rust-lang.org/unstable-book/library-features/os-str-bytes.html //! [memchr complexity]: RawOsStr#complexity //! [memchr]: https://crates.io/crates/memchr //! [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt //! [`OsStringExt`]: ::std::os::unix::ffi::OsStringExt //! [print\_bytes]: https://crates.io/crates/print_bytes //! [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed #![cfg_attr(not(feature = "checked_conversions"), allow(deprecated))] // Only require a nightly compiler when building documentation for docs.rs. // This is a private option that should not be used. // https://github.com/rust-lang/docs.rs/issues/147#issuecomment-389544407 // https://github.com/dylni/os_str_bytes/issues/2 #![cfg_attr(os_str_bytes_docs_rs, feature(doc_cfg))] // Nightly is also currently required for the SGX platform. #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), feature(sgx_platform) )] #![warn(unused_results)] use std::borrow::Cow; use std::error::Error; use std::ffi::OsStr; use std::ffi::OsString; use std::fmt; use std::fmt::Display; use std::fmt::Formatter; use std::path::Path; use std::path::PathBuf; use std::result; macro_rules! if_checked_conversions { ( $($item:item)+ ) => { $( #[cfg(feature = "checked_conversions")] $item )+ }; } #[cfg(not(os_str_bytes_docs_rs))] if_checked_conversions! { const _: &str = env!( "OS_STR_BYTES_CHECKED_CONVERSIONS", "The 'OS_STR_BYTES_CHECKED_CONVERSIONS' environment variable must be \ defined to use the 'checked_conversions' feature.", ); } macro_rules! if_nightly { ( $($item:item)+ ) => { $( #[cfg(feature = "nightly")] $item )+ }; } #[cfg(not(os_str_bytes_docs_rs))] if_nightly! { const _: &str = env!( "OS_STR_BYTES_NIGHTLY", "The 'OS_STR_BYTES_NIGHTLY' environment variable must be defined to \ use the 'nightly' feature.", ); } #[rustfmt::skip] macro_rules! deprecated_checked_conversion { ( $message:expr , $item:item ) => { #[cfg_attr( not(feature = "checked_conversions"), deprecated = $message )] $item }; } #[rustfmt::skip] macro_rules! deprecated_conversions { ( $($item:item)+ ) => { $( #[cfg_attr( not(feature = "conversions"), deprecated = "enable the 'conversions' feature" )] $item )+ }; } macro_rules! if_raw_str { ( $($item:item)+ ) => { $( #[cfg(feature = "raw_os_str")] $item )+ }; } if_raw_str! { macro_rules! if_not_nightly { ( $($item:item)+ ) => { $( #[cfg(not(feature = "nightly"))] $item )+ }; } macro_rules! if_nightly_return { ( $nightly_value:block $($not_nightly_token:tt)* ) => { #[cfg(feature = "nightly")] return $nightly_value; #[cfg(not(feature = "nightly"))] { $($not_nightly_token)* } }; } } if_raw_str! { if_nightly! { macro_rules! if_conversions { ( $($item:item)+ ) => { $( #[cfg(feature = "conversions")] $item )+ }; } } } macro_rules! expect_encoded { ( $result:expr ) => { $result.expect("invalid raw bytes") }; } #[cfg_attr( all(target_family = "wasm", target_os = "unknown"), path = "wasm/mod.rs" )] #[cfg_attr(windows, path = "windows/mod.rs")] #[cfg_attr( not(any(all(target_family = "wasm", target_os = "unknown"), windows)), path = "common/mod.rs" )] mod imp; #[cfg(any( all( feature = "raw_os_str", any( feature = "nightly", all(target_family = "wasm", target_os = "unknown"), ), ), windows, ))] mod util; if_raw_str! { pub mod iter; mod pattern; pub use pattern::Pattern; mod raw_str; pub use raw_str::RawOsStr; pub use raw_str::RawOsStrCow; pub use raw_str::RawOsString; } deprecated_checked_conversion! { "use `OsStrBytes::assert_from_raw_bytes` or \ `OsStringBytes::assert_from_raw_vec` instead, or enable the \ 'checked_conversions' feature", /// The error that occurs when a byte sequence is not representable in the /// platform encoding. /// /// [`Result::unwrap`] should almost always be called on results containing /// this error. It should be known whether or not byte sequences are /// properly encoded for the platform, since [the module-level /// documentation][encoding] discourages using encoded bytes in /// interchange. Results are returned primarily to make panicking behavior /// explicit. /// /// On Unix, this error is never returned, but [`OsStrExt`] or /// [`OsStringExt`] should be used instead if that needs to be guaranteed. /// /// [encoding]: self#encoding /// [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt /// [`OsStringExt`]: ::std::os::unix::ffi::OsStringExt /// [`Result::unwrap`]: ::std::result::Result::unwrap #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr( os_str_bytes_docs_rs, doc(cfg(feature = "checked_conversions")) )] pub struct EncodingError(imp::EncodingError); } impl Display for EncodingError { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl Error for EncodingError {} type Result = result::Result; fn from_raw_bytes<'a, S>(string: S) -> imp::Result> where S: Into>, { match string.into() { Cow::Borrowed(string) => imp::os_str_from_bytes(string), Cow::Owned(string) => imp::os_string_from_vec(string).map(Cow::Owned), } } fn cow_os_str_into_path(string: Cow<'_, OsStr>) -> Cow<'_, Path> { match string { Cow::Borrowed(string) => Cow::Borrowed(Path::new(string)), Cow::Owned(string) => Cow::Owned(string.into()), } } deprecated_conversions! { /// A platform agnostic variant of [`OsStrExt`]. /// /// For more information, see [the module-level documentation][module]. /// /// [module]: self /// [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))] pub trait OsStrBytes: private::Sealed + ToOwned { /// Converts a byte string into an equivalent platform-native string. /// /// # Panics /// /// Panics if the string is not valid for the [unspecified encoding] /// used by this crate. /// /// # Examples /// /// ``` /// use std::env; /// use std::ffi::OsStr; /// # use std::io; /// /// use os_str_bytes::OsStrBytes; /// /// let os_string = env::current_exe()?; /// let os_bytes = os_string.to_raw_bytes(); /// assert_eq!(os_string, OsStr::assert_from_raw_bytes(os_bytes)); /// # /// # Ok::<_, io::Error>(()) /// ``` /// /// [unspecified encoding]: self#encoding #[must_use = "method should not be used for validation"] #[track_caller] fn assert_from_raw_bytes<'a, S>(string: S) -> Cow<'a, Self> where S: Into>; deprecated_checked_conversion! { "use `assert_from_raw_bytes` instead, or enable the \ 'checked_conversions' feature", /// Converts a byte string into an equivalent platform-native /// string. /// /// [`assert_from_raw_bytes`] should almost always be used instead. /// For more information, see [`EncodingError`]. /// /// # Errors /// /// See documentation for [`EncodingError`]. /// /// # Examples /// /// ``` /// use std::env; /// use std::ffi::OsStr; /// # use std::io; /// /// use os_str_bytes::OsStrBytes; /// /// let os_string = env::current_exe()?; /// let os_bytes = os_string.to_raw_bytes(); /// assert_eq!(os_string, OsStr::from_raw_bytes(os_bytes).unwrap()); /// # /// # Ok::<_, io::Error>(()) /// ``` /// /// [`assert_from_raw_bytes`]: Self::assert_from_raw_bytes #[cfg_attr( os_str_bytes_docs_rs, doc(cfg(feature = "checked_conversions")) )] fn from_raw_bytes<'a, S>(string: S) -> Result> where S: Into>; } /// Converts a platform-native string into an equivalent byte string. /// /// The returned string will use an [unspecified encoding]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytes; /// /// let string = "foobar"; /// let os_string = OsStr::new(string); /// assert_eq!(string.as_bytes(), &*os_string.to_raw_bytes()); /// ``` /// /// [unspecified encoding]: self#encoding #[must_use] fn to_raw_bytes(&self) -> Cow<'_, [u8]>; } #[cfg_attr(not(feature = "conversions"), allow(useless_deprecated))] impl OsStrBytes for OsStr { #[inline] fn assert_from_raw_bytes<'a, S>(string: S) -> Cow<'a, Self> where S: Into>, { expect_encoded!(from_raw_bytes(string)) } #[inline] fn from_raw_bytes<'a, S>(string: S) -> Result> where S: Into>, { from_raw_bytes(string).map_err(EncodingError) } #[inline] fn to_raw_bytes(&self) -> Cow<'_, [u8]> { imp::os_str_to_bytes(self) } } #[cfg_attr(not(feature = "conversions"), allow(useless_deprecated))] impl OsStrBytes for Path { #[inline] fn assert_from_raw_bytes<'a, S>(string: S) -> Cow<'a, Self> where S: Into>, { cow_os_str_into_path(OsStr::assert_from_raw_bytes(string)) } #[inline] fn from_raw_bytes<'a, S>(string: S) -> Result> where S: Into>, { OsStr::from_raw_bytes(string).map(cow_os_str_into_path) } #[inline] fn to_raw_bytes(&self) -> Cow<'_, [u8]> { self.as_os_str().to_raw_bytes() } } } if_raw_str! { if_nightly! { /// An extension trait providing methods from [`RawOsStr`]. #[cfg_attr( os_str_bytes_docs_rs, doc(cfg(all(feature = "nightly", feature = "raw_os_str"))) )] pub trait OsStrBytesExt: OsStrBytes { /// Equivalent to [`str::contains`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert!(os_string.contains("oo")); /// assert!(!os_string.contains("of")); /// ``` #[must_use] fn contains

(&self, pat: P) -> bool where P: Pattern; /// Equivalent to [`str::ends_with`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert!(os_string.ends_with("bar")); /// assert!(!os_string.ends_with("foo")); /// ``` #[must_use] fn ends_with

(&self, pat: P) -> bool where P: Pattern; if_conversions! { /// Equivalent to [`str::ends_with`] but accepts this type for /// the pattern. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert!(os_string.ends_with_os(OsStr::new("bar"))); /// assert!(!os_string.ends_with_os(OsStr::new("foo"))); /// ``` #[cfg_attr( os_str_bytes_docs_rs, doc(cfg(feature = "conversions")) )] #[must_use] fn ends_with_os(&self, pat: &Self) -> bool; } /// Equivalent to [`str::find`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert_eq!(Some(1), os_string.find("o")); /// assert_eq!(None, os_string.find("of")); /// ``` #[must_use] fn find

(&self, pat: P) -> Option where P: Pattern; /// Equivalent to [`str::rfind`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert_eq!(Some(2), os_string.rfind("o")); /// assert_eq!(None, os_string.rfind("of")); /// ``` #[must_use] fn rfind

(&self, pat: P) -> Option where P: Pattern; /// Equivalent to [`str::rsplit_once`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert_eq!( /// Some((OsStr::new("fo"), OsStr::new("bar"))), /// os_string.rsplit_once("o"), /// ); /// assert_eq!(None, os_string.rsplit_once("of")); /// ``` #[must_use] fn rsplit_once

(&self, pat: P) -> Option<(&Self, &Self)> where P: Pattern; /// Equivalent to [`str::split_at`]. /// /// # Panics /// /// Panics if the index is not a [valid boundary]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert_eq!( /// ((OsStr::new("fo"), OsStr::new("obar"))), /// os_string.split_at(2), /// ); /// ``` /// /// [valid boundary]: RawOsStr#indices #[must_use] #[track_caller] fn split_at(&self, mid: usize) -> (&Self, &Self); /// Equivalent to [`str::split_once`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert_eq!( /// Some((OsStr::new("f"), OsStr::new("obar"))), /// os_string.split_once("o"), /// ); /// assert_eq!(None, os_string.split_once("of")); /// ``` #[must_use] fn split_once

(&self, pat: P) -> Option<(&Self, &Self)> where P: Pattern; /// Equivalent to [`str::starts_with`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert!(os_string.starts_with("foo")); /// assert!(!os_string.starts_with("bar")); /// ``` #[must_use] fn starts_with

(&self, pat: P) -> bool where P: Pattern; if_conversions! { /// Equivalent to [`str::starts_with`] but accepts this type /// for the pattern. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("foobar"); /// assert!(os_string.starts_with_os(OsStr::new("foo"))); /// assert!(!os_string.starts_with_os(OsStr::new("bar"))); /// ``` #[cfg_attr( os_str_bytes_docs_rs, doc(cfg(feature = "conversions")) )] #[must_use] fn starts_with_os(&self, pat: &Self) -> bool; } /// Equivalent to [`str::strip_prefix`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("111foo1bar111"); /// assert_eq!( /// Some(OsStr::new("11foo1bar111")), /// os_string.strip_prefix("1"), /// ); /// assert_eq!(None, os_string.strip_prefix("o")); /// ``` #[must_use] fn strip_prefix

(&self, pat: P) -> Option<&Self> where P: Pattern; /// Equivalent to [`str::strip_suffix`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("111foo1bar111"); /// assert_eq!( /// Some(OsStr::new("111foo1bar11")), /// os_string.strip_suffix("1"), /// ); /// assert_eq!(None, os_string.strip_suffix("o")); /// ``` #[must_use] fn strip_suffix

(&self, pat: P) -> Option<&Self> where P: Pattern; /// Equivalent to [`str::trim_end_matches`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("111foo1bar111"); /// assert_eq!("111foo1bar", os_string.trim_end_matches("1")); /// assert_eq!("111foo1bar111", os_string.trim_end_matches("o")); /// ``` #[must_use] fn trim_end_matches

(&self, pat: P) -> &Self where P: Pattern; /// Equivalent to [`str::trim_matches`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("111foo1bar111"); /// assert_eq!("foo1bar", os_string.trim_matches("1")); /// assert_eq!("111foo1bar111", os_string.trim_matches("o")); /// ``` #[must_use] fn trim_matches

(&self, pat: P) -> &Self where P: Pattern; /// Equivalent to [`str::trim_start_matches`]. /// /// # Examples /// /// ``` /// use std::ffi::OsStr; /// /// use os_str_bytes::OsStrBytesExt; /// /// let os_string = OsStr::new("111foo1bar111"); /// assert_eq!("foo1bar111", os_string.trim_start_matches("1")); /// assert_eq!("111foo1bar111", os_string.trim_start_matches("o")); /// ``` #[must_use] fn trim_start_matches

(&self, pat: P) -> &Self where P: Pattern; } impl OsStrBytesExt for OsStr { #[inline] fn contains

(&self, pat: P) -> bool where P: Pattern, { RawOsStr::from_os_str(self).contains(pat) } #[inline] fn ends_with

(&self, pat: P) -> bool where P: Pattern, { RawOsStr::from_os_str(self).ends_with(pat) } if_conversions! { #[inline] fn ends_with_os(&self, pat: &Self) -> bool { RawOsStr::from_os_str(self) .ends_with_os(RawOsStr::from_os_str(pat)) } } #[inline] fn find

(&self, pat: P) -> Option where P: Pattern, { RawOsStr::from_os_str(self).find(pat) } #[inline] fn rfind

(&self, pat: P) -> Option where P: Pattern, { RawOsStr::from_os_str(self).rfind(pat) } #[inline] fn rsplit_once

(&self, pat: P) -> Option<(&Self, &Self)> where P: Pattern, { RawOsStr::from_os_str(self) .rsplit_once(pat) .map(|(prefix, suffix)| { (prefix.as_os_str(), suffix.as_os_str()) }) } #[inline] fn split_at(&self, mid: usize) -> (&Self, &Self) { let (prefix, suffix) = RawOsStr::from_os_str(self).split_at(mid); (prefix.as_os_str(), suffix.as_os_str()) } #[inline] fn split_once

(&self, pat: P) -> Option<(&Self, &Self)> where P: Pattern, { RawOsStr::from_os_str(self) .split_once(pat) .map(|(prefix, suffix)| { (prefix.as_os_str(), suffix.as_os_str()) }) } #[inline] fn starts_with

(&self, pat: P) -> bool where P: Pattern, { RawOsStr::from_os_str(self).starts_with(pat) } if_conversions! { #[inline] fn starts_with_os(&self, pat: &Self) -> bool { RawOsStr::from_os_str(self) .starts_with_os(RawOsStr::from_os_str(pat)) } } #[inline] fn strip_prefix

(&self, pat: P) -> Option<&Self> where P: Pattern, { RawOsStr::from_os_str(self) .strip_prefix(pat) .map(RawOsStr::as_os_str) } #[inline] fn strip_suffix

(&self, pat: P) -> Option<&Self> where P: Pattern, { RawOsStr::from_os_str(self) .strip_suffix(pat) .map(RawOsStr::as_os_str) } #[inline] fn trim_end_matches

(&self, pat: P) -> &Self where P: Pattern, { RawOsStr::from_os_str(self).trim_end_matches(pat).as_os_str() } #[inline] fn trim_matches

(&self, pat: P) -> &Self where P: Pattern, { RawOsStr::from_os_str(self).trim_matches(pat).as_os_str() } #[inline] fn trim_start_matches

(&self, pat: P) -> &Self where P: Pattern, { RawOsStr::from_os_str(self).trim_start_matches(pat).as_os_str() } } } } deprecated_conversions! { /// A platform agnostic variant of [`OsStringExt`]. /// /// For more information, see [the module-level documentation][module]. /// /// [module]: self /// [`OsStringExt`]: ::std::os::unix::ffi::OsStringExt #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(any(feature = "conversions"))))] pub trait OsStringBytes: private::Sealed + Sized { /// Converts a byte string into an equivalent platform-native string. /// /// # Panics /// /// Panics if the string is not valid for the [unspecified encoding] /// used by this crate. /// /// # Examples /// /// ``` /// use std::env; /// use std::ffi::OsString; /// # use std::io; /// /// use os_str_bytes::OsStringBytes; /// /// let os_string = env::current_exe()?; /// let os_bytes = os_string.clone().into_raw_vec(); /// assert_eq!(os_string, OsString::assert_from_raw_vec(os_bytes)); /// # /// # Ok::<_, io::Error>(()) /// ``` /// /// [unspecified encoding]: self#encoding #[must_use = "method should not be used for validation"] #[track_caller] fn assert_from_raw_vec(string: Vec) -> Self; deprecated_checked_conversion! { "use `assert_from_raw_vec` instead, or enable the \ 'checked_conversions' feature", /// Converts a byte string into an equivalent platform-native /// string. /// /// [`assert_from_raw_vec`] should almost always be used instead. /// For more information, see [`EncodingError`]. /// /// # Errors /// /// See documentation for [`EncodingError`]. /// /// # Examples /// /// ``` /// use std::env; /// use std::ffi::OsString; /// # use std::io; /// /// use os_str_bytes::OsStringBytes; /// /// let os_string = env::current_exe()?; /// let os_bytes = os_string.clone().into_raw_vec(); /// assert_eq!( /// os_string, /// OsString::from_raw_vec(os_bytes).unwrap(), /// ); /// # /// # Ok::<_, io::Error>(()) /// ``` /// /// [`assert_from_raw_vec`]: Self::assert_from_raw_vec #[cfg_attr( os_str_bytes_docs_rs, doc(cfg(feature = "checked_conversions")) )] fn from_raw_vec(string: Vec) -> Result; } /// Converts a platform-native string into an equivalent byte string. /// /// The returned string will use an [unspecified encoding]. /// /// # Examples /// /// ``` /// use std::ffi::OsString; /// /// use os_str_bytes::OsStringBytes; /// /// let string = "foobar".to_owned(); /// let os_string: OsString = string.clone().into(); /// assert_eq!(string.into_bytes(), os_string.into_raw_vec()); /// ``` /// /// [unspecified encoding]: self#encoding #[must_use] fn into_raw_vec(self) -> Vec; } #[cfg_attr(not(feature = "conversions"), allow(useless_deprecated))] impl OsStringBytes for OsString { #[inline] fn assert_from_raw_vec(string: Vec) -> Self { expect_encoded!(imp::os_string_from_vec(string)) } #[inline] fn from_raw_vec(string: Vec) -> Result { imp::os_string_from_vec(string).map_err(EncodingError) } #[inline] fn into_raw_vec(self) -> Vec { imp::os_string_into_vec(self) } } #[cfg_attr(not(feature = "conversions"), allow(useless_deprecated))] impl OsStringBytes for PathBuf { #[inline] fn assert_from_raw_vec(string: Vec) -> Self { OsString::assert_from_raw_vec(string).into() } #[inline] fn from_raw_vec(string: Vec) -> Result { OsString::from_raw_vec(string).map(Into::into) } #[inline] fn into_raw_vec(self) -> Vec { self.into_os_string().into_raw_vec() } } } mod private { use std::ffi::OsStr; use std::ffi::OsString; use std::path::Path; use std::path::PathBuf; if_raw_str! { use std::borrow::Cow; use super::RawOsStr; } pub trait Sealed {} impl Sealed for char {} impl Sealed for OsStr {} impl Sealed for OsString {} impl Sealed for Path {} impl Sealed for PathBuf {} impl Sealed for &str {} impl Sealed for &String {} if_raw_str! { impl Sealed for Cow<'_, RawOsStr> {} } }