From fd518ac4b0fe30b13d2fdf0f1b4b9fa129d75dff Mon Sep 17 00:00:00 2001 From: Simon Sapin 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> { + /// let foo = String::from_utf8(File::read_contents("foo.txt")?)?; + /// # Ok(()) + /// # } + /// ``` + #[unstable(feature = "file_read_write_contents", issue = /* FIXME */ "0")] + pub fn read_contents>(path: P) -> io::Result> { + 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>(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 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> { +/// let foo: SocketAddr = String::from_utf8_lossy(&fs::read("address.txt")?).parse()?; +/// # Ok(()) +/// # } +/// ``` +#[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")] +pub fn read>(path: P) -> io::Result> { + 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>(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> { - /// let foo = String::from_utf8(File::read_contents("foo.txt")?)?; - /// # Ok(()) - /// # } - /// ``` - #[unstable(feature = "file_read_write_contents", issue = /* FIXME */ "0")] - pub fn read_contents>(path: P) -> io::Result> { - 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>(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 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>(path: P) -> io::Result> { 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> { +/// let foo: SocketAddr = fs::read_utf8("address.txt")?.parse()?; +/// # Ok(()) +/// # } +/// ``` +#[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")] +pub fn read_utf8>(path: P) -> io::Result { + 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 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>(path: P) -> io::Result { /// # } /// ``` #[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")] -pub fn write>(path: P, contents: &[u8]) -> io::Result<()> { - File::create(path)?.write_all(contents) +pub fn write, 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 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>(path: P) -> io::Result> { /// use std::net::SocketAddr; /// /// # fn foo() -> Result<(), Box> { -/// 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>(path: P) -> io::Result { +pub fn read_string>(path: P) -> io::Result { 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 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>(path: P) -> io::Result> { let mut bytes = Vec::new(); File::open(path)?.read_to_end(&mut bytes)?; @@ -285,7 +285,7 @@ pub fn read>(path: P) -> io::Result> { /// # Ok(()) /// # } /// ``` -#[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")] +#[unstable(feature = "fs_read_write", issue = "46588")] pub fn read_string>(path: P) -> io::Result { let mut string = String::new(); File::open(path)?.read_to_string(&mut string)?; @@ -315,7 +315,7 @@ pub fn read_string>(path: P) -> io::Result { /// # Ok(()) /// # } /// ``` -#[unstable(feature = "fs_read_write", issue = /* FIXME */ "0")] +#[unstable(feature = "fs_read_write", issue = "46588")] pub fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { File::create(path)?.write_all(contents.as_ref()) }