• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
225 use std::borrow::Cow;
226 use std::error::Error;
227 use std::ffi::OsStr;
228 use std::ffi::OsString;
229 use std::fmt;
230 use std::fmt::Display;
231 use std::fmt::Formatter;
232 use std::path::Path;
233 use std::path::PathBuf;
234 use std::result;
235 
236 macro_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))]
246 if_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 
254 macro_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))]
264 if_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]
273 macro_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]
284 macro_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 
296 macro_rules! if_raw_str {
297     ( $($item:item)+ ) => {
298         $(
299             #[cfg(feature = "raw_os_str")]
300             $item
301         )+
302     };
303 }
304 
305 if_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 
327 if_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 
340 macro_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 )]
355 mod 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 ))]
367 mod util;
368 
369 if_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 
381 deprecated_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 
410 impl Display for EncodingError {
411     #[inline]
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result412     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
413         self.0.fmt(f)
414     }
415 }
416 
417 impl Error for EncodingError {}
418 
419 type Result<T> = result::Result<T, EncodingError>;
420 
from_raw_bytes<'a, S>(string: S) -> imp::Result<Cow<'a, OsStr>> where S: Into<Cow<'a, [u8]>>,421 fn from_raw_bytes<'a, S>(string: S) -> imp::Result<Cow<'a, OsStr>>
422 where
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 
cow_os_str_into_path(string: Cow<'_, OsStr>) -> Cow<'_, Path>431 fn 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 
438 deprecated_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 
586 if_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 
1029 deprecated_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 
1164 mod 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 }
1190