1use std::{
4 borrow::Borrow,
5 convert::Infallible,
6 ffi::{OsStr, OsString},
7 fmt,
8 ops::Deref,
9 path::{
10 Ancestors as NativeAncestors, Components as NativeComponents, Path as NativePath,
11 PathBuf as NativePathBuf,
12 },
13 str::FromStr,
14};
15
16use ref_cast::RefCast;
17use serde::{Deserialize, Deserializer, Serialize, Serializer};
18
19#[derive(Eq, Hash, Ord, PartialEq, PartialOrd, RefCast)]
24#[repr(transparent)]
25pub struct Path(NativePath);
26
27impl Path {
28 #[must_use]
30 pub fn new<S>(path: &S) -> &Self
31 where
32 S: AsRef<OsStr> + ?Sized,
33 {
34 Self::ref_cast(NativePath::new(path))
35 }
36
37 #[must_use]
39 pub fn as_os_str(&self) -> &OsStr {
40 self.0.as_os_str()
41 }
42
43 #[must_use]
45 pub fn to_str(&self) -> Option<&str> {
46 self.0.to_str()
47 }
48
49 #[must_use]
51 pub fn display(&self) -> Display<'_> {
52 Display(self)
53 }
54
55 #[must_use]
57 pub fn to_string_lossy(&self) -> std::borrow::Cow<'_, str> {
58 self.0.to_string_lossy()
59 }
60
61 #[must_use]
63 pub fn to_path_buf(&self) -> PathBuf {
64 PathBuf(self.0.to_path_buf())
65 }
66
67 #[must_use]
69 pub fn join<S>(&self, path: S) -> PathBuf
70 where
71 S: AsRef<NativePath>,
72 {
73 PathBuf(self.0.join(path))
74 }
75
76 #[must_use]
78 pub fn parent(&self) -> Option<&Self> {
79 self.0.parent().map(Self::ref_cast)
80 }
81
82 #[must_use]
84 pub fn ancestors(&self) -> Ancestors<'_> {
85 Ancestors(self.0.ancestors())
86 }
87
88 #[must_use]
90 pub fn file_name(&self) -> Option<&OsStr> {
91 self.0.file_name()
92 }
93
94 #[must_use]
96 pub fn file_stem(&self) -> Option<&OsStr> {
97 self.0.file_stem()
98 }
99
100 #[must_use]
102 pub fn extension(&self) -> Option<&OsStr> {
103 self.0.extension()
104 }
105
106 #[must_use]
108 pub fn with_file_name<S>(&self, file_name: S) -> PathBuf
109 where
110 S: AsRef<OsStr>,
111 {
112 PathBuf(self.0.with_file_name(file_name))
113 }
114
115 #[must_use]
117 pub fn with_extension<S>(&self, extension: S) -> PathBuf
118 where
119 S: AsRef<OsStr>,
120 {
121 PathBuf(self.0.with_extension(extension))
122 }
123
124 #[must_use]
126 pub fn components(&self) -> Components<'_> {
127 Components(self.0.components())
128 }
129
130 #[must_use]
132 pub fn starts_with<S>(&self, base: S) -> bool
133 where
134 S: AsRef<NativePath>,
135 {
136 self.0.starts_with(base)
137 }
138
139 pub fn strip_prefix<S>(&self, base: S) -> Result<&Self, StripPrefixError>
145 where
146 S: AsRef<NativePath>,
147 {
148 let path = self.0.strip_prefix(base)?;
149
150 Ok(Self::ref_cast(path))
151 }
152
153 #[must_use]
155 pub fn is_absolute(&self) -> bool {
156 self.0.is_absolute()
157 }
158
159 #[must_use]
161 pub fn is_empty(&self) -> bool {
162 self.as_os_str().is_empty()
163 }
164}
165
166impl AsRef<NativePath> for Path {
167 fn as_ref(&self) -> &NativePath {
168 &self.0
169 }
170}
171
172impl AsRef<Path> for Path {
173 fn as_ref(&self) -> &Path {
174 self
175 }
176}
177
178impl AsRef<OsStr> for Path {
179 fn as_ref(&self) -> &OsStr {
180 self.as_os_str()
181 }
182}
183
184impl fmt::Debug for Path {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 self.0.fmt(f)
187 }
188}
189
190#[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
192#[repr(transparent)]
193pub struct PathBuf(NativePathBuf);
194
195impl PathBuf {
196 #[must_use]
198 pub fn new() -> Self {
199 Self(NativePathBuf::new())
200 }
201
202 #[must_use]
204 pub fn as_path(&self) -> &Path {
205 self
206 }
207
208 pub fn push<S>(&mut self, path: S)
210 where
211 S: AsRef<NativePath>,
212 {
213 self.0.push(path);
214 }
215
216 pub fn pop(&mut self) -> bool {
218 self.0.pop()
219 }
220
221 pub fn set_extension<S>(&mut self, extension: S) -> bool
223 where
224 S: AsRef<OsStr>,
225 {
226 self.0.set_extension(extension)
227 }
228
229 #[must_use]
231 pub fn into_os_string(self) -> OsString {
232 self.0.into_os_string()
233 }
234}
235
236impl AsRef<NativePath> for PathBuf {
237 fn as_ref(&self) -> &NativePath {
238 &self.0
239 }
240}
241
242impl AsRef<OsStr> for PathBuf {
243 fn as_ref(&self) -> &OsStr {
244 self.as_os_str()
245 }
246}
247
248impl AsRef<Path> for PathBuf {
249 fn as_ref(&self) -> &Path {
250 self
251 }
252}
253
254impl Borrow<Path> for PathBuf {
255 fn borrow(&self) -> &Path {
256 self
257 }
258}
259
260impl Deref for PathBuf {
261 type Target = Path;
262
263 fn deref(&self) -> &Self::Target {
264 Path::ref_cast(self.0.as_path())
265 }
266}
267
268impl fmt::Debug for PathBuf {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 self.0.fmt(f)
271 }
272}
273
274impl fmt::Display for PathBuf {
275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 self.display().fmt(f)
277 }
278}
279
280impl From<NativePathBuf> for PathBuf {
281 fn from(path: NativePathBuf) -> Self {
282 Self(path)
283 }
284}
285
286impl From<&NativePath> for PathBuf {
287 fn from(path: &NativePath) -> Self {
288 Self(path.to_path_buf())
289 }
290}
291
292impl From<&Path> for PathBuf {
293 fn from(path: &Path) -> Self {
294 path.to_path_buf()
295 }
296}
297
298impl From<OsString> for PathBuf {
299 fn from(path: OsString) -> Self {
300 Self(NativePathBuf::from(path))
301 }
302}
303
304impl From<&OsStr> for PathBuf {
305 fn from(path: &OsStr) -> Self {
306 Self(NativePathBuf::from(path))
307 }
308}
309
310impl From<String> for PathBuf {
311 fn from(path: String) -> Self {
312 Self(NativePathBuf::from(path))
313 }
314}
315
316impl From<&str> for PathBuf {
317 fn from(path: &str) -> Self {
318 Self(NativePathBuf::from(path))
319 }
320}
321
322impl FromStr for PathBuf {
323 type Err = Infallible;
324
325 fn from_str(path: &str) -> Result<Self, Self::Err> {
326 Ok(Self(NativePathBuf::from(path)))
327 }
328}
329
330impl From<PathBuf> for OsString {
331 fn from(path: PathBuf) -> Self {
332 path.into_os_string()
333 }
334}
335
336impl Serialize for PathBuf {
337 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
338 where
339 S: Serializer,
340 {
341 self.0.serialize(serializer)
342 }
343}
344
345impl<'de> Deserialize<'de> for PathBuf {
346 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
347 where
348 D: Deserializer<'de>,
349 {
350 NativePathBuf::deserialize(deserializer).map(Self)
351 }
352}
353
354#[derive(Clone, Copy, Debug, Eq, PartialEq)]
356#[non_exhaustive]
357pub enum Component<'a> {
358 Prefix(&'a OsStr),
360 Root,
362 Current,
364 Parent,
366 Normal(&'a OsStr),
368}
369
370impl Component<'_> {
371 #[must_use]
373 pub fn as_os_str(&self) -> &OsStr {
374 match self {
375 Self::Prefix(value) | Self::Normal(value) => value,
376 Self::Root => std::path::MAIN_SEPARATOR_STR.as_ref(),
377 Self::Current => ".".as_ref(),
378 Self::Parent => "..".as_ref(),
379 }
380 }
381}
382
383#[derive(Clone, Debug)]
385pub struct Components<'a>(NativeComponents<'a>);
386
387impl<'a> Iterator for Components<'a> {
388 type Item = Component<'a>;
389
390 fn next(&mut self) -> Option<Self::Item> {
391 self.0.next().map(|component| match component {
392 std::path::Component::Prefix(prefix) => Component::Prefix(prefix.as_os_str()),
393 std::path::Component::RootDir => Component::Root,
394 std::path::Component::CurDir => Component::Current,
395 std::path::Component::ParentDir => Component::Parent,
396 std::path::Component::Normal(value) => Component::Normal(value),
397 })
398 }
399}
400
401impl DoubleEndedIterator for Components<'_> {
402 fn next_back(&mut self) -> Option<Self::Item> {
403 self.0.next_back().map(|component| match component {
404 std::path::Component::Prefix(prefix) => Component::Prefix(prefix.as_os_str()),
405 std::path::Component::RootDir => Component::Root,
406 std::path::Component::CurDir => Component::Current,
407 std::path::Component::ParentDir => Component::Parent,
408 std::path::Component::Normal(value) => Component::Normal(value),
409 })
410 }
411}
412
413#[derive(Clone, Debug)]
415pub struct Ancestors<'a>(NativeAncestors<'a>);
416
417impl<'a> Iterator for Ancestors<'a> {
418 type Item = &'a Path;
419
420 fn next(&mut self) -> Option<Self::Item> {
421 self.0.next().map(Path::ref_cast)
422 }
423}
424
425#[derive(Debug, thiserror::Error)]
427#[error(transparent)]
428pub struct StripPrefixError(#[from] std::path::StripPrefixError);
429
430#[derive(Clone, Copy, Debug)]
432pub struct Display<'a>(&'a Path);
433
434impl fmt::Display for Display<'_> {
435 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436 self.0.0.display().fmt(f)
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use googletest::prelude::*;
443
444 use super::*;
445
446 #[gtest]
447 fn lexical_api_preserves_components_and_parents() -> Result<()> {
448 let path = Path::new("crates/fs/src/lib.rs");
449 let components = path
450 .components()
451 .map(|component| component.as_os_str().to_string_lossy().into_owned())
452 .collect::<Vec<_>>();
453
454 verify_that!(
455 components,
456 elements_are![eq("crates"), eq("fs"), eq("src"), eq("lib.rs"),]
457 )?;
458
459 verify_that!(
460 path.parent()
461 .map(Path::display)
462 .map(|value| value.to_string()),
463 some(eq("crates/fs/src"))
464 )
465 }
466
467 #[gtest]
468 fn owned_path_dereferences_only_to_workspace_path() -> Result<()> {
469 let path = PathBuf::from("crates").join("fs");
470
471 verify_that!(path.as_path(), eq(Path::new("crates/fs")))?;
472 verify_that!(path.file_name(), some(eq(OsStr::new("fs"))))?;
473
474 verify_that!(path.strip_prefix("crates").or_fail()?, eq(Path::new("fs")))
475 }
476
477 #[gtest]
478 fn owned_path_parses_lexically_without_filesystem_access() -> Result<()> {
479 let expected = PathBuf::from("crates/fs/src");
480
481 verify_that!("crates/fs/src".parse::<PathBuf>(), ok(eq(&expected)))
482 }
483}