path_abs/
file.rs

1/* Copyright (c) 2018 Garrett Berg, vitiral@gmail.com
2 *
3 * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 * http://opensource.org/licenses/MIT>, at your option. This file may not be
6 * copied, modified, or distributed except according to those terms.
7 */
8use std::ffi;
9use std::fmt;
10use std::fs;
11use std::io;
12use std_prelude::*;
13
14use super::{Error, Result};
15use super::{FileEdit, FileRead, FileWrite, PathAbs, PathDir, PathInfo, PathOps};
16
17#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
18/// a `PathAbs` that was a file at the time of initialization, with associated methods.
19pub struct PathFile(pub(crate) PathAbs);
20
21impl PathFile {
22    /// Instantiate a new `PathFile`. The file must exist or `io::Error` will be returned.
23    ///
24    /// Returns `io::ErrorKind::InvalidInput` if the path exists but is not a file.
25    ///
26    /// # Examples
27    /// ```rust
28    /// # extern crate path_abs;
29    /// use path_abs::PathFile;
30    ///
31    /// # fn try_main() -> ::std::io::Result<()> {
32    /// let lib = PathFile::new("src/lib.rs")?;
33    /// # Ok(()) } fn main() { try_main().unwrap() }
34    /// ```
35    pub fn new<P: AsRef<Path>>(path: P) -> Result<PathFile> {
36        let abs = PathAbs::new(path)?;
37        PathFile::try_from(abs)
38    }
39
40    /// Create a `PathFile` unchecked.
41    ///
42    /// This is mostly used for constructing during tests, or if the path was previously validated.
43    /// This is effectively the same as a `Arc<PathBuf>`.
44    ///
45    /// > Note: This is memory safe, so is not marked `unsafe`. However, it could cause
46    /// > panics in some methods if the path was not properly validated.
47    pub fn new_unchecked<P: Into<Arc<PathBuf>>>(path: P) -> PathFile {
48        PathFile(PathAbs::new_unchecked(path))
49    }
50
51    /// Convert a `PathAbs` into a `PathFile`, first validating that the path is a file.
52    ///
53    /// # Error
54    /// If the path is not a file.
55    ///
56    /// # Examples
57    /// ```rust
58    /// # extern crate path_abs;
59    /// use path_abs::{PathAbs, PathFile};
60    ///
61    /// # fn try_main() -> ::std::io::Result<()> {
62    /// let lib_abs = PathAbs::new("src/lib.rs")?;
63    /// let lib_file = PathFile::try_from(lib_abs)?;
64    /// # Ok(()) } fn main() { try_main().unwrap() }
65    /// ```
66    pub fn try_from<P: Into<PathAbs>>(path: P) -> Result<PathFile> {
67        let abs = path.into();
68        if abs.is_file() {
69            Ok(PathFile::new_unchecked(abs))
70        } else {
71            Err(Error::new(
72                io::Error::new(io::ErrorKind::InvalidInput, "path is not a file"),
73                "resolving",
74                abs.into(),
75            ))
76        }
77    }
78
79    /// Get the parent directory of this file as a `PathDir`.
80    ///
81    /// > This does not make aditional syscalls, as the parent by definition must be a directory
82    /// > and exist.
83    ///
84    /// # Panics
85    /// Panics if there is no parent. The only way this could happen is if
86    /// it was constructed with `new_unchecked` using a relative path.
87    ///
88    /// # Examples
89    /// ```rust
90    /// # extern crate path_abs;
91    /// use path_abs::{PathDir, PathFile};
92    ///
93    /// # fn try_main() -> ::std::io::Result<()> {
94    /// let lib = PathFile::new("src/lib.rs")?;
95    /// let src = lib.parent_dir();
96    /// assert_eq!(PathDir::new("src")?, src);
97    /// # Ok(()) } fn main() { try_main().unwrap() }
98    /// ```
99    pub fn parent_dir(&self) -> PathDir {
100        let path = self.parent().expect("PathFile did not have a parent.");
101        PathDir::new_unchecked(PathAbs::new_unchecked(path.to_path_buf()))
102    }
103
104    /// Instantiate a new `PathFile`, creating an empty file if it doesn't exist.
105    ///
106    /// # Examples
107    /// ```rust
108    /// # extern crate path_abs;
109    /// # extern crate tempdir;
110    /// use path_abs::PathFile;
111    ///
112    /// # fn try_main() -> ::std::io::Result<()> {
113    /// let example = "example.txt";
114    /// # let tmp = tempdir::TempDir::new("ex")?;
115    /// # let example = &tmp.path().join(example);
116    ///
117    /// # let tmp = tempdir::TempDir::new("ex")?;
118    /// # let example = &tmp.path().join(example);
119    ///
120    /// let file = PathFile::create(example)?;
121    ///
122    /// // It can be done twice with no effect.
123    /// let _ = PathFile::create(example)?;
124    /// # Ok(()) } fn main() { try_main().unwrap() }
125    /// ```
126    pub fn create<P: AsRef<Path>>(path: P) -> Result<PathFile> {
127        fs::OpenOptions::new()
128            .write(true)
129            .create(true)
130            .open(&path)
131            .map_err(|err| Error::new(err, "opening", path.as_ref().to_path_buf().into()))?;
132        PathFile::new(path)
133    }
134
135    /// Read the entire contents of the file into a `String`.
136    ///
137    /// # Examples
138    /// ```rust
139    /// # extern crate path_abs;
140    /// # extern crate tempdir;
141    /// use path_abs::PathFile;
142    ///
143    /// # fn try_main() -> ::std::io::Result<()> {
144    /// let example = "example.txt";
145    /// # let tmp = tempdir::TempDir::new("ex")?;
146    /// # let example = &tmp.path().join(example);
147    /// let file = PathFile::create(example)?;
148    ///
149    /// let expected = "foo\nbar";
150    /// file.write_str(expected)?;
151    /// assert_eq!(expected, file.read_string()?);
152    /// # Ok(()) } fn main() { try_main().unwrap() }
153    /// ```
154    pub fn read_string(&self) -> Result<String> {
155        let mut f = self.open_read()?;
156        f.read_string()
157    }
158
159    /// Write the `str` to a file, truncating it first if it exists and creating it otherwise.
160    ///
161    /// # Examples
162    /// ```rust
163    /// # extern crate path_abs;
164    /// # extern crate tempdir;
165    /// use path_abs::PathFile;
166    ///
167    /// # fn try_main() -> ::std::io::Result<()> {
168    /// let example = "example.txt";
169    /// # let tmp = tempdir::TempDir::new("ex")?;
170    /// # let example = &tmp.path().join(example);
171    /// let file = PathFile::create(example)?;
172    ///
173    /// let expected = "foo\nbar";
174    /// file.write_str(expected)?;
175    /// assert_eq!(expected, file.read_string()?);
176    /// # Ok(()) } fn main() { try_main().unwrap() }
177    /// ```
178    pub fn write_str(&self, s: &str) -> Result<()> {
179        let mut options = fs::OpenOptions::new();
180        options.create(true);
181        options.truncate(true);
182        let mut f = FileWrite::open_abs(self.clone(), options)?;
183        if s.is_empty() {
184            return Ok(());
185        }
186        f.write_str(s)?;
187        f.flush()
188    }
189
190    /// Append the `str` to a file, creating it if it doesn't exist.
191    ///
192    /// # Examples
193    /// ```rust
194    /// # extern crate path_abs;
195    /// # extern crate tempdir;
196    /// use path_abs::PathFile;
197    ///
198    /// # fn try_main() -> ::std::io::Result<()> {
199    /// let example = "example.txt";
200    /// # let tmp = tempdir::TempDir::new("ex")?;
201    /// # let example = &tmp.path().join(example);
202    /// let file = PathFile::create(example)?;
203    ///
204    /// let expected = "foo\nbar\nbaz";
205    /// file.append_str("foo\nbar")?;
206    /// file.append_str("\nbaz")?;
207    /// assert_eq!(expected, file.read_string()?);
208    /// # Ok(()) } fn main() { try_main().unwrap() }
209    /// ```
210    pub fn append_str(&self, s: &str) -> Result<()> {
211        let mut f = self.open_append()?;
212        if s.is_empty() {
213            return Ok(());
214        }
215        f.write_str(s)?;
216        f.flush()
217    }
218
219    /// Open the file as read-only.
220    ///
221    /// # Examples
222    /// ```rust
223    /// # extern crate path_abs;
224    /// # extern crate tempdir;
225    /// use std::io::Read;
226    /// use path_abs::PathFile;
227    ///
228    /// # fn try_main() -> ::std::io::Result<()> {
229    /// let example = "example.txt";
230    /// # let tmp = tempdir::TempDir::new("ex")?;
231    /// # let example = &tmp.path().join(example);
232    /// let file = PathFile::create(example)?;
233    ///
234    /// let expected = "foo\nbar";
235    /// file.write_str(expected)?;
236    ///
237    /// let mut read = file.open_read()?;
238    /// let mut s = String::new();
239    /// read.read_to_string(&mut s)?;
240    /// assert_eq!(expected, s);
241    /// # Ok(()) } fn main() { try_main().unwrap() }
242    /// ```
243    pub fn open_read(&self) -> Result<FileRead> {
244        FileRead::open_abs(self.clone())
245    }
246
247    /// Open the file as write-only in append mode.
248    ///
249    /// # Examples
250    /// ```rust
251    /// # extern crate path_abs;
252    /// # extern crate tempdir;
253    /// use std::io::Write;
254    /// use path_abs::PathFile;
255    ///
256    /// # fn try_main() -> ::std::io::Result<()> {
257    /// let example = "example.txt";
258    /// # let tmp = tempdir::TempDir::new("ex")?;
259    /// # let example = &tmp.path().join(example);
260    /// let file = PathFile::create(example)?;
261    ///
262    /// let expected = "foo\nbar\n";
263    /// file.write_str("foo\n")?;
264    ///
265    /// let mut append = file.open_append()?;
266    /// append.write_all(b"bar\n")?;
267    /// append.flush();
268    /// assert_eq!(expected, file.read_string()?);
269    /// # Ok(()) } fn main() { try_main().unwrap() }
270    /// ```
271    pub fn open_append(&self) -> Result<FileWrite> {
272        let mut options = fs::OpenOptions::new();
273        options.append(true);
274        FileWrite::open_abs(self.clone(), options)
275    }
276
277    /// Open the file for editing (reading and writing).
278    ///
279    /// # Examples
280    /// ```rust
281    /// # extern crate path_abs;
282    /// # extern crate tempdir;
283    /// use std::io::{Read, Seek, Write, SeekFrom};
284    /// use path_abs::PathFile;
285    ///
286    /// # fn try_main() -> ::std::io::Result<()> {
287    /// let example = "example.txt";
288    /// # let tmp = tempdir::TempDir::new("ex")?;
289    /// # let example = &tmp.path().join(example);
290    /// let file = PathFile::create(example)?;
291    ///
292    /// let expected = "foo\nbar";
293    ///
294    /// let mut edit = file.open_edit()?;
295    /// let mut s = String::new();
296    ///
297    /// edit.write_all(expected.as_bytes())?;
298    /// edit.seek(SeekFrom::Start(0))?;
299    /// edit.read_to_string(&mut s)?;
300    /// assert_eq!(expected, s);
301    /// # Ok(()) } fn main() { try_main().unwrap() }
302    /// ```
303    pub fn open_edit(&self) -> Result<FileEdit> {
304        FileEdit::open_abs(self.clone(), fs::OpenOptions::new())
305    }
306
307    /// Copy the file to another location, including permission bits
308    ///
309    /// # Examples
310    ///
311    /// ```rust
312    /// # extern crate path_abs;
313    /// # extern crate tempdir;
314    /// use path_abs::PathFile;
315    /// use std::path::Path;
316    ///
317    /// # fn try_main() -> ::std::io::Result<()> {
318    /// let example = "example.txt";
319    /// let example_bk = "example.txt.bk";
320    /// # let tmp = tempdir::TempDir::new("ex")?;
321    /// # let example = &tmp.path().join(example);
322    /// # let example_bk = &tmp.path().join(example_bk);
323    /// let file = PathFile::create(example)?;
324    ///
325    /// let contents = "This is some contents";
326    /// file.write_str(contents);
327    /// let file_bk = file.copy(example_bk)?;
328    /// assert_eq!(contents, file.read_string()?);
329    /// assert_eq!(contents, file_bk.read_string()?);
330    /// # Ok(()) } fn main() { try_main().unwrap() }
331    /// ```
332    pub fn copy<P: AsRef<Path>>(&self, path: P) -> Result<PathFile> {
333        fs::copy(&self, &path).map_err(|err| {
334            Error::new(
335                err,
336                &format!("copying {} from", path.as_ref().display()),
337                self.clone().into(),
338            )
339        })?;
340        Ok(PathFile::new(path)?)
341    }
342
343    /// Rename a file, replacing the original file if `to` already exists.
344    ///
345    /// This will not work if the new name is on a different mount point.
346    ///
347    /// # Examples
348    /// ```rust
349    /// # extern crate path_abs;
350    /// # extern crate tempdir;
351    /// use path_abs::{PathFile, PathInfo};
352    /// use std::path::Path;
353    ///
354    /// # fn try_main() -> ::std::io::Result<()> {
355    /// let example = "example.txt";
356    /// let example_bk = "example.txt.bk";
357    /// # let tmp = tempdir::TempDir::new("ex")?;
358    /// # let example = &tmp.path().join(example);
359    /// # let example_bk = &tmp.path().join(example_bk);
360    /// let file = PathFile::create(example)?;
361    ///
362    /// let contents = "This is some contents";
363    /// file.write_str(contents);
364    /// let file_bk = file.clone().rename(example_bk)?;
365    /// assert!(!file.exists());
366    /// assert_eq!(contents, file_bk.read_string()?);
367    /// # Ok(()) } fn main() { try_main().unwrap() }
368    /// ```
369    pub fn rename<P: AsRef<Path>>(self, to: P) -> Result<PathFile> {
370        fs::rename(&self, &to).map_err(|err| {
371            Error::new(
372                err,
373                &format!("renaming to {} from", to.as_ref().display()),
374                self.clone().into(),
375            )
376        })?;
377        Ok(PathFile::new(to)?)
378    }
379
380    /// Creates a new symbolic link on the filesystem to the dst.
381    ///
382    /// This handles platform specific behavior correctly.
383    ///
384    /// # Examples
385    ///
386    /// ```rust
387    /// # extern crate path_abs;
388    /// # extern crate tempdir;
389    /// use path_abs::PathFile;
390    /// use std::path::Path;
391    ///
392    /// # fn try_main() -> ::std::io::Result<()> {
393    /// let example = "example.txt";
394    /// let example_sym = "example.txt.sym";
395    /// # let tmp = tempdir::TempDir::new("ex")?;
396    /// # let example = &tmp.path().join(example);
397    /// # let example_sym = &tmp.path().join(example_sym);
398    /// let file = PathFile::create(example)?;
399    ///
400    /// let contents = "This is some contents";
401    /// file.write_str(contents);
402    /// let file_sym = file.symlink(example_sym)?;
403    ///
404    /// // They have a different "absolute path"
405    /// assert_ne!(file, file_sym);
406    ///
407    /// // But they can be canonicalized to the same file.
408    /// let file_can = file_sym.canonicalize()?;
409    /// assert_eq!(file, file_can);
410    /// # Ok(()) } fn main() { try_main().unwrap() }
411    /// ```
412    pub fn symlink<P: AsRef<Path>>(&self, dst: P) -> Result<PathFile> {
413        symlink_file(&self, &dst).map_err(|err| {
414            Error::new(
415                err,
416                &format!("linking from {} to", dst.as_ref().display()),
417                self.clone().into(),
418            )
419        })?;
420        PathFile::new(dst)
421    }
422
423    /// Remove (delete) the file from the filesystem, consuming self.
424    ///
425    /// # Examples
426    ///
427    /// ```rust
428    /// # extern crate path_abs;
429    /// # extern crate tempdir;
430    /// use path_abs::{PathFile, PathInfo};
431    /// use std::path::Path;
432    ///
433    /// # fn try_main() -> ::std::io::Result<()> {
434    /// let example = "example.txt";
435    /// # let tmp = tempdir::TempDir::new("ex")?;
436    /// # let example = &tmp.path().join(example);
437    /// let file = PathFile::create(example)?;
438    /// assert!(file.exists());
439    /// file.remove()?;
440    ///
441    /// // file.exists() <--- COMPILER ERROR, `file` was consumed
442    ///
443    /// assert!(!Path::new(example).exists());
444    /// # Ok(()) } fn main() { try_main().unwrap() }
445    /// ```
446    pub fn remove(self) -> Result<()> {
447        fs::remove_file(&self).map_err(|err| Error::new(err, "removing", self.into()))
448    }
449
450    /// Return a reference to a basic `std::path::Path`
451    pub fn as_path(&self) -> &Path {
452        self.as_ref()
453    }
454
455    /// Returns the canonical form of the path with all intermediate components normalized and
456    /// symbolic links resolved.
457    ///
458    /// See [`PathAbs::canonicalize`]
459    ///
460    /// [`PathAbs::canonicalize`]: struct.PathAbs.html#method.canonicalize
461    pub fn canonicalize(&self) -> Result<PathFile> {
462        Ok(PathFile(self.0.canonicalize()?))
463    }
464}
465
466impl fmt::Debug for PathFile {
467    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468        self.0.fmt(f)
469    }
470}
471
472impl AsRef<ffi::OsStr> for PathFile {
473    fn as_ref(&self) -> &std::ffi::OsStr {
474        self.0.as_ref()
475    }
476}
477
478impl AsRef<PathAbs> for PathFile {
479    fn as_ref(&self) -> &PathAbs {
480        &self.0
481    }
482}
483
484impl AsRef<Path> for PathFile {
485    fn as_ref(&self) -> &Path {
486        self.0.as_ref()
487    }
488}
489
490impl AsRef<PathBuf> for PathFile {
491    fn as_ref(&self) -> &PathBuf {
492        self.0.as_ref()
493    }
494}
495
496impl Borrow<PathAbs> for PathFile {
497    fn borrow(&self) -> &PathAbs {
498        self.as_ref()
499    }
500}
501
502impl Borrow<Path> for PathFile {
503    fn borrow(&self) -> &Path {
504        self.as_ref()
505    }
506}
507
508impl Borrow<PathBuf> for PathFile {
509    fn borrow(&self) -> &PathBuf {
510        self.as_ref()
511    }
512}
513
514impl<'a> Borrow<PathAbs> for &'a PathFile {
515    fn borrow(&self) -> &PathAbs {
516        self.as_ref()
517    }
518}
519
520impl<'a> Borrow<Path> for &'a PathFile {
521    fn borrow(&self) -> &Path {
522        self.as_ref()
523    }
524}
525
526impl<'a> Borrow<PathBuf> for &'a PathFile {
527    fn borrow(&self) -> &PathBuf {
528        self.as_ref()
529    }
530}
531
532impl From<PathFile> for PathAbs {
533    fn from(path: PathFile) -> PathAbs {
534        path.0
535    }
536}
537
538impl From<PathFile> for Arc<PathBuf> {
539    fn from(path: PathFile) -> Arc<PathBuf> {
540        let abs: PathAbs = path.into();
541        abs.into()
542    }
543}
544
545impl From<PathFile> for PathBuf {
546    fn from(path: PathFile) -> PathBuf {
547        let abs: PathAbs = path.into();
548        abs.into()
549    }
550}
551
552impl PathOps for PathFile {
553    type Output = PathAbs;
554
555    fn concat<P: AsRef<Path>>(&self, path: P) -> Result<Self::Output> {
556        Ok(self.0.concat(path)?)
557    }
558
559    fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
560        let buf = Path::join(self.as_path(), path);
561        Self::Output::new_unchecked(buf)
562    }
563
564    fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
565        self.0.with_file_name(file_name)
566    }
567
568    fn with_extension<S: AsRef<ffi::OsStr>>(&self, extension: S) -> Self::Output {
569        self.0.with_extension(extension)
570    }
571}
572
573#[cfg(target_os = "wasi")]
574fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
575    std::os::wasi::fs::symlink_path(src, dst)
576}
577
578#[cfg(unix)]
579fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
580    ::std::os::unix::fs::symlink(src, dst)
581}
582
583#[cfg(windows)]
584fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
585    ::std::os::windows::fs::symlink_file(src, dst)
586}