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 restriction: Arc<crate::PathBoundary<Marker>>,
32 _marker: PhantomData<Marker>,
33}
34
35impl<Marker> StrictPath<Marker> {
36 pub(crate) fn new(
37 restriction: Arc<crate::PathBoundary<Marker>>,
38 validated_path: PathHistory<((Raw, Canonicalized), BoundaryChecked)>,
39 ) -> Self {
40 Self {
41 path: validated_path,
42 restriction,
43 _marker: PhantomData,
44 }
45 }
46
47 #[inline]
48 pub(crate) fn restriction(&self) -> &crate::PathBoundary<Marker> {
49 &self.restriction
50 }
51
52 #[inline]
53 pub(crate) fn path(&self) -> &Path {
54 &self.path
55 }
56
57 #[inline]
60 pub fn strictpath_to_string_lossy(&self) -> std::borrow::Cow<'_, str> {
61 self.path.to_string_lossy()
62 }
63
64 #[inline]
68 pub fn strictpath_to_str(&self) -> Option<&str> {
69 self.path.to_str()
70 }
71
72 #[inline]
76 pub fn interop_path(&self) -> &OsStr {
77 self.path.as_os_str()
78 }
79
80 #[inline]
82 pub fn strictpath_display(&self) -> std::path::Display<'_> {
83 self.path.display()
84 }
85
86 #[inline]
90 pub fn unstrict(self) -> PathBuf {
91 self.path.into_inner()
92 }
93
94 #[inline]
96 pub fn virtualize(self) -> crate::path::virtual_path::VirtualPath<Marker> {
97 crate::path::virtual_path::VirtualPath::new(self)
98 }
99
100 #[inline]
105 pub fn strict_join<P: AsRef<Path>>(&self, path: P) -> Result<Self> {
106 let new_systempath = self.path.join(path);
107 self.restriction.strict_join(new_systempath)
108 }
109
110 pub fn strictpath_parent(&self) -> Result<Option<Self>> {
112 match self.path.parent() {
113 Some(p) => match self.restriction.strict_join(p) {
114 Ok(p) => Ok(Some(p)),
115 Err(e) => Err(e),
116 },
117 None => Ok(None),
118 }
119 }
120
121 #[inline]
123 pub fn strictpath_with_file_name<S: AsRef<OsStr>>(&self, file_name: S) -> Result<Self> {
124 let new_systempath = self.path.with_file_name(file_name);
125 self.restriction.strict_join(new_systempath)
126 }
127
128 pub fn strictpath_with_extension<S: AsRef<OsStr>>(&self, extension: S) -> Result<Self> {
130 let system_path = &self.path;
131 if system_path.file_name().is_none() {
132 return Err(StrictPathError::path_escapes_boundary(
133 self.path.to_path_buf(),
134 self.restriction.path().to_path_buf(),
135 ));
136 }
137 let new_systempath = system_path.with_extension(extension);
138 self.restriction.strict_join(new_systempath)
139 }
140
141 #[inline]
143 pub fn strictpath_file_name(&self) -> Option<&OsStr> {
144 self.path.file_name()
145 }
146
147 #[inline]
149 pub fn strictpath_file_stem(&self) -> Option<&OsStr> {
150 self.path.file_stem()
151 }
152
153 #[inline]
155 pub fn strictpath_extension(&self) -> Option<&OsStr> {
156 self.path.extension()
157 }
158
159 #[inline]
161 pub fn strictpath_starts_with<P: AsRef<Path>>(&self, p: P) -> bool {
162 self.path.starts_with(p.as_ref())
163 }
164
165 #[inline]
167 pub fn strictpath_ends_with<P: AsRef<Path>>(&self, p: P) -> bool {
168 self.path.ends_with(p.as_ref())
169 }
170
171 pub fn exists(&self) -> bool {
173 self.path.exists()
174 }
175
176 pub fn is_file(&self) -> bool {
178 self.path.is_file()
179 }
180
181 pub fn is_dir(&self) -> bool {
183 self.path.is_dir()
184 }
185
186 pub fn metadata(&self) -> std::io::Result<std::fs::Metadata> {
188 std::fs::metadata(&self.path)
189 }
190
191 pub fn read_to_string(&self) -> std::io::Result<String> {
193 std::fs::read_to_string(&self.path)
194 }
195
196 pub fn read_bytes(&self) -> std::io::Result<Vec<u8>> {
198 std::fs::read(&self.path)
199 }
200
201 pub fn write_bytes(&self, data: &[u8]) -> std::io::Result<()> {
203 std::fs::write(&self.path, data)
204 }
205
206 pub fn write_string(&self, data: &str) -> std::io::Result<()> {
208 std::fs::write(&self.path, data)
209 }
210
211 pub fn create_dir_all(&self) -> std::io::Result<()> {
213 std::fs::create_dir_all(&self.path)
214 }
215
216 pub fn create_dir(&self) -> std::io::Result<()> {
221 std::fs::create_dir(&self.path)
222 }
223
224 pub fn create_parent_dir(&self) -> std::io::Result<()> {
229 match self.strictpath_parent() {
230 Ok(Some(parent)) => parent.create_dir(),
231 Ok(None) => Ok(()),
232 Err(StrictPathError::PathEscapesBoundary { .. }) => Ok(()),
233 Err(e) => Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
234 }
235 }
236
237 pub fn create_parent_dir_all(&self) -> std::io::Result<()> {
241 match self.strictpath_parent() {
242 Ok(Some(parent)) => parent.create_dir_all(),
243 Ok(None) => Ok(()),
244 Err(StrictPathError::PathEscapesBoundary { .. }) => Ok(()),
245 Err(e) => Err(std::io::Error::new(std::io::ErrorKind::Other, e)),
246 }
247 }
248
249 pub fn remove_file(&self) -> std::io::Result<()> {
251 std::fs::remove_file(&self.path)
252 }
253
254 pub fn remove_dir(&self) -> std::io::Result<()> {
256 std::fs::remove_dir(&self.path)
257 }
258
259 pub fn remove_dir_all(&self) -> std::io::Result<()> {
261 std::fs::remove_dir_all(&self.path)
262 }
263}
264
265#[cfg(feature = "serde")]
266impl<Marker> serde::Serialize for StrictPath<Marker> {
267 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
268 where
269 S: serde::Serializer,
270 {
271 serializer.serialize_str(self.strictpath_to_string_lossy().as_ref())
272 }
273}
274
275impl<Marker> fmt::Debug for StrictPath<Marker> {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 f.debug_struct("StrictPath")
278 .field("path", &self.path)
279 .field("restriction", &self.restriction.path())
280 .field("marker", &std::any::type_name::<Marker>())
281 .finish()
282 }
283}
284
285impl<Marker> PartialEq for StrictPath<Marker> {
286 #[inline]
287 fn eq(&self, other: &Self) -> bool {
288 self.path.as_ref() == other.path.as_ref()
289 }
290}
291
292impl<Marker> Eq for StrictPath<Marker> {}
293
294impl<Marker> Hash for StrictPath<Marker> {
295 #[inline]
296 fn hash<H: Hasher>(&self, state: &mut H) {
297 self.path.hash(state);
298 }
299}
300
301impl<Marker> PartialOrd for StrictPath<Marker> {
302 #[inline]
303 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
304 Some(self.cmp(other))
305 }
306}
307
308impl<Marker> Ord for StrictPath<Marker> {
309 #[inline]
310 fn cmp(&self, other: &Self) -> Ordering {
311 self.path.cmp(&other.path)
312 }
313}
314
315impl<T: AsRef<Path>, Marker> PartialEq<T> for StrictPath<Marker> {
316 fn eq(&self, other: &T) -> bool {
317 self.path.as_ref() == other.as_ref()
318 }
319}
320
321impl<T: AsRef<Path>, Marker> PartialOrd<T> for StrictPath<Marker> {
322 fn partial_cmp(&self, other: &T) -> Option<Ordering> {
323 Some(self.path.as_ref().cmp(other.as_ref()))
324 }
325}
326
327impl<Marker> PartialEq<crate::path::virtual_path::VirtualPath<Marker>> for StrictPath<Marker> {
328 #[inline]
329 fn eq(&self, other: &crate::path::virtual_path::VirtualPath<Marker>) -> bool {
330 self.path.as_ref() == other.interop_path()
331 }
332}