strict_path/path/
strict_path.rs1use crate::validator::path_history::{BoundaryChecked, Canonicalized, PathHistory, Raw};
3use crate::{Result, StrictPathError};
4use std::cmp::Ordering;
5use std::ffi::OsStr;
6use std::fmt;
7use std::hash::{Hash, Hasher};
8use std::marker::PhantomData;
9use std::path::{Path, PathBuf};
10use std::sync::Arc;
11
12#[derive(Clone)]
29pub struct StrictPath<Marker = ()> {
30 path: PathHistory<((Raw, Canonicalized), BoundaryChecked)>,
31 boundary: Arc<crate::PathBoundary<Marker>>,
32 _marker: PhantomData<Marker>,
33}
34
35impl<Marker> StrictPath<Marker> {
36 pub fn with_boundary<P: AsRef<Path>>(root: P) -> Result<Self> {
42 let boundary = crate::PathBoundary::try_new(root)?;
43 boundary.strict_join("")
44 }
45
46 pub fn with_boundary_create<P: AsRef<Path>>(root: P) -> Result<Self> {
50 let boundary = crate::PathBoundary::try_new_create(root)?;
51 boundary.strict_join("")
52 }
53 pub(crate) fn new(
54 boundary: Arc<crate::PathBoundary<Marker>>,
55 validated_path: PathHistory<((Raw, Canonicalized), BoundaryChecked)>,
56 ) -> Self {
57 Self {
58 path: validated_path,
59 boundary,
60 _marker: PhantomData,
61 }
62 }
63
64 #[inline]
65 pub(crate) fn boundary(&self) -> &crate::PathBoundary<Marker> {
66 &self.boundary
67 }
68
69 #[inline]
70 pub(crate) fn path(&self) -> &Path {
71 &self.path
72 }
73
74 #[inline]
77 pub fn strictpath_to_string_lossy(&self) -> std::borrow::Cow<'_, str> {
78 self.path.to_string_lossy()
79 }
80
81 #[inline]
85 pub fn strictpath_to_str(&self) -> Option<&str> {
86 self.path.to_str()
87 }
88
89 #[inline]
93 pub fn interop_path(&self) -> &OsStr {
94 self.path.as_os_str()
95 }
96
97 #[inline]
99 pub fn strictpath_display(&self) -> std::path::Display<'_> {
100 self.path.display()
101 }
102
103 #[inline]
107 pub fn unstrict(self) -> PathBuf {
108 self.path.into_inner()
109 }
110
111 #[inline]
113 pub fn virtualize(self) -> crate::path::virtual_path::VirtualPath<Marker> {
114 crate::path::virtual_path::VirtualPath::new(self)
115 }
116
117 #[inline]
122 pub fn strict_join<P: AsRef<Path>>(&self, path: P) -> Result<Self> {
123 let new_systempath = self.path.join(path);
124 self.boundary.strict_join(new_systempath)
125 }
126
127 pub fn strictpath_parent(&self) -> Result<Option<Self>> {
129 match self.path.parent() {
130 Some(p) => match self.boundary.strict_join(p) {
131 Ok(p) => Ok(Some(p)),
132 Err(e) => Err(e),
133 },
134 None => Ok(None),
135 }
136 }
137
138 #[inline]
140 pub fn strictpath_with_file_name<S: AsRef<OsStr>>(&self, file_name: S) -> Result<Self> {
141 let new_systempath = self.path.with_file_name(file_name);
142 self.boundary.strict_join(new_systempath)
143 }
144
145 pub fn strictpath_with_extension<S: AsRef<OsStr>>(&self, extension: S) -> Result<Self> {
147 let system_path = &self.path;
148 if system_path.file_name().is_none() {
149 return Err(StrictPathError::path_escapes_boundary(
150 self.path.to_path_buf(),
151 self.boundary.path().to_path_buf(),
152 ));
153 }
154 let new_systempath = system_path.with_extension(extension);
155 self.boundary.strict_join(new_systempath)
156 }
157
158 #[inline]
160 pub fn strictpath_file_name(&self) -> Option<&OsStr> {
161 self.path.file_name()
162 }
163
164 #[inline]
166 pub fn strictpath_file_stem(&self) -> Option<&OsStr> {
167 self.path.file_stem()
168 }
169
170 #[inline]
172 pub fn strictpath_extension(&self) -> Option<&OsStr> {
173 self.path.extension()
174 }
175
176 #[inline]
178 pub fn strictpath_starts_with<P: AsRef<Path>>(&self, p: P) -> bool {
179 self.path.starts_with(p.as_ref())
180 }
181
182 #[inline]
184 pub fn strictpath_ends_with<P: AsRef<Path>>(&self, p: P) -> bool {
185 self.path.ends_with(p.as_ref())
186 }
187
188 pub fn exists(&self) -> bool {
190 self.path.exists()
191 }
192
193 pub fn is_file(&self) -> bool {
195 self.path.is_file()
196 }
197
198 pub fn is_dir(&self) -> bool {
200 self.path.is_dir()
201 }
202
203 pub fn metadata(&self) -> std::io::Result<std::fs::Metadata> {
205 std::fs::metadata(&self.path)
206 }
207
208 pub fn read_to_string(&self) -> std::io::Result<String> {
210 std::fs::read_to_string(&self.path)
211 }
212
213 pub fn read_bytes(&self) -> std::io::Result<Vec<u8>> {
215 std::fs::read(&self.path)
216 }
217
218 pub fn write_bytes(&self, data: &[u8]) -> std::io::Result<()> {
220 std::fs::write(&self.path, data)
221 }
222
223 pub fn write_string(&self, data: &str) -> std::io::Result<()> {
225 std::fs::write(&self.path, data)
226 }
227
228 pub fn create_dir_all(&self) -> std::io::Result<()> {
230 std::fs::create_dir_all(&self.path)
231 }
232
233 pub fn create_dir(&self) -> std::io::Result<()> {
238 std::fs::create_dir(&self.path)
239 }
240
241 pub fn create_parent_dir(&self) -> std::io::Result<()> {
246 match self.strictpath_parent() {
247 Ok(Some(parent)) => parent.create_dir(),
248 Ok(None) => Ok(()),
249 Err(StrictPathError::PathEscapesBoundary { .. }) => Ok(()),
250 Err(e) => Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
251 }
252 }
253
254 pub fn create_parent_dir_all(&self) -> std::io::Result<()> {
258 match self.strictpath_parent() {
259 Ok(Some(parent)) => parent.create_dir_all(),
260 Ok(None) => Ok(()),
261 Err(StrictPathError::PathEscapesBoundary { .. }) => Ok(()),
262 Err(e) => Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
263 }
264 }
265
266 pub fn strict_rename<P: AsRef<Path>>(&self, dest: P) -> std::io::Result<Self> {
273 let dest_ref = dest.as_ref();
274
275 let dest_path = if dest_ref.is_absolute() {
277 match self.boundary.strict_join(dest_ref) {
278 Ok(p) => p,
279 Err(e) => return Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
280 }
281 } else {
282 let parent = match self.strictpath_parent() {
283 Ok(Some(p)) => p,
284 Ok(None) => match self.boundary.strict_join("") {
285 Ok(root) => root,
286 Err(e) => return Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
287 },
288 Err(e) => return Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
289 };
290 match parent.strict_join(dest_ref) {
291 Ok(p) => p,
292 Err(e) => return Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
293 }
294 };
295
296 std::fs::rename(self.path(), dest_path.path())?;
297 Ok(dest_path)
298 }
299
300 pub fn remove_file(&self) -> std::io::Result<()> {
302 std::fs::remove_file(&self.path)
303 }
304
305 pub fn remove_dir(&self) -> std::io::Result<()> {
307 std::fs::remove_dir(&self.path)
308 }
309
310 pub fn remove_dir_all(&self) -> std::io::Result<()> {
312 std::fs::remove_dir_all(&self.path)
313 }
314}
315
316#[cfg(feature = "serde")]
317impl<Marker> serde::Serialize for StrictPath<Marker> {
318 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
319 where
320 S: serde::Serializer,
321 {
322 serializer.serialize_str(self.strictpath_to_string_lossy().as_ref())
323 }
324}
325
326impl<Marker> fmt::Debug for StrictPath<Marker> {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 f.debug_struct("StrictPath")
329 .field("path", &self.path)
330 .field("boundary", &self.boundary.path())
331 .field("marker", &std::any::type_name::<Marker>())
332 .finish()
333 }
334}
335
336impl<Marker> PartialEq for StrictPath<Marker> {
337 #[inline]
338 fn eq(&self, other: &Self) -> bool {
339 self.path.as_ref() == other.path.as_ref()
340 }
341}
342
343impl<Marker> Eq for StrictPath<Marker> {}
344
345impl<Marker> Hash for StrictPath<Marker> {
346 #[inline]
347 fn hash<H: Hasher>(&self, state: &mut H) {
348 self.path.hash(state);
349 }
350}
351
352impl<Marker> PartialOrd for StrictPath<Marker> {
353 #[inline]
354 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
355 Some(self.cmp(other))
356 }
357}
358
359impl<Marker> Ord for StrictPath<Marker> {
360 #[inline]
361 fn cmp(&self, other: &Self) -> Ordering {
362 self.path.cmp(&other.path)
363 }
364}
365
366impl<T: AsRef<Path>, Marker> PartialEq<T> for StrictPath<Marker> {
367 fn eq(&self, other: &T) -> bool {
368 self.path.as_ref() == other.as_ref()
369 }
370}
371
372impl<T: AsRef<Path>, Marker> PartialOrd<T> for StrictPath<Marker> {
373 fn partial_cmp(&self, other: &T) -> Option<Ordering> {
374 Some(self.path.as_ref().cmp(other.as_ref()))
375 }
376}
377
378impl<Marker> PartialEq<crate::path::virtual_path::VirtualPath<Marker>> for StrictPath<Marker> {
379 #[inline]
380 fn eq(&self, other: &crate::path::virtual_path::VirtualPath<Marker>) -> bool {
381 self.path.as_ref() == other.interop_path()
382 }
383}