os_str_bytes/
lib.rs

1//! This crate provides additional functionality for [`OsStr`] and
2//! [`OsString`], without resorting to panics or corruption for invalid UTF-8.
3//! Thus, familiar methods from [`str`] and [`String`] can be used.
4//!
5//! # Usage
6//!
7//! The most important trait included is [`OsStrBytesExt`], which provides
8//! methods analogous to those of [`str`] but for [`OsStr`]. These methods will
9//! never panic for invalid UTF-8 in a platform string, so they can be used to
10//! manipulate [`OsStr`] values with the same simplicity possible for [`str`].
11//!
12//! Additionally, the following wrappers are provided. They are primarily
13//! legacy types from when this crate needed to perform more frequent encoding
14//! conversions. However, they may be useful for their trait implementations.
15//! - [`RawOsStr`] is a wrapper for [`OsStr`].
16//! - [`RawOsString`] is a wrapper for [`OsString`].
17//!
18//! # User Input
19//!
20//! Most methods in this crate should not be used to convert byte sequences
21//! that did not originate from [`OsStr`] or a related struct. The encoding
22//! used by this crate is an implementation detail, so it does not make sense
23//! to expose it to users.
24//!
25//! For user input with an unknown encoding similar to UTF-8, use the following
26//! IO-safe methods, which avoid errors when [writing to streams on
27//! Windows][windows_considerations]. These methods will not accept or return
28//! byte sequences that are invalid for input and output streams. Therefore,
29//! they can be used to convert between bytes strings exposed to users and
30//! platform strings.
31//! - [`OsStrBytes::from_io_bytes`]
32//! - [`OsStrBytes::to_io_bytes`]
33//! - [`OsStrBytes::to_io_bytes_lossy`]
34//! - [`OsStringBytes::from_io_vec`]
35//! - [`OsStringBytes::into_io_vec`]
36//! - [`OsStringBytes::into_io_vec_lossy`]
37//!
38//! # Features
39//!
40//! These features are optional and can be enabled or disabled in a
41//! "Cargo.toml" file.
42//!
43//! ### Default Features
44//!
45//! - **memchr** -
46//!   Changes the implementation to use crate [memchr] for better performance.
47//!   This feature is useless when the "raw\_os\_str" feature is disabled.
48//!
49//!   For more information, see [`OsStrBytesExt`][memchr_complexity].
50//!
51//! - **raw\_os\_str** -
52//!   Provides:
53//!   - [`iter`]
54//!   - [`NonUnicodeOsStr`]
55//!   - [`OsStrBytesExt`]
56//!   - [`Pattern`]
57//!   - [`RawOsStr`]
58//!   - [`RawOsStrCow`]
59//!   - [`RawOsString`]
60//!
61//! ### Optional Features
62//!
63//! - **checked\_conversions** -
64//!   Provides:
65//!   - [`EncodingError`]
66//!   - [`OsStrBytes::from_raw_bytes`]
67//!   - [`OsStringBytes::from_raw_vec`]
68//!   - [`RawOsStr::cow_from_raw_bytes`]
69//!   - [`RawOsString::from_raw_vec`]
70//!
71//!   Because this feature should not be used in libraries, the
72//!   "OS\_STR\_BYTES\_CHECKED\_CONVERSIONS" environment variable must be
73//!   defined during compilation.
74//!
75//! - **conversions** -
76//!   Provides methods that require encoding conversion and may be expensive:
77//!   - [`OsStrBytesExt::ends_with_os`]
78//!   - [`OsStrBytesExt::starts_with_os`]
79//!   - [`RawOsStr::assert_cow_from_raw_bytes`]
80//!   - [`RawOsStr::ends_with_os`]
81//!   - [`RawOsStr::starts_with_os`]
82//!   - [`RawOsStr::to_raw_bytes`]
83//!   - [`RawOsString::assert_from_raw_vec`]
84//!   - [`RawOsString::into_raw_vec`]
85//!   - [`OsStrBytes::assert_from_raw_bytes`]
86//!   - [`OsStrBytes::to_raw_bytes`]
87//!   - [`OsStringBytes::assert_from_raw_vec`]
88//!   - [`OsStringBytes::into_raw_vec`]
89//!
90//!   For more information, see [Encoding Conversions].
91//!
92//! # Implementation
93//!
94//! Some methods return [`Cow`] to account for platform differences. However,
95//! no guarantee is made that the same variant of that enum will always be
96//! returned for the same platform. Whichever can be constructed most
97//! efficiently will be returned.
98//!
99//! All traits are [sealed], meaning that they can only be implemented by this
100//! crate. Otherwise, backward compatibility would be more difficult to
101//! maintain for new features.
102//!
103//! # Encoding Conversions
104//!
105//! Methods provided by the "conversions" feature use an intentionally
106//! unspecified encoding. It may vary for different platforms, so defining it
107//! would run contrary to the goal of generic string handling. However, the
108//! following invariants will always be upheld:
109//!
110//! - The encoding will be compatible with UTF-8. In particular, splitting an
111//!   encoded byte sequence by a UTF-8–encoded character always produces
112//!   other valid byte sequences. They can be re-encoded without error using
113//!   [`RawOsString::into_os_string`] and similar methods.
114//!
115//! - All characters valid in platform strings are representable. [`OsStr`] and
116//!   [`OsString`] can always be losslessly reconstructed from extracted bytes.
117//!
118//! Note that the chosen encoding may not match how [`OsStr`] stores these
119//! strings internally, which is undocumented. For instance, the result of
120//! calling [`OsStr::len`] will not necessarily match the number of bytes this
121//! crate uses to represent the same string. However, unlike the encoding used
122//! by [`OsStr`], the encoding used by this crate can be validated safely using
123//! the following methods:
124//! - [`OsStrBytes::assert_from_raw_bytes`]
125//! - [`RawOsStr::assert_cow_from_raw_bytes`]
126//! - [`RawOsString::assert_from_raw_vec`]
127//!
128//! Concatenation may yield unexpected results without a UTF-8 separator. If
129//! two platform strings need to be concatenated, the only safe way to do so is
130//! using [`OsString::push`]. This limitation also makes it undesirable to use
131//! the bytes in interchange.
132//!
133//! Since this encoding can change between versions and platforms, it should
134//! not be used for storage. The standard library provides implementations of
135//! [`OsStrExt`] and [`OsStringExt`] for various platforms, which should be
136//! preferred for that use case.
137//!
138//! # Related Crates
139//!
140//! - [print\_bytes] -
141//!   Used to print byte and platform strings as losslessly as possible.
142//!
143//! - [uniquote] -
144//!   Used to display paths using escapes instead of replacement characters.
145//!
146//! # Examples
147//!
148//! ```
149//! # use std::io;
150//! #
151//! # #[cfg(feature = "raw_os_str")]
152//! # {
153//! # #[cfg(any())]
154//! use std::env;
155//! use std::fs;
156//! # use std::path::Path;
157//!
158//! use os_str_bytes::OsStrBytesExt;
159//!
160//! # mod env {
161//! #   use std::ffi::OsString;
162//! #   use std::iter;
163//! #
164//! #   use tempfile::NamedTempFile;
165//! #
166//! #   pub(super) fn args_os() -> impl Iterator<Item = OsString> {
167//! #       let file = NamedTempFile::with_prefix("os_str_bytes_").unwrap();
168//! #       iter::from_fn(move || Some(file.path().as_os_str().to_owned()))
169//! #           .take(2)
170//! #   }
171//! # }
172//! #
173//! for file in env::args_os().skip(1) {
174//!     if !file.starts_with('-') {
175//!         let string = "Hello, world!";
176//! #       assert!(Path::new(&file).exists());
177//!         fs::write(&file, string)?;
178//!         assert_eq!(string, fs::read_to_string(file)?);
179//!     }
180//! }
181//! # }
182//! #
183//! # Ok::<_, io::Error>(())
184//! ```
185//!
186//! [Encoding Conversions]: #encoding-conversions
187//! [memchr]: https://crates.io/crates/memchr
188//! [memchr_complexity]: OsStrBytesExt#complexity
189//! [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt
190//! [`OsStringExt`]: ::std::os::unix::ffi::OsStringExt
191//! [print\_bytes]: https://crates.io/crates/print_bytes
192//! [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed
193//! [uniquote]: https://crates.io/crates/uniquote
194//! [windows_considerations]: https://doc.rust-lang.org/std/io/struct.Stdout.html#note-windows-portability-considerations
195
196// Only require a nightly compiler when building documentation for docs.rs.
197// This is a private option that should not be used.
198// https://github.com/rust-lang/docs.rs/issues/147#issuecomment-389544407
199// https://github.com/dylni/os_str_bytes/issues/2
200#![cfg_attr(os_str_bytes_docs_rs, feature(doc_cfg))]
201#![cfg_attr(
202    all(target_vendor = "fortanix", target_env = "sgx"),
203    feature(sgx_platform)
204)]
205#![cfg_attr(target_os = "uefi", feature(uefi_std))]
206#![cfg_attr(all(target_os = "wasi", target_env = "p2"), feature(wasip2))]
207#![warn(unused_results)]
208
209use std::borrow::Cow;
210use std::ffi::OsStr;
211use std::ffi::OsString;
212use std::path::Path;
213use std::path::PathBuf;
214
215macro_rules! if_checked_conversions {
216    ( $($item:item)+ ) => {
217    $(
218        #[cfg(feature = "checked_conversions")]
219        $item
220    )+
221    };
222}
223
224if_checked_conversions! {
225    use std::error::Error;
226    use std::fmt;
227    use std::fmt::Display;
228    use std::fmt::Formatter;
229    use std::result;
230}
231
232#[cfg(not(os_str_bytes_docs_rs))]
233if_checked_conversions! {
234    const _: &str = env!(
235        "OS_STR_BYTES_CHECKED_CONVERSIONS",
236        "The 'OS_STR_BYTES_CHECKED_CONVERSIONS' environment variable must be \
237         defined to use the 'checked_conversions' feature.",
238    );
239}
240
241#[cfg(all(feature = "memchr", not(feature = "raw_os_str")))]
242const _: &str = env!(
243    "__OS_STR_BYTES_CI",
244    concat!(
245        "The 'memchr' feature is useless when 'raw_os_str' is disabled; it \
246         should be disabled too.",
247    ),
248);
249
250macro_rules! if_conversions {
251    ( $($item:item)+ ) => {
252    $(
253        #[cfg(feature = "conversions")]
254        $item
255    )+
256    };
257}
258
259if_conversions! {
260    macro_rules! expect_encoded {
261        ( $result:expr ) => {
262            $result.expect("invalid raw bytes")
263        };
264    }
265}
266
267macro_rules! if_raw_str {
268    ( $($item:item)+ ) => {
269    $(
270        #[cfg(feature = "raw_os_str")]
271        $item
272    )+
273    };
274}
275
276#[cfg_attr(
277    all(target_family = "wasm", target_os = "unknown"),
278    path = "wasm/mod.rs"
279)]
280#[cfg_attr(any(target_os = "uefi", windows), path = "windows/mod.rs")]
281#[cfg_attr(
282    not(any(
283        all(target_family = "wasm", target_os = "unknown"),
284        target_os = "uefi",
285        windows,
286    )),
287    path = "common/mod.rs"
288)]
289mod imp;
290use imp::convert_io;
291
292if_conversions! {
293    use imp::convert;
294}
295
296#[cfg(any(
297    all(feature = "conversions", any(target_os = "uefi", windows)),
298    feature = "raw_os_str",
299))]
300mod util;
301
302if_raw_str! {
303    mod ext;
304    pub use ext::NonUnicodeOsStr;
305    pub use ext::OsStrBytesExt;
306
307    pub mod iter;
308
309    mod pattern;
310    pub use pattern::Pattern;
311
312    mod raw_str;
313    pub use raw_str::RawOsStr;
314    pub use raw_str::RawOsStrCow;
315    pub use raw_str::RawOsString;
316}
317
318if_checked_conversions! {
319    /// The error that occurs when a byte sequence is not representable in the
320    /// platform encoding.
321    ///
322    /// [`Result::unwrap`] should almost always be called on results containing
323    /// this error. It should be known whether or not byte sequences are
324    /// properly encoded for the platform, since [the module-level
325    /// documentation][encoding] discourages using encoded bytes in
326    /// interchange. Results are returned primarily to make panicking behavior
327    /// explicit.
328    ///
329    /// On Unix, this error is never returned, but [`OsStrExt`] or
330    /// [`OsStringExt`] should be used instead if that needs to be guaranteed.
331    ///
332    /// [encoding]: self#encoding-conversions
333    /// [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt
334    /// [`OsStringExt`]: ::std::os::unix::ffi::OsStringExt
335    /// [`Result::unwrap`]: ::std::result::Result::unwrap
336    #[derive(Clone, Debug, PartialEq)]
337    #[cfg_attr(
338        os_str_bytes_docs_rs,
339        doc(cfg(feature = "checked_conversions"))
340    )]
341    pub struct EncodingError(convert::EncodingError);
342
343    impl Display for EncodingError {
344        #[inline]
345        fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
346            self.0.fmt(f)
347        }
348    }
349
350    impl Error for EncodingError {}
351}
352
353if_checked_conversions! {
354    type Result<T> = result::Result<T, EncodingError>;
355}
356
357if_conversions! {
358    fn from_raw_bytes<'a, S>(string: S) -> convert::Result<Cow<'a, OsStr>>
359    where
360        S: Into<Cow<'a, [u8]>>,
361    {
362        match string.into() {
363            Cow::Borrowed(string) => convert::os_str_from_bytes(string),
364            Cow::Owned(string) => {
365                convert::os_string_from_vec(string).map(Cow::Owned)
366            }
367        }
368    }
369}
370
371if_conversions! {
372    fn cow_os_str_into_path(string: Cow<'_, OsStr>) -> Cow<'_, Path> {
373        match string {
374            Cow::Borrowed(string) => Cow::Borrowed(Path::new(string)),
375            Cow::Owned(string) => Cow::Owned(string.into()),
376        }
377    }
378}
379
380/// A platform agnostic variant of [`OsStrExt`].
381///
382/// For more information, see [the module-level documentation][module].
383///
384/// [module]: self
385/// [`OsStrExt`]: ::std::os::unix::ffi::OsStrExt
386pub trait OsStrBytes: private::Sealed + ToOwned {
387    if_conversions! {
388        /// Converts a byte string into an equivalent platform-native string.
389        ///
390        /// # Panics
391        ///
392        /// Panics if the string is not valid for the [unspecified encoding]
393        /// used by this crate.
394        ///
395        /// # Examples
396        ///
397        /// ```
398        /// use std::env;
399        /// use std::ffi::OsStr;
400        /// # use std::io;
401        ///
402        /// use os_str_bytes::OsStrBytes;
403        ///
404        /// let os_string = env::current_exe()?;
405        /// let os_bytes = os_string.to_raw_bytes();
406        /// assert_eq!(os_string, OsStr::assert_from_raw_bytes(os_bytes));
407        /// #
408        /// # Ok::<_, io::Error>(())
409        /// ```
410        ///
411        /// [unspecified encoding]: self#encoding-conversions
412        #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
413        #[must_use = "method should not be used for validation"]
414        #[track_caller]
415        fn assert_from_raw_bytes<'a, S>(string: S) -> Cow<'a, Self>
416        where
417            S: Into<Cow<'a, [u8]>>;
418    }
419
420    /// Converts a byte string into an equivalent platform-native string, if it
421    /// is [IO-safe].
422    ///
423    /// # Examples
424    ///
425    /// ```
426    /// use std::ffi::OsStr;
427    /// use std::io;
428    /// use std::io::Read;
429    ///
430    /// use os_str_bytes::OsStrBytes;
431    ///
432    /// let mut io_string = Vec::new();
433    /// let _ = io::stdin().read_to_end(&mut io_string)?;
434    /// let os_string = OsStr::from_io_bytes(&io_string).ok_or_else(|| {
435    ///     io::Error::new(io::ErrorKind::InvalidInput, "invalid input")
436    /// })?;
437    /// println!("{:?}", os_string);
438    /// #
439    /// # Ok::<_, io::Error>(())
440    /// ```
441    ///
442    /// [IO-safe]: self#user-input
443    #[must_use]
444    fn from_io_bytes(string: &[u8]) -> Option<&Self>;
445
446    if_checked_conversions! {
447        /// Converts a byte string into an equivalent platform-native string.
448        ///
449        /// [`assert_from_raw_bytes`] should almost always be used instead. For
450        /// more information, see [`EncodingError`].
451        ///
452        /// # Errors
453        ///
454        /// See documentation for [`EncodingError`].
455        ///
456        /// # Examples
457        ///
458        /// ```
459        /// use std::env;
460        /// use std::ffi::OsStr;
461        /// # use std::io;
462        ///
463        /// use os_str_bytes::OsStrBytes;
464        ///
465        /// let os_string = env::current_exe()?;
466        /// let os_bytes = os_string.to_raw_bytes();
467        /// assert_eq!(os_string, OsStr::from_raw_bytes(os_bytes).unwrap());
468        /// #
469        /// # Ok::<_, io::Error>(())
470        /// ```
471        ///
472        /// [`assert_from_raw_bytes`]: Self::assert_from_raw_bytes
473        #[cfg_attr(
474            os_str_bytes_docs_rs,
475            doc(cfg(feature = "checked_conversions"))
476        )]
477        fn from_raw_bytes<'a, S>(string: S) -> Result<Cow<'a, Self>>
478        where
479            S: Into<Cow<'a, [u8]>>;
480    }
481
482    /// Converts a platform-native string into an equivalent byte string, if it
483    /// is [IO-safe].
484    ///
485    /// # Examples
486    ///
487    /// ```
488    /// use std::env;
489    /// use std::io;
490    /// use std::io::Write;
491    ///
492    /// use os_str_bytes::OsStrBytes;
493    ///
494    /// let os_string = env::current_exe()?;
495    /// let io_string = os_string.to_io_bytes().ok_or_else(|| {
496    ///     io::Error::new(io::ErrorKind::InvalidInput, "invalid input")
497    /// })?;
498    /// io::stdout().write_all(io_string)?;
499    /// #
500    /// # Ok::<_, io::Error>(())
501    /// ```
502    ///
503    /// [IO-safe]: self#user-input
504    #[must_use]
505    fn to_io_bytes(&self) -> Option<&'_ [u8]>;
506
507    /// Converts a platform-native string into an equivalent byte string.
508    ///
509    /// If the string is not [IO-safe], invalid characters will be replaced
510    /// with [`REPLACEMENT_CHARACTER`].
511    ///
512    /// # Examples
513    ///
514    /// ```
515    /// use std::env;
516    /// use std::io;
517    /// use std::io::Write;
518    ///
519    /// use os_str_bytes::OsStrBytes;
520    ///
521    /// let os_string = env::current_exe()?;
522    /// let io_string = os_string.to_io_bytes_lossy();
523    /// io::stdout().write_all(&io_string)?;
524    /// #
525    /// # Ok::<_, io::Error>(())
526    /// ```
527    ///
528    /// [IO-safe]: self#user-input
529    /// [`REPLACEMENT_CHARACTER`]: char::REPLACEMENT_CHARACTER
530    #[must_use]
531    fn to_io_bytes_lossy(&self) -> Cow<'_, [u8]>;
532
533    if_conversions! {
534        /// Converts a platform-native string into an equivalent byte string.
535        ///
536        /// The returned string will use an [unspecified encoding].
537        ///
538        /// # Examples
539        ///
540        /// ```
541        /// use std::ffi::OsStr;
542        ///
543        /// use os_str_bytes::OsStrBytes;
544        ///
545        /// let string = "foobar";
546        /// let os_string = OsStr::new(string);
547        /// assert_eq!(string.as_bytes(), &*os_string.to_raw_bytes());
548        /// ```
549        ///
550        /// [unspecified encoding]: self#encoding-conversions
551        #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
552        #[must_use]
553        fn to_raw_bytes(&self) -> Cow<'_, [u8]>;
554    }
555}
556
557impl OsStrBytes for OsStr {
558    if_conversions! {
559        #[inline]
560        fn assert_from_raw_bytes<'a, S>(string: S) -> Cow<'a, Self>
561        where
562            S: Into<Cow<'a, [u8]>>,
563        {
564            expect_encoded!(from_raw_bytes(string))
565        }
566    }
567
568    #[inline]
569    fn from_io_bytes(string: &[u8]) -> Option<&Self> {
570        convert_io::os_str_from_bytes(string)
571    }
572
573    if_checked_conversions! {
574        #[inline]
575        fn from_raw_bytes<'a, S>(string: S) -> Result<Cow<'a, Self>>
576        where
577            S: Into<Cow<'a, [u8]>>,
578        {
579            from_raw_bytes(string).map_err(EncodingError)
580        }
581    }
582
583    #[inline]
584    fn to_io_bytes(&self) -> Option<&'_ [u8]> {
585        convert_io::os_str_to_bytes(self)
586    }
587
588    #[inline]
589    fn to_io_bytes_lossy(&self) -> Cow<'_, [u8]> {
590        convert_io::os_str_to_bytes_lossy(self)
591    }
592
593    if_conversions! {
594        #[inline]
595        fn to_raw_bytes(&self) -> Cow<'_, [u8]> {
596            convert::os_str_to_bytes(self)
597        }
598    }
599}
600
601impl OsStrBytes for Path {
602    if_conversions! {
603        #[inline]
604        fn assert_from_raw_bytes<'a, S>(string: S) -> Cow<'a, Self>
605        where
606            S: Into<Cow<'a, [u8]>>,
607        {
608            cow_os_str_into_path(OsStr::assert_from_raw_bytes(string))
609        }
610    }
611
612    #[inline]
613    fn from_io_bytes(string: &[u8]) -> Option<&Self> {
614        OsStr::from_io_bytes(string).map(Self::new)
615    }
616
617    if_checked_conversions! {
618        #[inline]
619        fn from_raw_bytes<'a, S>(string: S) -> Result<Cow<'a, Self>>
620        where
621            S: Into<Cow<'a, [u8]>>,
622        {
623            OsStr::from_raw_bytes(string).map(cow_os_str_into_path)
624        }
625    }
626
627    #[inline]
628    fn to_io_bytes(&self) -> Option<&'_ [u8]> {
629        self.as_os_str().to_io_bytes()
630    }
631
632    #[inline]
633    fn to_io_bytes_lossy(&self) -> Cow<'_, [u8]> {
634        self.as_os_str().to_io_bytes_lossy()
635    }
636
637    if_conversions! {
638        #[inline]
639        fn to_raw_bytes(&self) -> Cow<'_, [u8]> {
640            self.as_os_str().to_raw_bytes()
641        }
642    }
643}
644
645/// A platform agnostic variant of [`OsStringExt`].
646///
647/// For more information, see [the module-level documentation][module].
648///
649/// [module]: self
650/// [`OsStringExt`]: ::std::os::unix::ffi::OsStringExt
651pub trait OsStringBytes: private::Sealed + Sized {
652    if_conversions! {
653        /// Converts a byte string into an equivalent platform-native string.
654        ///
655        /// # Panics
656        ///
657        /// Panics if the string is not valid for the [unspecified encoding]
658        /// used by this crate.
659        ///
660        /// # Examples
661        ///
662        /// ```
663        /// use std::env;
664        /// use std::ffi::OsString;
665        /// # use std::io;
666        ///
667        /// use os_str_bytes::OsStringBytes;
668        ///
669        /// let os_string = env::current_exe()?;
670        /// let os_bytes = os_string.clone().into_raw_vec();
671        /// assert_eq!(os_string, OsString::assert_from_raw_vec(os_bytes));
672        /// #
673        /// # Ok::<_, io::Error>(())
674        /// ```
675        ///
676        /// [unspecified encoding]: self#encoding-conversions
677        #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
678        #[must_use = "method should not be used for validation"]
679        #[track_caller]
680        fn assert_from_raw_vec(string: Vec<u8>) -> Self;
681    }
682
683    /// Converts a byte string into an equivalent platform-native string, if it
684    /// is [IO-safe].
685    ///
686    /// # Examples
687    ///
688    /// ```
689    /// use std::ffi::OsString;
690    /// use std::io;
691    /// use std::io::Read;
692    ///
693    /// use os_str_bytes::OsStringBytes;
694    ///
695    /// let mut io_string = Vec::new();
696    /// let _ = io::stdin().read_to_end(&mut io_string)?;
697    /// let os_string = OsString::from_io_vec(io_string).ok_or_else(|| {
698    ///     io::Error::new(io::ErrorKind::InvalidInput, "invalid input")
699    /// })?;
700    /// println!("{:?}", os_string);
701    /// #
702    /// # Ok::<_, io::Error>(())
703    /// ```
704    ///
705    /// [IO-safe]: self#user-input
706    #[must_use]
707    fn from_io_vec(string: Vec<u8>) -> Option<Self>;
708
709    if_checked_conversions! {
710        /// Converts a byte string into an equivalent platform-native string.
711        ///
712        /// [`assert_from_raw_vec`] should almost always be used instead. For
713        /// more information, see [`EncodingError`].
714        ///
715        /// # Errors
716        ///
717        /// See documentation for [`EncodingError`].
718        ///
719        /// # Examples
720        ///
721        /// ```
722        /// use std::env;
723        /// use std::ffi::OsString;
724        /// # use std::io;
725        ///
726        /// use os_str_bytes::OsStringBytes;
727        ///
728        /// let os_string = env::current_exe()?;
729        /// let os_bytes = os_string.clone().into_raw_vec();
730        /// assert_eq!(
731        ///     os_string,
732        ///     OsString::from_raw_vec(os_bytes).unwrap(),
733        /// );
734        /// #
735        /// # Ok::<_, io::Error>(())
736        /// ```
737        ///
738        /// [`assert_from_raw_vec`]: Self::assert_from_raw_vec
739        #[cfg_attr(
740            os_str_bytes_docs_rs,
741            doc(cfg(feature = "checked_conversions"))
742        )]
743        fn from_raw_vec(string: Vec<u8>) -> Result<Self>;
744    }
745
746    /// Converts a platform-native string into an equivalent byte string, if it
747    /// is [IO-safe].
748    ///
749    /// # Examples
750    ///
751    /// ```
752    /// use std::env;
753    /// use std::io;
754    /// use std::io::Write;
755    ///
756    /// use os_str_bytes::OsStringBytes;
757    ///
758    /// let os_string = env::current_exe()?;
759    /// let io_string = os_string.into_io_vec().ok_or_else(|| {
760    ///     io::Error::new(io::ErrorKind::InvalidInput, "invalid input")
761    /// })?;
762    /// io::stdout().write_all(&io_string)?;
763    /// #
764    /// # Ok::<_, io::Error>(())
765    /// ```
766    ///
767    /// [IO-safe]: self#user-input
768    #[must_use]
769    fn into_io_vec(self) -> Option<Vec<u8>>;
770
771    /// Converts a platform-native string into an equivalent byte string.
772    ///
773    /// If the string is not [IO-safe], invalid characters will be replaced
774    /// with [`REPLACEMENT_CHARACTER`].
775    ///
776    /// # Examples
777    ///
778    /// ```
779    /// use std::env;
780    /// use std::io;
781    /// use std::io::Write;
782    ///
783    /// use os_str_bytes::OsStringBytes;
784    ///
785    /// let os_string = env::current_exe()?;
786    /// let io_string = os_string.into_io_vec_lossy();
787    /// io::stdout().write_all(&io_string)?;
788    /// #
789    /// # Ok::<_, io::Error>(())
790    /// ```
791    ///
792    /// [IO-safe]: self#user-input
793    /// [`REPLACEMENT_CHARACTER`]: char::REPLACEMENT_CHARACTER
794    #[must_use]
795    fn into_io_vec_lossy(self) -> Vec<u8>;
796
797    if_conversions! {
798        /// Converts a platform-native string into an equivalent byte string.
799        ///
800        /// The returned string will use an [unspecified encoding].
801        ///
802        /// # Examples
803        ///
804        /// ```
805        /// use std::ffi::OsString;
806        ///
807        /// use os_str_bytes::OsStringBytes;
808        ///
809        /// let string = "foobar".to_owned();
810        /// let os_string: OsString = string.clone().into();
811        /// assert_eq!(string.into_bytes(), os_string.into_raw_vec());
812        /// ```
813        ///
814        /// [unspecified encoding]: self#encoding-conversions
815        #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "conversions")))]
816        #[must_use]
817        fn into_raw_vec(self) -> Vec<u8>;
818    }
819}
820
821impl OsStringBytes for OsString {
822    if_conversions! {
823        #[inline]
824        fn assert_from_raw_vec(string: Vec<u8>) -> Self {
825            expect_encoded!(convert::os_string_from_vec(string))
826        }
827    }
828
829    if_checked_conversions! {
830        #[inline]
831        fn from_raw_vec(string: Vec<u8>) -> Result<Self> {
832            convert::os_string_from_vec(string).map_err(EncodingError)
833        }
834    }
835
836    #[inline]
837    fn from_io_vec(string: Vec<u8>) -> Option<Self> {
838        convert_io::os_string_from_vec(string)
839    }
840
841    #[inline]
842    fn into_io_vec(self) -> Option<Vec<u8>> {
843        convert_io::os_string_into_vec(self)
844    }
845
846    #[inline]
847    fn into_io_vec_lossy(self) -> Vec<u8> {
848        convert_io::os_string_into_vec_lossy(self)
849    }
850
851    if_conversions! {
852        #[inline]
853        fn into_raw_vec(self) -> Vec<u8> {
854            convert::os_string_into_vec(self)
855        }
856    }
857}
858
859impl OsStringBytes for PathBuf {
860    if_conversions! {
861        #[inline]
862        fn assert_from_raw_vec(string: Vec<u8>) -> Self {
863            OsString::assert_from_raw_vec(string).into()
864        }
865    }
866
867    if_checked_conversions! {
868        #[inline]
869        fn from_raw_vec(string: Vec<u8>) -> Result<Self> {
870            OsString::from_raw_vec(string).map(Into::into)
871        }
872    }
873
874    #[inline]
875    fn from_io_vec(string: Vec<u8>) -> Option<Self> {
876        OsString::from_io_vec(string).map(Into::into)
877    }
878
879    #[inline]
880    fn into_io_vec(self) -> Option<Vec<u8>> {
881        self.into_os_string().into_io_vec()
882    }
883
884    #[inline]
885    fn into_io_vec_lossy(self) -> Vec<u8> {
886        self.into_os_string().into_io_vec_lossy()
887    }
888
889    if_conversions! {
890        #[inline]
891        fn into_raw_vec(self) -> Vec<u8> {
892            self.into_os_string().into_raw_vec()
893        }
894    }
895}
896
897mod private {
898    use std::ffi::OsStr;
899    use std::ffi::OsString;
900    use std::path::Path;
901    use std::path::PathBuf;
902
903    if_raw_str! {
904        use std::borrow::Cow;
905
906        use super::RawOsStr;
907    }
908
909    pub trait Sealed {}
910
911    impl Sealed for char {}
912    impl Sealed for OsStr {}
913    impl Sealed for OsString {}
914    impl Sealed for Path {}
915    impl Sealed for PathBuf {}
916    impl Sealed for &str {}
917    impl Sealed for &String {}
918
919    if_raw_str! {
920        impl Sealed for Cow<'_, RawOsStr> {}
921    }
922}