1use crate::{Error, Result};
2use crate::{SMeta, SPath};
3use camino::{Utf8Path, Utf8PathBuf};
4use core::fmt;
5use std::fs::{self, Metadata};
6use std::path::{Path, PathBuf};
7use std::time::SystemTime;
8
9#[derive(Debug, Clone)]
16pub struct SFile {
17 path: SPath,
18}
19
20impl SFile {
22 pub fn new(path: impl Into<Utf8PathBuf>) -> Result<Self> {
24 let path = SPath::new(path);
25 validate_sfile_for_result(&path)?;
26 Ok(Self { path })
27 }
28
29 pub fn from_std_path_buf(path_buf: PathBuf) -> Result<Self> {
31 let path = SPath::from_std_path_buf(path_buf)?;
32 validate_sfile_for_result(&path)?;
33 Ok(Self { path })
34 }
35
36 pub fn from_std_path(path: impl AsRef<Path>) -> Result<Self> {
38 let path = SPath::from_std_path(path)?;
39 validate_sfile_for_result(&path)?;
40 Ok(Self { path })
41 }
42
43 pub fn from_walkdir_entry(wd_entry: walkdir::DirEntry) -> Result<Self> {
45 let path = SPath::from_walkdir_entry(wd_entry)?;
46 validate_sfile_for_result(&path)?;
47 Ok(Self { path })
48 }
49
50 pub fn from_std_path_ok(path: impl AsRef<Path>) -> Option<Self> {
54 let path = SPath::from_std_path_ok(path)?;
55 validate_sfile_for_option(&path)?;
56 Some(Self { path })
57 }
58
59 pub fn from_std_path_buf_ok(path_buf: PathBuf) -> Option<Self> {
62 let path = SPath::from_std_path_buf_ok(path_buf)?;
63 validate_sfile_for_option(&path)?;
64 Some(Self { path })
65 }
66
67 pub fn from_fs_entry_ok(fs_entry: fs::DirEntry) -> Option<Self> {
70 let path = SPath::from_fs_entry_ok(fs_entry)?;
71 validate_sfile_for_option(&path)?;
72 Some(Self { path })
73 }
74
75 pub fn from_walkdir_entry_ok(wd_entry: walkdir::DirEntry) -> Option<Self> {
78 let path = SPath::from_walkdir_entry_ok(wd_entry)?;
79 validate_sfile_for_option(&path)?;
80 Some(Self { path })
81 }
82}
83
84impl SFile {
86 pub fn into_std_path_buf(self) -> PathBuf {
88 self.path.into_std_path_buf()
89 }
90
91 pub fn std_path(&self) -> &Path {
93 self.path.std_path()
94 }
95
96 pub fn path(&self) -> &SPath {
98 &self.path
99 }
100}
101
102impl SFile {
104 #[deprecated(note = "Use `as_str()` instead")]
109 pub fn to_str(&self) -> &str {
110 self.path.as_str()
111 }
112
113 pub fn as_str(&self) -> &str {
114 self.path.as_str()
115 }
116
117 pub fn file_name(&self) -> Option<&str> {
119 self.path.file_name()
120 }
121
122 pub fn name(&self) -> &str {
126 self.path.name()
127 }
128
129 pub fn parent_name(&self) -> &str {
131 self.path.parent_name()
132 }
133
134 pub fn file_stem(&self) -> Option<&str> {
136 self.path.file_stem()
137 }
138
139 pub fn stem(&self) -> &str {
143 self.path.stem()
144 }
145
146 pub fn extension(&self) -> Option<&str> {
151 self.path.extension()
152 }
153
154 pub fn ext(&self) -> &str {
156 self.path.ext()
157 }
158
159 pub fn is_absolute(&self) -> bool {
161 self.path.is_absolute()
162 }
163
164 pub fn is_relative(&self) -> bool {
166 self.path.is_relative()
167 }
168}
169
170impl SFile {
172 pub fn meta(&self) -> Result<SMeta> {
176 self.path.meta()
177 }
178
179 pub fn metadata(&self) -> Result<Metadata> {
181 self.path.metadata()
182 }
183
184 #[allow(deprecated)]
187 #[deprecated = "use spath.meta() or spath.metadata"]
188 pub fn modified(&self) -> Result<SystemTime> {
189 self.path.modified()
190 }
191
192 #[deprecated = "use spath.meta()"]
196 pub fn modified_us(&self) -> Result<i64> {
197 Ok(self.meta()?.modified_epoch_us)
198 }
199
200 #[deprecated = "use spath.meta()"]
202 pub fn file_size(&self) -> Result<u64> {
203 let path = self.std_path();
204 let metadata = fs::metadata(path).map_err(|ex| Error::CantGetMetadata((path, ex).into()))?;
205 Ok(metadata.len())
206 }
207}
208
209impl SFile {
211 pub fn canonicalize(&self) -> Result<SFile> {
212 let path = self.path.canonicalize()?;
213 Ok(SFile { path })
216 }
217
218 pub fn collapse(&self) -> SFile {
226 SFile {
227 path: self.path.collapse(),
228 }
229 }
230
231 pub fn into_collapsed(self) -> SFile {
233 if self.is_collapsed() { self } else { self.collapse() }
234 }
235
236 pub fn is_collapsed(&self) -> bool {
243 crate::is_collapsed(self)
244 }
245
246 pub fn parent(&self) -> Option<SPath> {
252 self.path.parent()
253 }
254
255 pub fn join(&self, leaf_path: impl Into<Utf8PathBuf>) -> SPath {
260 self.path.join(leaf_path)
261 }
262
263 pub fn join_std_path(&self, leaf_path: impl AsRef<Path>) -> Result<SPath> {
265 self.path.join_std_path(leaf_path)
266 }
267
268 pub fn new_sibling(&self, leaf_path: &str) -> SPath {
272 self.path.new_sibling(leaf_path)
273 }
274
275 pub fn new_sibling_std_path(&self, leaf_path: impl AsRef<Path>) -> Result<SPath> {
277 self.path.new_sibling_std_path(leaf_path)
278 }
279
280 pub fn diff(&self, base: impl AsRef<Utf8Path>) -> Option<SPath> {
285 self.path.diff(base)
286 }
287
288 pub fn try_diff(&self, base: impl AsRef<Utf8Path>) -> Result<SPath> {
289 self.path.try_diff(base)
290 }
291
292 pub fn replace_prefix(&self, base: impl AsRef<str>, with: impl AsRef<str>) -> SPath {
297 let path = &self.path;
298 path.replace_prefix(base, with)
299 }
300
301 pub fn into_replace_prefix(self, base: impl AsRef<str>, with: impl AsRef<str>) -> SPath {
302 let path = self.path;
303 path.into_replace_prefix(base, with)
304 }
305
306 }
308
309impl SFile {
311 pub fn as_std_path(&self) -> &Path {
312 self.path.std_path()
313 }
314
315 pub fn strip_prefix(&self, prefix: impl AsRef<Path>) -> Result<SPath> {
321 self.path.strip_prefix(prefix)
322 }
323
324 pub fn starts_with(&self, base: impl AsRef<Path>) -> bool {
347 self.path.starts_with(base)
348 }
349}
350
351impl AsRef<Path> for SFile {
354 fn as_ref(&self) -> &Path {
355 self.path.as_ref()
356 }
357}
358
359impl fmt::Display for SFile {
360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 write!(f, "{}", self.as_str())
362 }
363}
364
365impl AsRef<SFile> for SFile {
370 fn as_ref(&self) -> &SFile {
371 self
372 }
373}
374
375impl AsRef<Utf8Path> for SFile {
376 fn as_ref(&self) -> &Utf8Path {
377 self.path.as_ref()
378 }
379}
380
381impl AsRef<str> for SFile {
382 fn as_ref(&self) -> &str {
383 self.as_str()
384 }
385}
386
387impl AsRef<SPath> for SFile {
388 fn as_ref(&self) -> &SPath {
389 &self.path
390 }
391}
392
393impl From<SFile> for String {
398 fn from(val: SFile) -> Self {
399 val.as_str().to_string()
400 }
401}
402
403impl From<&SFile> for String {
404 fn from(val: &SFile) -> Self {
405 val.as_str().to_string()
406 }
407}
408
409impl From<SFile> for PathBuf {
410 fn from(val: SFile) -> Self {
411 val.into_std_path_buf()
412 }
413}
414
415impl From<&SFile> for PathBuf {
416 fn from(val: &SFile) -> Self {
417 val.std_path().to_path_buf()
418 }
419}
420
421impl From<SFile> for Utf8PathBuf {
422 fn from(val: SFile) -> Self {
423 val.path.path_buf
424 }
425}
426
427impl From<SFile> for SPath {
428 fn from(val: SFile) -> Self {
429 val.path
430 }
431}
432
433impl From<&SFile> for SPath {
434 fn from(val: &SFile) -> Self {
435 val.path.clone()
436 }
437}
438
439impl TryFrom<&str> for SFile {
444 type Error = Error;
445 fn try_from(path: &str) -> Result<SFile> {
446 let path = SPath::from(path);
447 validate_sfile_for_result(&path)?;
448 Ok(Self { path })
449 }
450}
451
452impl TryFrom<String> for SFile {
453 type Error = Error;
454 fn try_from(path: String) -> Result<SFile> {
455 SFile::try_from(path.as_str())
456 }
457}
458
459impl TryFrom<&String> for SFile {
460 type Error = Error;
461 fn try_from(path: &String) -> Result<SFile> {
462 SFile::try_from(path.as_str())
463 }
464}
465
466impl TryFrom<PathBuf> for SFile {
467 type Error = Error;
468 fn try_from(path_buf: PathBuf) -> Result<SFile> {
469 SFile::from_std_path_buf(path_buf)
470 }
471}
472
473impl TryFrom<fs::DirEntry> for SFile {
474 type Error = Error;
475 fn try_from(fs_entry: fs::DirEntry) -> Result<SFile> {
476 let path = SPath::try_from(fs_entry)?;
477 validate_sfile_for_result(&path)?;
478 Ok(Self { path })
479 }
480}
481
482impl TryFrom<walkdir::DirEntry> for SFile {
483 type Error = Error;
484 fn try_from(wd_entry: walkdir::DirEntry) -> Result<SFile> {
485 let path = SPath::try_from(wd_entry)?;
486 validate_sfile_for_result(&path)?;
487 Ok(Self { path })
488 }
489}
490
491impl TryFrom<SPath> for SFile {
492 type Error = Error;
493 fn try_from(path: SPath) -> Result<SFile> {
494 validate_sfile_for_result(&path)?;
495 Ok(Self { path })
496 }
497}
498fn validate_sfile_for_result(path: &SPath) -> Result<()> {
503 if path.is_file() {
504 Ok(())
505 } else {
506 Err(Error::FileNotFound(path.as_str().to_string()))
507 }
508}
509
510fn validate_sfile_for_option(path: &SPath) -> Option<()> {
512 if path.is_file() { Some(()) } else { None }
513}
514
515