conch_runtime_pshaw/
path.rs1use std::fmt;
4use std::io;
5use std::mem;
6use std::ops::Deref;
7use std::path::{Component, Path, PathBuf};
8
9#[derive(Debug, thiserror::Error)]
11pub struct NormalizationError {
12 #[source]
14 err: io::Error,
15 path: PathBuf,
17}
18
19impl fmt::Display for NormalizationError {
20 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(fmt, "{}: {}", self.err, self.path.display())
22 }
23}
24
25#[derive(PartialEq, Eq, Clone, Debug, Default)]
27pub struct NormalizedPath {
28 normalized_path: PathBuf,
30}
31
32pub(crate) fn has_dot_components(path: &Path) -> bool {
33 path.components().any(|c| match c {
34 Component::CurDir | Component::ParentDir => true,
35
36 Component::Prefix(_) | Component::RootDir | Component::Normal(_) => false,
37 })
38}
39
40impl NormalizedPath {
41 pub fn new() -> Self {
43 Self {
44 normalized_path: PathBuf::new(),
45 }
46 }
47
48 pub fn new_normalized_logical(buf: PathBuf) -> Self {
54 if has_dot_components(&buf) {
55 let mut normalized = Self::new();
56 normalized.perform_join_normalized_logical(&buf);
57 normalized
58 } else {
59 Self {
60 normalized_path: buf,
61 }
62 }
63 }
64
65 pub fn new_normalized_physical(buf: PathBuf) -> Result<Self, NormalizationError> {
71 if has_dot_components(&buf) {
72 let mut normalized_path = Self::new();
73 normalized_path.perform_join_normalized_physical_for_dot_components(&buf)?;
74 Ok(normalized_path)
75 } else {
76 let normalized_path = buf
78 .canonicalize()
79 .map_err(|e| NormalizationError { err: e, path: buf })?;
80
81 Ok(Self { normalized_path })
82 }
83 }
84
85 pub fn join_normalized_logial<P: AsRef<Path>>(&mut self, path: P) {
94 self.join_normalized_logial_(path.as_ref())
95 }
96
97 fn join_normalized_logial_(&mut self, path: &Path) {
98 if !has_dot_components(path) {
101 self.normalized_path.push(path);
102 return;
103 }
104
105 self.perform_join_normalized_logical(path);
106 }
107
108 fn perform_join_normalized_logical(&mut self, path: &Path) {
109 for component in path.components() {
110 match component {
111 c @ Component::Prefix(_) | c @ Component::RootDir | c @ Component::Normal(_) => {
112 self.normalized_path.push(c.as_os_str())
113 }
114
115 Component::CurDir => {}
116 Component::ParentDir => {
117 self.normalized_path.pop();
118 }
119 }
120 }
121 }
122
123 pub fn join_normalized_physical<P: AsRef<Path>>(
138 &mut self,
139 path: P,
140 ) -> Result<(), NormalizationError> {
141 self.join_normalized_physical_(path.as_ref())
142 }
143
144 fn join_normalized_physical_(&mut self, path: &Path) -> Result<(), NormalizationError> {
145 if has_dot_components(path) {
146 self.perform_join_normalized_physical_for_dot_components(path)
147 } else {
148 self.normalized_path.push(path);
151 self.normalized_path =
152 self.normalized_path
153 .canonicalize()
154 .map_err(|e| NormalizationError {
155 err: e,
156 path: self.normalized_path.clone(),
157 })?;
158
159 Ok(())
160 }
161 }
162
163 fn perform_join_normalized_physical_for_dot_components(
164 &mut self,
165 path: &Path,
166 ) -> Result<(), NormalizationError> {
167 let orig_path = self.normalized_path.clone();
168 self.perform_join_normalized_physical(path)
169 .map_err(|e| NormalizationError {
170 err: e,
171 path: mem::replace(&mut self.normalized_path, orig_path),
172 })
173 }
174
175 fn perform_join_normalized_physical(&mut self, path: &Path) -> io::Result<()> {
176 for component in path.components() {
177 match component {
178 c @ Component::Prefix(_) | c @ Component::RootDir | c @ Component::Normal(_) => {
179 self.normalized_path.push(c.as_os_str())
180 }
181
182 Component::CurDir => {}
183 Component::ParentDir => {
184 self.normalized_path = self.normalized_path.canonicalize()?;
185 self.normalized_path.pop();
186 }
187 }
188 }
189
190 self.normalized_path = self.normalized_path.canonicalize()?;
192 Ok(())
193 }
194
195 pub fn into_inner(self) -> PathBuf {
197 self.normalized_path
198 }
199}
200
201impl Deref for NormalizedPath {
202 type Target = PathBuf;
203
204 fn deref(&self) -> &PathBuf {
205 &self.normalized_path
206 }
207}