std: Stabilize the fs module

This commit performs a stabilization pass over the `std::fs` module now that
it's had some time to bake. The change was largely just adding `#[stable]` tags,
but there are a few APIs that remain `#[unstable]`.

The following apis are now marked `#[stable]`:

* `std::fs` (the name)
* `File`
* `Metadata`
* `ReadDir`
* `DirEntry`
* `OpenOptions`
* `Permissions`
* `File::{open, create}`
* `File::{sync_all, sync_data}`
* `File::set_len`
* `File::metadata`
* Trait implementations for `File` and `&File`
* `OpenOptions::new`
* `OpenOptions::{read, write, append, truncate, create}`
* `OpenOptions::open` - this function was modified, however, to not attempt to
  reject cross-platform openings of directories. This means that some platforms
  will succeed in opening a directory and others will fail.
* `Metadata::{is_dir, is_file, len, permissions}`
* `Permissions::{readonly, set_readonly}`
* `Iterator for ReadDir`
* `DirEntry::path`
* `remove_file` - like with `OpenOptions::open`, the extra windows code to
  remove a readonly file has been removed. This means that removing a readonly
  file will succeed on some platforms but fail on others.
* `metadata`
* `rename`
* `copy`
* `hard_link`
* `soft_link`
* `read_link`
* `create_dir`
* `create_dir_all`
* `remove_dir`
* `remove_dir_all`
* `read_dir`

The following apis remain `#[unstable]`.

* `WalkDir` and `walk` - there are many methods by which a directory walk can be
  constructed, and it's unclear whether the current semantics are the right
  ones. For example symlinks are not handled super well currently. This is now
  behind a new `fs_walk` feature.
* `File::path` - this is an extra abstraction which the standard library
  provides on top of what the system offers and it's unclear whether we should
  be doing so. This is now behind a new `file_path` feature.
* `Metadata::{accessed, modified}` - we do not currently have a good
  abstraction for a moment in time which is what these APIs should likely be
  returning, so these remain `#[unstable]` for now. These are now behind a new
  `fs_time` feature
* `set_file_times` - like with `Metadata::accessed`, we do not currently have
  the appropriate abstraction for the arguments here so this API remains
  unstable behind the `fs_time` feature gate.
* `PathExt` - the precise set of methods on this trait may change over time and
  some methods may be removed. This API remains unstable behind the `path_ext`
  feature gate.
* `set_permissions` - we may wish to expose a more granular ability to set the
  permissions on a file instead of just a blanket "set all permissions" method.
  This function remains behind the `fs` feature.

The following apis are now `#[deprecated]`

* The `TempDir` type is now entirely deprecated and is [located on
  crates.io][tempdir] as the `tempdir` crate with [its source][github] at
  rust-lang/tempdir.

[tempdir]: https://crates.io/crates/tempdir
[github]: https://github.com/rust-lang/tempdir

The stability of some of these APIs has been questioned over the past few weeks
in using these APIs, and it is intentional that the majority of APIs here are
marked `#[stable]`. The `std::fs` module has a lot of room to grow and the
material is [being tracked in a RFC issue][rfc-issue].

[rfc-issue]: https://github.com/rust-lang/rfcs/issues/939

[breaking-change]
This commit is contained in:
Alex Crichton 2015-03-03 19:18:29 -08:00
parent f0c74f85f3
commit 73b0b25e32
18 changed files with 229 additions and 56 deletions

View File

@ -24,8 +24,8 @@
#![feature(path)] #![feature(path)]
#![feature(os)] #![feature(os)]
#![feature(io)] #![feature(io)]
#![feature(fs)]
#![feature(net)] #![feature(net)]
#![feature(path_ext)]
#![deny(warnings)] #![deny(warnings)]

View File

@ -92,8 +92,8 @@
#![feature(collections)] #![feature(collections)]
#![feature(int_uint)] #![feature(int_uint)]
#![feature(staged_api)] #![feature(staged_api)]
#![feature(str_words)]
#![feature(core)] #![feature(core)]
#![feature(str_words)]
#![cfg_attr(test, feature(rustc_private))] #![cfg_attr(test, feature(rustc_private))]
#[cfg(test)] #[macro_use] extern crate log; #[cfg(test)] #[macro_use] extern crate log;

View File

@ -40,8 +40,8 @@
#![feature(std_misc)] #![feature(std_misc)]
#![feature(os)] #![feature(os)]
#![feature(path)] #![feature(path)]
#![feature(fs)]
#![feature(io)] #![feature(io)]
#![feature(path_ext)]
#![cfg_attr(test, feature(test))] #![cfg_attr(test, feature(test))]
extern crate arena; extern crate arena;

View File

@ -11,7 +11,7 @@
//! A helper class for dealing with static archives //! A helper class for dealing with static archives
use std::env; use std::env;
use std::fs::{self, TempDir}; use std::fs;
use std::io::prelude::*; use std::io::prelude::*;
use std::io; use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -19,6 +19,8 @@ use std::process::{Command, Output, Stdio};
use std::str; use std::str;
use syntax::diagnostic::Handler as ErrorHandler; use syntax::diagnostic::Handler as ErrorHandler;
use tempdir::TempDir;
pub const METADATA_FILENAME: &'static str = "rust.metadata.bin"; pub const METADATA_FILENAME: &'static str = "rust.metadata.bin";
pub struct ArchiveConfig<'a> { pub struct ArchiveConfig<'a> {

View File

@ -34,7 +34,6 @@
#![feature(collections)] #![feature(collections)]
#![feature(core)] #![feature(core)]
#![feature(old_fs)] #![feature(old_fs)]
#![feature(fs)]
#![feature(hash)] #![feature(hash)]
#![feature(int_uint)] #![feature(int_uint)]
#![feature(io)] #![feature(io)]
@ -44,7 +43,8 @@
#![feature(path)] #![feature(path)]
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(staged_api)] #![feature(staged_api)]
#![feature(tempdir)] #![feature(rand)]
#![feature(path_ext)]
extern crate syntax; extern crate syntax;
extern crate serialize; extern crate serialize;
@ -52,6 +52,7 @@ extern crate serialize;
pub mod abi; pub mod abi;
pub mod archive; pub mod archive;
pub mod tempdir;
pub mod arm; pub mod arm;
pub mod fs; pub mod fs;
pub mod mips; pub mod mips;

View File

@ -0,0 +1,121 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::env;
use std::io::{self, Error, ErrorKind};
use std::fs;
use std::path::{self, PathBuf, AsPath};
use std::rand::{thread_rng, Rng};
/// A wrapper for a path to temporary directory implementing automatic
/// scope-based deletion.
pub struct TempDir {
path: Option<PathBuf>,
}
// How many times should we (re)try finding an unused random name? It should be
// enough that an attacker will run out of luck before we run out of patience.
const NUM_RETRIES: u32 = 1 << 31;
// How many characters should we include in a random file name? It needs to
// be enough to dissuade an attacker from trying to preemptively create names
// of that length, but not so huge that we unnecessarily drain the random number
// generator of entropy.
const NUM_RAND_CHARS: uint = 12;
impl TempDir {
/// Attempts to make a temporary directory inside of `tmpdir` whose name
/// will have the prefix `prefix`. The directory will be automatically
/// deleted once the returned wrapper is destroyed.
///
/// If no directory can be created, `Err` is returned.
#[allow(deprecated)] // rand usage
pub fn new_in<P: AsPath + ?Sized>(tmpdir: &P, prefix: &str)
-> io::Result<TempDir> {
let storage;
let mut tmpdir = tmpdir.as_path();
if !tmpdir.is_absolute() {
let cur_dir = try!(env::current_dir());
storage = cur_dir.join(tmpdir);
tmpdir = &storage;
// return TempDir::new_in(&cur_dir.join(tmpdir), prefix);
}
let mut rng = thread_rng();
for _ in 0..NUM_RETRIES {
let suffix: String = rng.gen_ascii_chars().take(NUM_RAND_CHARS).collect();
let leaf = if prefix.len() > 0 {
format!("{}.{}", prefix, suffix)
} else {
// If we're given an empty string for a prefix, then creating a
// directory starting with "." would lead to it being
// semi-invisible on some systems.
suffix
};
let path = tmpdir.join(&leaf);
match fs::create_dir(&path) {
Ok(_) => return Ok(TempDir { path: Some(path) }),
Err(ref e) if e.kind() == ErrorKind::PathAlreadyExists => {}
Err(e) => return Err(e)
}
}
Err(Error::new(ErrorKind::PathAlreadyExists,
"too many temporary directories already exist",
None))
}
/// Attempts to make a temporary directory inside of `env::temp_dir()` whose
/// name will have the prefix `prefix`. The directory will be automatically
/// deleted once the returned wrapper is destroyed.
///
/// If no directory can be created, `Err` is returned.
#[allow(deprecated)]
pub fn new(prefix: &str) -> io::Result<TempDir> {
TempDir::new_in(&env::temp_dir(), prefix)
}
/// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper.
/// This discards the wrapper so that the automatic deletion of the
/// temporary directory is prevented.
pub fn into_path(mut self) -> PathBuf {
self.path.take().unwrap()
}
/// Access the wrapped `std::path::Path` to the temporary directory.
pub fn path(&self) -> &path::Path {
self.path.as_ref().unwrap()
}
/// Close and remove the temporary directory
///
/// Although `TempDir` removes the directory on drop, in the destructor
/// any errors are ignored. To detect errors cleaning up the temporary
/// directory, call `close` instead.
pub fn close(mut self) -> io::Result<()> {
self.cleanup_dir()
}
fn cleanup_dir(&mut self) -> io::Result<()> {
match self.path {
Some(ref p) => fs::remove_dir_all(p),
None => Ok(())
}
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = self.cleanup_dir();
}
}
// the tests for this module need to change the path using change_dir,
// and this doesn't play nicely with other tests so these unit tests are located
// in src/test/run-pass/tempfile.rs

View File

@ -39,7 +39,6 @@
#![feature(exit_status)] #![feature(exit_status)]
#![feature(path)] #![feature(path)]
#![feature(io)] #![feature(io)]
#![feature(fs)]
extern crate arena; extern crate arena;
extern crate flate; extern crate flate;

View File

@ -26,9 +26,10 @@ use middle::ty::{self, Ty};
use util::common::time; use util::common::time;
use util::ppaux; use util::ppaux;
use util::sha2::{Digest, Sha256}; use util::sha2::{Digest, Sha256};
use rustc_back::tempdir::TempDir;
use std::ffi::{AsOsStr, OsString}; use std::ffi::{AsOsStr, OsString};
use std::fs::{self, TempDir, PathExt}; use std::fs::{self, PathExt};
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::mem; use std::mem;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};

View File

@ -38,10 +38,10 @@
#![feature(std_misc)] #![feature(std_misc)]
#![feature(unicode)] #![feature(unicode)]
#![feature(io)] #![feature(io)]
#![feature(fs)]
#![feature(path)] #![feature(path)]
#![feature(os)] #![feature(os)]
#![feature(tempdir)] #![feature(path_ext)]
#![feature(fs)]
extern crate arena; extern crate arena;
extern crate flate; extern crate flate;

View File

@ -35,9 +35,8 @@
#![feature(unicode)] #![feature(unicode)]
#![feature(str_words)] #![feature(str_words)]
#![feature(io)] #![feature(io)]
#![feature(fs)]
#![feature(path)] #![feature(path)]
#![feature(tempdir)] #![feature(path_ext)]
extern crate arena; extern crate arena;
extern crate getopts; extern crate getopts;
@ -47,6 +46,7 @@ extern crate rustc_trans;
extern crate rustc_driver; extern crate rustc_driver;
extern crate rustc_resolve; extern crate rustc_resolve;
extern crate rustc_lint; extern crate rustc_lint;
extern crate rustc_back;
extern crate serialize; extern crate serialize;
extern crate syntax; extern crate syntax;
extern crate "test" as testing; extern crate "test" as testing;

View File

@ -13,7 +13,6 @@ use std::collections::{HashSet, HashMap};
use std::dynamic_lib::DynamicLibrary; use std::dynamic_lib::DynamicLibrary;
use std::env; use std::env;
use std::ffi::OsString; use std::ffi::OsString;
use std::fs::TempDir;
use std::old_io; use std::old_io;
use std::io; use std::io;
use std::path::PathBuf; use std::path::PathBuf;
@ -28,6 +27,7 @@ use rustc_lint;
use rustc::session::{self, config}; use rustc::session::{self, config};
use rustc::session::config::get_unstable_features_setting; use rustc::session::config::get_unstable_features_setting;
use rustc::session::search_paths::{SearchPaths, PathKind}; use rustc::session::search_paths::{SearchPaths, PathKind};
use rustc_back::tempdir::TempDir;
use rustc_driver::{driver, Compilation}; use rustc_driver::{driver, Compilation};
use syntax::codemap::CodeMap; use syntax::codemap::CodeMap;
use syntax::diagnostic; use syntax::diagnostic;

View File

@ -15,7 +15,7 @@
//! operations. Extra platform-specific functionality can be found in the //! operations. Extra platform-specific functionality can be found in the
//! extension traits of `std::os::$platform`. //! extension traits of `std::os::$platform`.
#![unstable(feature = "fs")] #![stable(feature = "rust1", since = "1.0.0")]
use core::prelude::*; use core::prelude::*;
@ -25,6 +25,7 @@ use sys::fs2 as fs_imp;
use sys_common::{AsInnerMut, FromInner, AsInner}; use sys_common::{AsInnerMut, FromInner, AsInner};
use vec::Vec; use vec::Vec;
#[allow(deprecated)]
pub use self::tempdir::TempDir; pub use self::tempdir::TempDir;
mod tempdir; mod tempdir;
@ -52,6 +53,7 @@ mod tempdir;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub struct File { pub struct File {
inner: fs_imp::File, inner: fs_imp::File,
path: PathBuf, path: PathBuf,
@ -62,6 +64,7 @@ pub struct File {
/// This structure is returned from the `metadata` function or method and /// This structure is returned from the `metadata` function or method and
/// represents known metadata about a file such as its permissions, size, /// represents known metadata about a file such as its permissions, size,
/// modification times, etc. /// modification times, etc.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Metadata(fs_imp::FileAttr); pub struct Metadata(fs_imp::FileAttr);
/// Iterator over the entries in a directory. /// Iterator over the entries in a directory.
@ -70,6 +73,7 @@ pub struct Metadata(fs_imp::FileAttr);
/// will yield instances of `io::Result<DirEntry>`. Through a `DirEntry` /// will yield instances of `io::Result<DirEntry>`. Through a `DirEntry`
/// information like the entry's path and possibly other metadata can be /// information like the entry's path and possibly other metadata can be
/// learned. /// learned.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct ReadDir(fs_imp::ReadDir); pub struct ReadDir(fs_imp::ReadDir);
/// Entries returned by the `ReadDir` iterator. /// Entries returned by the `ReadDir` iterator.
@ -77,9 +81,14 @@ pub struct ReadDir(fs_imp::ReadDir);
/// An instance of `DirEntry` represents an entry inside of a directory on the /// An instance of `DirEntry` represents an entry inside of a directory on the
/// filesystem. Each entry can be inspected via methods to learn about the full /// filesystem. Each entry can be inspected via methods to learn about the full
/// path or possibly other metadata through per-platform extension traits. /// path or possibly other metadata through per-platform extension traits.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct DirEntry(fs_imp::DirEntry); pub struct DirEntry(fs_imp::DirEntry);
/// An iterator that recursively walks over the contents of a directory. /// An iterator that recursively walks over the contents of a directory.
#[unstable(feature = "fs_walk",
reason = "the precise semantics and defaults for a recursive walk \
may change and this may end up accounting for files such \
as symlinks differently")]
pub struct WalkDir { pub struct WalkDir {
cur: Option<ReadDir>, cur: Option<ReadDir>,
stack: Vec<io::Result<ReadDir>>, stack: Vec<io::Result<ReadDir>>,
@ -92,6 +101,7 @@ pub struct WalkDir {
/// `File::create` methods are aliases for commonly used options using this /// `File::create` methods are aliases for commonly used options using this
/// builder. /// builder.
#[derive(Clone)] #[derive(Clone)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct OpenOptions(fs_imp::OpenOptions); pub struct OpenOptions(fs_imp::OpenOptions);
/// Representation of the various permissions on a file. /// Representation of the various permissions on a file.
@ -101,6 +111,7 @@ pub struct OpenOptions(fs_imp::OpenOptions);
/// functionality, such as mode bits, is available through the /// functionality, such as mode bits, is available through the
/// `os::unix::PermissionsExt` trait. /// `os::unix::PermissionsExt` trait.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Permissions(fs_imp::FilePermissions); pub struct Permissions(fs_imp::FilePermissions);
impl File { impl File {
@ -112,6 +123,7 @@ impl File {
/// ///
/// This function will return an error if `path` does not already exist. /// This function will return an error if `path` does not already exist.
/// Other errors may also be returned according to `OpenOptions::open`. /// Other errors may also be returned according to `OpenOptions::open`.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn open<P: AsPath + ?Sized>(path: &P) -> io::Result<File> { pub fn open<P: AsPath + ?Sized>(path: &P) -> io::Result<File> {
OpenOptions::new().read(true).open(path) OpenOptions::new().read(true).open(path)
} }
@ -122,11 +134,15 @@ impl File {
/// and will truncate it if it does. /// and will truncate it if it does.
/// ///
/// See the `OpenOptions::open` function for more details. /// See the `OpenOptions::open` function for more details.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn create<P: AsPath + ?Sized>(path: &P) -> io::Result<File> { pub fn create<P: AsPath + ?Sized>(path: &P) -> io::Result<File> {
OpenOptions::new().write(true).create(true).truncate(true).open(path) OpenOptions::new().write(true).create(true).truncate(true).open(path)
} }
/// Returns the original path that was used to open this file. /// Returns the original path that was used to open this file.
#[unstable(feature = "file_path",
reason = "this abstraction is imposed by this library instead \
of the underlying OS and may be removed")]
pub fn path(&self) -> Option<&Path> { pub fn path(&self) -> Option<&Path> {
Some(&self.path) Some(&self.path)
} }
@ -135,6 +151,7 @@ impl File {
/// ///
/// This function will attempt to ensure that all in-core data reaches the /// This function will attempt to ensure that all in-core data reaches the
/// filesystem before returning. /// filesystem before returning.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn sync_all(&self) -> io::Result<()> { pub fn sync_all(&self) -> io::Result<()> {
self.inner.fsync() self.inner.fsync()
} }
@ -148,6 +165,7 @@ impl File {
/// ///
/// Note that some platforms may simply implement this in terms of /// Note that some platforms may simply implement this in terms of
/// `sync_all`. /// `sync_all`.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn sync_data(&self) -> io::Result<()> { pub fn sync_data(&self) -> io::Result<()> {
self.inner.datasync() self.inner.datasync()
} }
@ -159,11 +177,13 @@ impl File {
/// be shrunk. If it is greater than the current file's size, then the file /// be shrunk. If it is greater than the current file's size, then the file
/// will be extended to `size` and have all of the intermediate data filled /// will be extended to `size` and have all of the intermediate data filled
/// in with 0s. /// in with 0s.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn set_len(&self, size: u64) -> io::Result<()> { pub fn set_len(&self, size: u64) -> io::Result<()> {
self.inner.truncate(size) self.inner.truncate(size)
} }
/// Queries information about the underlying file. /// Queries metadata about the underlying file.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn metadata(&self) -> io::Result<Metadata> { pub fn metadata(&self) -> io::Result<Metadata> {
self.inner.file_attr().map(Metadata) self.inner.file_attr().map(Metadata)
} }
@ -172,33 +192,39 @@ impl File {
impl AsInner<fs_imp::File> for File { impl AsInner<fs_imp::File> for File {
fn as_inner(&self) -> &fs_imp::File { &self.inner } fn as_inner(&self) -> &fs_imp::File { &self.inner }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl Read for File { impl Read for File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf) self.inner.read(buf)
} }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl Write for File { impl Write for File {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf) self.inner.write(buf)
} }
fn flush(&mut self) -> io::Result<()> { self.inner.flush() } fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl Seek for File { impl Seek for File {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.inner.seek(pos) self.inner.seek(pos)
} }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Read for &'a File { impl<'a> Read for &'a File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf) self.inner.read(buf)
} }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Write for &'a File { impl<'a> Write for &'a File {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf) self.inner.write(buf)
} }
fn flush(&mut self) -> io::Result<()> { self.inner.flush() } fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> Seek for &'a File { impl<'a> Seek for &'a File {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.inner.seek(pos) self.inner.seek(pos)
@ -209,6 +235,7 @@ impl OpenOptions {
/// Creates a blank net set of options ready for configuration. /// Creates a blank net set of options ready for configuration.
/// ///
/// All options are initially set to `false`. /// All options are initially set to `false`.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new() -> OpenOptions { pub fn new() -> OpenOptions {
OpenOptions(fs_imp::OpenOptions::new()) OpenOptions(fs_imp::OpenOptions::new())
} }
@ -217,6 +244,7 @@ impl OpenOptions {
/// ///
/// This option, when true, will indicate that the file should be /// This option, when true, will indicate that the file should be
/// `read`-able if opened. /// `read`-able if opened.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn read(&mut self, read: bool) -> &mut OpenOptions { pub fn read(&mut self, read: bool) -> &mut OpenOptions {
self.0.read(read); self self.0.read(read); self
} }
@ -225,6 +253,7 @@ impl OpenOptions {
/// ///
/// This option, when true, will indicate that the file should be /// This option, when true, will indicate that the file should be
/// `write`-able if opened. /// `write`-able if opened.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn write(&mut self, write: bool) -> &mut OpenOptions { pub fn write(&mut self, write: bool) -> &mut OpenOptions {
self.0.write(write); self self.0.write(write); self
} }
@ -233,6 +262,7 @@ impl OpenOptions {
/// ///
/// This option, when true, means that writes will append to a file instead /// This option, when true, means that writes will append to a file instead
/// of overwriting previous contents. /// of overwriting previous contents.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn append(&mut self, append: bool) -> &mut OpenOptions { pub fn append(&mut self, append: bool) -> &mut OpenOptions {
self.0.append(append); self self.0.append(append); self
} }
@ -241,6 +271,7 @@ impl OpenOptions {
/// ///
/// If a file is successfully opened with this option set it will truncate /// If a file is successfully opened with this option set it will truncate
/// the file to 0 length if it already exists. /// the file to 0 length if it already exists.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions { pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
self.0.truncate(truncate); self self.0.truncate(truncate); self
} }
@ -249,6 +280,7 @@ impl OpenOptions {
/// ///
/// This option indicates whether a new file will be created if the file /// This option indicates whether a new file will be created if the file
/// does not yet already exist. /// does not yet already exist.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn create(&mut self, create: bool) -> &mut OpenOptions { pub fn create(&mut self, create: bool) -> &mut OpenOptions {
self.0.create(create); self self.0.create(create); self
} }
@ -264,37 +296,33 @@ impl OpenOptions {
/// * Attempting to open a file with access that the user lacks /// * Attempting to open a file with access that the user lacks
/// permissions for /// permissions for
/// * Filesystem-level errors (full disk, etc) /// * Filesystem-level errors (full disk, etc)
#[stable(feature = "rust1", since = "1.0.0")]
pub fn open<P: AsPath + ?Sized>(&self, path: &P) -> io::Result<File> { pub fn open<P: AsPath + ?Sized>(&self, path: &P) -> io::Result<File> {
let path = path.as_path(); let path = path.as_path();
let inner = try!(fs_imp::File::open(path, &self.0)); let inner = try!(fs_imp::File::open(path, &self.0));
Ok(File { path: path.to_path_buf(), inner: inner })
// On *BSD systems, we can open a directory as a file and read from
// it: fd=open("/tmp", O_RDONLY); read(fd, buf, N); due to an old
// tradition before the introduction of opendir(3). We explicitly
// reject it because there are few use cases.
if cfg!(not(any(target_os = "linux", target_os = "android"))) &&
try!(inner.file_attr()).is_dir() {
Err(Error::new(ErrorKind::InvalidInput, "is a directory", None))
} else {
Ok(File { path: path.to_path_buf(), inner: inner })
}
} }
} }
impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions { impl AsInnerMut<fs_imp::OpenOptions> for OpenOptions {
fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { &mut self.0 } fn as_inner_mut(&mut self) -> &mut fs_imp::OpenOptions { &mut self.0 }
} }
impl Metadata { impl Metadata {
/// Returns whether this metadata is for a directory. /// Returns whether this metadata is for a directory.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn is_dir(&self) -> bool { self.0.is_dir() } pub fn is_dir(&self) -> bool { self.0.is_dir() }
/// Returns whether this metadata is for a regular file. /// Returns whether this metadata is for a regular file.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn is_file(&self) -> bool { self.0.is_file() } pub fn is_file(&self) -> bool { self.0.is_file() }
/// Returns the size of the file, in bytes, this metadata is for. /// Returns the size of the file, in bytes, this metadata is for.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn len(&self) -> u64 { self.0.size() } pub fn len(&self) -> u64 { self.0.size() }
/// Returns the permissions of the file this metadata is for. /// Returns the permissions of the file this metadata is for.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn permissions(&self) -> Permissions { pub fn permissions(&self) -> Permissions {
Permissions(self.0.perm()) Permissions(self.0.perm())
} }
@ -302,22 +330,32 @@ impl Metadata {
/// Returns the most recent access time for a file. /// Returns the most recent access time for a file.
/// ///
/// The return value is in milliseconds since the epoch. /// The return value is in milliseconds since the epoch.
#[unstable(feature = "fs_time",
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() } pub fn accessed(&self) -> u64 { self.0.accessed() }
/// Returns the most recent modification time for a file. /// Returns the most recent modification time for a file.
/// ///
/// The return value is in milliseconds since the epoch. /// The return value is in milliseconds since the epoch.
#[unstable(feature = "fs_time",
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() } pub fn modified(&self) -> u64 { self.0.modified() }
} }
impl Permissions { impl Permissions {
/// Returns whether these permissions describe a readonly file. /// Returns whether these permissions describe a readonly file.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn readonly(&self) -> bool { self.0.readonly() } pub fn readonly(&self) -> bool { self.0.readonly() }
/// Modify the readonly flag for this set of permissions. /// Modify the readonly flag for this set of permissions.
/// ///
/// This operation does **not** modify the filesystem. To modify the /// This operation does **not** modify the filesystem. To modify the
/// filesystem use the `fs::set_permissions` function. /// filesystem use the `fs::set_permissions` function.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn set_readonly(&mut self, readonly: bool) { pub fn set_readonly(&mut self, readonly: bool) {
self.0.set_readonly(readonly) self.0.set_readonly(readonly)
} }
@ -333,6 +371,7 @@ impl AsInner<fs_imp::FilePermissions> for Permissions {
fn as_inner(&self) -> &fs_imp::FilePermissions { &self.0 } fn as_inner(&self) -> &fs_imp::FilePermissions { &self.0 }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl Iterator for ReadDir { impl Iterator for ReadDir {
type Item = io::Result<DirEntry>; type Item = io::Result<DirEntry>;
@ -341,11 +380,13 @@ impl Iterator for ReadDir {
} }
} }
#[stable(feature = "rust1", since = "1.0.0")]
impl DirEntry { impl DirEntry {
/// Returns the full path to the file that this entry represents. /// Returns the full path to the file that this entry represents.
/// ///
/// The full path is created by joining the original path to `read_dir` or /// The full path is created by joining the original path to `read_dir` or
/// `walk_dir` with the filename of this entry. /// `walk_dir` with the filename of this entry.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn path(&self) -> PathBuf { self.0.path() } pub fn path(&self) -> PathBuf { self.0.path() }
} }
@ -368,31 +409,9 @@ impl DirEntry {
/// This function will return an error if `path` points to a directory, if the /// This function will return an error if `path` points to a directory, if the
/// user lacks permissions to remove the file, or if some other filesystem-level /// user lacks permissions to remove the file, or if some other filesystem-level
/// error occurs. /// error occurs.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn remove_file<P: AsPath + ?Sized>(path: &P) -> io::Result<()> { pub fn remove_file<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
let path = path.as_path(); fs_imp::unlink(path.as_path())
let e = match fs_imp::unlink(path) {
Ok(()) => return Ok(()),
Err(e) => e,
};
if !cfg!(windows) { return Err(e) }
// On unix, a readonly file can be successfully removed. On windows,
// however, it cannot. To keep the two platforms in line with
// respect to their behavior, catch this case on windows, attempt to
// change it to read-write, and then remove the file.
if e.kind() != ErrorKind::PermissionDenied { return Err(e) }
let attr = match metadata(path) { Ok(a) => a, Err(..) => return Err(e) };
let mut perms = attr.permissions();
if !perms.readonly() { return Err(e) }
perms.set_readonly(false);
if set_permissions(path, perms).is_err() { return Err(e) }
if fs_imp::unlink(path).is_ok() { return Ok(()) }
// Oops, try to put things back the way we found it
let _ = set_permissions(path, attr.permissions());
Err(e)
} }
/// Given a path, query the file system to get information about a file, /// Given a path, query the file system to get information about a file,
@ -418,6 +437,7 @@ pub fn remove_file<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
/// This function will return an error if the user lacks the requisite /// This function will return an error if the user lacks the requisite
/// permissions to perform a `metadata` call on the given `path` or if there /// permissions to perform a `metadata` call on the given `path` or if there
/// is no entry in the filesystem at the provided path. /// is no entry in the filesystem at the provided path.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn metadata<P: AsPath + ?Sized>(path: &P) -> io::Result<Metadata> { pub fn metadata<P: AsPath + ?Sized>(path: &P) -> io::Result<Metadata> {
fs_imp::stat(path.as_path()).map(Metadata) fs_imp::stat(path.as_path()).map(Metadata)
} }
@ -438,6 +458,7 @@ pub fn metadata<P: AsPath + ?Sized>(path: &P) -> io::Result<Metadata> {
/// the process lacks permissions to view the contents, if `from` and `to` /// the process lacks permissions to view the contents, if `from` and `to`
/// reside on separate filesystems, or if some other intermittent I/O error /// reside on separate filesystems, or if some other intermittent I/O error
/// occurs. /// occurs.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn rename<P: AsPath + ?Sized, Q: AsPath + ?Sized>(from: &P, to: &Q) pub fn rename<P: AsPath + ?Sized, Q: AsPath + ?Sized>(from: &P, to: &Q)
-> io::Result<()> { -> io::Result<()> {
fs_imp::rename(from.as_path(), to.as_path()) fs_imp::rename(from.as_path(), to.as_path())
@ -468,6 +489,7 @@ pub fn rename<P: AsPath + ?Sized, Q: AsPath + ?Sized>(from: &P, to: &Q)
/// * The `from` file does not exist /// * The `from` file does not exist
/// * The current process does not have the permission rights to access /// * The current process does not have the permission rights to access
/// `from` or write `to` /// `from` or write `to`
#[stable(feature = "rust1", since = "1.0.0")]
pub fn copy<P: AsPath + ?Sized, Q: AsPath + ?Sized>(from: &P, to: &Q) pub fn copy<P: AsPath + ?Sized, Q: AsPath + ?Sized>(from: &P, to: &Q)
-> io::Result<u64> { -> io::Result<u64> {
let from = from.as_path(); let from = from.as_path();
@ -490,6 +512,7 @@ pub fn copy<P: AsPath + ?Sized, Q: AsPath + ?Sized>(from: &P, to: &Q)
/// ///
/// The `dst` path will be a link pointing to the `src` path. Note that systems /// The `dst` path will be a link pointing to the `src` path. Note that systems
/// often require these two paths to both be located on the same filesystem. /// often require these two paths to both be located on the same filesystem.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn hard_link<P: AsPath + ?Sized, Q: AsPath + ?Sized>(src: &P, dst: &Q) pub fn hard_link<P: AsPath + ?Sized, Q: AsPath + ?Sized>(src: &P, dst: &Q)
-> io::Result<()> { -> io::Result<()> {
fs_imp::link(src.as_path(), dst.as_path()) fs_imp::link(src.as_path(), dst.as_path())
@ -498,6 +521,7 @@ pub fn hard_link<P: AsPath + ?Sized, Q: AsPath + ?Sized>(src: &P, dst: &Q)
/// Creates a new soft link on the filesystem. /// Creates a new soft link on the filesystem.
/// ///
/// The `dst` path will be a soft link pointing to the `src` path. /// The `dst` path will be a soft link pointing to the `src` path.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn soft_link<P: AsPath + ?Sized, Q: AsPath + ?Sized>(src: &P, dst: &Q) pub fn soft_link<P: AsPath + ?Sized, Q: AsPath + ?Sized>(src: &P, dst: &Q)
-> io::Result<()> { -> io::Result<()> {
fs_imp::symlink(src.as_path(), dst.as_path()) fs_imp::symlink(src.as_path(), dst.as_path())
@ -510,6 +534,7 @@ pub fn soft_link<P: AsPath + ?Sized, Q: AsPath + ?Sized>(src: &P, dst: &Q)
/// This function will return an error on failure. Failure conditions include /// This function will return an error on failure. Failure conditions include
/// reading a file that does not exist or reading a file that is not a soft /// reading a file that does not exist or reading a file that is not a soft
/// link. /// link.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn read_link<P: AsPath + ?Sized>(path: &P) -> io::Result<PathBuf> { pub fn read_link<P: AsPath + ?Sized>(path: &P) -> io::Result<PathBuf> {
fs_imp::readlink(path.as_path()) fs_imp::readlink(path.as_path())
} }
@ -528,6 +553,7 @@ pub fn read_link<P: AsPath + ?Sized>(path: &P) -> io::Result<PathBuf> {
/// ///
/// This function will return an error if the user lacks permissions to make a /// This function will return an error if the user lacks permissions to make a
/// new directory at the provided `path`, or if the directory already exists. /// new directory at the provided `path`, or if the directory already exists.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn create_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<()> { pub fn create_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
fs_imp::mkdir(path.as_path()) fs_imp::mkdir(path.as_path())
} }
@ -541,6 +567,7 @@ pub fn create_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
/// does not already exist and it could not be created otherwise. The specific /// does not already exist and it could not be created otherwise. The specific
/// error conditions for when a directory is being created (after it is /// error conditions for when a directory is being created (after it is
/// determined to not exist) are outlined by `fs::create_dir`. /// determined to not exist) are outlined by `fs::create_dir`.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn create_dir_all<P: AsPath + ?Sized>(path: &P) -> io::Result<()> { pub fn create_dir_all<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
let path = path.as_path(); let path = path.as_path();
if path.is_dir() { return Ok(()) } if path.is_dir() { return Ok(()) }
@ -572,6 +599,7 @@ pub fn create_dir_all<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
/// ///
/// This function will return an error if the user lacks permissions to remove /// This function will return an error if the user lacks permissions to remove
/// the directory at the provided `path`, or if the directory isn't empty. /// the directory at the provided `path`, or if the directory isn't empty.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn remove_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<()> { pub fn remove_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
fs_imp::rmdir(path.as_path()) fs_imp::rmdir(path.as_path())
} }
@ -585,6 +613,7 @@ pub fn remove_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
/// # Errors /// # Errors
/// ///
/// See `file::remove_file` and `fs::remove_dir` /// See `file::remove_file` and `fs::remove_dir`
#[stable(feature = "rust1", since = "1.0.0")]
pub fn remove_dir_all<P: AsPath + ?Sized>(path: &P) -> io::Result<()> { pub fn remove_dir_all<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
let path = path.as_path(); let path = path.as_path();
for child in try!(read_dir(path)) { for child in try!(read_dir(path)) {
@ -637,6 +666,7 @@ pub fn remove_dir_all<P: AsPath + ?Sized>(path: &P) -> io::Result<()> {
/// This function will return an error if the provided `path` doesn't exist, if /// This function will return an error if the provided `path` doesn't exist, if
/// the process lacks permissions to view the contents or if the `path` points /// the process lacks permissions to view the contents or if the `path` points
/// at a non-directory file /// at a non-directory file
#[stable(feature = "rust1", since = "1.0.0")]
pub fn read_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<ReadDir> { pub fn read_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<ReadDir> {
fs_imp::readdir(path.as_path()).map(ReadDir) fs_imp::readdir(path.as_path()).map(ReadDir)
} }
@ -649,11 +679,16 @@ pub fn read_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<ReadDir> {
/// ///
/// The iterator will yield instances of `io::Result<DirEntry>`. New errors may /// The iterator will yield instances of `io::Result<DirEntry>`. New errors may
/// be encountered after an iterator is initially constructed. /// be encountered after an iterator is initially constructed.
#[unstable(feature = "fs_walk",
reason = "the precise semantics and defaults for a recursive walk \
may change and this may end up accounting for files such \
as symlinks differently")]
pub fn walk_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<WalkDir> { pub fn walk_dir<P: AsPath + ?Sized>(path: &P) -> io::Result<WalkDir> {
let start = try!(read_dir(path)); let start = try!(read_dir(path));
Ok(WalkDir { cur: Some(start), stack: Vec::new() }) Ok(WalkDir { cur: Some(start), stack: Vec::new() })
} }
#[unstable(feature = "fs_walk")]
impl Iterator for WalkDir { impl Iterator for WalkDir {
type Item = io::Result<DirEntry>; type Item = io::Result<DirEntry>;
@ -683,6 +718,9 @@ impl Iterator for WalkDir {
} }
/// Utility methods for paths. /// Utility methods for paths.
#[unstable(feature = "path_ext",
reason = "the precise set of methods exposed on this trait may \
change and some methods may be removed")]
pub trait PathExt { pub trait PathExt {
/// Get information on the file, directory, etc at this path. /// Get information on the file, directory, etc at this path.
/// ///
@ -727,6 +765,10 @@ impl PathExt for Path {
/// The file at the path specified will have its last access time set to /// The file at the path specified will have its last access time set to
/// `atime` and its modification time set to `mtime`. The times specified should /// `atime` and its modification time set to `mtime`. The times specified should
/// be in milliseconds. /// be in milliseconds.
#[unstable(feature = "fs_time",
reason = "the argument type of u64 is not quite appropriate for \
this function and may change if the standard library \
gains a type to represent a moment in time")]
pub fn set_file_times<P: AsPath + ?Sized>(path: &P, accessed: u64, pub fn set_file_times<P: AsPath + ?Sized>(path: &P, accessed: u64,
modified: u64) -> io::Result<()> { modified: u64) -> io::Result<()> {
fs_imp::utimes(path.as_path(), accessed, modified) fs_imp::utimes(path.as_path(), accessed, modified)
@ -752,6 +794,10 @@ pub fn set_file_times<P: AsPath + ?Sized>(path: &P, accessed: u64,
/// This function will return an error if the provided `path` doesn't exist, if /// This function will return an error if the provided `path` doesn't exist, if
/// the process lacks permissions to change the attributes of the file, or if /// the process lacks permissions to change the attributes of the file, or if
/// some other I/O error is encountered. /// some other I/O error is encountered.
#[unstable(feature = "fs",
reason = "a more granual ability to set specific permissions may \
be exposed on the Permissions structure itself and this \
method may not always exist")]
pub fn set_permissions<P: AsPath + ?Sized>(path: &P, perm: Permissions) pub fn set_permissions<P: AsPath + ?Sized>(path: &P, perm: Permissions)
-> io::Result<()> { -> io::Result<()> {
fs_imp::set_perm(path.as_path(), perm.0) fs_imp::set_perm(path.as_path(), perm.0)

View File

@ -9,6 +9,9 @@
// except according to those terms. // except according to those terms.
#![unstable(feature = "tempdir", reason = "needs an RFC before stabilization")] #![unstable(feature = "tempdir", reason = "needs an RFC before stabilization")]
#![deprecated(since = "1.0.0",
reason = "use the `tempdir` crate from crates.io instead")]
#![allow(deprecated)]
use prelude::v1::*; use prelude::v1::*;

View File

@ -37,8 +37,8 @@
#![feature(std_misc)] #![feature(std_misc)]
#![feature(unicode)] #![feature(unicode)]
#![feature(path)] #![feature(path)]
#![feature(fs)]
#![feature(io)] #![feature(io)]
#![feature(path_ext)]
extern crate arena; extern crate arena;
extern crate fmt_macros; extern crate fmt_macros;

View File

@ -52,7 +52,6 @@
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(collections)] #![feature(collections)]
#![feature(fs)]
#![feature(int_uint)] #![feature(int_uint)]
#![feature(io)] #![feature(io)]
#![feature(old_io)] #![feature(old_io)]
@ -61,6 +60,7 @@
#![feature(staged_api)] #![feature(staged_api)]
#![feature(std_misc)] #![feature(std_misc)]
#![feature(unicode)] #![feature(unicode)]
#![feature(path_ext)]
#![cfg_attr(windows, feature(libc))] #![cfg_attr(windows, feature(libc))]
#[macro_use] extern crate log; #[macro_use] extern crate log;

View File

@ -39,7 +39,6 @@
#![feature(int_uint)] #![feature(int_uint)]
#![feature(old_io)] #![feature(old_io)]
#![feature(path)] #![feature(path)]
#![feature(fs)]
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(staged_api)] #![feature(staged_api)]
#![feature(std_misc)] #![feature(std_misc)]

View File

@ -11,10 +11,11 @@
//! Implementation of the `build` subcommand, used to compile a book. //! Implementation of the `build` subcommand, used to compile a book.
use std::env; use std::env;
use std::fs::{self, File, TempDir}; use std::fs::{self, File};
use std::io::prelude::*; use std::io::prelude::*;
use std::io::{self, BufWriter}; use std::io::{self, BufWriter};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use rustc_back::tempdir::TempDir;
use subcommand::Subcommand; use subcommand::Subcommand;
use term::Term; use term::Term;

View File

@ -12,14 +12,14 @@
#![feature(core)] #![feature(core)]
#![feature(exit_status)] #![feature(exit_status)]
#![feature(fs)]
#![feature(io)] #![feature(io)]
#![feature(old_io)] #![feature(old_io)]
#![feature(path)] #![feature(path)]
#![feature(rustdoc)] #![feature(rustdoc)]
#![feature(tempdir)] #![feature(rustc_private)]
extern crate rustdoc; extern crate rustdoc;
extern crate rustc_back;
use std::env; use std::env;
use std::error::Error; use std::error::Error;