1#[cfg(feature = "std")]
2pub use std::path::{Path, PathBuf};
3
4#[cfg(not(feature = "std"))]
5pub use self::fallback::{Path, PathBuf};
6
7#[cfg(not(feature = "std"))]
8mod fallback {
9 use alloc::{borrow::Cow, boxed::Box, vec::Vec};
10 use core::{borrow::Borrow, fmt::Display, ops::Deref};
11
12 #[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
13 pub struct PathBuf(Vec<u8>);
14
15 impl PathBuf {
16 pub const fn new() -> Self {
17 Self(Vec::new())
18 }
19
20 #[inline]
21 pub fn as_path(&self) -> &Path {
22 unsafe { core::mem::transmute(&*self.0) }
24 }
25
26 #[inline]
27 pub fn into_boxed_path(self) -> Box<Path> {
28 unsafe { core::mem::transmute(self.0.into_boxed_slice()) }
29 }
30 }
31
32 impl From<&str> for PathBuf {
33 fn from(s: &str) -> Self {
34 Self(s.as_bytes().to_vec())
35 }
36 }
37
38 impl core::fmt::Debug for PathBuf {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 let s = self.as_path().to_string_lossy();
41 write!(f, "{s}")
42 }
43 }
44
45 impl Deref for PathBuf {
46 type Target = Path;
47
48 #[inline]
49 fn deref(&self) -> &Self::Target {
50 self.as_path()
51 }
52 }
53
54 impl AsRef<Path> for PathBuf {
55 #[inline]
56 fn as_ref(&self) -> &Path {
57 self.as_path()
58 }
59 }
60
61 impl Borrow<Path> for PathBuf {
62 #[inline]
63 fn borrow(&self) -> &Path {
64 self.as_path()
65 }
66 }
67
68 #[derive(PartialEq, Eq, PartialOrd, Ord)]
69 #[repr(transparent)]
70 pub struct Path([u8]);
71
72 impl core::fmt::Debug for Path {
73 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
74 let s = self.to_string_lossy();
75 write!(f, "{s}")
76 }
77 }
78
79 impl Path {
80 pub fn to_str(&self) -> Option<&str> {
81 core::str::from_utf8(&self.0).ok()
82 }
83
84 pub fn to_string_lossy(&self) -> Cow<'_, str> {
85 alloc::string::String::from_utf8_lossy(&self.0)
86 }
87
88 pub fn to_path_buf(&self) -> PathBuf {
89 PathBuf(self.0.to_vec())
90 }
91
92 pub fn display(&self) -> impl Display + '_ {
93 self.to_string_lossy()
94 }
95
96 pub fn is_absolute(&self) -> bool {
97 self.0.starts_with(b"/")
98 }
99
100 pub fn parent(&self) -> Option<&Self> {
101 if self.0.is_empty() || self.0.ends_with(b"/") {
102 return None;
103 }
104 match self.to_str() {
105 None => match self.0.rsplit_once(|b| *b == b'/') {
106 None => Some(Self::from_bytes(&self.0)),
107 Some((before, _)) => Some(Self::from_bytes(before)),
108 },
109 Some(s) => match s.rsplit_once('/') {
110 None => Some(s.as_ref()),
111 Some((before, _)) => Some(before.as_ref()),
112 },
113 }
114 }
115
116 pub fn file_name(&self) -> Option<&Self> {
117 if self.0.ends_with(b"..") {
118 return None;
119 }
120 match self.to_str() {
121 None => match self.0.rsplit_once(|b| *b == b'/') {
122 None => Some(Self::from_bytes(&self.0)),
123 Some((_, after)) => Some(Self::from_bytes(after)),
124 },
125 Some(s) => match s.rsplit_once('/') {
126 None => Some(s.as_ref()),
127 Some((_, after)) => Some(after.as_ref()),
128 },
129 }
130 }
131
132 pub fn file_stem(&self) -> Option<&Self> {
133 let file_name = self.file_name()?;
134 match file_name.0.rsplit_once(|b| *b == b'.') {
135 None => Some(file_name),
136 Some(([], _)) => Some(file_name),
137 Some((stem, _)) => Some(Self::from_bytes(stem)),
138 }
139 }
140
141 pub fn extension(&self) -> Option<&Self> {
142 let file_name = self.file_name()?;
143 match file_name.0.rsplit_once(|b| *b == b'.') {
144 None => None,
145 Some(([], _)) => None,
146 Some((_, ext)) => Some(Self::from_bytes(ext)),
147 }
148 }
149
150 pub fn is_dir(&self) -> bool {
151 self.extension().is_none()
152 }
153
154 pub fn join<P>(&self, path: P) -> PathBuf
155 where
156 P: AsRef<Path>,
157 {
158 let path = path.as_ref();
159 if self.0.is_empty() {
160 return path.to_path_buf();
161 }
162
163 let mut buf = Vec::with_capacity(self.0.len() + path.0.len() + 1);
164 buf.extend_from_slice(&self.0);
165 buf.push(b'/');
166 buf.extend_from_slice(&path.0);
167
168 PathBuf(buf)
169 }
170
171 pub fn with_stem<S>(&self, stem: S) -> PathBuf
172 where
173 S: AsRef<str>,
174 {
175 let stem = stem.as_ref().as_bytes();
176 match self.file_name() {
177 None => {
178 let mut buf = Vec::with_capacity(self.0.len() + stem.len());
179 buf.extend_from_slice(&self.0);
180 buf.extend_from_slice(stem);
181 PathBuf(buf)
182 }
183 Some(file_name) => {
184 let (prefix, ext) = match file_name.0.rsplit_once(|b| *b == b'.') {
185 None => (self.0.strip_suffix(&file_name.0).unwrap(), None),
186 Some((name, ext)) => {
187 let len = self.0.len() - name.len() - 1 - ext.len();
188 (&self.0[..len], Some(ext))
189 }
190 };
191 let mut buf = Vec::with_capacity(
192 prefix.len() + ext.map(|ext| ext.len()).unwrap_or_default() + stem.len(),
193 );
194 buf.extend_from_slice(prefix);
195 buf.extend_from_slice(stem);
196 if let Some(ext) = ext {
197 buf.push(b'.');
198 buf.extend_from_slice(ext)
199 }
200 PathBuf(buf)
201 }
202 }
203 }
204
205 pub fn with_extension<S>(&self, extension: S) -> PathBuf
206 where
207 S: AsRef<str>,
208 {
209 let extension = extension.as_ref().as_bytes();
210 match self.extension() {
211 None => {
212 let mut buf = self.to_path_buf();
213 buf.0.push(b'.');
214 buf.0.extend_from_slice(extension);
215 buf
216 }
217 Some(prev) => {
218 let bytes = self.0.strip_suffix(&prev.0).unwrap();
219 let mut buf = Vec::with_capacity(bytes.len() + extension.len());
220 buf.extend_from_slice(bytes);
221 buf.extend_from_slice(extension);
222 PathBuf(buf)
223 }
224 }
225 }
226
227 pub fn with_stem_and_extension<S, E>(&self, stem: S, extension: E) -> PathBuf
228 where
229 S: AsRef<str>,
230 E: AsRef<str>,
231 {
232 let stem = stem.as_ref().as_bytes();
233 let extension = extension.as_ref().as_bytes();
234 match self.file_name() {
235 None => {
236 let mut buf =
237 Vec::with_capacity(self.0.len() + stem.len() + 1 + extension.len());
238 buf.extend_from_slice(&self.0);
239 buf.extend_from_slice(stem);
240 buf.push(b'.');
241 buf.extend_from_slice(extension);
242 PathBuf(buf)
243 }
244 Some(file_name) => {
245 let bytes = self.0.strip_suffix(&file_name.0).unwrap();
246 let mut buf =
247 Vec::with_capacity(bytes.len() + stem.len() + 1 + extension.len());
248 buf.extend_from_slice(bytes);
249 buf.extend_from_slice(stem);
250 buf.push(b'.');
251 buf.extend_from_slice(extension);
252 PathBuf(buf)
253 }
254 }
255 }
256
257 #[inline(always)]
258 fn from_bytes(bytes: &[u8]) -> &Self {
259 unsafe { core::mem::transmute(bytes) }
260 }
261 }
262
263 impl AsRef<Path> for str {
264 fn as_ref(&self) -> &Path {
265 unsafe { core::mem::transmute(self.as_bytes()) }
266 }
267 }
268
269 impl AsRef<Path> for alloc::string::String {
270 fn as_ref(&self) -> &Path {
271 unsafe { core::mem::transmute(self.as_bytes()) }
272 }
273 }
274
275 impl Clone for Box<Path> {
276 fn clone(&self) -> Self {
277 self.to_path_buf().into_boxed_path()
278 }
279 }
280}