diff options
Diffstat (limited to 'src/libstd/fs.rs')
| -rw-r--r-- | src/libstd/fs.rs | 232 |
1 files changed, 218 insertions, 14 deletions
diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index b5dfbf796d3..b47c0c696bf 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -20,6 +20,7 @@ use core::prelude::*; use fmt; +use ffi::OsString; use io::{self, Error, ErrorKind, SeekFrom, Seek, Read, Write}; use path::{Path, PathBuf}; use sys::fs2 as fs_imp; @@ -146,6 +147,11 @@ pub struct OpenOptions(fs_imp::OpenOptions); #[stable(feature = "rust1", since = "1.0.0")] pub struct Permissions(fs_imp::FilePermissions); +/// An structure representing a type of file with accessors for each file type. +#[unstable(feature = "file_type", reason = "recently added API")] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct FileType(fs_imp::FileType); + impl File { /// Attempts to open a file in read-only mode. /// @@ -485,6 +491,12 @@ impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions { } impl Metadata { + /// Returns the file type for this metadata. + #[unstable(feature = "file_type", reason = "recently added API")] + pub fn file_type(&self) -> FileType { + FileType(self.0.file_type()) + } + /// Returns whether this metadata is for a directory. /// /// # Examples @@ -500,7 +512,7 @@ impl Metadata { /// # } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_dir(&self) -> bool { self.0.is_dir() } + pub fn is_dir(&self) -> bool { self.file_type().is_dir() } /// Returns whether this metadata is for a regular file. /// @@ -517,7 +529,7 @@ impl Metadata { /// # } /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_file(&self) -> bool { self.0.is_file() } + pub fn is_file(&self) -> bool { self.file_type().is_file() } /// Returns the size of the file, in bytes, this metadata is for. /// @@ -562,7 +574,11 @@ impl Metadata { reason = "the return type of u64 is not quite appropriate for \ this method and may change if the standard library \ gains a type to represent a moment in time")] - pub fn accessed(&self) -> u64 { self.0.accessed() } + #[deprecated(since = "1.1.0", + reason = "use os::platform::fs::MetadataExt extension traits")] + pub fn accessed(&self) -> u64 { + self.adjust_time(self.0.accessed()) + } /// Returns the most recent modification time for a file. /// @@ -571,7 +587,21 @@ impl Metadata { reason = "the return type of u64 is not quite appropriate for \ this method and may change if the standard library \ gains a type to represent a moment in time")] - pub fn modified(&self) -> u64 { self.0.modified() } + #[deprecated(since = "1.1.0", + reason = "use os::platform::fs::MetadataExt extension traits")] + pub fn modified(&self) -> u64 { + self.adjust_time(self.0.modified()) + } + + fn adjust_time(&self, val: u64) -> u64 { + // FILETIME (what `val` represents) is in 100ns intervals and there are + // 10000 intervals in a millisecond. + if cfg!(windows) {val / 10000} else {val} + } +} + +impl AsInner<fs_imp::FileAttr> for Metadata { + fn as_inner(&self) -> &fs_imp::FileAttr { &self.0 } } impl Permissions { @@ -624,6 +654,18 @@ impl Permissions { } } +#[unstable(feature = "file_type", reason = "recently added API")] +impl FileType { + /// Test whether this file type represents a directory. + pub fn is_dir(&self) -> bool { self.0.is_dir() } + + /// Test whether this file type represents a regular file. + pub fn is_file(&self) -> bool { self.0.is_file() } + + /// Test whether this file type represents a symbolic link. + pub fn is_symlink(&self) -> bool { self.0.is_symlink() } +} + impl FromInner<fs_imp::FilePermissions> for Permissions { fn from_inner(f: fs_imp::FilePermissions) -> Permissions { Permissions(f) @@ -674,6 +716,47 @@ impl DirEntry { /// The exact text, of course, depends on what files you have in `.`. #[stable(feature = "rust1", since = "1.0.0")] pub fn path(&self) -> PathBuf { self.0.path() } + + /// Return the metadata for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. + /// + /// # Platform behavior + /// + /// On Windows this function is cheap to call (no extra system calls + /// needed), but on Unix platforms this function is the equivalent of + /// calling `symlink_metadata` on the path. + #[unstable(feature = "dir_entry_ext", reason = "recently added API")] + pub fn metadata(&self) -> io::Result<Metadata> { + self.0.metadata().map(Metadata) + } + + /// Return the file type for the file that this entry points at. + /// + /// This function will not traverse symlinks if this entry points at a + /// symlink. + /// + /// # Platform behavior + /// + /// On Windows and most Unix platforms this function is free (no extra + /// system calls needed), but some Unix platforms may require the equivalent + /// call to `symlink_metadata` to learn about the target file type. + #[unstable(feature = "dir_entry_ext", reason = "recently added API")] + pub fn file_type(&self) -> io::Result<FileType> { + self.0.file_type().map(FileType) + } + + /// Returns the bare file name of this directory entry without any other + /// leading path component. + #[unstable(feature = "dir_entry_ext", reason = "recently added API")] + pub fn file_name(&self) -> OsString { + self.0.file_name() + } +} + +impl AsInner<fs_imp::DirEntry> for DirEntry { + fn as_inner(&self) -> &fs_imp::DirEntry { &self.0 } } /// Removes a file from the underlying filesystem. @@ -731,6 +814,25 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { fs_imp::stat(path.as_ref()).map(Metadata) } +/// Query the metadata about a file without following symlinks. +/// +/// # Examples +/// +/// ```rust +/// #![feature(symlink_metadata)] +/// # fn foo() -> std::io::Result<()> { +/// use std::fs; +/// +/// let attr = try!(fs::symlink_metadata("/some/file/path.txt")); +/// // inspect attr ... +/// # Ok(()) +/// # } +/// ``` +#[unstable(feature = "symlink_metadata", reason = "recently added API")] +pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { + fs_imp::lstat(path.as_ref()).map(Metadata) +} + /// Rename a file or directory to a new name. /// /// # Errors @@ -869,6 +971,13 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { fs_imp::readlink(path.as_ref()) } +/// Returns the canonical form of a path with all intermediate components +/// normalized and symbolic links resolved. +#[unstable(feature = "fs_canonicalize", reason = "recently added API")] +pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { + fs_imp::canonicalize(path.as_ref()) +} + /// Creates a new, empty directory at the provided path /// /// # Errors @@ -966,19 +1075,14 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { let path = path.as_ref(); for child in try!(read_dir(path)) { let child = try!(child).path(); - let stat = try!(lstat(&*child)); + let stat = try!(symlink_metadata(&*child)); if stat.is_dir() { try!(remove_dir_all(&*child)); } else { try!(remove_file(&*child)); } } - return remove_dir(path); - - #[cfg(unix)] - fn lstat(path: &Path) -> io::Result<fs_imp::FileAttr> { fs_imp::lstat(path) } - #[cfg(windows)] - fn lstat(path: &Path) -> io::Result<fs_imp::FileAttr> { fs_imp::stat(path) } + remove_dir(path) } /// Returns an iterator over the entries within a directory. @@ -1073,11 +1177,37 @@ impl Iterator for WalkDir { pub trait PathExt { /// Gets information on the file, directory, etc at this path. /// - /// Consult the `fs::stat` documentation for more info. + /// Consult the `fs::metadata` documentation for more info. /// - /// This call preserves identical runtime/error semantics with `file::stat`. + /// This call preserves identical runtime/error semantics with + /// `fs::metadata`. fn metadata(&self) -> io::Result<Metadata>; + /// Gets information on the file, directory, etc at this path. + /// + /// Consult the `fs::symlink_metadata` documentation for more info. + /// + /// This call preserves identical runtime/error semantics with + /// `fs::symlink_metadata`. + fn symlink_metadata(&self) -> io::Result<Metadata>; + + /// Returns the canonical form of a path, normalizing all components and + /// eliminate all symlinks. + /// + /// This call preserves identical runtime/error semantics with + /// `fs::canonicalize`. + fn canonicalize(&self) -> io::Result<PathBuf>; + + /// Reads the symlink at this path. + /// + /// For more information see `fs::read_link`. + fn read_link(&self) -> io::Result<PathBuf>; + + /// Reads the directory at this path. + /// + /// For more information see `fs::read_dir`. + fn read_dir(&self) -> io::Result<ReadDir>; + /// Boolean value indicator whether the underlying file exists on the local /// filesystem. Returns false in exactly the cases where `fs::stat` fails. fn exists(&self) -> bool; @@ -1098,12 +1228,16 @@ pub trait PathExt { impl PathExt for Path { fn metadata(&self) -> io::Result<Metadata> { metadata(self) } - + fn symlink_metadata(&self) -> io::Result<Metadata> { symlink_metadata(self) } + fn canonicalize(&self) -> io::Result<PathBuf> { canonicalize(self) } + fn read_link(&self) -> io::Result<PathBuf> { read_link(self) } + fn read_dir(&self) -> io::Result<ReadDir> { read_dir(self) } fn exists(&self) -> bool { metadata(self).is_ok() } fn is_file(&self) -> bool { metadata(self).map(|s| s.is_file()).unwrap_or(false) } + fn is_dir(&self) -> bool { metadata(self).map(|s| s.is_dir()).unwrap_or(false) } @@ -1924,4 +2058,74 @@ mod tests { let path = tmpdir.join("file"); check!(fs::create_dir_all(&path.join("a/"))); } + + #[test] + #[cfg(not(windows))] + fn realpath_works() { + let tmpdir = tmpdir(); + let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); + let file = tmpdir.join("test"); + let dir = tmpdir.join("test2"); + let link = dir.join("link"); + let linkdir = tmpdir.join("test3"); + + File::create(&file).unwrap(); + fs::create_dir(&dir).unwrap(); + fs::soft_link(&file, &link).unwrap(); + fs::soft_link(&dir, &linkdir).unwrap(); + + assert!(link.symlink_metadata().unwrap().file_type().is_symlink()); + + assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir); + assert_eq!(fs::canonicalize(&file).unwrap(), file); + assert_eq!(fs::canonicalize(&link).unwrap(), file); + assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir); + assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file); + } + + #[test] + #[cfg(not(windows))] + fn realpath_works_tricky() { + let tmpdir = tmpdir(); + let tmpdir = fs::canonicalize(tmpdir.path()).unwrap(); + + let a = tmpdir.join("a"); + let b = a.join("b"); + let c = b.join("c"); + let d = a.join("d"); + let e = d.join("e"); + let f = a.join("f"); + + fs::create_dir_all(&b).unwrap(); + fs::create_dir_all(&d).unwrap(); + File::create(&f).unwrap(); + fs::soft_link("../d/e", &c).unwrap(); + fs::soft_link("../f", &e).unwrap(); + + assert_eq!(fs::canonicalize(&c).unwrap(), f); + assert_eq!(fs::canonicalize(&e).unwrap(), f); + } + + #[test] + fn dir_entry_methods() { + let tmpdir = tmpdir(); + + fs::create_dir_all(&tmpdir.join("a")).unwrap(); + File::create(&tmpdir.join("b")).unwrap(); + + for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) { + let fname = file.file_name(); + match fname.to_str() { + Some("a") => { + assert!(file.file_type().unwrap().is_dir()); + assert!(file.metadata().unwrap().is_dir()); + } + Some("b") => { + assert!(file.file_type().unwrap().is_file()); + assert!(file.metadata().unwrap().is_file()); + } + f => panic!("unknown file name: {:?}", f), + } + } + } } |
