1use std::ffi::OsStr;
2
3pub(crate) trait OsStrExt: private::Sealed {
4 fn try_str(&self) -> Result<&str, std::str::Utf8Error>;
8 #[allow(dead_code)]
13 fn contains(&self, needle: &str) -> bool;
14 fn find(&self, needle: &str) -> Option<usize>;
19 fn strip_prefix(&self, prefix: &str) -> Option<&OsStr>;
26 fn starts_with(&self, prefix: &str) -> bool;
31 #[allow(dead_code)]
34 fn split<'s, 'n>(&'s self, needle: &'n str) -> Split<'s, 'n>;
35 fn split_once(&self, needle: &'_ str) -> Option<(&OsStr, &OsStr)>;
38}
39
40impl OsStrExt for OsStr {
41 fn try_str(&self) -> Result<&str, std::str::Utf8Error> {
42 let bytes = self.as_encoded_bytes();
43 std::str::from_utf8(bytes)
44 }
45
46 fn contains(&self, needle: &str) -> bool {
47 self.find(needle).is_some()
48 }
49
50 fn find(&self, needle: &str) -> Option<usize> {
51 let bytes = self.as_encoded_bytes();
52 (0..=self.len().checked_sub(needle.len())?)
53 .find(|&x| bytes[x..].starts_with(needle.as_bytes()))
54 }
55
56 fn strip_prefix(&self, prefix: &str) -> Option<&OsStr> {
57 let bytes = self.as_encoded_bytes();
58 bytes.strip_prefix(prefix.as_bytes()).map(|s| {
59 unsafe { OsStr::from_encoded_bytes_unchecked(s) }
63 })
64 }
65 fn starts_with(&self, prefix: &str) -> bool {
66 let bytes = self.as_encoded_bytes();
67 bytes.starts_with(prefix.as_bytes())
68 }
69
70 fn split<'s, 'n>(&'s self, needle: &'n str) -> Split<'s, 'n> {
71 assert_ne!(needle, "");
72 Split {
73 haystack: Some(self),
74 needle,
75 }
76 }
77
78 fn split_once(&self, needle: &'_ str) -> Option<(&OsStr, &OsStr)> {
79 let start = self.find(needle)?;
80 let end = start + needle.len();
81 let haystack = self.as_encoded_bytes();
82 let first = &haystack[0..start];
83 let second = &haystack[end..];
84 unsafe {
88 Some((
89 OsStr::from_encoded_bytes_unchecked(first),
90 OsStr::from_encoded_bytes_unchecked(second),
91 ))
92 }
93 }
94}
95
96mod private {
97 pub(crate) trait Sealed {}
98
99 impl Sealed for std::ffi::OsStr {}
100}
101
102#[allow(dead_code)]
103pub(crate) struct Split<'s, 'n> {
104 haystack: Option<&'s OsStr>,
105 needle: &'n str,
106}
107
108impl<'s> Iterator for Split<'s, '_> {
109 type Item = &'s OsStr;
110
111 fn next(&mut self) -> Option<Self::Item> {
112 let haystack = self.haystack?;
113 match haystack.split_once(self.needle) {
114 Some((first, second)) => {
115 if !haystack.is_empty() {
116 debug_assert_ne!(haystack, second);
117 }
118 self.haystack = Some(second);
119 Some(first)
120 }
121 None => {
122 self.haystack = None;
123 Some(haystack)
124 }
125 }
126 }
127}
128
129pub(crate) unsafe fn split_at(os: &OsStr, index: usize) -> (&OsStr, &OsStr) {
135 unsafe {
136 let bytes = os.as_encoded_bytes();
137 let (first, second) = bytes.split_at(index);
138 (
139 OsStr::from_encoded_bytes_unchecked(first),
140 OsStr::from_encoded_bytes_unchecked(second),
141 )
142 }
143}