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}