Skip to content

Commit

Permalink
[Enhancement] Read from Option<T> Values (#43)
Browse files Browse the repository at this point in the history
* Added ::= OptionReader

* Added ::= OptionReader::read_silently

* Added ::= OptionReader::read_loudly

* Added ::= tests for OptionReader

* Skip ::= fix typos

* Create summary of recent changes

---------

Co-authored-by: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
kevinmatthes and github-actions[bot] authored Jan 14, 2024
1 parent 9135be6 commit 0bc03b1
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 53 deletions.
11 changes: 11 additions & 0 deletions changelog.d/20240114_202912_GitHub_Actions_option-reader.ron
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(
references: {},
changes: {
"Added": [
"OptionReader",
"OptionReader::read_loudly",
"OptionReader::read_silently",
"tests for OptionReader",
],
},
)
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@

mod reading;

pub use reading::{BufReadReader, PathBufLikeReader};
pub use reading::{BufReadReader, OptionReader, PathBufLikeReader};

/// This crate's name.
pub const NAME: &str = "aeruginous-io";
Expand Down
73 changes: 63 additions & 10 deletions src/reading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,59 @@ impl<T: std::io::BufRead> BufReadReader for T {
}
}

/// Read from either source, dependending on the circumstances.
pub trait OptionReader<T>
where
T: BufReadReader,
{
/// Read from this instance or the given alternative.
///
/// This method behaves just like
/// [`crate::OptionReader::read_silently`] despite also printing error
/// messages to [`std::io::Stderr`].
///
/// # Errors
///
/// See [`sysexits::ExitCode`].
fn read_loudly(&self, alternative: T) -> Result<String>;

/// Read from this instance or the given alternative.
///
/// If the instance this method is called has [`Some`] value, the contained
/// value will be interpreted as file to read from. Therefore, the value
/// needs to implement [`crate::PathBufLikeReader`]. In case the instance
/// this method is called on is [`None`], the given alternative will be
/// considered the source to read from. Therefore, the alternative needs to
/// implement [`crate::BufReadReader`].
///
/// The return value is either the read content as a [`String`], in case of
/// success, or a [`sysexits::ExitCode`] to describe the error cause,
/// otherwise.
///
/// Error messages are not written to [`std::io::Stderr`].
///
/// # Errors
///
/// See [`sysexits::ExitCode`].
fn read_silently(&self, alternative: T) -> Result<String>;
}

impl<P: PathBufLikeReader, T: std::io::BufRead> OptionReader<T> for Option<P> {
fn read_loudly(&self, alternative: T) -> Result<String> {
self.as_ref().map_or_else(
|| alternative.read_loudly(),
PathBufLikeReader::read_loudly,
)
}

fn read_silently(&self, alternative: T) -> Result<String> {
self.as_ref().map_or_else(
|| alternative.read_silently(),
PathBufLikeReader::read_silently,
)
}
}

/// Read from files given as instances convertible to a [`std::path::PathBuf`].
pub trait PathBufLikeReader {
/// Read from the file this method is called on.
Expand All @@ -104,9 +157,9 @@ pub trait PathBufLikeReader {
/// The instance this method is called on needs to be convertible to a
/// [`std::path::PathBuf`]. The referenced file will be opened and read.
///
/// The return value is either the read file content as a [`String`], in
/// case of success, or a [`sysexits::ExitCode`] to describe the error
/// cause, otherwise.
/// The return value is either the read content as a [`String`], in case of
/// success, or a [`sysexits::ExitCode`] to describe the error cause,
/// otherwise.
///
/// Error messages are not written to [`std::io::Stderr`].
///
Expand All @@ -121,13 +174,6 @@ where
PathBuf: From<T>,
T: Clone,
{
fn read_silently(&self) -> Result<String> {
match std::fs::read_to_string(PathBuf::from(self.clone())) {
Ok(s) => Ok(s),
Err(e) => Err(e.into()),
}
}

fn read_loudly(&self) -> Result<String> {
match std::fs::read_to_string(PathBuf::from(self.clone())) {
Ok(s) => Ok(s),
Expand All @@ -137,6 +183,13 @@ where
}
}
}

fn read_silently(&self) -> Result<String> {
match std::fs::read_to_string(PathBuf::from(self.clone())) {
Ok(s) => Ok(s),
Err(e) => Err(e.into()),
}
}
}

/******************************************************************************/
163 changes: 121 additions & 42 deletions tests/reading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,132 @@
| |
\******************************************************************************/

use aeruginous_io::{BufReadReader, PathBufLikeReader};

#[test]
fn read_buf_reader_failure() {
assert!("👍"
.as_bytes()
.iter()
.map(|b| if b > &200 { 0 } else { *b })
.collect::<Vec<u8>>()
.read_loudly()
.is_err());
assert!("👍"
.as_bytes()
.iter()
.map(|b| if b > &200 { 0 } else { *b })
.collect::<Vec<u8>>()
.read_silently()
.is_err());
}
mod buf_read_reader {
use aeruginous_io::BufReadReader;

#[test]
fn read_buf_read_success() {
assert_eq!({ &(b"test"[..]) }.read_loudly().unwrap(), "test\n");
assert_eq!({ &(b"test"[..]) }.read_silently().unwrap(), "test\n");
}
#[test]
fn read_loudly_failure() {
assert!("👍"
.as_bytes()
.iter()
.map(|b| if b > &200 { 0 } else { *b })
.collect::<Vec<u8>>()
.read_loudly()
.is_err());
}

#[test]
fn read_loudly_success() {
assert_eq!({ &(b"test"[..]) }.read_loudly().unwrap(), "test\n");
}

#[test]
fn read_silently_failure() {
assert!("👍"
.as_bytes()
.iter()
.map(|b| if b > &200 { 0 } else { *b })
.collect::<Vec<u8>>()
.read_silently()
.is_err());
}

#[test]
fn read_path_buf_like_failure_directory() {
assert!(".github/".read_loudly().is_err());
assert!(".github/".read_silently().is_err());
#[test]
fn read_silently_success() {
assert_eq!({ &(b"test"[..]) }.read_silently().unwrap(), "test\n");
}
}

#[test]
fn read_path_buf_like_failure_does_not_exist() {
assert!("no_such_file.txt".read_loudly().is_err());
assert!("no_such_file.txt".read_silently().is_err());
mod path_buf_like_reader {
use aeruginous_io::PathBufLikeReader;

#[test]
fn method_result_equality() {
assert_eq!(
"tests/assets/GPL-3.0.rs".read_loudly().unwrap(),
"tests/assets/GPL-3.0.rs".read_silently().unwrap(),
);
}

#[test]
fn read_loudly_failure_attempt_to_read_directory() {
assert!(".github/".read_loudly().is_err());
}

#[test]
fn read_loudly_failure_file_does_not_exist() {
assert!("no_such_file.txt".read_loudly().is_err());
}

#[test]
fn read_silently_failure_attempt_to_read_directory() {
assert!(".github/".read_silently().is_err());
}

#[test]
fn read_silently_failure_file_does_not_exist() {
assert!("no_such_file.txt".read_silently().is_err());
}

#[test]
fn read_silently_success() {
assert_eq!(
"tests/assets/GPL-3.0.rs".read_silently().unwrap(),
"\
/// Copyright (C) 2024 Kevin Matthes
///
/// This program is free software: you can redistribute it and/or modify
/// it under the terms of the GNU General Public License as published by
/// the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
"
);
}
}

#[test]
fn read_path_buf_like_success() {
assert_eq!(
"tests/assets/GPL-3.0.rs".read_loudly().unwrap(),
"tests/assets/GPL-3.0.rs".read_silently().unwrap(),
);
mod option_reader {
use aeruginous_io::OptionReader;

#[test]
fn method_result_equality_none() {
assert_eq!(
None::<&str>.read_loudly(&b"test"[..]).unwrap(),
None::<&str>.read_silently(&b"test"[..]).unwrap()
);
}

#[test]
fn method_result_equality_some() {
assert_eq!(
Some("tests/assets/GPL-3.0.rs")
.read_loudly(&b""[..])
.unwrap(),
Some("tests/assets/GPL-3.0.rs")
.read_silently(&b""[..])
.unwrap(),
);
}

#[test]
fn read_silently_success_none() {
assert_eq!(None::<&str>.read_silently(&b"test"[..]).unwrap(), "test\n");
}

assert_eq!(
"tests/assets/GPL-3.0.rs".read_silently().unwrap(),
"\
#[test]
fn read_silently_success_some() {
assert_eq!(
Some("tests/assets/GPL-3.0.rs")
.read_silently(&b""[..])
.unwrap(),
"\
/// Copyright (C) 2024 Kevin Matthes
///
/// This program is free software: you can redistribute it and/or modify
Expand All @@ -80,7 +158,8 @@ fn read_path_buf_like_success() {
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <https://www.gnu.org/licenses/>.
"
);
);
}
}

/******************************************************************************/

0 comments on commit 0bc03b1

Please sign in to comment.