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–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