use std::fmt::Display;
pub fn unwrap_io<T>(msg: &str, res: std::io::Result<T>) -> T {
let err = match res {
Ok(v) => return v,
Err(err) => err,
};
let code = err.raw_os_error().unwrap_or_else(|| {
eprintln!("note: the error code could not be found for this variant; reverting to -1...");
-1
});
err.into_user_err().print_all(msg).exit(code);
}
#[derive(Default)]
pub struct UserError {
message: String,
reasons: Vec<String>,
help: Vec<String>,
}
impl UserError {
fn enumerator<'a, I>(&self, i: I, first: &str, rest: &str)
where
I: IntoIterator<Item = &'a String>,
{
let mut it = i.into_iter();
if let Some(f) = it.next() {
eprintln!("{first}{f}");
}
for f in it {
eprintln!("{rest}{f}");
}
}
#[inline]
pub fn exit(&self, code: i32) -> ! {
std::process::exit(code);
}
pub fn print_all<D>(&self, prefix: D) -> &Self
where
D: Display,
{
eprintln!("{prefix}{}", self.message);
self.enumerator(&self.reasons, " - caused by: ", " | ");
self.enumerator(&self.help, " + help: ", " | ");
self
}
#[inline]
pub fn add_help(&mut self, help: impl Into<String>) {
self.help.push(help.into());
}
#[inline]
pub fn add_reason(&mut self, reason: impl Into<String>) {
self.reasons.push(reason.into());
}
#[inline]
pub fn and_help(mut self, help: impl Into<String>) -> Self {
self.help.push(help.into());
self
}
#[inline]
pub fn and_reason(mut self, reason: impl Into<String>) -> Self {
self.reasons.push(reason.into());
self
}
#[inline]
pub fn new(message: String) -> Self {
Self {
message,
reasons: Vec::new(),
help: Vec::new(),
}
}
#[inline]
pub fn from(message: &str) -> Self {
Self::new(message.to_string())
}
#[inline]
pub const fn message(&self) -> &String {
&self.message
}
#[inline]
pub const fn reasons(&self) -> &Vec<String> {
&self.reasons
}
#[inline]
pub fn reasons_mut(&mut self) -> &mut Vec<String> {
&mut self.reasons
}
#[inline]
pub const fn help(&self) -> &Vec<String> {
&self.help
}
#[inline]
pub fn help_mut(&mut self) -> &mut Vec<String> {
&mut self.help
}
}
pub trait IntoUserError {
fn into_user_err(self) -> UserError;
}
impl<D> IntoUserError for D
where
D: Display,
{
fn into_user_err(self) -> UserError {
UserError::new(self.to_string())
}
}
#[cfg(test)]
mod tests {
use crate::UserError;
#[test]
fn sample_error() {
UserError::from("could not open file")
.and_reason("The system cannot find the file specified.")
.and_reason("Filler reason.")
.and_help("Does this file exist?")
.and_help("Filler help.")
.print_all("program.exe: ");
}
}