1 use std::borrow::Borrow;
2 use std::borrow::Cow;
3 use std::borrow::ToOwned;
4 use std::ffi::OsStr;
5 use std::ffi::OsString;
6 use std::fmt;
7 use std::fmt::Debug;
8 use std::fmt::Display;
9 use std::fmt::Formatter;
10 use std::mem;
11 use std::ops::Deref;
12 use std::ops::Index;
13 use std::ops::Range;
14 use std::ops::RangeFrom;
15 use std::ops::RangeFull;
16 use std::ops::RangeInclusive;
17 use std::ops::RangeTo;
18 use std::ops::RangeToInclusive;
19 use std::result;
20 use std::str;
21
22 #[cfg(feature = "memchr")]
23 use memchr::memmem::find;
24 #[cfg(feature = "memchr")]
25 use memchr::memmem::rfind;
26
27 use super::imp;
28 use super::imp::raw;
29 use super::iter::Split;
30 use super::pattern::Encoded as EncodedPattern;
31 use super::private;
32 use super::Pattern;
33
34 if_checked_conversions! {
35 use super::EncodingError;
36 use super::Result;
37 }
38
39 #[cfg(not(feature = "memchr"))]
find(string: &[u8], pat: &[u8]) -> Option<usize>40 fn find(string: &[u8], pat: &[u8]) -> Option<usize> {
41 (0..=string.len().checked_sub(pat.len())?)
42 .find(|&x| string[x..].starts_with(pat))
43 }
44
45 #[cfg(not(feature = "memchr"))]
rfind(string: &[u8], pat: &[u8]) -> Option<usize>46 fn rfind(string: &[u8], pat: &[u8]) -> Option<usize> {
47 (pat.len()..=string.len())
48 .rfind(|&x| string[..x].ends_with(pat))
49 .map(|x| x - pat.len())
50 }
51
52 #[allow(clippy::missing_safety_doc)]
53 unsafe trait TransmuteBox {
transmute_box<R>(self: Box<Self>) -> Box<R> where R: ?Sized + TransmuteBox,54 fn transmute_box<R>(self: Box<Self>) -> Box<R>
55 where
56 R: ?Sized + TransmuteBox,
57 {
58 let value = Box::into_raw(self);
59 // SAFETY: This trait is only implemented for types that can be
60 // transmuted.
61 unsafe { Box::from_raw(mem::transmute_copy(&value)) }
62 }
63 }
64
65 // SAFETY: This struct has a layout that makes this operation safe.
66 unsafe impl TransmuteBox for RawOsStr {}
67 unsafe impl TransmuteBox for [u8] {}
68
69 /// A container for borrowed byte strings converted by this crate.
70 ///
71 /// This wrapper is intended to prevent violating the invariants of the
72 /// [unspecified encoding] used by this crate and minimize encoding
73 /// conversions.
74 ///
75 /// # Indices
76 ///
77 /// Methods of this struct that accept indices require that the index lie on a
78 /// UTF-8 boundary. Although it is possible to manipulate platform strings
79 /// based on other indices, this crate currently does not support them for
80 /// slicing methods. They would add significant complication to the
81 /// implementation and are generally not necessary. However, all indices
82 /// returned by this struct can be used for slicing.
83 ///
84 /// On Unix, all indices are permitted, to avoid false positives. However,
85 /// relying on this implementation detail is discouraged. Platform-specific
86 /// indices are error-prone.
87 ///
88 /// # Complexity
89 ///
90 /// All searching methods have worst-case multiplicative time complexity (i.e.,
91 /// `O(self.raw_len() * pat.len())`). Enabling the "memchr" feature allows
92 /// these methods to instead run in linear time in the worst case (documented
93 /// for [`memchr::memmem::find`][memchr complexity]).
94 ///
95 /// # Safety
96 ///
97 /// Although this type is annotated with `#[repr(transparent)]`, the inner
98 /// representation is not stable. Transmuting between this type and any other
99 /// causes immediate undefined behavior.
100 ///
101 /// [memchr complexity]: memchr::memmem::find#complexity
102 /// [unspecified encoding]: super#encoding
103 #[derive(Eq, Hash, Ord, PartialEq, PartialOrd)]
104 #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "raw_os_str")))]
105 #[repr(transparent)]
106 pub struct RawOsStr([u8]);
107
108 impl RawOsStr {
from_inner(string: &[u8]) -> &Self109 const fn from_inner(string: &[u8]) -> &Self {
110 // SAFETY: This struct has a layout that makes this operation safe.
111 unsafe { mem::transmute(string) }
112 }
113
114 /// Converts a platform-native string into a representation that can be
115 /// more easily manipulated.
116 ///
117 /// This method performs the necessary conversion immediately, so it can be
118 /// expensive to call. It is recommended to continue using the returned
119 /// instance as long as possible (instead of the original [`OsStr`]), to
120 /// avoid repeated conversions.
121 ///
122 /// # Examples
123 ///
124 /// ```
125 /// use std::env;
126 /// # use std::io;
127 ///
128 /// use os_str_bytes::RawOsStr;
129 ///
130 /// let os_string = env::current_exe()?.into_os_string();
131 /// println!("{:?}", RawOsStr::new(&os_string));
132 /// #
133 /// # Ok::<_, io::Error>(())
134 /// ```
135 #[inline]
136 #[must_use]
new(string: &OsStr) -> Cow<'_, Self>137 pub fn new(string: &OsStr) -> Cow<'_, Self> {
138 match imp::os_str_to_bytes(string) {
139 Cow::Borrowed(string) => Cow::Borrowed(Self::from_inner(string)),
140 Cow::Owned(string) => Cow::Owned(RawOsString(string)),
141 }
142 }
143
144 /// Wraps a string, without copying or encoding conversion.
145 ///
146 /// This method is much more efficient than [`RawOsStr::new`], since the
147 /// [encoding] used by this crate is compatible with UTF-8.
148 ///
149 /// # Examples
150 ///
151 /// ```
152 /// use os_str_bytes::RawOsStr;
153 ///
154 /// let string = "foobar";
155 /// let raw = RawOsStr::from_str(string);
156 /// assert_eq!(string, raw);
157 /// ```
158 ///
159 /// [encoding]: super#encoding
160 #[allow(clippy::should_implement_trait)]
161 #[inline]
162 #[must_use]
from_str(string: &str) -> &Self163 pub fn from_str(string: &str) -> &Self {
164 Self::from_inner(string.as_bytes())
165 }
166
167 /// Wraps a byte string, without copying or encoding conversion.
168 ///
169 /// # Panics
170 ///
171 /// Panics if the string is not valid for the [unspecified encoding] used
172 /// by this crate.
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use std::env;
178 /// # use std::io;
179 ///
180 /// use os_str_bytes::RawOsStr;
181 ///
182 /// let os_string = env::current_exe()?.into_os_string();
183 /// let raw = RawOsStr::new(&os_string);
184 /// let raw_bytes = raw.as_raw_bytes();
185 /// assert_eq!(&*raw, RawOsStr::assert_from_raw_bytes(raw_bytes));
186 /// #
187 /// # Ok::<_, io::Error>(())
188 /// ```
189 ///
190 /// [unspecified encoding]: super#encoding
191 #[inline]
192 #[must_use = "method should not be used for validation"]
193 #[track_caller]
assert_from_raw_bytes(string: &[u8]) -> &Self194 pub fn assert_from_raw_bytes(string: &[u8]) -> &Self {
195 expect_encoded!(raw::validate_bytes(string));
196
197 Self::from_inner(string)
198 }
199
200 if_checked_conversions! {
201 /// Wraps a byte string, without copying or encoding conversion.
202 ///
203 /// [`assert_from_raw_bytes`] should almost always be used instead. For
204 /// more information, see [`EncodingError`].
205 ///
206 /// # Errors
207 ///
208 /// See documentation for [`EncodingError`].
209 ///
210 /// # Examples
211 ///
212 /// ```
213 /// use std::env;
214 /// # use std::io;
215 ///
216 /// use os_str_bytes::RawOsStr;
217 ///
218 /// let os_string = env::current_exe()?.into_os_string();
219 /// let raw = RawOsStr::new(&os_string);
220 /// assert_eq!(Ok(&*raw), RawOsStr::from_raw_bytes(raw.as_raw_bytes()));
221 /// #
222 /// # Ok::<_, io::Error>(())
223 /// ```
224 ///
225 /// [`assert_from_raw_bytes`]: Self::assert_from_raw_bytes
226 #[cfg_attr(
227 os_str_bytes_docs_rs,
228 doc(cfg(feature = "checked_conversions"))
229 )]
230 #[inline]
231 pub fn from_raw_bytes(string: &[u8]) -> Result<&Self> {
232 raw::validate_bytes(string)
233 .map(|()| Self::from_inner(string))
234 .map_err(EncodingError)
235 }
236 }
237
238 /// Wraps a byte string, without copying or encoding conversion.
239 ///
240 /// # Safety
241 ///
242 /// The string must be valid for the [unspecified encoding] used by this
243 /// crate.
244 ///
245 /// # Examples
246 ///
247 /// ```
248 /// use std::env;
249 /// # use std::io;
250 ///
251 /// use os_str_bytes::RawOsStr;
252 ///
253 /// let os_string = env::current_exe()?.into_os_string();
254 /// let raw = RawOsStr::new(&os_string);
255 /// let raw_bytes = raw.as_raw_bytes();
256 /// assert_eq!(&*raw, unsafe {
257 /// RawOsStr::from_raw_bytes_unchecked(raw_bytes)
258 /// });
259 /// #
260 /// # Ok::<_, io::Error>(())
261 /// ```
262 ///
263 /// [unspecified encoding]: super#encoding
264 #[inline]
265 #[must_use]
266 #[track_caller]
from_raw_bytes_unchecked(string: &[u8]) -> &Self267 pub unsafe fn from_raw_bytes_unchecked(string: &[u8]) -> &Self {
268 if cfg!(debug_assertions) {
269 expect_encoded!(raw::validate_bytes(string));
270 }
271
272 Self::from_inner(string)
273 }
274
275 /// Returns the byte string stored by this container.
276 ///
277 /// The returned string will use an [unspecified encoding].
278 ///
279 /// # Examples
280 ///
281 /// ```
282 /// use os_str_bytes::RawOsStr;
283 ///
284 /// let string = "foobar";
285 /// let raw = RawOsStr::from_str(string);
286 /// assert_eq!(string.as_bytes(), raw.as_raw_bytes());
287 /// ```
288 ///
289 /// [unspecified encoding]: super#encoding
290 #[inline]
291 #[must_use]
as_raw_bytes(&self) -> &[u8]292 pub fn as_raw_bytes(&self) -> &[u8] {
293 &self.0
294 }
295
296 /// Equivalent to [`str::contains`].
297 ///
298 /// # Examples
299 ///
300 /// ```
301 /// use os_str_bytes::RawOsStr;
302 ///
303 /// let raw = RawOsStr::from_str("foobar");
304 /// assert!(raw.contains("oo"));
305 /// assert!(!raw.contains("of"));
306 /// ```
307 #[inline]
308 #[must_use]
contains<P>(&self, pat: P) -> bool where P: Pattern,309 pub fn contains<P>(&self, pat: P) -> bool
310 where
311 P: Pattern,
312 {
313 self.find(pat).is_some()
314 }
315
316 /// Equivalent to [`str::ends_with`].
317 ///
318 /// # Examples
319 ///
320 /// ```
321 /// use os_str_bytes::RawOsStr;
322 ///
323 /// let raw = RawOsStr::from_str("foobar");
324 /// assert!(raw.ends_with("bar"));
325 /// assert!(!raw.ends_with("foo"));
326 /// ```
327 #[inline]
328 #[must_use]
ends_with<P>(&self, pat: P) -> bool where P: Pattern,329 pub fn ends_with<P>(&self, pat: P) -> bool
330 where
331 P: Pattern,
332 {
333 let pat = pat.__encode();
334 let pat = pat.__get();
335
336 self.0.ends_with(pat)
337 }
338
339 /// Equivalent to [`str::ends_with`] but accepts this type for the pattern.
340 ///
341 /// # Examples
342 ///
343 /// ```
344 /// use os_str_bytes::RawOsStr;
345 ///
346 /// let raw = RawOsStr::from_str("foobar");
347 /// assert!(raw.ends_with_os(RawOsStr::from_str("bar")));
348 /// assert!(!raw.ends_with_os(RawOsStr::from_str("foo")));
349 /// ```
350 #[inline]
351 #[must_use]
ends_with_os(&self, pat: &Self) -> bool352 pub fn ends_with_os(&self, pat: &Self) -> bool {
353 raw::ends_with(&self.0, &pat.0)
354 }
355
356 /// Equivalent to [`str::find`].
357 ///
358 /// # Examples
359 ///
360 /// ```
361 /// use os_str_bytes::RawOsStr;
362 ///
363 /// let raw = RawOsStr::from_str("foobar");
364 /// assert_eq!(Some(1), raw.find("o"));
365 /// assert_eq!(None, raw.find("of"));
366 /// ```
367 #[inline]
368 #[must_use]
find<P>(&self, pat: P) -> Option<usize> where P: Pattern,369 pub fn find<P>(&self, pat: P) -> Option<usize>
370 where
371 P: Pattern,
372 {
373 let pat = pat.__encode();
374 let pat = pat.__get();
375
376 find(&self.0, pat)
377 }
378
379 /// Equivalent to [`str::is_empty`].
380 ///
381 /// # Examples
382 ///
383 /// ```
384 /// use os_str_bytes::RawOsStr;
385 ///
386 /// assert!(RawOsStr::from_str("").is_empty());
387 /// assert!(!RawOsStr::from_str("foobar").is_empty());
388 /// ```
389 #[inline]
390 #[must_use]
is_empty(&self) -> bool391 pub fn is_empty(&self) -> bool {
392 self.0.is_empty()
393 }
394
395 /// Returns the length of the byte string stored by this container.
396 ///
397 /// Only the following assumptions can be made about the result:
398 /// - The length of any Unicode character is the length of its UTF-8
399 /// representation (i.e., [`char::len_utf8`]).
400 /// - Splitting a string at a UTF-8 boundary will return two strings with
401 /// lengths that sum to the length of the original string.
402 ///
403 /// This method may return a different result than would [`OsStr::len`]
404 /// when called on same string, since [`OsStr`] uses an unspecified
405 /// encoding.
406 ///
407 /// # Examples
408 ///
409 /// ```
410 /// use os_str_bytes::RawOsStr;
411 ///
412 /// assert_eq!(6, RawOsStr::from_str("foobar").raw_len());
413 /// assert_eq!(0, RawOsStr::from_str("").raw_len());
414 /// ```
415 #[inline]
416 #[must_use]
raw_len(&self) -> usize417 pub fn raw_len(&self) -> usize {
418 self.0.len()
419 }
420
421 /// Equivalent to [`str::rfind`].
422 ///
423 /// # Examples
424 ///
425 /// ```
426 /// use os_str_bytes::RawOsStr;
427 ///
428 /// let raw = RawOsStr::from_str("foobar");
429 /// assert_eq!(Some(2), raw.rfind("o"));
430 /// assert_eq!(None, raw.rfind("of"));
431 /// ```
432 #[inline]
433 #[must_use]
rfind<P>(&self, pat: P) -> Option<usize> where P: Pattern,434 pub fn rfind<P>(&self, pat: P) -> Option<usize>
435 where
436 P: Pattern,
437 {
438 let pat = pat.__encode();
439 let pat = pat.__get();
440
441 rfind(&self.0, pat)
442 }
443
split_once_raw_with<P, F>( &self, pat: &P, find_fn: F, ) -> Option<(&Self, &Self)> where F: FnOnce(&[u8], &[u8]) -> Option<usize>, P: EncodedPattern,444 fn split_once_raw_with<P, F>(
445 &self,
446 pat: &P,
447 find_fn: F,
448 ) -> Option<(&Self, &Self)>
449 where
450 F: FnOnce(&[u8], &[u8]) -> Option<usize>,
451 P: EncodedPattern,
452 {
453 let pat = pat.__get();
454
455 let index = find_fn(&self.0, pat)?;
456 let prefix = &self.0[..index];
457 let suffix = &self.0[index + pat.len()..];
458 Some((Self::from_inner(prefix), Self::from_inner(suffix)))
459 }
460
rsplit_once_raw<P>(&self, pat: &P) -> Option<(&Self, &Self)> where P: EncodedPattern,461 pub(super) fn rsplit_once_raw<P>(&self, pat: &P) -> Option<(&Self, &Self)>
462 where
463 P: EncodedPattern,
464 {
465 self.split_once_raw_with(pat, rfind)
466 }
467
468 /// Equivalent to [`str::rsplit_once`].
469 ///
470 /// # Examples
471 ///
472 /// ```
473 /// use os_str_bytes::RawOsStr;
474 ///
475 /// let raw = RawOsStr::from_str("foobar");
476 /// assert_eq!(
477 /// Some((RawOsStr::from_str("fo"), RawOsStr::from_str("bar"))),
478 /// raw.rsplit_once("o"),
479 /// );
480 /// assert_eq!(None, raw.rsplit_once("of"));
481 /// ```
482 #[inline]
483 #[must_use]
rsplit_once<P>(&self, pat: P) -> Option<(&Self, &Self)> where P: Pattern,484 pub fn rsplit_once<P>(&self, pat: P) -> Option<(&Self, &Self)>
485 where
486 P: Pattern,
487 {
488 self.rsplit_once_raw(&pat.__encode())
489 }
490
491 // https://github.com/rust-lang/rust/blob/49c68bd53f90e375bfb3cbba8c1c67a9e0adb9c0/src/libcore/str/mod.rs#L2184-L2221
492 #[cold]
493 #[inline(never)]
494 #[track_caller]
index_boundary_error(&self, index: usize) -> !495 fn index_boundary_error(&self, index: usize) -> ! {
496 debug_assert!(raw::is_continuation(self.0[index]));
497
498 let start = expect_encoded!(self.0[..index]
499 .iter()
500 .rposition(|&x| !raw::is_continuation(x)));
501 let mut end = index + 1;
502 end += self.0[end..]
503 .iter()
504 .take_while(|&&x| raw::is_continuation(x))
505 .count();
506 let code_point = raw::decode_code_point(&self.0[start..end]);
507 panic!(
508 "byte index {} is not a valid boundary; it is inside U+{:04X} \
509 (bytes {}..{})",
510 index, code_point, start, end,
511 );
512 }
513
514 #[track_caller]
check_bound(&self, index: usize)515 fn check_bound(&self, index: usize) {
516 if let Some(&byte) = self.0.get(index) {
517 if raw::is_continuation(byte) {
518 self.index_boundary_error(index);
519 }
520 }
521 }
522
523 /// Equivalent to [`str::split`], but empty patterns are not accepted.
524 ///
525 /// # Panics
526 ///
527 /// Panics if the pattern is empty.
528 ///
529 /// # Examples
530 ///
531 /// ```
532 /// use os_str_bytes::RawOsStr;
533 ///
534 /// let raw = RawOsStr::from_str("foobar");
535 /// assert_eq!(["f", "", "bar"], *raw.split("o").collect::<Vec<_>>());
536 /// ```
537 #[inline]
538 #[must_use]
539 #[track_caller]
split<P>(&self, pat: P) -> Split<'_, P> where P: Pattern,540 pub fn split<P>(&self, pat: P) -> Split<'_, P>
541 where
542 P: Pattern,
543 {
544 Split::new(self, pat)
545 }
546
547 /// Equivalent to [`str::split_at`].
548 ///
549 /// # Panics
550 ///
551 /// Panics if the index is not a [valid boundary].
552 ///
553 /// # Examples
554 ///
555 /// ```
556 /// use os_str_bytes::RawOsStr;
557 ///
558 /// let raw = RawOsStr::from_str("foobar");
559 /// assert_eq!(
560 /// ((RawOsStr::from_str("fo"), RawOsStr::from_str("obar"))),
561 /// raw.split_at(2),
562 /// );
563 /// ```
564 ///
565 /// [valid boundary]: #indices
566 #[inline]
567 #[must_use]
568 #[track_caller]
split_at(&self, mid: usize) -> (&Self, &Self)569 pub fn split_at(&self, mid: usize) -> (&Self, &Self) {
570 self.check_bound(mid);
571
572 let (prefix, suffix) = self.0.split_at(mid);
573 (Self::from_inner(prefix), Self::from_inner(suffix))
574 }
575
split_once_raw<P>(&self, pat: &P) -> Option<(&Self, &Self)> where P: EncodedPattern,576 pub(super) fn split_once_raw<P>(&self, pat: &P) -> Option<(&Self, &Self)>
577 where
578 P: EncodedPattern,
579 {
580 self.split_once_raw_with(pat, find)
581 }
582
583 /// Equivalent to [`str::split_once`].
584 ///
585 /// # Examples
586 ///
587 /// ```
588 /// use os_str_bytes::RawOsStr;
589 ///
590 /// let raw = RawOsStr::from_str("foobar");
591 /// assert_eq!(
592 /// Some((RawOsStr::from_str("f"), RawOsStr::from_str("obar"))),
593 /// raw.split_once("o"),
594 /// );
595 /// assert_eq!(None, raw.split_once("of"));
596 /// ```
597 #[inline]
598 #[must_use]
split_once<P>(&self, pat: P) -> Option<(&Self, &Self)> where P: Pattern,599 pub fn split_once<P>(&self, pat: P) -> Option<(&Self, &Self)>
600 where
601 P: Pattern,
602 {
603 self.split_once_raw(&pat.__encode())
604 }
605
606 /// Equivalent to [`str::starts_with`].
607 ///
608 /// # Examples
609 ///
610 /// ```
611 /// use os_str_bytes::RawOsStr;
612 ///
613 /// let raw = RawOsStr::from_str("foobar");
614 /// assert!(raw.starts_with("foo"));
615 /// assert!(!raw.starts_with("bar"));
616 /// ```
617 #[inline]
618 #[must_use]
starts_with<P>(&self, pat: P) -> bool where P: Pattern,619 pub fn starts_with<P>(&self, pat: P) -> bool
620 where
621 P: Pattern,
622 {
623 let pat = pat.__encode();
624 let pat = pat.__get();
625
626 self.0.starts_with(pat)
627 }
628
629 /// Equivalent to [`str::starts_with`] but accepts this type for the
630 /// pattern.
631 ///
632 /// # Examples
633 ///
634 /// ```
635 /// use os_str_bytes::RawOsStr;
636 ///
637 /// let raw = RawOsStr::from_str("foobar");
638 /// assert!(raw.starts_with_os(RawOsStr::from_str("foo")));
639 /// assert!(!raw.starts_with_os(RawOsStr::from_str("bar")));
640 /// ```
641 #[inline]
642 #[must_use]
starts_with_os(&self, pat: &Self) -> bool643 pub fn starts_with_os(&self, pat: &Self) -> bool {
644 raw::starts_with(&self.0, &pat.0)
645 }
646
647 /// Equivalent to [`str::strip_prefix`].
648 ///
649 /// # Examples
650 ///
651 /// ```
652 /// use os_str_bytes::RawOsStr;
653 ///
654 /// let raw = RawOsStr::from_str("111foo1bar111");
655 /// assert_eq!(
656 /// Some(RawOsStr::from_str("11foo1bar111")),
657 /// raw.strip_prefix("1"),
658 /// );
659 /// assert_eq!(None, raw.strip_prefix("o"));
660 /// ```
661 #[inline]
662 #[must_use]
strip_prefix<P>(&self, pat: P) -> Option<&Self> where P: Pattern,663 pub fn strip_prefix<P>(&self, pat: P) -> Option<&Self>
664 where
665 P: Pattern,
666 {
667 let pat = pat.__encode();
668 let pat = pat.__get();
669
670 self.0.strip_prefix(pat).map(Self::from_inner)
671 }
672
673 /// Equivalent to [`str::strip_suffix`].
674 ///
675 /// # Examples
676 ///
677 /// ```
678 /// use os_str_bytes::RawOsStr;
679 ///
680 /// let raw = RawOsStr::from_str("111foo1bar111");
681 /// assert_eq!(
682 /// Some(RawOsStr::from_str("111foo1bar11")),
683 /// raw.strip_suffix("1"),
684 /// );
685 /// assert_eq!(None, raw.strip_suffix("o"));
686 /// ```
687 #[inline]
688 #[must_use]
strip_suffix<P>(&self, pat: P) -> Option<&Self> where P: Pattern,689 pub fn strip_suffix<P>(&self, pat: P) -> Option<&Self>
690 where
691 P: Pattern,
692 {
693 let pat = pat.__encode();
694 let pat = pat.__get();
695
696 self.0.strip_suffix(pat).map(Self::from_inner)
697 }
698
699 /// Converts this representation back to a platform-native string.
700 ///
701 /// When possible, use [`RawOsStrCow::into_os_str`] for a more efficient
702 /// conversion on some platforms.
703 ///
704 /// # Examples
705 ///
706 /// ```
707 /// use std::env;
708 /// # use std::io;
709 ///
710 /// use os_str_bytes::RawOsStr;
711 ///
712 /// let os_string = env::current_exe()?.into_os_string();
713 /// let raw = RawOsStr::new(&os_string);
714 /// assert_eq!(os_string, raw.to_os_str());
715 /// #
716 /// # Ok::<_, io::Error>(())
717 /// ```
718 #[inline]
719 #[must_use]
to_os_str(&self) -> Cow<'_, OsStr>720 pub fn to_os_str(&self) -> Cow<'_, OsStr> {
721 expect_encoded!(imp::os_str_from_bytes(&self.0))
722 }
723
724 /// Equivalent to [`OsStr::to_str`].
725 ///
726 /// # Examples
727 ///
728 /// ```
729 /// use os_str_bytes::RawOsStr;
730 ///
731 /// let string = "foobar";
732 /// let raw = RawOsStr::from_str(string);
733 /// assert_eq!(Some(string), raw.to_str());
734 /// ```
735 #[inline]
736 #[must_use]
to_str(&self) -> Option<&str>737 pub fn to_str(&self) -> Option<&str> {
738 str::from_utf8(&self.0).ok()
739 }
740
741 /// Converts this string to the best UTF-8 representation possible.
742 ///
743 /// Invalid sequences will be replaced with
744 /// [`char::REPLACEMENT_CHARACTER`].
745 ///
746 /// This method may return a different result than would
747 /// [`OsStr::to_string_lossy`] when called on same string, since [`OsStr`]
748 /// uses an unspecified encoding.
749 ///
750 /// # Examples
751 ///
752 /// ```
753 /// use std::env;
754 /// # use std::io;
755 ///
756 /// use os_str_bytes::RawOsStr;
757 ///
758 /// let os_string = env::current_exe()?.into_os_string();
759 /// let raw = RawOsStr::new(&os_string);
760 /// println!("{}", raw.to_str_lossy());
761 /// #
762 /// # Ok::<_, io::Error>(())
763 /// ```
764 #[inline]
765 #[must_use]
to_str_lossy(&self) -> Cow<'_, str>766 pub fn to_str_lossy(&self) -> Cow<'_, str> {
767 String::from_utf8_lossy(&self.0)
768 }
769
trim_matches_raw_with<P, F>(&self, pat: &P, strip_fn: F) -> &Self where F: for<'a> Fn(&'a [u8], &[u8]) -> Option<&'a [u8]>, P: EncodedPattern,770 fn trim_matches_raw_with<P, F>(&self, pat: &P, strip_fn: F) -> &Self
771 where
772 F: for<'a> Fn(&'a [u8], &[u8]) -> Option<&'a [u8]>,
773 P: EncodedPattern,
774 {
775 let pat = pat.__get();
776 if pat.is_empty() {
777 return self;
778 }
779
780 let mut string = &self.0;
781 while let Some(substring) = strip_fn(string, pat) {
782 string = substring;
783 }
784 Self::from_inner(string)
785 }
786
trim_end_matches_raw<P>(&self, pat: &P) -> &Self where P: EncodedPattern,787 fn trim_end_matches_raw<P>(&self, pat: &P) -> &Self
788 where
789 P: EncodedPattern,
790 {
791 self.trim_matches_raw_with(pat, <[_]>::strip_suffix)
792 }
793
794 /// Equivalent to [`str::trim_end_matches`].
795 ///
796 /// # Examples
797 ///
798 /// ```
799 /// use os_str_bytes::RawOsStr;
800 ///
801 /// let raw = RawOsStr::from_str("111foo1bar111");
802 /// assert_eq!("111foo1bar", raw.trim_end_matches("1"));
803 /// assert_eq!("111foo1bar111", raw.trim_end_matches("o"));
804 /// ```
805 #[inline]
806 #[must_use]
trim_end_matches<P>(&self, pat: P) -> &Self where P: Pattern,807 pub fn trim_end_matches<P>(&self, pat: P) -> &Self
808 where
809 P: Pattern,
810 {
811 self.trim_end_matches_raw(&pat.__encode())
812 }
813
814 /// Equivalent to [`str::trim_matches`].
815 ///
816 /// # Examples
817 ///
818 /// ```
819 /// use os_str_bytes::RawOsStr;
820 ///
821 /// let raw = RawOsStr::from_str("111foo1bar111");
822 /// assert_eq!("foo1bar", raw.trim_matches("1"));
823 /// assert_eq!("111foo1bar111", raw.trim_matches("o"));
824 /// ```
825 #[inline]
826 #[must_use]
trim_matches<P>(&self, pat: P) -> &Self where P: Pattern,827 pub fn trim_matches<P>(&self, pat: P) -> &Self
828 where
829 P: Pattern,
830 {
831 let pat = pat.__encode();
832 self.trim_start_matches_raw(&pat).trim_end_matches_raw(&pat)
833 }
834
trim_start_matches_raw<P>(&self, pat: &P) -> &Self where P: EncodedPattern,835 fn trim_start_matches_raw<P>(&self, pat: &P) -> &Self
836 where
837 P: EncodedPattern,
838 {
839 self.trim_matches_raw_with(pat, <[_]>::strip_prefix)
840 }
841
842 /// Equivalent to [`str::trim_start_matches`].
843 ///
844 /// # Examples
845 ///
846 /// ```
847 /// use os_str_bytes::RawOsStr;
848 ///
849 /// let raw = RawOsStr::from_str("111foo1bar111");
850 /// assert_eq!("foo1bar111", raw.trim_start_matches("1"));
851 /// assert_eq!("111foo1bar111", raw.trim_start_matches("o"));
852 /// ```
853 #[inline]
854 #[must_use]
trim_start_matches<P>(&self, pat: P) -> &Self where P: Pattern,855 pub fn trim_start_matches<P>(&self, pat: P) -> &Self
856 where
857 P: Pattern,
858 {
859 self.trim_start_matches_raw(&pat.__encode())
860 }
861 }
862
863 impl AsRef<Self> for RawOsStr {
864 #[inline]
as_ref(&self) -> &Self865 fn as_ref(&self) -> &Self {
866 self
867 }
868 }
869
870 impl AsRef<RawOsStr> for str {
871 #[inline]
as_ref(&self) -> &RawOsStr872 fn as_ref(&self) -> &RawOsStr {
873 RawOsStr::from_str(self)
874 }
875 }
876
877 impl AsRef<RawOsStr> for String {
878 #[inline]
as_ref(&self) -> &RawOsStr879 fn as_ref(&self) -> &RawOsStr {
880 (**self).as_ref()
881 }
882 }
883
884 impl Default for &RawOsStr {
885 #[inline]
default() -> Self886 fn default() -> Self {
887 RawOsStr::from_str("")
888 }
889 }
890
891 impl<'a> From<&'a RawOsStr> for Cow<'a, RawOsStr> {
892 #[inline]
from(value: &'a RawOsStr) -> Self893 fn from(value: &'a RawOsStr) -> Self {
894 Cow::Borrowed(value)
895 }
896 }
897
898 impl From<Box<str>> for Box<RawOsStr> {
899 #[inline]
from(value: Box<str>) -> Self900 fn from(value: Box<str>) -> Self {
901 value.into_boxed_bytes().transmute_box()
902 }
903 }
904
905 impl ToOwned for RawOsStr {
906 type Owned = RawOsString;
907
908 #[inline]
to_owned(&self) -> Self::Owned909 fn to_owned(&self) -> Self::Owned {
910 RawOsString(self.0.to_owned())
911 }
912 }
913
914 /// Extensions to [`Cow<RawOsStr>`] for additional conversions.
915 ///
916 /// [`Cow<RawOsStr>`]: Cow
917 #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "raw_os_str")))]
918 pub trait RawOsStrCow<'a>: private::Sealed {
919 /// Converts this representation back to a platform-native string.
920 ///
921 /// # Examples
922 ///
923 /// ```
924 /// use std::env;
925 /// # use std::io;
926 ///
927 /// use os_str_bytes::RawOsStr;
928 /// use os_str_bytes::RawOsStrCow;
929 ///
930 /// let os_string = env::current_exe()?.into_os_string();
931 /// let raw = RawOsStr::new(&os_string);
932 /// assert_eq!(os_string, raw.into_os_str());
933 /// #
934 /// # Ok::<_, io::Error>(())
935 /// ```
936 #[must_use]
into_os_str(self) -> Cow<'a, OsStr>937 fn into_os_str(self) -> Cow<'a, OsStr>;
938
939 /// Returns the byte string stored by this container.
940 ///
941 /// The returned string will use an [unspecified encoding].
942 ///
943 /// # Examples
944 ///
945 /// ```
946 /// use std::borrow::Cow;
947 ///
948 /// use os_str_bytes::RawOsStr;
949 /// use os_str_bytes::RawOsStrCow;
950 ///
951 /// let string = "foobar";
952 /// let raw = Cow::Borrowed(RawOsStr::from_str(string));
953 /// assert_eq!(string.as_bytes(), &*raw.into_raw_bytes());
954 /// ```
955 ///
956 /// [unspecified encoding]: super#encoding
957 #[must_use]
into_raw_bytes(self) -> Cow<'a, [u8]>958 fn into_raw_bytes(self) -> Cow<'a, [u8]>;
959 }
960
961 impl<'a> RawOsStrCow<'a> for Cow<'a, RawOsStr> {
962 #[inline]
into_os_str(self) -> Cow<'a, OsStr>963 fn into_os_str(self) -> Cow<'a, OsStr> {
964 match self {
965 Cow::Borrowed(string) => string.to_os_str(),
966 Cow::Owned(string) => Cow::Owned(string.into_os_string()),
967 }
968 }
969
970 #[inline]
into_raw_bytes(self) -> Cow<'a, [u8]>971 fn into_raw_bytes(self) -> Cow<'a, [u8]> {
972 match self {
973 Cow::Borrowed(string) => Cow::Borrowed(&string.0),
974 Cow::Owned(string) => Cow::Owned(string.0),
975 }
976 }
977 }
978
979 /// A container for owned byte strings converted by this crate.
980 ///
981 /// For more information, see [`RawOsStr`].
982 #[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
983 #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "raw_os_str")))]
984 pub struct RawOsString(Vec<u8>);
985
986 impl RawOsString {
987 /// Converts a platform-native string into a representation that can be
988 /// more easily manipulated.
989 ///
990 /// For more information, see [`RawOsStr::new`].
991 ///
992 /// # Examples
993 ///
994 /// ```
995 /// use std::env;
996 /// # use std::io;
997 ///
998 /// use os_str_bytes::RawOsString;
999 ///
1000 /// let os_string = env::current_exe()?.into_os_string();
1001 /// println!("{:?}", RawOsString::new(os_string));
1002 /// #
1003 /// # Ok::<_, io::Error>(())
1004 /// ```
1005 #[inline]
1006 #[must_use]
new(string: OsString) -> Self1007 pub fn new(string: OsString) -> Self {
1008 Self(imp::os_string_into_vec(string))
1009 }
1010
1011 /// Wraps a string, without copying or encoding conversion.
1012 ///
1013 /// This method is much more efficient than [`RawOsString::new`], since the
1014 /// [encoding] used by this crate is compatible with UTF-8.
1015 ///
1016 /// # Examples
1017 ///
1018 /// ```
1019 /// use os_str_bytes::RawOsString;
1020 ///
1021 /// let string = "foobar".to_owned();
1022 /// let raw = RawOsString::from_string(string.clone());
1023 /// assert_eq!(string, raw);
1024 /// ```
1025 ///
1026 /// [encoding]: super#encoding
1027 #[inline]
1028 #[must_use]
from_string(string: String) -> Self1029 pub fn from_string(string: String) -> Self {
1030 Self(string.into_bytes())
1031 }
1032
1033 /// Wraps a byte string, without copying or encoding conversion.
1034 ///
1035 /// # Panics
1036 ///
1037 /// Panics if the string is not valid for the [unspecified encoding] used
1038 /// by this crate.
1039 ///
1040 /// # Examples
1041 ///
1042 /// ```
1043 /// use std::env;
1044 /// # use std::io;
1045 ///
1046 /// use os_str_bytes::RawOsString;
1047 ///
1048 /// let os_string = env::current_exe()?.into_os_string();
1049 /// let raw = RawOsString::new(os_string);
1050 /// let raw_bytes = raw.clone().into_raw_vec();
1051 /// assert_eq!(raw, RawOsString::assert_from_raw_vec(raw_bytes));
1052 /// #
1053 /// # Ok::<_, io::Error>(())
1054 /// ```
1055 ///
1056 /// [unspecified encoding]: super#encoding
1057 #[inline]
1058 #[must_use = "method should not be used for validation"]
1059 #[track_caller]
assert_from_raw_vec(string: Vec<u8>) -> Self1060 pub fn assert_from_raw_vec(string: Vec<u8>) -> Self {
1061 expect_encoded!(raw::validate_bytes(&string));
1062
1063 Self(string)
1064 }
1065
1066 if_checked_conversions! {
1067 /// Wraps a byte string, without copying or encoding conversion.
1068 ///
1069 /// [`assert_from_raw_vec`] should almost always be used instead. For
1070 /// more information, see [`EncodingError`].
1071 ///
1072 /// # Errors
1073 ///
1074 /// See documentation for [`EncodingError`].
1075 ///
1076 /// # Examples
1077 ///
1078 /// ```
1079 /// use std::env;
1080 /// # use std::io;
1081 ///
1082 /// use os_str_bytes::RawOsString;
1083 ///
1084 /// let os_string = env::current_exe()?.into_os_string();
1085 /// let raw = RawOsString::new(os_string);
1086 /// let raw_clone = raw.clone();
1087 /// assert_eq!(Ok(raw), RawOsString::from_raw_vec(raw_clone.into_raw_vec()));
1088 /// #
1089 /// # Ok::<_, io::Error>(())
1090 /// ```
1091 ///
1092 /// [`assert_from_raw_vec`]: Self::assert_from_raw_vec
1093 #[cfg_attr(
1094 os_str_bytes_docs_rs,
1095 doc(cfg(feature = "checked_conversions"))
1096 )]
1097 #[inline]
1098 pub fn from_raw_vec(string: Vec<u8>) -> Result<Self> {
1099 raw::validate_bytes(&string)
1100 .map(|()| Self(string))
1101 .map_err(EncodingError)
1102 }
1103 }
1104
1105 /// Wraps a byte string, without copying or encoding conversion.
1106 ///
1107 /// # Safety
1108 ///
1109 /// The string must be valid for the [unspecified encoding] used by this
1110 /// crate.
1111 ///
1112 /// # Examples
1113 ///
1114 /// ```
1115 /// use std::env;
1116 /// # use std::io;
1117 ///
1118 /// use os_str_bytes::RawOsString;
1119 ///
1120 /// let os_string = env::current_exe()?.into_os_string();
1121 /// let raw = RawOsString::new(os_string);
1122 /// let raw_bytes = raw.clone().into_raw_vec();
1123 /// assert_eq!(raw, unsafe {
1124 /// RawOsString::from_raw_vec_unchecked(raw_bytes)
1125 /// });
1126 /// #
1127 /// # Ok::<_, io::Error>(())
1128 /// ```
1129 ///
1130 /// [unspecified encoding]: super#encoding
1131 #[inline]
1132 #[must_use]
1133 #[track_caller]
from_raw_vec_unchecked(string: Vec<u8>) -> Self1134 pub unsafe fn from_raw_vec_unchecked(string: Vec<u8>) -> Self {
1135 if cfg!(debug_assertions) {
1136 expect_encoded!(raw::validate_bytes(&string));
1137 }
1138
1139 Self(string)
1140 }
1141
1142 /// Equivalent to [`String::clear`].
1143 ///
1144 /// # Examples
1145 ///
1146 /// ```
1147 /// use std::env;
1148 /// # use std::io;
1149 ///
1150 /// use os_str_bytes::RawOsString;
1151 ///
1152 /// let os_string = env::current_exe()?.into_os_string();
1153 /// let mut raw = RawOsString::new(os_string);
1154 /// raw.clear();
1155 /// assert!(raw.is_empty());
1156 /// #
1157 /// # Ok::<_, io::Error>(())
1158 /// ```
1159 #[inline]
clear(&mut self)1160 pub fn clear(&mut self) {
1161 self.0.clear();
1162 }
1163
1164 /// Equivalent to [`String::into_boxed_str`].
1165 ///
1166 /// # Examples
1167 ///
1168 /// ```
1169 /// use os_str_bytes::RawOsString;
1170 ///
1171 /// let string = "foobar".to_owned();
1172 /// let raw = RawOsString::from_string(string.clone());
1173 /// assert_eq!(string, *raw.into_box());
1174 /// ```
1175 #[inline]
1176 #[must_use]
into_box(self) -> Box<RawOsStr>1177 pub fn into_box(self) -> Box<RawOsStr> {
1178 self.0.into_boxed_slice().transmute_box()
1179 }
1180
1181 /// Converts this representation back to a platform-native string.
1182 ///
1183 /// # Examples
1184 ///
1185 /// ```
1186 /// use std::env;
1187 /// # use std::io;
1188 ///
1189 /// use os_str_bytes::RawOsString;
1190 ///
1191 /// let os_string = env::current_exe()?.into_os_string();
1192 /// let raw = RawOsString::new(os_string.clone());
1193 /// assert_eq!(os_string, raw.into_os_string());
1194 /// #
1195 /// # Ok::<_, io::Error>(())
1196 /// ```
1197 #[inline]
1198 #[must_use]
into_os_string(self) -> OsString1199 pub fn into_os_string(self) -> OsString {
1200 expect_encoded!(imp::os_string_from_vec(self.0))
1201 }
1202
1203 /// Returns the byte string stored by this container.
1204 ///
1205 /// The returned string will use an [unspecified encoding].
1206 ///
1207 /// # Examples
1208 ///
1209 /// ```
1210 /// use os_str_bytes::RawOsString;
1211 ///
1212 /// let string = "foobar".to_owned();
1213 /// let raw = RawOsString::from_string(string.clone());
1214 /// assert_eq!(string.into_bytes(), raw.into_raw_vec());
1215 /// ```
1216 ///
1217 /// [unspecified encoding]: super#encoding
1218 #[inline]
1219 #[must_use]
into_raw_vec(self) -> Vec<u8>1220 pub fn into_raw_vec(self) -> Vec<u8> {
1221 self.0
1222 }
1223
1224 /// Equivalent to [`OsString::into_string`].
1225 ///
1226 /// # Examples
1227 ///
1228 /// ```
1229 /// use os_str_bytes::RawOsString;
1230 ///
1231 /// let string = "foobar".to_owned();
1232 /// let raw = RawOsString::from_string(string.clone());
1233 /// assert_eq!(Ok(string), raw.into_string());
1234 /// ```
1235 #[inline]
into_string(self) -> result::Result<String, Self>1236 pub fn into_string(self) -> result::Result<String, Self> {
1237 String::from_utf8(self.0).map_err(|x| Self(x.into_bytes()))
1238 }
1239
1240 /// Equivalent to [`String::shrink_to_fit`].
1241 ///
1242 /// # Examples
1243 ///
1244 /// ```
1245 /// use os_str_bytes::RawOsString;
1246 ///
1247 /// let string = "foobar".to_owned();
1248 /// let mut raw = RawOsString::from_string(string.clone());
1249 /// raw.shrink_to_fit();
1250 /// assert_eq!(string, raw);
1251 /// ```
1252 #[inline]
shrink_to_fit(&mut self)1253 pub fn shrink_to_fit(&mut self) {
1254 self.0.shrink_to_fit();
1255 }
1256
1257 /// Equivalent to [`String::split_off`].
1258 ///
1259 /// # Panics
1260 ///
1261 /// Panics if the index is not a [valid boundary].
1262 ///
1263 /// # Examples
1264 ///
1265 /// ```
1266 /// use os_str_bytes::RawOsString;
1267 ///
1268 /// let mut raw = RawOsString::from_string("foobar".to_owned());
1269 /// assert_eq!("bar", raw.split_off(3));
1270 /// assert_eq!("foo", raw);
1271 /// ```
1272 ///
1273 /// [valid boundary]: RawOsStr#indices
1274 #[inline]
1275 #[must_use]
1276 #[track_caller]
split_off(&mut self, at: usize) -> Self1277 pub fn split_off(&mut self, at: usize) -> Self {
1278 self.check_bound(at);
1279
1280 Self(self.0.split_off(at))
1281 }
1282
1283 /// Equivalent to [`String::truncate`].
1284 ///
1285 /// # Panics
1286 ///
1287 /// Panics if the index is not a [valid boundary].
1288 ///
1289 /// # Examples
1290 ///
1291 /// ```
1292 /// use os_str_bytes::RawOsString;
1293 ///
1294 /// let mut raw = RawOsString::from_string("foobar".to_owned());
1295 /// raw.truncate(3);
1296 /// assert_eq!("foo", raw);
1297 /// ```
1298 ///
1299 /// [valid boundary]: RawOsStr#indices
1300 #[inline]
1301 #[track_caller]
truncate(&mut self, new_len: usize)1302 pub fn truncate(&mut self, new_len: usize) {
1303 self.check_bound(new_len);
1304
1305 self.0.truncate(new_len);
1306 }
1307 }
1308
1309 impl AsRef<RawOsStr> for RawOsString {
1310 #[inline]
as_ref(&self) -> &RawOsStr1311 fn as_ref(&self) -> &RawOsStr {
1312 self
1313 }
1314 }
1315
1316 impl Borrow<RawOsStr> for RawOsString {
1317 #[inline]
borrow(&self) -> &RawOsStr1318 fn borrow(&self) -> &RawOsStr {
1319 self
1320 }
1321 }
1322
1323 impl Deref for RawOsString {
1324 type Target = RawOsStr;
1325
1326 #[inline]
deref(&self) -> &Self::Target1327 fn deref(&self) -> &Self::Target {
1328 RawOsStr::from_inner(&self.0)
1329 }
1330 }
1331
1332 impl From<RawOsString> for Box<RawOsStr> {
1333 #[inline]
from(value: RawOsString) -> Self1334 fn from(value: RawOsString) -> Self {
1335 value.into_box()
1336 }
1337 }
1338
1339 impl From<Box<RawOsStr>> for RawOsString {
1340 #[inline]
from(value: Box<RawOsStr>) -> Self1341 fn from(value: Box<RawOsStr>) -> Self {
1342 Self(value.transmute_box::<[_]>().into_vec())
1343 }
1344 }
1345
1346 impl From<RawOsString> for Cow<'_, RawOsStr> {
1347 #[inline]
from(value: RawOsString) -> Self1348 fn from(value: RawOsString) -> Self {
1349 Cow::Owned(value)
1350 }
1351 }
1352
1353 impl From<String> for RawOsString {
1354 #[inline]
from(value: String) -> Self1355 fn from(value: String) -> Self {
1356 Self::from_string(value)
1357 }
1358 }
1359
1360 struct DebugBuffer<'a>(&'a [u8]);
1361
1362 impl Debug for DebugBuffer<'_> {
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result1363 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1364 f.write_str("\"")?;
1365
1366 let mut string = self.0;
1367 let mut invalid_length = 0;
1368 while !string.is_empty() {
1369 let (invalid, substring) = string.split_at(invalid_length);
1370
1371 let valid = match str::from_utf8(substring) {
1372 Ok(valid) => {
1373 string = &[];
1374 valid
1375 }
1376 Err(error) => {
1377 let (valid, substring) =
1378 substring.split_at(error.valid_up_to());
1379
1380 let invalid_char_length =
1381 error.error_len().unwrap_or_else(|| substring.len());
1382 if valid.is_empty() {
1383 invalid_length += invalid_char_length;
1384 continue;
1385 }
1386 string = substring;
1387 invalid_length = invalid_char_length;
1388
1389 // SAFETY: This slice was validated to be UTF-8.
1390 unsafe { str::from_utf8_unchecked(valid) }
1391 }
1392 };
1393
1394 raw::debug(invalid, f)?;
1395 Display::fmt(&valid.escape_debug(), f)?;
1396 }
1397
1398 f.write_str("\"")
1399 }
1400 }
1401
1402 macro_rules! r#impl {
1403 ( $type:ty ) => {
1404 impl Debug for $type {
1405 #[inline]
fmt(&self, f: &mut Formatter<'_>) -> fmt::Result1406 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1407 f.debug_tuple(stringify!($type))
1408 .field(&DebugBuffer(&self.0))
1409 .finish()
1410 }
1411 }
1412 };
1413 }
1414 r#impl!(RawOsStr);
1415 r#impl!(RawOsString);
1416
1417 macro_rules! r#impl {
1418 ( $index_type:ty $(, $index_var:ident , $($bound:expr),+)? ) => {
1419 impl Index<$index_type> for RawOsStr {
1420 type Output = Self;
1421
1422 #[inline]
index(&self, idx: $index_type) -> &Self::Output1423 fn index(&self, idx: $index_type) -> &Self::Output {
1424 $(
1425 let $index_var = &idx;
1426 $(self.check_bound($bound);)+
1427 )?
1428
1429 Self::from_inner(&self.0[idx])
1430 }
1431 }
1432
1433 impl Index<$index_type> for RawOsString {
1434 type Output = RawOsStr;
1435
1436 #[allow(clippy::indexing_slicing)]
1437 #[inline]
index(&self, idx: $index_type) -> &Self::Output1438 fn index(&self, idx: $index_type) -> &Self::Output {
1439 &(**self)[idx]
1440 }
1441 }
1442 };
1443 }
1444 r#impl!(Range<usize>, x, x.start, x.end);
1445 r#impl!(RangeFrom<usize>, x, x.start);
1446 r#impl!(RangeFull);
1447 // [usize::MAX] will always be a valid inclusive end index.
1448 #[rustfmt::skip]
1449 r#impl!(RangeInclusive<usize>, x, *x.start(), x.end().wrapping_add(1));
1450 r#impl!(RangeTo<usize>, x, x.end);
1451 r#impl!(RangeToInclusive<usize>, x, x.end.wrapping_add(1));
1452
1453 macro_rules! r#impl {
1454 ( $type:ty , $other_type:ty ) => {
1455 impl PartialEq<$other_type> for $type {
1456 #[inline]
eq(&self, other: &$other_type) -> bool1457 fn eq(&self, other: &$other_type) -> bool {
1458 let raw: &RawOsStr = self;
1459 let other: &RawOsStr = other.as_ref();
1460 raw == other
1461 }
1462 }
1463
1464 impl PartialEq<$type> for $other_type {
1465 #[inline]
eq(&self, other: &$type) -> bool1466 fn eq(&self, other: &$type) -> bool {
1467 other == self
1468 }
1469 }
1470 };
1471 }
1472 r#impl!(RawOsStr, RawOsString);
1473 r#impl!(&RawOsStr, RawOsString);
1474 r#impl!(RawOsStr, str);
1475 r#impl!(RawOsStr, String);
1476 r#impl!(&RawOsStr, String);
1477 r#impl!(RawOsString, str);
1478 r#impl!(RawOsString, &str);
1479 r#impl!(RawOsString, String);
1480
1481 #[cfg(feature = "print_bytes")]
1482 #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "print_bytes")))]
1483 mod print_bytes {
1484 use print_bytes::ByteStr;
1485 use print_bytes::ToBytes;
1486 #[cfg(windows)]
1487 use print_bytes::WideStr;
1488
1489 #[cfg(windows)]
1490 use crate::imp::raw;
1491
1492 use super::RawOsStr;
1493 use super::RawOsString;
1494
1495 impl ToBytes for RawOsStr {
1496 #[inline]
to_bytes(&self) -> ByteStr<'_>1497 fn to_bytes(&self) -> ByteStr<'_> {
1498 self.0.to_bytes()
1499 }
1500
1501 #[cfg(windows)]
1502 #[inline]
to_wide(&self) -> Option<WideStr>1503 fn to_wide(&self) -> Option<WideStr> {
1504 Some(WideStr::new(raw::encode_wide_unchecked(&self.0).collect()))
1505 }
1506 }
1507
1508 impl ToBytes for RawOsString {
1509 #[inline]
to_bytes(&self) -> ByteStr<'_>1510 fn to_bytes(&self) -> ByteStr<'_> {
1511 (**self).to_bytes()
1512 }
1513
1514 #[cfg(windows)]
1515 #[inline]
to_wide(&self) -> Option<WideStr>1516 fn to_wide(&self) -> Option<WideStr> {
1517 (**self).to_wide()
1518 }
1519 }
1520 }
1521
1522 #[cfg(feature = "uniquote")]
1523 #[cfg_attr(os_str_bytes_docs_rs, doc(cfg(feature = "uniquote")))]
1524 mod uniquote {
1525 use uniquote::Formatter;
1526 use uniquote::Quote;
1527 use uniquote::Result;
1528
1529 use crate::imp::raw;
1530
1531 use super::RawOsStr;
1532 use super::RawOsString;
1533
1534 impl Quote for RawOsStr {
1535 #[inline]
escape(&self, f: &mut Formatter<'_>) -> Result1536 fn escape(&self, f: &mut Formatter<'_>) -> Result {
1537 raw::uniquote::escape(&self.0, f)
1538 }
1539 }
1540
1541 impl Quote for RawOsString {
1542 #[inline]
escape(&self, f: &mut Formatter<'_>) -> Result1543 fn escape(&self, f: &mut Formatter<'_>) -> Result {
1544 (**self).escape(f)
1545 }
1546 }
1547 }
1548