os_str_bytes/
lib.rs

1//! This crate allows interacting with the data stored by [`OsStr`] and
2//! [`OsString`], without resorting to panics or corruption for invalid UTF-8.
3//! Thus, methods can be used that are already defined on [`[u8]`][slice] and
4//! [`Vec<u8>`].
5//!
6//! Typically, the only way to losslessly construct [`OsStr`] or [`OsString`]
7//! from a byte sequence is to use `OsStr::new(str::from_utf8(bytes)?)`, which
8//! requires the bytes to be valid in UTF-8. However, since this crate makes
9//! conversions directly between the platform encoding and raw bytes, even some
10//! strings invalid in UTF-8 can be converted.
11//!
12//! In most cases, [`RawOsStr`] and [`RawOsString`] should be used.
13//! [`OsStrBytes`] and [`OsStringBytes`] provide lower-level APIs that are
14//! easier to misuse.
15//!
16//! # Encoding
17//!
18//! The encoding of bytes returned or accepted by methods of this crate is
19//! intentionally left unspecified. It may vary for different platforms, so
20//! defining it would run contrary to the goal of generic string handling.
21//! However, the following invariants will always be upheld:
22//!
23//! - The encoding will be compatible with UTF-8. In particular, splitting an
24//!   encoded byte sequence by a UTF-8&ndash;encoded character always produces
25//!   other valid byte sequences. They can be re-encoded without error using
26//!   [`RawOsString::into_os_string`] and similar methods.
27//!
28//! - All characters valid in platform strings are representable. [`OsStr`] and
29//!   [`OsString`] can always be losslessly reconstructed from extracted bytes.
30//!
31//! Note that the chosen encoding may not match how Rust stores these strings
32//! internally, which is undocumented. For instance, the result of calling
33//! [`OsStr::len`] will not necessarily match the number of bytes this crate
34//! uses to represent the same string.
35//!
36//! Additionally, concatenation may yield unexpected results without a UTF-8
37//! separator. If two platform strings need to be concatenated, the only safe
38//! way to do so is using [`OsString::push`]. This limitation also makes it
39//! undesirable to use the bytes in interchange.
40//!
41//! Since this encoding can change between versions and platforms, it should
42//! not be used for storage. The standard library provides implementations of
43//! [`OsStrExt`] and [`OsStringExt`] for various platforms, which should be
44//! preferred for that use case.
45//!
46//! # User Input
47//!
48//! Traits in this crate should ideally not be used to convert byte sequences
49//! that did not originate from [`OsStr`] or a related struct. The encoding
50//! used by this crate is an implementation detail, so it does not make sense
51//! to expose it to users.
52//!
53//! Crate [bstr] offers some useful alternative methods, such as
54//! [`ByteSlice::to_os_str`] and [`ByteVec::into_os_string`], that are meant
55//! for user input. But, they reject some byte sequences used to represent
56//! valid platform strings, which would be undesirable for reliable path
57//! handling. They are best used only when accepting unknown input.
58//!
59//! This crate is meant to help when you already have an instance of [`OsStr`]
60//! and need to modify the data in a lossless way.
61//!
62//! # Features
63//!
64//! These features are optional and can be enabled or disabled in a
65//! "Cargo.toml" file.
66//!
67//! ### Default Features
68//!
69//! - **memchr** -
70//!   Changes the implementation to use crate [memchr] for better performance.
71//!   This feature is useless when "raw\_os\_str" is disabled.
72//!
73//!   For more information, see [`RawOsStr`][memchr complexity].
74//!
75//! - **raw\_os\_str** -
76//!   Provides:
77//!   - [`iter`]
78//!   - [`Pattern`]
79//!   - [`OsStrBytesExt`]
80//!   - [`RawOsStr`]
81//!   - [`RawOsStrCow`]
82//!   - [`RawOsString`]
83//!
84//! ### Optional Features
85//!
86//! - **checked\_conversions** -
87//!   Provides:
88//!   - [`EncodingError`]
89//!   - [`OsStrBytes::from_raw_bytes`]
90//!   - [`OsStringBytes::from_raw_vec`]
91//!   - [`RawOsStr::cow_from_raw_bytes`]
92//!   - [`RawOsString::from_raw_vec`]
93//!
94//!   Because this feature should not be used in libraries, the
95//!   "OS_STR_BYTES_CHECKED_CONVERSIONS" environment variable must be defined
96//!   during compilation.
97//!
98//! - **conversions** -
99//!   Provides methods that require encoding conversion and may be expensive:
100//!   - [`OsStrBytesExt::ends_with_os`]
101//!   - [`OsStrBytesExt::starts_with_os`]
102//!   - [`RawOsStr::assert_cow_from_raw_bytes`]
103//!   - [`RawOsStr::ends_with_os`]
104//!   - [`RawOsStr::starts_with_os`]
105//!   - [`RawOsStr::to_raw_bytes`]
106//!   - [`RawOsString::assert_from_raw_vec`]
107//!   - [`RawOsString::into_raw_vec`]
108//!   - [`OsStrBytes`]
109//!   - [`OsStringBytes`]
110//!
111//! - **print\_bytes** -
112//!   Provides implementations of [`print_bytes::ToBytes`] for [`RawOsStr`] and
113//!   [`RawOsString`].
114//!
115//! - **uniquote** -
116//!   Provides implementations of [`uniquote::Quote`] for [`RawOsStr`] and
117//!   [`RawOsString`].
118//!
119//! ### Nightly Features
120//!
121//! These features are unstable, since they rely on unstable Rust features.
122//!
123//! - **nightly** -
124//!   Changes the implementation to use the ["os\_str\_bytes" nightly
125//!   feature][feature] and provides:
126//!   - [`RawOsStr::as_encoded_bytes`]
127//!   - [`RawOsStr::as_os_str`]
128//!   - [`RawOsStr::from_encoded_bytes_unchecked`]
129//!   - [`RawOsStr::from_os_str`]
130//!   - [`RawOsString::from_encoded_vec_unchecked`]
131//!   - [`RawOsString::into_encoded_vec`]
132//!   - additional trait implementations for [`RawOsStr`] and [`RawOsString`]
133//!
134//!   When applicable, a "Nightly Notes" section will be added to documentation
135//!   descriptions, indicating differences when this feature is enabled.
136//!   However, it will not cause any breaking changes.
137//!
138//!   This feature will cause memory leaks for some newly deprecated methods.
139//!   Therefore, it is not recommended to use this feature until the next major
140//!   version, when those methods will be removed. However, it can be used to
141//!   prepare for upgrading and determine impact of the new feature.
142//!
143//!   Because this feature should not be used in libraries, the
144//!   "OS_STR_BYTES_NIGHTLY" environment variable must be defined during
145//!   compilation.
146//!
147//! # Implementation
148//!
149//! Some methods return [`Cow`] to account for platform differences. However,
150//! no guarantee is made that the same variant of that enum will always be
151//! returned for the same platform. Whichever can be constructed most
152//! efficiently will be returned.
153//!
154//! All traits are [sealed], meaning that they can only be implemented by this
155//! crate. Otherwise, backward compatibility would be more difficult to
156//! maintain for new features.
157//!
158//! # Complexity
159//!
160//! Conversion method complexities will vary based on what functionality is
161//! available for the platform. At worst, they will all be linear, but some can
162//! take constant time. For example, [`RawOsString::into_os_string`] might be
163//! able to reuse its allocation.
164//!
165//! # Examples
166//!
167//! ```
168//! # use std::io;
169//! #
170//! # #[cfg(feature = "raw_os_str")]
171//! # {
172//! # #[cfg(any())]
173//! use std::env;
174//! use std::fs;
175//!
176//! use os_str_bytes::RawOsStr;
177//!
178//! # mod env {
179//! #   use std::env;
180//! #   use std::ffi::OsString;
181//! #
182//! #   pub fn args_os() -> impl Iterator<Item = OsString> {
183//! #       let mut file = env::temp_dir();
184//! #       file.push("os_str_bytes\u{E9}.txt");
185//! #       return vec![OsString::new(), file.into_os_string()].into_iter();
186//! #   }
187//! # }
188//! #
189//! for file in env::args_os().skip(1) {
190//!     if !RawOsStr::new(&file).starts_with('-') {
191//!         let string = "Hello, world!";
192//!         fs::write(&file, string)?;
193//!         assert_eq!(string, fs::read_to_string(file)?);
194//!     }
195//! }
196//! # }
197//! #
198//! # Ok::<_, io::Error>(())
199//! ```
200//!
201//! [bstr]: https://crates.io/crates/bstr
202//! [`ByteSlice::to_os_str`]: https://docs.rs/bstr/0.2.12/bstr/trait.ByteSlice.html#method.to_os_str
203//! [`ByteVec::into_os_string`]: https://docs.rs/bstr/0.2.12/bstr/trait.ByteVec.html#method.into_os_string
204//! [feature]: https://doc.rust-lang.org/unstable-book/library-features/os-str-bytes.html
205//! [memchr complexity]: RawOsStr#complexity
206//! [memchr]: https://crates.io/crates/memchr
207//! [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt
208//! [`OsStringExt`]: ::std::os::unix::ffi::OsStringExt
209//! [print\_bytes]: https://crates.io/crates/print_bytes
210//! [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed
211
212#![cfg_attr(not(feature = "checked_conversions"), allow(deprecated))]
213// Only require a nightly compiler when building documentation for docs.rs.
214// This is a private option that should not be used.
215// https://github.com/rust-lang/docs.rs/issues/147#issuecomment-389544407
216// https://github.com/dylni/os_str_bytes/issues/2
217#![cfg_attr(os_str_bytes_docs_rs, feature(doc_cfg))]
218// Nightly is also currently required for the SGX platform.
219#![cfg_attr(
220    all(target_vendor = "fortanix", target_env = "sgx"),
221    feature(sgx_platform)
222)]
223#![warn(unused_results)]
224
225use std::borrow::Cow;
226use std::error::Error;
227use std::ffi::OsStr;
228use std::ffi::OsString;
229use std::fmt;
230use std::fmt::Display;
231use std::fmt::Formatter;
232use std::path::Path;
233use std::path::PathBuf;
234use std::result;
235
236macro_rules! if_checked_conversions {
237    ( $($item:item)+ ) => {
238        $(
239            #[cfg(feature = "checked_conversions")]
240            $item
241        )+
242    };
243}
244
245#[cfg(not(os_str_bytes_docs_rs))]
246if_checked_conversions! {
247    const _: &str = env!(
248        "OS_STR_BYTES_CHECKED_CONVERSIONS",
249        "The 'OS_STR_BYTES_CHECKED_CONVERSIONS' environment variable must be \
250         defined to use the 'checked_conversions' feature.",
251    );
252}
253
254macro_rules! if_nightly {
255    ( $($item:item)+ ) => {
256        $(
257            #[cfg(feature = "nightly")]
258            $item
259        )+
260    };
261}
262
263#[cfg(not(os_str_bytes_docs_rs))]
264if_nightly! {
265    const _: &str = env!(
266        "OS_STR_BYTES_NIGHTLY",
267        "The 'OS_STR_BYTES_NIGHTLY' environment variable must be defined to \
268         use the 'nightly' feature.",
269    );
270}
271
272#[rustfmt::skip]
273macro_rules! deprecated_checked_conversion {
274    ( $message:expr , $item:item ) => {
275        #[cfg_attr(
276            not(feature = "checked_conversions"),
277            deprecated = $message
278        )]
279        $item
280    };
281}
282
283#[rustfmt::skip]
284macro_rules! deprecated_conversions {
285    ( $($item:item)+ ) => {
286        $(
287            #[cfg_attr(
288                not(feature = "conversions"),
289                deprecated = "enable the 'conversions' feature"
290            )]
291            $item
292        )+
293    };
294}
295
296macro_rules! if_raw_str {
297    ( $($item:item)+ ) => {
298        $(
299            #[cfg(feature = "raw_os_str")]
300            $item
301        )+
302    };
303}
304
305if_raw_str! {
306    macro_rules! if_not_nightly {
307        ( $($item:item)+ ) => {
308            $(
309                #[cfg(not(feature = "nightly"))]
310                $item
311            )+
312        };
313    }
314
315    macro_rules! if_nightly_return {
316        ( $nightly_value:block $($not_nightly_token:tt)* ) => {
317            #[cfg(feature = "nightly")]
318            return $nightly_value;
319            #[cfg(not(feature = "nightly"))]
320            {
321                $($not_nightly_token)*
322            }
323        };
324    }
325}
326
327if_raw_str! {
328    if_nightly! {
329        macro_rules! if_conversions {
330            ( $($item:item)+ ) => {
331                $(
332                    #[cfg(feature = "conversions")]
333                    $item
334                )+
335            };
336        }
337    }
338}
339
340macro_rules! expect_encoded {
341    ( $result:expr ) => {
342        $result.expect("invalid raw bytes")
343    };
344}
345
346#[cfg_attr(
347    all(target_family = "wasm", target_os = "unknown"),
348    path = "wasm/mod.rs"
349)]
350#[cfg_attr(windows, path = "windows/mod.rs")]
351#[cfg_attr(
352    not(any(all(target_family = "wasm", target_os = "unknown"), windows)),
353    path = "common/mod.rs"
354)]
355mod imp;
356
357#[cfg(any(
358    all(
359        feature = "raw_os_str",
360        any(
361            feature = "nightly",
362            all(target_family = "wasm", target_os = "unknown"),
363        ),
364    ),
365    windows,
366))]
367mod util;
368
369if_raw_str! {
370    pub mod iter;
371
372    mod pattern;
373    pub use pattern::Pattern;
374
375    mod raw_str;
376    pub use raw_str::RawOsStr;
377    pub use raw_str::RawOsStrCow;
378    pub use raw_str::RawOsString;
379}
380
381deprecated_checked_conversion! {
382    "use `OsStrBytes::assert_from_raw_bytes` or \
383     `OsStringBytes::assert_from_raw_vec` instead, or enable the \
384     'checked_conversions' feature",
385    /// The error that occurs when a byte sequence is not representable in the
386    /// platform encoding.
387    ///
388    /// [`Result::unwrap`] should almost always be called on results containing
389    /// this error. It should be known whether or not byte sequences are
390    /// properly encoded for the platform, since [the module-level
391    /// documentation][encoding] discourages using encoded bytes in
392    /// interchange. Results are returned primarily to make panicking behavior
393    /// explicit.
394    ///
395    /// On Unix, this error is never returned, but [`OsStrExt`] or
396    /// [`OsStringExt`] should be used instead if that needs to be guaranteed.
397    ///
398    /// [encoding]: self#encoding
399    /// [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt
400    /// [`OsStringExt`]: ::std::os::unix::ffi::OsStringExt
401    /// [`Result::unwrap`]: ::std::result::Result::unwrap
402    #[derive(Clone, Debug, Eq, PartialEq)]
403    #[cfg_attr(
404        os_str_bytes_docs_rs,
405        doc(cfg(feature = "checked_conversions"))
406    )]
407    pub struct EncodingError(imp::EncodingError);
408}
409
410impl Display for EncodingError {
411    #[inline]
412    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
413        self.0.fmt(f)
414    }
415}
416
417impl Error for EncodingError {}
418
419type Result<T> = result::Result<T, EncodingError>;
420
421fn from_raw_bytes<'a, S>(string: S) -> imp::Result<Cow<'a, OsStr>>
422where
423    S: Into<Cow<'a, [u8]>>,
424{
425    match string.into() {
426        Cow::Borrowed(string) => imp::os_str_from_bytes(string),
427        Cow::Owned(string) => imp::os_string_from_vec(string).map(Cow::Owned),
428    }
429}
430
431fn cow_os_str_into_path(string: Cow<'_, OsStr>) -> Cow<'_, Path> {
432    match string {
433        Cow::Borrowed(string) => Cow::Borrowed(Path::new(string)),
434        Cow::Owned(string) => Cow::Owned(string.into()),
435    }
436}
437
438deprecated_conversions! {
439    /// A platform agnostic variant of [`OsStrExt`].
440    ///
441    /// For more information, see [the module-level documentation][module].
442    ///
443    /// [module]: self
444    /// [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt
445    #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
446    pub trait OsStrBytes: private::Sealed + ToOwned {
447        /// Converts a byte string into an equivalent platform-native string.
448        ///
449        /// # Panics
450        ///
451        /// Panics if the string is not valid for the [unspecified encoding]
452        /// used by this crate.
453        ///
454        /// # Examples
455        ///
456        /// ```
457        /// use std::env;
458        /// use std::ffi::OsStr;
459        /// # use std::io;
460        ///
461        /// use os_str_bytes::OsStrBytes;
462        ///
463        /// let os_string = env::current_exe()?;
464        /// let os_bytes = os_string.to_raw_bytes();
465        /// assert_eq!(os_string, OsStr::assert_from_raw_bytes(os_bytes));
466        /// #
467        /// # Ok::<_, io::Error>(())
468        /// ```
469        ///
470        /// [unspecified encoding]: self#encoding
471        #[must_use = "method should not be used for validation"]
472        #[track_caller]
473        fn assert_from_raw_bytes<'a, S>(string: S) -> Cow<'a, Self>
474        where
475            S: Into<Cow<'a, [u8]>>;
476
477        deprecated_checked_conversion! {
478            "use `assert_from_raw_bytes` instead, or enable the \
479             'checked_conversions' feature",
480            /// Converts a byte string into an equivalent platform-native
481            /// string.
482            ///
483            /// [`assert_from_raw_bytes`] should almost always be used instead.
484            /// For more information, see [`EncodingError`].
485            ///
486            /// # Errors
487            ///
488            /// See documentation for [`EncodingError`].
489            ///
490            /// # Examples
491            ///
492            /// ```
493            /// use std::env;
494            /// use std::ffi::OsStr;
495            /// # use std::io;
496            ///
497            /// use os_str_bytes::OsStrBytes;
498            ///
499            /// let os_string = env::current_exe()?;
500            /// let os_bytes = os_string.to_raw_bytes();
501            /// assert_eq!(os_string, OsStr::from_raw_bytes(os_bytes).unwrap());
502            /// #
503            /// # Ok::<_, io::Error>(())
504            /// ```
505            ///
506            /// [`assert_from_raw_bytes`]: Self::assert_from_raw_bytes
507            #[cfg_attr(
508                os_str_bytes_docs_rs,
509                doc(cfg(feature = "checked_conversions"))
510            )]
511            fn from_raw_bytes<'a, S>(string: S) -> Result<Cow<'a, Self>>
512            where
513                S: Into<Cow<'a, [u8]>>;
514        }
515
516        /// Converts a platform-native string into an equivalent byte string.
517        ///
518        /// The returned string will use an [unspecified encoding].
519        ///
520        /// # Examples
521        ///
522        /// ```
523        /// use std::ffi::OsStr;
524        ///
525        /// use os_str_bytes::OsStrBytes;
526        ///
527        /// let string = "foobar";
528        /// let os_string = OsStr::new(string);
529        /// assert_eq!(string.as_bytes(), &*os_string.to_raw_bytes());
530        /// ```
531        ///
532        /// [unspecified encoding]: self#encoding
533        #[must_use]
534        fn to_raw_bytes(&self) -> Cow<'_, [u8]>;
535    }
536
537    #[cfg_attr(not(feature = "conversions"), allow(useless_deprecated))]
538    impl OsStrBytes for OsStr {
539        #[inline]
540        fn assert_from_raw_bytes<'a, S>(string: S) -> Cow<'a, Self>
541        where
542            S: Into<Cow<'a, [u8]>>,
543        {
544            expect_encoded!(from_raw_bytes(string))
545        }
546
547        #[inline]
548        fn from_raw_bytes<'a, S>(string: S) -> Result<Cow<'a, Self>>
549        where
550            S: Into<Cow<'a, [u8]>>,
551        {
552            from_raw_bytes(string).map_err(EncodingError)
553        }
554
555        #[inline]
556        fn to_raw_bytes(&self) -> Cow<'_, [u8]> {
557            imp::os_str_to_bytes(self)
558        }
559    }
560
561    #[cfg_attr(not(feature = "conversions"), allow(useless_deprecated))]
562    impl OsStrBytes for Path {
563        #[inline]
564        fn assert_from_raw_bytes<'a, S>(string: S) -> Cow<'a, Self>
565        where
566            S: Into<Cow<'a, [u8]>>,
567        {
568            cow_os_str_into_path(OsStr::assert_from_raw_bytes(string))
569        }
570
571        #[inline]
572        fn from_raw_bytes<'a, S>(string: S) -> Result<Cow<'a, Self>>
573        where
574            S: Into<Cow<'a, [u8]>>,
575        {
576            OsStr::from_raw_bytes(string).map(cow_os_str_into_path)
577        }
578
579        #[inline]
580        fn to_raw_bytes(&self) -> Cow<'_, [u8]> {
581            self.as_os_str().to_raw_bytes()
582        }
583    }
584}
585
586if_raw_str! {
587    if_nightly! {
588        /// An extension trait providing methods from [`RawOsStr`].
589        #[cfg_attr(
590            os_str_bytes_docs_rs,
591            doc(cfg(all(feature = "nightly", feature = "raw_os_str")))
592        )]
593        pub trait OsStrBytesExt: OsStrBytes {
594            /// Equivalent to [`str::contains`].
595            ///
596            /// # Examples
597            ///
598            /// ```
599            /// use std::ffi::OsStr;
600            ///
601            /// use os_str_bytes::OsStrBytesExt;
602            ///
603            /// let os_string = OsStr::new("foobar");
604            /// assert!(os_string.contains("oo"));
605            /// assert!(!os_string.contains("of"));
606            /// ```
607            #[must_use]
608            fn contains<P>(&self, pat: P) -> bool
609            where
610                P: Pattern;
611
612            /// Equivalent to [`str::ends_with`].
613            ///
614            /// # Examples
615            ///
616            /// ```
617            /// use std::ffi::OsStr;
618            ///
619            /// use os_str_bytes::OsStrBytesExt;
620            ///
621            /// let os_string = OsStr::new("foobar");
622            /// assert!(os_string.ends_with("bar"));
623            /// assert!(!os_string.ends_with("foo"));
624            /// ```
625            #[must_use]
626            fn ends_with<P>(&self, pat: P) -> bool
627            where
628                P: Pattern;
629
630            if_conversions! {
631                /// Equivalent to [`str::ends_with`] but accepts this type for
632                /// the pattern.
633                ///
634                /// # Examples
635                ///
636                /// ```
637                /// use std::ffi::OsStr;
638                ///
639                /// use os_str_bytes::OsStrBytesExt;
640                ///
641                /// let os_string = OsStr::new("foobar");
642                /// assert!(os_string.ends_with_os(OsStr::new("bar")));
643                /// assert!(!os_string.ends_with_os(OsStr::new("foo")));
644                /// ```
645                #[cfg_attr(
646                    os_str_bytes_docs_rs,
647                    doc(cfg(feature = "conversions"))
648                )]
649                #[must_use]
650                fn ends_with_os(&self, pat: &Self) -> bool;
651            }
652
653            /// Equivalent to [`str::find`].
654            ///
655            /// # Examples
656            ///
657            /// ```
658            /// use std::ffi::OsStr;
659            ///
660            /// use os_str_bytes::OsStrBytesExt;
661            ///
662            /// let os_string = OsStr::new("foobar");
663            /// assert_eq!(Some(1), os_string.find("o"));
664            /// assert_eq!(None, os_string.find("of"));
665            /// ```
666            #[must_use]
667            fn find<P>(&self, pat: P) -> Option<usize>
668            where
669                P: Pattern;
670
671            /// Equivalent to [`str::rfind`].
672            ///
673            /// # Examples
674            ///
675            /// ```
676            /// use std::ffi::OsStr;
677            ///
678            /// use os_str_bytes::OsStrBytesExt;
679            ///
680            /// let os_string = OsStr::new("foobar");
681            /// assert_eq!(Some(2), os_string.rfind("o"));
682            /// assert_eq!(None, os_string.rfind("of"));
683            /// ```
684            #[must_use]
685            fn rfind<P>(&self, pat: P) -> Option<usize>
686            where
687                P: Pattern;
688
689            /// Equivalent to [`str::rsplit_once`].
690            ///
691            /// # Examples
692            ///
693            /// ```
694            /// use std::ffi::OsStr;
695            ///
696            /// use os_str_bytes::OsStrBytesExt;
697            ///
698            /// let os_string = OsStr::new("foobar");
699            /// assert_eq!(
700            ///     Some((OsStr::new("fo"), OsStr::new("bar"))),
701            ///     os_string.rsplit_once("o"),
702            /// );
703            /// assert_eq!(None, os_string.rsplit_once("of"));
704            /// ```
705            #[must_use]
706            fn rsplit_once<P>(&self, pat: P) -> Option<(&Self, &Self)>
707            where
708                P: Pattern;
709
710            /// Equivalent to [`str::split_at`].
711            ///
712            /// # Panics
713            ///
714            /// Panics if the index is not a [valid boundary].
715            ///
716            /// # Examples
717            ///
718            /// ```
719            /// use std::ffi::OsStr;
720            ///
721            /// use os_str_bytes::OsStrBytesExt;
722            ///
723            /// let os_string = OsStr::new("foobar");
724            /// assert_eq!(
725            ///     ((OsStr::new("fo"), OsStr::new("obar"))),
726            ///     os_string.split_at(2),
727            /// );
728            /// ```
729            ///
730            /// [valid boundary]: RawOsStr#indices
731            #[must_use]
732            #[track_caller]
733            fn split_at(&self, mid: usize) -> (&Self, &Self);
734
735            /// Equivalent to [`str::split_once`].
736            ///
737            /// # Examples
738            ///
739            /// ```
740            /// use std::ffi::OsStr;
741            ///
742            /// use os_str_bytes::OsStrBytesExt;
743            ///
744            /// let os_string = OsStr::new("foobar");
745            /// assert_eq!(
746            ///     Some((OsStr::new("f"), OsStr::new("obar"))),
747            ///     os_string.split_once("o"),
748            /// );
749            /// assert_eq!(None, os_string.split_once("of"));
750            /// ```
751            #[must_use]
752            fn split_once<P>(&self, pat: P) -> Option<(&Self, &Self)>
753            where
754                P: Pattern;
755
756            /// Equivalent to [`str::starts_with`].
757            ///
758            /// # Examples
759            ///
760            /// ```
761            /// use std::ffi::OsStr;
762            ///
763            /// use os_str_bytes::OsStrBytesExt;
764            ///
765            /// let os_string = OsStr::new("foobar");
766            /// assert!(os_string.starts_with("foo"));
767            /// assert!(!os_string.starts_with("bar"));
768            /// ```
769            #[must_use]
770            fn starts_with<P>(&self, pat: P) -> bool
771            where
772                P: Pattern;
773
774            if_conversions! {
775                /// Equivalent to [`str::starts_with`] but accepts this type
776                /// for the pattern.
777                ///
778                /// # Examples
779                ///
780                /// ```
781                /// use std::ffi::OsStr;
782                ///
783                /// use os_str_bytes::OsStrBytesExt;
784                ///
785                /// let os_string = OsStr::new("foobar");
786                /// assert!(os_string.starts_with_os(OsStr::new("foo")));
787                /// assert!(!os_string.starts_with_os(OsStr::new("bar")));
788                /// ```
789                #[cfg_attr(
790                    os_str_bytes_docs_rs,
791                    doc(cfg(feature = "conversions"))
792                )]
793                #[must_use]
794                fn starts_with_os(&self, pat: &Self) -> bool;
795            }
796
797            /// Equivalent to [`str::strip_prefix`].
798            ///
799            /// # Examples
800            ///
801            /// ```
802            /// use std::ffi::OsStr;
803            ///
804            /// use os_str_bytes::OsStrBytesExt;
805            ///
806            /// let os_string = OsStr::new("111foo1bar111");
807            /// assert_eq!(
808            ///     Some(OsStr::new("11foo1bar111")),
809            ///     os_string.strip_prefix("1"),
810            /// );
811            /// assert_eq!(None, os_string.strip_prefix("o"));
812            /// ```
813            #[must_use]
814            fn strip_prefix<P>(&self, pat: P) -> Option<&Self>
815            where
816                P: Pattern;
817
818            /// Equivalent to [`str::strip_suffix`].
819            ///
820            /// # Examples
821            ///
822            /// ```
823            /// use std::ffi::OsStr;
824            ///
825            /// use os_str_bytes::OsStrBytesExt;
826            ///
827            /// let os_string = OsStr::new("111foo1bar111");
828            /// assert_eq!(
829            ///     Some(OsStr::new("111foo1bar11")),
830            ///     os_string.strip_suffix("1"),
831            /// );
832            /// assert_eq!(None, os_string.strip_suffix("o"));
833            /// ```
834            #[must_use]
835            fn strip_suffix<P>(&self, pat: P) -> Option<&Self>
836            where
837                P: Pattern;
838
839            /// Equivalent to [`str::trim_end_matches`].
840            ///
841            /// # Examples
842            ///
843            /// ```
844            /// use std::ffi::OsStr;
845            ///
846            /// use os_str_bytes::OsStrBytesExt;
847            ///
848            /// let os_string = OsStr::new("111foo1bar111");
849            /// assert_eq!("111foo1bar", os_string.trim_end_matches("1"));
850            /// assert_eq!("111foo1bar111", os_string.trim_end_matches("o"));
851            /// ```
852            #[must_use]
853            fn trim_end_matches<P>(&self, pat: P) -> &Self
854            where
855                P: Pattern;
856
857            /// Equivalent to [`str::trim_matches`].
858            ///
859            /// # Examples
860            ///
861            /// ```
862            /// use std::ffi::OsStr;
863            ///
864            /// use os_str_bytes::OsStrBytesExt;
865            ///
866            /// let os_string = OsStr::new("111foo1bar111");
867            /// assert_eq!("foo1bar", os_string.trim_matches("1"));
868            /// assert_eq!("111foo1bar111", os_string.trim_matches("o"));
869            /// ```
870            #[must_use]
871            fn trim_matches<P>(&self, pat: P) -> &Self
872            where
873                P: Pattern;
874
875            /// Equivalent to [`str::trim_start_matches`].
876            ///
877            /// # Examples
878            ///
879            /// ```
880            /// use std::ffi::OsStr;
881            ///
882            /// use os_str_bytes::OsStrBytesExt;
883            ///
884            /// let os_string = OsStr::new("111foo1bar111");
885            /// assert_eq!("foo1bar111", os_string.trim_start_matches("1"));
886            /// assert_eq!("111foo1bar111", os_string.trim_start_matches("o"));
887            /// ```
888            #[must_use]
889            fn trim_start_matches<P>(&self, pat: P) -> &Self
890            where
891                P: Pattern;
892        }
893
894        impl OsStrBytesExt for OsStr {
895            #[inline]
896            fn contains<P>(&self, pat: P) -> bool
897            where
898                P: Pattern,
899            {
900                RawOsStr::from_os_str(self).contains(pat)
901            }
902
903            #[inline]
904            fn ends_with<P>(&self, pat: P) -> bool
905            where
906                P: Pattern,
907            {
908                RawOsStr::from_os_str(self).ends_with(pat)
909            }
910
911            if_conversions! {
912                #[inline]
913                fn ends_with_os(&self, pat: &Self) -> bool {
914                    RawOsStr::from_os_str(self)
915                        .ends_with_os(RawOsStr::from_os_str(pat))
916                }
917            }
918
919            #[inline]
920            fn find<P>(&self, pat: P) -> Option<usize>
921            where
922                P: Pattern,
923            {
924                RawOsStr::from_os_str(self).find(pat)
925            }
926
927            #[inline]
928            fn rfind<P>(&self, pat: P) -> Option<usize>
929            where
930                P: Pattern,
931            {
932                RawOsStr::from_os_str(self).rfind(pat)
933            }
934
935            #[inline]
936            fn rsplit_once<P>(&self, pat: P) -> Option<(&Self, &Self)>
937            where
938                P: Pattern,
939            {
940                RawOsStr::from_os_str(self)
941                    .rsplit_once(pat)
942                    .map(|(prefix, suffix)| {
943                        (prefix.as_os_str(), suffix.as_os_str())
944                    })
945            }
946
947            #[inline]
948            fn split_at(&self, mid: usize) -> (&Self, &Self) {
949                let (prefix, suffix) =
950                    RawOsStr::from_os_str(self).split_at(mid);
951                (prefix.as_os_str(), suffix.as_os_str())
952            }
953
954            #[inline]
955            fn split_once<P>(&self, pat: P) -> Option<(&Self, &Self)>
956            where
957                P: Pattern,
958            {
959                RawOsStr::from_os_str(self)
960                    .split_once(pat)
961                    .map(|(prefix, suffix)| {
962                        (prefix.as_os_str(), suffix.as_os_str())
963                    })
964            }
965
966            #[inline]
967            fn starts_with<P>(&self, pat: P) -> bool
968            where
969                P: Pattern,
970            {
971                RawOsStr::from_os_str(self).starts_with(pat)
972            }
973
974            if_conversions! {
975                #[inline]
976                fn starts_with_os(&self, pat: &Self) -> bool {
977                    RawOsStr::from_os_str(self)
978                        .starts_with_os(RawOsStr::from_os_str(pat))
979                }
980            }
981
982            #[inline]
983            fn strip_prefix<P>(&self, pat: P) -> Option<&Self>
984            where
985                P: Pattern,
986            {
987                RawOsStr::from_os_str(self)
988                    .strip_prefix(pat)
989                    .map(RawOsStr::as_os_str)
990            }
991
992            #[inline]
993            fn strip_suffix<P>(&self, pat: P) -> Option<&Self>
994            where
995                P: Pattern,
996            {
997                RawOsStr::from_os_str(self)
998                    .strip_suffix(pat)
999                    .map(RawOsStr::as_os_str)
1000            }
1001
1002            #[inline]
1003            fn trim_end_matches<P>(&self, pat: P) -> &Self
1004            where
1005                P: Pattern,
1006            {
1007                RawOsStr::from_os_str(self).trim_end_matches(pat).as_os_str()
1008            }
1009
1010            #[inline]
1011            fn trim_matches<P>(&self, pat: P) -> &Self
1012            where
1013                P: Pattern,
1014            {
1015                RawOsStr::from_os_str(self).trim_matches(pat).as_os_str()
1016            }
1017
1018            #[inline]
1019            fn trim_start_matches<P>(&self, pat: P) -> &Self
1020            where
1021                P: Pattern,
1022            {
1023                RawOsStr::from_os_str(self).trim_start_matches(pat).as_os_str()
1024            }
1025        }
1026    }
1027}
1028
1029deprecated_conversions! {
1030    /// A platform agnostic variant of [`OsStringExt`].
1031    ///
1032    /// For more information, see [the module-level documentation][module].
1033    ///
1034    /// [module]: self
1035    /// [`OsStringExt`]: ::std::os::unix::ffi::OsStringExt
1036    #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(any(feature = "conversions"))))]
1037    pub trait OsStringBytes: private::Sealed + Sized {
1038        /// Converts a byte string into an equivalent platform-native string.
1039        ///
1040        /// # Panics
1041        ///
1042        /// Panics if the string is not valid for the [unspecified encoding]
1043        /// used by this crate.
1044        ///
1045        /// # Examples
1046        ///
1047        /// ```
1048        /// use std::env;
1049        /// use std::ffi::OsString;
1050        /// # use std::io;
1051        ///
1052        /// use os_str_bytes::OsStringBytes;
1053        ///
1054        /// let os_string = env::current_exe()?;
1055        /// let os_bytes = os_string.clone().into_raw_vec();
1056        /// assert_eq!(os_string, OsString::assert_from_raw_vec(os_bytes));
1057        /// #
1058        /// # Ok::<_, io::Error>(())
1059        /// ```
1060        ///
1061        /// [unspecified encoding]: self#encoding
1062        #[must_use = "method should not be used for validation"]
1063        #[track_caller]
1064        fn assert_from_raw_vec(string: Vec<u8>) -> Self;
1065
1066        deprecated_checked_conversion! {
1067            "use `assert_from_raw_vec` instead, or enable the \
1068             'checked_conversions' feature",
1069            /// Converts a byte string into an equivalent platform-native
1070            /// string.
1071            ///
1072            /// [`assert_from_raw_vec`] should almost always be used instead.
1073            /// For more information, see [`EncodingError`].
1074            ///
1075            /// # Errors
1076            ///
1077            /// See documentation for [`EncodingError`].
1078            ///
1079            /// # Examples
1080            ///
1081            /// ```
1082            /// use std::env;
1083            /// use std::ffi::OsString;
1084            /// # use std::io;
1085            ///
1086            /// use os_str_bytes::OsStringBytes;
1087            ///
1088            /// let os_string = env::current_exe()?;
1089            /// let os_bytes = os_string.clone().into_raw_vec();
1090            /// assert_eq!(
1091            ///     os_string,
1092            ///     OsString::from_raw_vec(os_bytes).unwrap(),
1093            /// );
1094            /// #
1095            /// # Ok::<_, io::Error>(())
1096            /// ```
1097            ///
1098            /// [`assert_from_raw_vec`]: Self::assert_from_raw_vec
1099            #[cfg_attr(
1100                os_str_bytes_docs_rs,
1101                doc(cfg(feature = "checked_conversions"))
1102            )]
1103            fn from_raw_vec(string: Vec<u8>) -> Result<Self>;
1104        }
1105
1106        /// Converts a platform-native string into an equivalent byte string.
1107        ///
1108        /// The returned string will use an [unspecified encoding].
1109        ///
1110        /// # Examples
1111        ///
1112        /// ```
1113        /// use std::ffi::OsString;
1114        ///
1115        /// use os_str_bytes::OsStringBytes;
1116        ///
1117        /// let string = "foobar".to_owned();
1118        /// let os_string: OsString = string.clone().into();
1119        /// assert_eq!(string.into_bytes(), os_string.into_raw_vec());
1120        /// ```
1121        ///
1122        /// [unspecified encoding]: self#encoding
1123        #[must_use]
1124        fn into_raw_vec(self) -> Vec<u8>;
1125    }
1126
1127    #[cfg_attr(not(feature = "conversions"), allow(useless_deprecated))]
1128    impl OsStringBytes for OsString {
1129        #[inline]
1130        fn assert_from_raw_vec(string: Vec<u8>) -> Self {
1131            expect_encoded!(imp::os_string_from_vec(string))
1132        }
1133
1134        #[inline]
1135        fn from_raw_vec(string: Vec<u8>) -> Result<Self> {
1136            imp::os_string_from_vec(string).map_err(EncodingError)
1137        }
1138
1139        #[inline]
1140        fn into_raw_vec(self) -> Vec<u8> {
1141            imp::os_string_into_vec(self)
1142        }
1143    }
1144
1145    #[cfg_attr(not(feature = "conversions"), allow(useless_deprecated))]
1146    impl OsStringBytes for PathBuf {
1147        #[inline]
1148        fn assert_from_raw_vec(string: Vec<u8>) -> Self {
1149            OsString::assert_from_raw_vec(string).into()
1150        }
1151
1152        #[inline]
1153        fn from_raw_vec(string: Vec<u8>) -> Result<Self> {
1154            OsString::from_raw_vec(string).map(Into::into)
1155        }
1156
1157        #[inline]
1158        fn into_raw_vec(self) -> Vec<u8> {
1159            self.into_os_string().into_raw_vec()
1160        }
1161    }
1162}
1163
1164mod private {
1165    use std::ffi::OsStr;
1166    use std::ffi::OsString;
1167    use std::path::Path;
1168    use std::path::PathBuf;
1169
1170    if_raw_str! {
1171        use std::borrow::Cow;
1172
1173        use super::RawOsStr;
1174    }
1175
1176    pub trait Sealed {}
1177
1178    impl Sealed for char {}
1179    impl Sealed for OsStr {}
1180    impl Sealed for OsString {}
1181    impl Sealed for Path {}
1182    impl Sealed for PathBuf {}
1183    impl Sealed for &str {}
1184    impl Sealed for &String {}
1185
1186    if_raw_str! {
1187        impl Sealed for Cow<'_, RawOsStr> {}
1188    }
1189}