diff --git a/dev/Cargo.toml b/dev/Cargo.toml index 4f725b704b..dc3b67ca27 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -188,6 +188,12 @@ bin = [ { name = "try_from_into_sol", path = "../solutions/23_conversions/try_from_into.rs" }, { name = "as_ref_mut", path = "../exercises/23_conversions/as_ref_mut.rs" }, { name = "as_ref_mut_sol", path = "../solutions/23_conversions/as_ref_mut.rs" }, + { name = "file_io1", path = "../exercises/24_file_io/file_io1.rs" }, + { name = "file_io1_sol", path = "../solutions/24_file_io/file_io1.rs" }, + { name = "file_io2", path = "../exercises/24_file_io/file_io2.rs" }, + { name = "file_io2_sol", path = "../solutions/24_file_io/file_io2.rs" }, + { name = "file_io3", path = "../exercises/24_file_io/file_io3.rs" }, + { name = "file_io3_sol", path = "../solutions/24_file_io/file_io3.rs" }, ] [package] diff --git a/exercises/24_file_io/README.md b/exercises/24_file_io/README.md new file mode 100644 index 0000000000..9277959155 --- /dev/null +++ b/exercises/24_file_io/README.md @@ -0,0 +1,16 @@ +# File IO + +Rust Provides several file I/O functions in the standard library. Buffered reads and writes provides better performance by reducing underlying reads. + +## Further information + +Here is the documentation for these functions in the standard library. + +- [ReadToString](https://doc.rust-lang.org/std/fs/fn.read_to_string.html) +- [BufReader](https://doc.rust-lang.org/std/io/struct.BufReader.html) +- [BufWriter](https://doc.rust-lang.org/std/io/struct.BufWriter.html) +- [Path](https://doc.rust-lang.org/stable/std/path/struct.Path.html) +- [PathBuf](https://doc.rust-lang.org/std/path/struct.PathBuf.html) + + + diff --git a/exercises/24_file_io/file_io1.rs b/exercises/24_file_io/file_io1.rs new file mode 100644 index 0000000000..54647dc461 --- /dev/null +++ b/exercises/24_file_io/file_io1.rs @@ -0,0 +1,51 @@ +use std::fs; +use std::path::Path; + +const TEST_FILE_NAME: &str = "SampleTextFile.txt"; +fn main() -> Result<(), std::io::Error> { + create_required_files()?; + + let read_str_result = fs::read_to_string(TEST_FILE_NAME); + + match read_str_result { + Ok(contents) => { + // TODO : What would be the expected text ? + assert_eq!(, contents); + } + Err(err) => { + eprintln!("File read error. {}", err); + } + } + + file_cleanup()?; + Ok(()) +} + +fn create_required_files() -> Result<(), std::io::Error> { + let file_path = Path::new(TEST_FILE_NAME); + + if !file_path.exists() { + fs::write(file_path, "This is the file content.")?; + } else { + println!("File already exist."); + } + + Ok(()) +} + +fn file_cleanup() -> Result<(), std::io::Error> { + let file_path = Path::new(TEST_FILE_NAME); + + if file_path.exists() { + fs::remove_file(file_path).inspect(|_| { + println!("Test file {} deleted.", TEST_FILE_NAME); + })?; + } else { + println!( + "No cleanup necessary since {} not exist.", + file_path.display() + ); + } + + Ok(()) +} diff --git a/exercises/24_file_io/file_io2.rs b/exercises/24_file_io/file_io2.rs new file mode 100644 index 0000000000..f19d2a4784 --- /dev/null +++ b/exercises/24_file_io/file_io2.rs @@ -0,0 +1,75 @@ +use std::fs; +use std::io::{BufRead, BufReader, BufWriter, Write}; +use std::path::Path; + +const TEST_INPUT_FILE_NAME: &str = "MultiLineTextFile.txt"; +const TEST_OUTPUT_FILE_NAME: &str = "MultiLineOutputFile.txt"; + +fn main() -> Result<(), std::io::Error> { + create_required_files()?; + + let input_file = fs::File::open(TEST_INPUT_FILE_NAME).inspect_err(|err| { + eprintln!("{} file open error {:?}", TEST_INPUT_FILE_NAME, err); + })?; + + // TODO : How to create a new BufReader using input file + let buffered_input_file =; + + let output_file = fs::File::create(TEST_OUTPUT_FILE_NAME).inspect_err(|err| { + eprintln!("{} file open error {:?}", TEST_OUTPUT_FILE_NAME, err); + })?; + + let mut buffered_file_writer = BufWriter::new(output_file); + + let mut line_number = 1; + + for line in buffered_input_file.lines() { + let line = line.inspect_err(|err| { + eprintln!("{} line parse error {:?}", TEST_INPUT_FILE_NAME, err); + })?; + + buffered_file_writer + .write(format!("Line {} : {}\n", line_number, line).as_bytes()) + .inspect_err(|err| { + eprintln!("{} line write error {:?}", TEST_INPUT_FILE_NAME, err); + })?; + + line_number += 1; + } + + println!("{} : lines processed", line_number - 1); + file_cleanup() +} + +fn create_required_files() -> Result<(), std::io::Error> { + let file_path = Path::new(TEST_INPUT_FILE_NAME); + + if !file_path.exists() { + let text = "This is the first line of the text. + This is the second line. + And this is the third and the last line."; + fs::write(file_path, text).inspect_err(|err| { + eprintln!("Couldn't create the test file : {}", err); + })?; + } + + Ok(()) +} + +fn file_cleanup() -> Result<(), std::io::Error> { + let file_names = vec![TEST_INPUT_FILE_NAME, TEST_OUTPUT_FILE_NAME]; + + for file_name in file_names { + let file_path = Path::new(file_name); + + if file_path.exists() { + fs::remove_file(file_path).inspect(|_| { + println!("Test file {} removed", file_name); + })?; + } else { + println!("No cleanup necessary since {} not exist.", file_name); + } + } + + Ok(()) +} diff --git a/exercises/24_file_io/file_io3.rs b/exercises/24_file_io/file_io3.rs new file mode 100644 index 0000000000..7966bfcca1 --- /dev/null +++ b/exercises/24_file_io/file_io3.rs @@ -0,0 +1,78 @@ +use std::fs; +use std::io::Error; +use std::path::PathBuf; + +fn main() -> Result<(), std::io::Error> { + create_required_files()?; + let mut path_buffer = PathBuf::new(); + + path_buffer.push("SampleFilesFolder"); + path_buffer.push("MultiLineTextFile.txt"); + + // TODO : How to get metadata using path_buffer ? + let meta_data_result = path_buffer. + + match meta_data_result { + Ok(meta_data) => { + println!("Metadata about the file : {:?}", path_buffer); + println!("File creation time {:?}", meta_data.created()); + println!("File size {}", meta_data.len()); + assert_eq!(meta_data.len(), 117); + println!("File permissions {:?}", meta_data.permissions()); + assert!(!meta_data.permissions().readonly()); + } + Err(error) => { + eprintln!("Could not get metadata. Error: {:?}", error); + } + } + + file_cleanup() +} + +fn create_required_files() -> Result<(), std::io::Error> { + let file_path = PathBuf::from("SampleFilesFolder/MultiLineTextFile.txt"); + + let dir_path = match file_path.parent(){ + Some(parent) => parent, + None => return Err(Error::other("Could not get parent path")), + }; + + if !dir_path.exists() { + fs::create_dir(dir_path).inspect_err(|x| { + eprintln!("Could not create directory: {:?}", x); + })?; + } + + if !file_path.exists() { + let text = "This is the first line of the text. + This is the second line. + And this is the third and the last line."; + fs::write(&file_path, text).inspect_err(|err| { + eprintln!("Couldn't create test file: {:?}", err); + })?; + } + + Ok(()) +} + +fn file_cleanup() -> Result<(), std::io::Error> { + let mut path_buffer = PathBuf::new(); + + path_buffer.push("SampleFilesFolder"); + path_buffer.push("MultiLineTextFile.txt"); + + if path_buffer.exists() { + fs::remove_file(&path_buffer).inspect(|_| { + println!("Test file removed"); + })?; + } + path_buffer.pop(); + + if path_buffer.exists() { + fs::remove_dir(&path_buffer).inspect(|_| { + println!("Test dir removed"); + })?; + } + + Ok(()) +} diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index ca3ecf1f03..b0dbda543d 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -1200,3 +1200,29 @@ name = "as_ref_mut" dir = "23_conversions" hint = """ Add `AsRef` or `AsMut` as a trait bound to the functions.""" + +# File IO Exercises + +[[exercises]] +name = "file_io1" +dir = "24_file_io" +test = false +hint = """ +Basic File Reading +""" + +[[exercises]] +name = "file_io2" +dir = "24_file_io" +test = false +hint = """ +Buffered Reading & Writing +""" + +[[exercises]] +name = "file_io3" +dir = "24_file_io" +test = false +hint = """ +Path Manipulation & Metadata +""" diff --git a/solutions/24_file_io/file_io1.rs b/solutions/24_file_io/file_io1.rs new file mode 100644 index 0000000000..9331eeea57 --- /dev/null +++ b/solutions/24_file_io/file_io1.rs @@ -0,0 +1,50 @@ +use std::fs; +use std::path::Path; + +const TEST_FILE_NAME: &str = "SampleTextFile.txt"; +fn main() -> Result<(), std::io::Error> { + create_required_files()?; + + let read_str_result = fs::read_to_string(TEST_FILE_NAME); + + match read_str_result { + Ok(contents) => { + assert_eq!("This is the file content.", contents); + } + Err(err) => { + eprintln!("File read error. {}", err); + } + } + + file_cleanup()?; + Ok(()) +} + +fn create_required_files() -> Result<(), std::io::Error> { + let file_path = Path::new(TEST_FILE_NAME); + + if !file_path.exists() { + fs::write(file_path, "This is the file content.")?; + } else { + println!("File already exist."); + } + + Ok(()) +} + +fn file_cleanup() -> Result<(), std::io::Error> { + let file_path = Path::new(TEST_FILE_NAME); + + if file_path.exists() { + fs::remove_file(file_path).inspect(|_| { + println!("Test file {} deleted.", TEST_FILE_NAME); + })?; + } else { + println!( + "No cleanup necessary since {} not exist.", + file_path.display() + ); + } + + Ok(()) +} diff --git a/solutions/24_file_io/file_io2.rs b/solutions/24_file_io/file_io2.rs new file mode 100644 index 0000000000..bfcaff1ead --- /dev/null +++ b/solutions/24_file_io/file_io2.rs @@ -0,0 +1,74 @@ +use std::fs; +use std::io::{BufRead, BufReader, BufWriter, Write}; +use std::path::Path; + +const TEST_INPUT_FILE_NAME: &str = "MultiLineTextFile.txt"; +const TEST_OUTPUT_FILE_NAME: &str = "MultiLineOutputFile.txt"; + +fn main() -> Result<(), std::io::Error> { + create_required_files()?; + + let input_file = fs::File::open(TEST_INPUT_FILE_NAME).inspect_err(|err| { + eprintln!("{} file open error {:?}", TEST_INPUT_FILE_NAME, err); + })?; + + let buffered_input_file = BufReader::new(input_file); + + let output_file = fs::File::create(TEST_OUTPUT_FILE_NAME).inspect_err(|err| { + eprintln!("{} file open error {:?}", TEST_OUTPUT_FILE_NAME, err); + })?; + + let mut buffered_file_writer = BufWriter::new(output_file); + + let mut line_number = 1; + + for line in buffered_input_file.lines() { + let line = line.inspect_err(|err| { + eprintln!("{} line parse error {:?}", TEST_INPUT_FILE_NAME, err); + })?; + + buffered_file_writer + .write(format!("Line {} : {}\n", line_number, line).as_bytes()) + .inspect_err(|err| { + eprintln!("{} line write error {:?}", TEST_INPUT_FILE_NAME, err); + })?; + + line_number += 1; + } + + println!("{} : lines processed", line_number - 1); + file_cleanup() +} + +fn create_required_files() -> Result<(), std::io::Error> { + let file_path = Path::new(TEST_INPUT_FILE_NAME); + + if !file_path.exists() { + let text = "This is the first line of the text. + This is the second line. + And this is the third and the last line."; + fs::write(file_path, text).inspect_err(|err| { + eprintln!("Couldn't create the test file : {}", err); + })?; + } + + Ok(()) +} + +fn file_cleanup() -> Result<(), std::io::Error> { + let file_names = vec![TEST_INPUT_FILE_NAME, TEST_OUTPUT_FILE_NAME]; + + for file_name in file_names { + let file_path = Path::new(file_name); + + if file_path.exists() { + fs::remove_file(file_path).inspect(|_| { + println!("Test file {} removed", file_name); + })?; + } else { + println!("No cleanup necessary since {} not exist.", file_name); + } + } + + Ok(()) +} diff --git a/solutions/24_file_io/file_io3.rs b/solutions/24_file_io/file_io3.rs new file mode 100644 index 0000000000..a68dad6efb --- /dev/null +++ b/solutions/24_file_io/file_io3.rs @@ -0,0 +1,77 @@ +use std::fs; +use std::io::Error; +use std::path::PathBuf; + +fn main() -> Result<(), std::io::Error> { + create_required_files()?; + let mut path_buffer = PathBuf::new(); + + path_buffer.push("SampleFilesFolder"); + path_buffer.push("MultiLineTextFile.txt"); + + let meta_data_result = path_buffer.metadata(); + + match meta_data_result { + Ok(meta_data) => { + println!("Metadata about the file : {:?}", path_buffer); + println!("File creation time {:?}", meta_data.created()); + println!("File size {}", meta_data.len()); + assert_eq!(meta_data.len(), 117); + println!("File permissions {:?}", meta_data.permissions()); + assert!(!meta_data.permissions().readonly()); + } + Err(error) => { + eprintln!("Could not get metadata. Error: {:?}", error); + } + } + + file_cleanup() +} + +fn create_required_files() -> Result<(), std::io::Error> { + let file_path = PathBuf::from("SampleFilesFolder/MultiLineTextFile.txt"); + + let dir_path = match file_path.parent() { + Some(parent) => parent, + None => return Err(Error::other("Could not get parent path")), + }; + + if !dir_path.exists() { + fs::create_dir(dir_path).inspect_err(|x| { + eprintln!("Could not create directory: {:?}", x); + })?; + } + + if !file_path.exists() { + let text = "This is the first line of the text. + This is the second line. + And this is the third and the last line."; + fs::write(&file_path, text).inspect_err(|err| { + eprintln!("Couldn't create test file: {:?}", err); + })?; + } + + Ok(()) +} + +fn file_cleanup() -> Result<(), std::io::Error> { + let mut path_buffer = PathBuf::new(); + + path_buffer.push("SampleFilesFolder"); + path_buffer.push("MultiLineTextFile.txt"); + + if path_buffer.exists() { + fs::remove_file(&path_buffer).inspect(|_| { + println!("Test file removed"); + })?; + } + path_buffer.pop(); + + if path_buffer.exists() { + fs::remove_dir(&path_buffer).inspect(|_| { + println!("Test dir removed"); + })?; + } + + Ok(()) +}