Skip to content

Commit

Permalink
Auto merge of #45837 - SimonSapin:file_read_write, r=dtolnay
Browse files Browse the repository at this point in the history
Add read, read_string, and write functions to std::fs

New APIs in `std::fs`:

```rust
pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> { … }
pub fn read_string<P: AsRef<Path>>(path: P) -> io::Result<String> { … }
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { ... }
```

(`read_string` is based on `read_to_string` and so returns an error on non-UTF-8 content.)

Before:

```rust
use std::fs::File;
use std::io::Read;

let mut bytes = Vec::new();
File::open(filename)?.read_to_end(&mut bytes)?;
do_something_with(bytes)
```

After:

```rust
use std::fs;

do_something_with(fs::read(filename)?)
```
  • Loading branch information
bors committed Dec 8, 2017
2 parents ad3543d + c5eff54 commit c7b6d82
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 1 deletion.
134 changes: 133 additions & 1 deletion src/libstd/fs.rs
Expand Up @@ -211,6 +211,115 @@ pub struct DirBuilder {
recursive: bool,
}

/// Read the entire contents of a file into a bytes vector.
///
/// This is a convenience function for using [`File::open`] and [`read_to_end`]
/// with fewer imports and without an intermediate variable.
///
/// [`File::open`]: struct.File.html#method.open
/// [`read_to_end`]: ../io/trait.Read.html#method.read_to_end
///
/// # Errors
///
/// This function will return an error if `path` does not already exist.
/// Other errors may also be returned according to [`OpenOptions::open`].
///
/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open
///
/// It will also return an error if it encounters while reading an error
/// of a kind other than [`ErrorKind::Interrupted`].
///
/// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted
///
/// # Examples
///
/// ```no_run
/// #![feature(fs_read_write)]
///
/// use std::fs;
/// use std::net::SocketAddr;
///
/// # fn foo() -> Result<(), Box<std::error::Error + 'static>> {
/// let foo: SocketAddr = String::from_utf8_lossy(&fs::read("address.txt")?).parse()?;
/// # Ok(())
/// # }
/// ```
#[unstable(feature = "fs_read_write", issue = "46588")]
pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
let mut bytes = Vec::new();
File::open(path)?.read_to_end(&mut bytes)?;
Ok(bytes)
}

/// Read the entire contents of a file into a string.
///
/// This is a convenience function for using [`File::open`] and [`read_to_string`]
/// with fewer imports and without an intermediate variable.
///
/// [`File::open`]: struct.File.html#method.open
/// [`read_to_string`]: ../io/trait.Read.html#method.read_to_string
///
/// # Errors
///
/// This function will return an error if `path` does not already exist.
/// Other errors may also be returned according to [`OpenOptions::open`].
///
/// [`OpenOptions::open`]: struct.OpenOptions.html#method.open
///
/// It will also return an error if it encounters while reading an error
/// of a kind other than [`ErrorKind::Interrupted`],
/// or if the contents of the file are not valid UTF-8.
///
/// [`ErrorKind::Interrupted`]: ../../std/io/enum.ErrorKind.html#variant.Interrupted
///
/// # Examples
///
/// ```no_run
/// #![feature(fs_read_write)]
///
/// use std::fs;
/// use std::net::SocketAddr;
///
/// # fn foo() -> Result<(), Box<std::error::Error + 'static>> {
/// let foo: SocketAddr = fs::read_string("address.txt")?.parse()?;
/// # Ok(())
/// # }
/// ```
#[unstable(feature = "fs_read_write", issue = "46588")]
pub fn read_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
let mut string = String::new();
File::open(path)?.read_to_string(&mut string)?;
Ok(string)
}

/// Write a slice as the entire contents of a file.
///
/// This function will create a file if it does not exist,
/// and will entirely replace its contents if it does.
///
/// This is a convenience function for using [`File::create`] and [`write_all`]
/// with fewer imports.
///
/// [`File::create`]: struct.File.html#method.create
/// [`write_all`]: ../io/trait.Write.html#method.write_all
///
/// # Examples
///
/// ```no_run
/// #![feature(fs_read_write)]
///
/// use std::fs;
///
/// # fn foo() -> std::io::Result<()> {
/// fs::write("foo.txt", b"Lorem ipsum")?;
/// # Ok(())
/// # }
/// ```
#[unstable(feature = "fs_read_write", issue = "46588")]
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
File::create(path)?.write_all(contents.as_ref())
}

impl File {
/// Attempts to open a file in read-only mode.
///
Expand Down Expand Up @@ -1912,7 +2021,9 @@ mod tests {
) }

#[cfg(unix)]
macro_rules! error { ($e:expr, $s:expr) => (
macro_rules! error { ($e:expr, $s:expr) => ( error_contains!($e, $s) ) }

macro_rules! error_contains { ($e:expr, $s:expr) => (
match $e {
Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
Err(ref err) => assert!(err.to_string().contains($s),
Expand Down Expand Up @@ -2921,6 +3032,27 @@ mod tests {
assert!(v == &bytes[..]);
}

#[test]
fn write_then_read() {
let mut bytes = [0; 1024];
StdRng::new().unwrap().fill_bytes(&mut bytes);

let tmpdir = tmpdir();

check!(fs::write(&tmpdir.join("test"), &bytes[..]));
let v = check!(fs::read(&tmpdir.join("test")));
assert!(v == &bytes[..]);

check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
error_contains!(fs::read_string(&tmpdir.join("not-utf8")),
"stream did not contain valid UTF-8");

let s = "𐁁𐀓𐀠𐀴𐀍";
check!(fs::write(&tmpdir.join("utf8"), s.as_bytes()));
let string = check!(fs::read_string(&tmpdir.join("utf8")));
assert_eq!(string, s);
}

#[test]
fn file_try_clone() {
let tmpdir = tmpdir();
Expand Down
1 change: 1 addition & 0 deletions src/libstd/lib.rs
Expand Up @@ -260,6 +260,7 @@
#![feature(core_intrinsics)]
#![feature(dropck_eyepatch)]
#![feature(exact_size_is_empty)]
#![feature(fs_read_write)]
#![feature(fixed_size_array)]
#![feature(float_from_str_radix)]
#![feature(fn_traits)]
Expand Down

0 comments on commit c7b6d82

Please sign in to comment.