This is an improved form of the previous `error::Unexpected` type, now with more capabilities and generally better naming.main
parent
42a033a50b
commit
420f1ff42a
@ -1,3 +1 @@ |
||||
- expect statements (pretend it's "expected", not "expect") |
||||
- map_unexpected annotation string |
||||
- clean up main a lot |
||||
|
@ -1,56 +1 @@ |
||||
use std::error; |
||||
use std::fmt; |
||||
use std::fmt::Write; |
||||
|
||||
#[derive(Debug, Clone)] |
||||
/// Unexpected is a String which implements the Error trait. It is intended to be used in
|
||||
/// situations where the caller is being given an error they can't really handle, except to pass it
|
||||
/// along or log it.
|
||||
///
|
||||
/// The error is intended to also implement Send + Sync + Clone, such that it is easy to use in
|
||||
/// async situations.
|
||||
pub struct Unexpected(String); |
||||
|
||||
impl fmt::Display for Unexpected { |
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||
write!(f, "Unexpected error occurred: {}", self.0) |
||||
} |
||||
} |
||||
|
||||
impl error::Error for Unexpected {} |
||||
|
||||
impl From<&str> for Unexpected { |
||||
fn from(s: &str) -> Self { |
||||
Unexpected(s.to_string()) |
||||
} |
||||
} |
||||
|
||||
pub trait ToUnexpected { |
||||
fn to_unexpected(&self) -> Unexpected; |
||||
} |
||||
|
||||
impl<E: error::Error> ToUnexpected for E { |
||||
fn to_unexpected(&self) -> Unexpected { |
||||
let mut w = String::new(); |
||||
write!(w, "{}", self.to_string()).expect("underlying error formatted correctly"); |
||||
|
||||
if let Some(src) = self.source() { |
||||
write!(w, " [{src}]").expect("underyling source formatted correctly"); |
||||
} |
||||
|
||||
Unexpected(w.to_string()) |
||||
} |
||||
} |
||||
|
||||
pub trait MapUnexpected<T> { |
||||
fn map_unexpected(self) -> Result<T, Unexpected>; |
||||
} |
||||
|
||||
impl<T, E: ToUnexpected> MapUnexpected<T> for Result<T, E> { |
||||
fn map_unexpected(self) -> Result<T, Unexpected> { |
||||
match self { |
||||
Ok(t) => Ok(t), |
||||
Err(err) => Err(err.to_unexpected()), |
||||
} |
||||
} |
||||
} |
||||
pub mod unexpected; |
||||
|
@ -0,0 +1,128 @@ |
||||
use std::fmt::Write; |
||||
use std::{error, fmt}; |
||||
|
||||
#[derive(Debug, Clone)] |
||||
/// Error is a String which implements the Error trait. It is intended to be used in
|
||||
/// situations where the caller is being given an error they can't really handle, except to pass it
|
||||
/// along or log it.
|
||||
///
|
||||
/// The error is intended to also implement Send + Sync + Clone, such that it is easy to use in
|
||||
/// async situations.
|
||||
pub struct Error(String); |
||||
|
||||
impl Error { |
||||
fn from_displays<D1, D2, D3>(prefix: Option<D1>, body: &D2, source: Option<D3>) -> Error |
||||
where |
||||
D1: fmt::Display, |
||||
D2: fmt::Display + ?Sized, |
||||
D3: fmt::Display, |
||||
{ |
||||
let mut w = String::new(); |
||||
|
||||
if let Some(prefix) = prefix { |
||||
write!(w, "{prefix}").expect("error writing prefix"); |
||||
} |
||||
|
||||
write!(w, "{body}").expect("error formatting body"); |
||||
|
||||
if let Some(source) = source { |
||||
write!(w, " [{source}]").expect("error formatting source"); |
||||
} |
||||
|
||||
Error(w.to_string()) |
||||
} |
||||
|
||||
fn from_err<E, D>(prefix: Option<D>, e: E) -> Error |
||||
where |
||||
E: error::Error, |
||||
D: fmt::Display, |
||||
{ |
||||
return Error::from_displays(prefix, &e, e.source()); |
||||
} |
||||
|
||||
pub fn from(s: &str) -> Error { |
||||
Error::from_displays(None::<String>, s, None::<String>) |
||||
} |
||||
} |
||||
|
||||
impl fmt::Display for Error { |
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
||||
write!(f, "Unexpected error occurred: {}", self.0) |
||||
} |
||||
} |
||||
|
||||
impl error::Error for Error {} |
||||
|
||||
pub trait Mappable<T> { |
||||
/// or_unexpected returns an Err(Error) wrapping self's Err, or the Ok value of self.
|
||||
fn or_unexpected(self) -> Result<T, Error>; |
||||
|
||||
/// or_unexpected_while is like or_unexpected, but will prefix the error message. The prefix
|
||||
/// should be worded as if it started with the word "while", e.g.: `opening file {path}`.
|
||||
fn or_unexpected_while<D: fmt::Display>(self, prefix: D) -> Result<T, Error>; |
||||
|
||||
/// map_unexpected_while is like or_unexpected_while, but uses a closure to produce the error
|
||||
/// prefix.
|
||||
fn map_unexpected_while<F, D>(self, f: F) -> Result<T, Error> |
||||
where |
||||
F: FnOnce() -> D, |
||||
D: fmt::Display; |
||||
} |
||||
|
||||
fn map_unexpected_maybe_while<T, E, F, D>( |
||||
res: Result<T, E>, |
||||
prefix_fn: Option<F>, |
||||
) -> Result<T, Error> |
||||
where |
||||
E: error::Error, |
||||
F: FnOnce() -> D, |
||||
D: std::fmt::Display, |
||||
{ |
||||
match res { |
||||
Ok(res) => Ok(res), |
||||
Err(err) => Err(Error::from_err(prefix_fn.map(|prefix_fn| prefix_fn()), err)), |
||||
} |
||||
} |
||||
|
||||
impl<T, E: error::Error> Mappable<T> for Result<T, E> { |
||||
fn or_unexpected(self) -> Result<T, Error> { |
||||
let no_fn = None::<Box<dyn FnOnce() -> Box<dyn fmt::Display>>>; // lol, good job rust
|
||||
map_unexpected_maybe_while(self, no_fn) |
||||
} |
||||
|
||||
fn or_unexpected_while<D: fmt::Display>(self, prefix: D) -> Result<T, Error> { |
||||
map_unexpected_maybe_while(self, Some(|| prefix)) |
||||
} |
||||
|
||||
fn map_unexpected_while<F, D>(self, f: F) -> Result<T, Error> |
||||
where |
||||
F: FnOnce() -> D, |
||||
D: fmt::Display, |
||||
{ |
||||
map_unexpected_maybe_while(self, Some(f)) |
||||
} |
||||
} |
||||
|
||||
pub trait Intoable { |
||||
fn into_unexpected(self) -> Error; |
||||
|
||||
/// into_unexpected_while is like to_unexpected, but will prefix the Error error. The
|
||||
/// prefix should be worded as if it started with the word "while", e.g.: `opening file
|
||||
/// {path}`.
|
||||
fn into_unexpected_while<D>(self, prefix: D) -> Error |
||||
where |
||||
D: fmt::Display; |
||||
} |
||||
|
||||
impl<E: error::Error> Intoable for E { |
||||
fn into_unexpected(self) -> Error { |
||||
Error::from_err(None::<String>, self) |
||||
} |
||||
|
||||
fn into_unexpected_while<D>(self, prefix: D) -> Error |
||||
where |
||||
D: fmt::Display, |
||||
{ |
||||
Error::from_err(Some(prefix), self) |
||||
} |
||||
} |
@ -0,0 +1,11 @@ |
||||
use std::{fs, io, path}; |
||||
|
||||
pub fn open_file(path: &path::Path) -> io::Result<Option<fs::File>> { |
||||
match fs::File::open(path) { |
||||
Ok(file) => Ok(Some(file)), |
||||
Err(err) => match err.kind() { |
||||
io::ErrorKind::NotFound => Ok(None), |
||||
_ => Err(err), |
||||
}, |
||||
} |
||||
} |
Loading…
Reference in new issue