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