From fd518ac4b0fe30b13d2fdf0f1b4b9fa129d75dff Mon Sep 17 00:00:00 2001
From: Simon Sapin <simon.sapin@exyr.org>
Date: Tue, 7 Nov 2017 16:17:03 +0100
Subject: [PATCH 1/6] Add File::read_contents and File::write_contents
 convenience functions.

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::File;

do_something_with(File::read_contents(filename)?)
```
---
 src/libstd/fs.rs  | 79 +++++++++++++++++++++++++++++++++++++++++++++++
 src/libstd/lib.rs |  1 +
 2 files changed, 80 insertions(+)

diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index b07733d3c803c..9b41cde30fb71 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -262,6 +262,73 @@ impl File {
         OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
     }
 
+    /// 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(file_read_write_contents)]
+    ///
+    /// use std::fs::File;
+    ///
+    /// # fn foo() -> Result<(), Box<std::error::Error + 'static>> {
+    /// let foo = String::from_utf8(File::read_contents("foo.txt")?)?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    #[unstable(feature = "file_read_write_contents", issue = /* FIXME */ "0")]
+    pub fn read_contents<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
+        let mut bytes = Vec::new();
+        File::open(path)?.read_to_end(&mut bytes)?;
+        Ok(bytes)
+    }
+
+    /// Write the give contents to 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(file_read_write_contents)]
+    ///
+    /// use std::fs::File;
+    ///
+    /// # fn foo() -> std::io::Result<()> {
+    /// File::write_contents("foo.txt", b"Lorem ipsum")?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    #[unstable(feature = "file_read_write_contents", issue = /* FIXME */ "0")]
+    pub fn write_contents<P: AsRef<Path>>(path: P, contents: &[u8]) -> io::Result<()> {
+        File::create(path)?.write_all(contents)
+    }
+
     /// Attempts to sync all OS-internal metadata to disk.
     ///
     /// This function will attempt to ensure that all in-core data reaches the
@@ -2921,6 +2988,18 @@ mod tests {
         assert!(v == &bytes[..]);
     }
 
+    #[test]
+    fn write_contents_then_read_contents() {
+        let mut bytes = [0; 1024];
+        StdRng::new().unwrap().fill_bytes(&mut bytes);
+
+        let tmpdir = tmpdir();
+
+        check!(File::write_contents(&tmpdir.join("test"), &bytes));
+        let v = check!(File::read_contents(&tmpdir.join("test")));
+        assert!(v == &bytes[..]);
+    }
+
     #[test]
     fn file_try_clone() {
         let tmpdir = tmpdir();
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 429153dc58b4c..698830cc45b3d 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -269,6 +269,7 @@
 #![feature(core_intrinsics)]
 #![feature(dropck_eyepatch)]
 #![feature(exact_size_is_empty)]
+#![feature(file_read_write_contents)]
 #![feature(fixed_size_array)]
 #![feature(float_from_str_radix)]
 #![feature(fn_traits)]

From c670424cbe0f59cc0030a16d220dcdc93ce17a12 Mon Sep 17 00:00:00 2001
From: Simon Sapin <simon.sapin@exyr.org>
Date: Tue, 7 Nov 2017 18:23:56 +0100
Subject: [PATCH 2/6] Move `File::{read,write}_contents` to `fs::{read,write}`
 free functions.

---
 src/libstd/fs.rs  | 141 +++++++++++++++++++++++-----------------------
 src/libstd/lib.rs |   2 +-
 2 files changed, 72 insertions(+), 71 deletions(-)

diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 9b41cde30fb71..733ae6d5ccd9a 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -211,6 +211,74 @@ 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 = /* FIXME */ "0")]
+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)
+}
+
+/// 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 = /* FIXME */ "0")]
+pub fn write<P: AsRef<Path>>(path: P, contents: &[u8]) -> io::Result<()> {
+    File::create(path)?.write_all(contents)
+}
+
 impl File {
     /// Attempts to open a file in read-only mode.
     ///
@@ -262,73 +330,6 @@ impl File {
         OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
     }
 
-    /// 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(file_read_write_contents)]
-    ///
-    /// use std::fs::File;
-    ///
-    /// # fn foo() -> Result<(), Box<std::error::Error + 'static>> {
-    /// let foo = String::from_utf8(File::read_contents("foo.txt")?)?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    #[unstable(feature = "file_read_write_contents", issue = /* FIXME */ "0")]
-    pub fn read_contents<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
-        let mut bytes = Vec::new();
-        File::open(path)?.read_to_end(&mut bytes)?;
-        Ok(bytes)
-    }
-
-    /// Write the give contents to 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(file_read_write_contents)]
-    ///
-    /// use std::fs::File;
-    ///
-    /// # fn foo() -> std::io::Result<()> {
-    /// File::write_contents("foo.txt", b"Lorem ipsum")?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    #[unstable(feature = "file_read_write_contents", issue = /* FIXME */ "0")]
-    pub fn write_contents<P: AsRef<Path>>(path: P, contents: &[u8]) -> io::Result<()> {
-        File::create(path)?.write_all(contents)
-    }
-
     /// Attempts to sync all OS-internal metadata to disk.
     ///
     /// This function will attempt to ensure that all in-core data reaches the
@@ -2989,14 +2990,14 @@ mod tests {
     }
 
     #[test]
-    fn write_contents_then_read_contents() {
+    fn write_then_read() {
         let mut bytes = [0; 1024];
         StdRng::new().unwrap().fill_bytes(&mut bytes);
 
         let tmpdir = tmpdir();
 
-        check!(File::write_contents(&tmpdir.join("test"), &bytes));
-        let v = check!(File::read_contents(&tmpdir.join("test")));
+        check!(fs::write(&tmpdir.join("test"), &bytes));
+        let v = check!(fs::read(&tmpdir.join("test")));
         assert!(v == &bytes[..]);
     }
 
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index 698830cc45b3d..4bc81efe4777e 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -269,7 +269,7 @@
 #![feature(core_intrinsics)]
 #![feature(dropck_eyepatch)]
 #![feature(exact_size_is_empty)]
-#![feature(file_read_write_contents)]
+#![feature(fs_read_write)]
 #![feature(fixed_size_array)]
 #![feature(float_from_str_radix)]
 #![feature(fn_traits)]

From a379e697ab8ed7f3e73c79e303870ac208670bd7 Mon Sep 17 00:00:00 2001
From: Simon Sapin <simon.sapin@exyr.org>
Date: Tue, 7 Nov 2017 18:42:15 +0100
Subject: [PATCH 3/6] Add `std::fs::read_utf8`, based on `File::open` and
 `read_to_string`

---
 src/libstd/fs.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 53 insertions(+), 1 deletion(-)

diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 733ae6d5ccd9a..ab02880218268 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -251,6 +251,47 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
     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_utf8("address.txt")?.parse()?;
+/// # Ok(())
+/// # }
+/// ```
+#[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")]
+pub fn read_utf8<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,
@@ -1980,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),
@@ -2999,6 +3042,15 @@ mod tests {
         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_utf8(&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_utf8(&tmpdir.join("utf8")));
+        assert_eq!(string, s);
     }
 
     #[test]

From 7e2f756583b4c7cbbb0bd2fc2543fa02eefea96b Mon Sep 17 00:00:00 2001
From: Simon Sapin <simon.sapin@exyr.org>
Date: Tue, 28 Nov 2017 19:16:56 +0100
Subject: [PATCH 4/6] Generalize fs::write from &[u8] to AsRef<[u8]>

---
 src/libstd/fs.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index ab02880218268..7bb3789da88ec 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -316,8 +316,8 @@ pub fn read_utf8<P: AsRef<Path>>(path: P) -> io::Result<String> {
 /// # }
 /// ```
 #[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")]
-pub fn write<P: AsRef<Path>>(path: P, contents: &[u8]) -> io::Result<()> {
-    File::create(path)?.write_all(contents)
+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 {
@@ -3039,7 +3039,7 @@ mod tests {
 
         let tmpdir = tmpdir();
 
-        check!(fs::write(&tmpdir.join("test"), &bytes));
+        check!(fs::write(&tmpdir.join("test"), &bytes[..]));
         let v = check!(fs::read(&tmpdir.join("test")));
         assert!(v == &bytes[..]);
 

From 1cf11b3d0bb0c448b221953d0227a28306d8e297 Mon Sep 17 00:00:00 2001
From: Simon Sapin <simon.sapin@exyr.org>
Date: Wed, 6 Dec 2017 14:10:51 +0100
Subject: [PATCH 5/6] Rename fs::read_utf8 to read_string

---
 src/libstd/fs.rs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index 7bb3789da88ec..a5884f75e0acd 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -281,12 +281,12 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
 /// use std::net::SocketAddr;
 ///
 /// # fn foo() -> Result<(), Box<std::error::Error + 'static>> {
-/// let foo: SocketAddr = fs::read_utf8("address.txt")?.parse()?;
+/// let foo: SocketAddr = fs::read_string("address.txt")?.parse()?;
 /// # Ok(())
 /// # }
 /// ```
 #[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")]
-pub fn read_utf8<P: AsRef<Path>>(path: P) -> io::Result<String> {
+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)
@@ -3044,12 +3044,12 @@ mod tests {
         assert!(v == &bytes[..]);
 
         check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
-        error_contains!(fs::read_utf8(&tmpdir.join("not-utf8")),
+        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_utf8(&tmpdir.join("utf8")));
+        let string = check!(fs::read_string(&tmpdir.join("utf8")));
         assert_eq!(string, s);
     }
 

From c5eff5442ca963e20225c8229aff7be28f65a0a6 Mon Sep 17 00:00:00 2001
From: Simon Sapin <simon.sapin@exyr.org>
Date: Fri, 8 Dec 2017 19:28:13 +0100
Subject: [PATCH 6/6] fs::{read, read_string, write}: add tracking issue number

---
 src/libstd/fs.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs
index a5884f75e0acd..f40aed2478a17 100644
--- a/src/libstd/fs.rs
+++ b/src/libstd/fs.rs
@@ -244,7 +244,7 @@ pub struct DirBuilder {
 /// # Ok(())
 /// # }
 /// ```
-#[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")]
+#[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)?;
@@ -285,7 +285,7 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
 /// # Ok(())
 /// # }
 /// ```
-#[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")]
+#[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)?;
@@ -315,7 +315,7 @@ pub fn read_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
 /// # Ok(())
 /// # }
 /// ```
-#[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")]
+#[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())
 }