Skip to content

Commit

Permalink
feat: Add CoerceResult
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmah309 committed Apr 7, 2024
1 parent aa0bd01 commit d594914
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 11 deletions.
113 changes: 109 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# error_set

A concise way to define errors and ergomically coerce a subset to a superset with with just `.into()`, or `?`.
[<img alt="github" src="https://img.shields.io/badge/github-mcmah309/error_set-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/mcmah309/error_set)
[<img alt="crates.io" src="https://img.shields.io/crates/v/error_set.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/error_set)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-error_set-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/error_set)
[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/mcmah309/error_set/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/mcmah309/error_set/actions?query=branch%3Amaster)


A concise way to define errors and ergomically coerce a subset into a superset with with just `.into()`, or `?`.

`error_set` was inspired by zig's [error set](https://ziglang.org/documentation/master/#Error-Set-Type)
and works functionally the same.
Expand Down Expand Up @@ -309,7 +315,7 @@ impl From<std::io::Error> for UploadError {
```
</details>

which is also equivlent to writing:
which is also equivalent to writing:
```rust
error_set! {
MediaError = {
Expand Down Expand Up @@ -339,7 +345,11 @@ error_set! {
```
Error enums and error variants can also accept doc comments and attributes like `#[derive(...)]`.

### Example
### Examples
<details>

<summary>Base Functionality In Action</summary>

```rust
fn main() {
let book_section_parsing_error = BookSectionParsingError::MissingNameArg;
Expand All @@ -353,4 +363,99 @@ fn main() {
let result_media_error: Result<(), MediaError> = result_download_error.coerce(); // `.coerce()` == `.map_err(Into::into)`
assert!(matches!(result_media_error, Err(MediaError::IoError(_))));
}
```
```
</details>

<details>
Here we can easily define all the different error states a function could exit with. Note this example is verbose as not all error states have downstream handlers that care about the error type, but imagine it so.
<summary>More Intricate Example</summary>

```rust
error_set::error_set! {
MediaError = {
IoError(std::io::Error)
} || BookParsingError || DownloadError || UploadError;
BookParsingError = {
MissingContent,
BookAccess(std::io::Error),
};
DownloadError = {
InvalidUrl,
CouldNotSaveBook(std::io::Error),
};
UploadError = {
MaximumUploadSizeReached,
TimedOut,
AuthenticationFailed,
};
}

fn parse_book(file_path: &str) -> Result<String, BookParsingError> {
let mut file = File::open(file_path).coerce::<BookParsingError>()?;
let mut content = String::new();
file.read_to_string(&mut content).coerce::<BookParsingError>()?;
if content.is_empty() {
Err(BookParsingError::MissingContent)
} else {
Ok(content)
}
}

fn download_book(url: &str, save_path: &str) -> Result<(), DownloadError> {
if url.is_empty() {
Err(DownloadError::InvalidUrl)
} else {
let simulated_book_content = "This is a downloaded book content.";
let mut file = File::create(save_path).coerce::<DownloadError>()?;
file.write_all(simulated_book_content.as_bytes()).coerce::<DownloadError>()?;
Ok(())
}
}

// Simulated function to upload the book content
fn upload_book(content: &str) -> Result<(), UploadError> {
let auth = true;
if !auth { // Simulate auth
return Err(UploadError::AuthenticationFailed);
}
let time_out = false;
if !time_out { // Simulate timeout uploading
return Err(UploadError::TimedOut);
}
if content.len() > 1024 { // Simulate an upload size limit
Err(UploadError::MaximumUploadSizeReached)
} else {
println!("Book uploaded successfully.");
Ok(())
}
}

fn process_book(download_path: &str, download_url: &str) -> Result<String, MediaError> {
download_book(download_url, download_path).coerce::<MediaError>()?;
let content = parse_book(download_path).coerce::<MediaError>()?;
const MAX_RETRIES: u8 = 3;
let mut current_retries = 0;
match upload_book(&content) {
Err(UploadError::TimedOut) => {
while current_retries < MAX_RETRIES {
current_retries += 1;
if let Ok(_) = upload_book(&content) {
break;
}
}
}
Err(e) => return Err(e.coerce()),
_ => (),
}
fs::remove_file(download_path).coerce::<MediaError>()?;
Ok(content)
}

fn main() {
match process_book("downloaded_book.txt", "http://example.com/book") {
Ok(content) => println!("Book processed successfully: {}", content),
Err(e) => eprintln!("An error occurred: {:?}", e),
}
}
```
</details>
12 changes: 7 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ pub trait Coerce<T> {
fn coerce(self) -> T;
}

impl<T, E1, E2> Coerce<Result<T, E2>> for Result<T, E1>
pub trait CoerceResult<T, E1> {
fn coerce<E2: From<E1> + std::error::Error>(self) -> Result<T,E2>;
}

impl<T, E1> CoerceResult<T,E1> for Result<T, E1>
where
E2: From<E1> + std::error::Error,
E1: std::error::Error,
{
#[inline(always)]
fn coerce(self) -> Result<T, E2> {
self.map_err(Into::into)
fn coerce<E2: From<E1> + std::error::Error>(self) -> Result<T, E2> {
self.map_err(Into::<E2>::into)
}
}

pub trait ErrorSetMarker {}

impl<E1, E2> Coerce<E2> for E1
Expand Down
4 changes: 2 additions & 2 deletions tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ pub mod readme_example_aggregation {
}

pub mod coerce_trait {
use error_set::{error_set, Coerce};
use error_set::{error_set, Coerce, CoerceResult};

error_set! {
MediaError = {
Expand Down Expand Up @@ -263,7 +263,7 @@ pub mod coerce_trait {
}

pub mod documentation {
use error_set::{error_set, Coerce};
use error_set::{error_set, Coerce, CoerceResult};

error_set! {
/// This is a MediaError doc
Expand Down

0 comments on commit d594914

Please sign in to comment.