Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_core/src/path.rs

4.3 KiB, 3 runs

created by r1870400018:114, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use crate::prelude::*;
2
3use std::{
4 ops::Deref,
5 path::{
6 Component,
7 Path,
8 PathBuf,
9 },
10};
11
12
13/// Check that the string contains no path components and is just a filename.
14pub fn is_filename(name: &str) -> bool {
15 !name.contains(std::path::MAIN_SEPARATOR) &&
16 cfg!(not(windows)) || !name.contains('/')
17}
18
19/// Normalise a relative path without reference to the state of the file system by:
20/// - Ensure path begins with "." or "..", e.g. /a/b -> ./a/b, ./../a -> ../a
21/// - Eliminate redundant path components, e.g. a/b/../c -> ./a/c
22/// - Eliminate trailing separator, e.g. a/b/ -> ./a/b
23/// The result will always begin with either "." or a sequence of "..". Neither will appear
24/// elsewhere in the path.
25pub trait NormalPath {
26 fn normalise(&self) -> NormPathBuf;
27}
28
29new_type_priv!(NormPathBuf, PathBuf, Clone, Debug);
30
31/// A `NormPathBuf` wraps a `PathBuf` with some extra methods. Making the inner object private
32/// ensures that a `NormPathBuf` can generally only be created by normalising a `Path`.
33impl NormPathBuf {
34 /// Test whether the normalised `PathBuf` escapes upwards out of the current directory.
35 pub fn escapes(&self) -> bool {
36 match self.components().next() {
37 Some(std::path::Component::ParentDir) => true,
38 _ => false,
39 }
40 }
41
42 /// Remove leading "." and ".." components, and trailing separator. We know that `self` is
43 /// normalised, so all of these components will be at the front of the path.
44 pub fn remove_relative(self) -> Self {
45 let mut components = self.0.components();
46 let mut pbuf = PathBuf::new();
47 loop {
48 let component = components.next();
49 //debug!(" component = {:?}", component);
50 match component {
51 Some(Component::CurDir) | Some(Component::ParentDir) => continue,
52 _ => {
53 pbuf.extend(component);
54 pbuf.extend(components);
55 break
56 },
57 }
58 }
59 Self(pbuf)
60 }
61
62 /// Ensure the path begins with "/". after removing relative components.
63 pub fn absolute(self) -> Self {
64 let path = self.remove_relative();
65 let mut components = path.0.components();
66 match components.next() {
67 Some(Component::RootDir) => path,
68 component => {
69 let mut pbuf = PathBuf::from("/");
70 pbuf.extend(component);
71 pbuf.extend(components);
72 Self(pbuf)
73 },
74 }
75 }
76
77 pub fn join(mut self, rel_path: Self) -> Self {
78 self.0.push(rel_path.0);
79 Self(self.0)
80 }
81
82 pub fn as_pathbuf(self) -> PathBuf { self.0 }
83}
84
85impl<T: ?Sized> AsRef<T> for NormPathBuf where <NormPathBuf as Deref>::Target: AsRef<T> {
86 fn as_ref(&self) -> &T {
87 self.deref().as_ref()
88 }
89}
90
91impl NormalPath for Path {
92
93 fn normalise(&self) -> NormPathBuf {
94 let mut normalised = PathBuf::new();
95
96 let mut first = true;
97 for component in self.components() {
98 //debug!(">> {:?} first={}", component, first);
99 match component {
100 Component::RootDir => {
101 normalised.push(".");
102 },
103 Component::ParentDir => {
104 if normalised.as_os_str().is_empty() || normalised == Path::new("..")
105 {
106 normalised.push(component.as_os_str());
107 } else if normalised == Path::new(".") {
108 // Replace "." with ".."
109 normalised.pop();
110 normalised.push(component.as_os_str());
111 } else {
112 normalised.pop();
113 }
114 },
115 Component::CurDir => {
116 if first {
117 normalised.push(component.as_os_str());
118 }
119 },
120 _ => {
121 if first {
122 normalised.push(".");
123 }
124 normalised.push(component.as_os_str());
125 },
126 }
127 //debug!(" > {:?}", normalised);
128 if first {
129 first = false;
130 }
131 }
132
133 NormPathBuf(normalised)
134 }
135}