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}