Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhancement] Append Data to PathBuf-like Instances #111

Merged
merged 7 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
(
references: {},
changes: {
"Added": [
"PathBufLikeAppendix::append_loudly",
"PathBufLikeAppendix",
"PathBufLikeAppendix::append_silently",
"tests for PathBufLikeAppendix",
],
"Changed": [
"MSRV: 1.76.0",
],
},
)
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,9 @@ mod writing;
pub use reading::{
BufReadReader, OptionReader, PathBufLikeReader, VectorReader,
};
pub use writing::{OptionTruncation, PathBufLikeTruncation, Writer};
pub use writing::{
OptionTruncation, PathBufLikeAppendix, PathBufLikeTruncation, Writer,
};

/// This crate's name.
pub const NAME: &str = "aeruginous-io";
Expand Down
101 changes: 101 additions & 0 deletions src/writing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,107 @@
use std::{io::Write, path::PathBuf};
use sysexits::Result;

/// Append to the files given as instances convertible to a
/// [`std::path::PathBuf`].
pub trait PathBufLikeAppendix<T>
where
PathBuf: From<T>,
{
/// Append the data this method is called on to the given destination.
///
/// This method behaves just like
/// [`crate::PathBufLikeAppendix::append_silently`] despite also printing
/// error messages to [`std::io::Stderr`].
///
/// # Errors
///
/// See [`sysexits::ExitCode`].
fn append_loudly(self, destination: T) -> Result<()>;

/// Append the data this method is called on to the given destination.
///
/// The data this method is called on will be converted to a [`String`] and
/// appended to the given file. The data therefore needs to implement
/// [`ToString`]. The file needs to be convertible to a
/// [`std::path::PathBuf`]. The data will be appended at the end of the
/// file, already existing data will not be changed. In case that the file
/// should not already exist, it will be created before writing to it.
///
/// The return value is either the unit type, 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 append_silently(self, destination: T) -> Result<()>;
}

impl<P, T: ToString> PathBufLikeAppendix<P> for T
where
PathBuf: From<P>,
{
fn append_loudly(self, destination: P) -> Result<()> {
match std::fs::File::options()
.append(true)
.create(true)
.truncate(false)
.open(PathBuf::from(destination))
{
Err(e) => {
eprintln!("{e}");
Err(e.into())
}
Ok(mut file) => {
let bytes = self.to_string().as_bytes().to_vec();

match file.write(&bytes) {
Err(e) => {
eprintln!("{e}");
Err(e.into())
}
Ok(n) => {
if n == bytes.len() {
Ok(())
} else {
eprintln!(
"Creating an exact copy was not possible."
);
Err(sysexits::ExitCode::IoErr)
}
}
}
}
}
}

fn append_silently(self, destination: P) -> Result<()> {
match std::fs::File::options()
.append(true)
.create(true)
.truncate(false)
.open(PathBuf::from(destination))
{
Err(e) => Err(e.into()),
Ok(mut file) => {
let bytes = self.to_string().as_bytes().to_vec();

match file.write(&bytes) {
Err(e) => Err(e.into()),
Ok(n) => {
if n == bytes.len() {
Ok(())
} else {
Err(sysexits::ExitCode::IoErr)
}
}
}
}
}
}
}

/// Truncate files given as instances convertible to a [`std::path::PathBuf`].
pub trait PathBufLikeTruncation<T>
where
Expand Down
82 changes: 41 additions & 41 deletions tests/reading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,41 +53,40 @@ mod buf_read_reader {
}
}

mod path_buf_like_reader {
use aeruginous_io::PathBufLikeReader;
mod option_reader {
use aeruginous_io::OptionReader;

#[test]
fn method_result_equality() {
fn method_result_equality_none() {
assert_eq!(
"tests/assets/GPL-3.0.rs".read_loudly().unwrap(),
"tests/assets/GPL-3.0.rs".read_silently().unwrap(),
None::<&str>.read_loudly(&b"test"[..]).unwrap(),
None::<&str>.read_silently(&b"test"[..]).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());
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_failure_file_does_not_exist() {
assert!("no_such_file.txt".read_silently().is_err());
fn read_silently_success_none() {
assert_eq!(None::<&str>.read_silently(&b"test"[..]).unwrap(), "test\n");
}

#[test]
fn read_silently_success() {
fn read_silently_success_some() {
assert_eq!(
"tests/assets/GPL-3.0.rs".read_silently().unwrap(),
Some("tests/assets/GPL-3.0.rs")
.read_silently(&b""[..])
.unwrap(),
"\
/// Copyright (C) 2024 Kevin Matthes
///
Expand All @@ -108,40 +107,41 @@ mod path_buf_like_reader {
}
}

mod option_reader {
use aeruginous_io::OptionReader;
mod path_buf_like_reader {
use aeruginous_io::PathBufLikeReader;

#[test]
fn method_result_equality_none() {
fn method_result_equality() {
assert_eq!(
None::<&str>.read_loudly(&b"test"[..]).unwrap(),
None::<&str>.read_silently(&b"test"[..]).unwrap()
"tests/assets/GPL-3.0.rs".read_loudly().unwrap(),
"tests/assets/GPL-3.0.rs".read_silently().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(),
);
fn read_loudly_failure_attempt_to_read_directory() {
assert!(".github/".read_loudly().is_err());
}

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

#[test]
fn read_silently_success_some() {
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!(
Some("tests/assets/GPL-3.0.rs")
.read_silently(&b""[..])
.unwrap(),
"tests/assets/GPL-3.0.rs".read_silently().unwrap(),
"\
/// Copyright (C) 2024 Kevin Matthes
///
Expand Down
38 changes: 38 additions & 0 deletions tests/writing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,44 @@
| |
\******************************************************************************/

mod path_buf_like_appendix {
use aeruginous_io::{PathBufLikeAppendix, PathBufLikeReader};

#[test]
fn append_loudly_failure() {
assert!(String::new().append_loudly("tests/").is_err());
}

#[test]
fn append_loudly_success() {
let f = "path_buf_like_appendix_append_loudly_success.txt";

assert!("test line 1\n".append_loudly(f).is_ok());
assert_eq!(f.read_silently().unwrap(), "test line 1\n");
assert!("test line 2\n".append_loudly(f).is_ok());
assert_eq!(f.read_silently().unwrap(), "test line 1\ntest line 2\n");

std::fs::remove_file(f).unwrap();
}

#[test]
fn append_silently_failure() {
assert!(String::new().append_silently("tests/").is_err());
}

#[test]
fn append_silently_success() {
let f = "path_buf_like_appendix_append_silently_success.txt";

assert!("test line 1\n".append_silently(f).is_ok());
assert_eq!(f.read_silently().unwrap(), "test line 1\n");
assert!("test line 2\n".append_silently(f).is_ok());
assert_eq!(f.read_silently().unwrap(), "test line 1\ntest line 2\n");

std::fs::remove_file(f).unwrap();
}
}

mod path_buf_like_io {
use aeruginous_io::{PathBufLikeReader, PathBufLikeTruncation};

Expand Down
Loading