1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use crate::ValueBox;
use thiserror::Error;
use user_error::{UserFacingError, UFE};
const SUMMARY_PREFIX: &str = "\u{001b}[97;41;22mError:\u{001b}[91;49;1m ";
const RESET: &str = "\u{001b}[0m";
const REASON_PREFIX: &str = "\u{001b}[93;49;1m - \u{001b}[97;49;1m";
#[derive(Error, Debug)]
pub enum BoxerError {
#[error("The pointer to the box of type {0} is null")]
NullPointer(String),
#[error("There is not value of type {0} in the box")]
NoValue(String),
#[error("There was an error")]
#[cfg(feature = "anyhow")]
AnyhowError(#[from] anyhow::Error),
#[error("There was an IO error")]
IOError(#[from] std::io::Error),
#[error("There was an error")]
AnyError(#[from] Box<dyn std::error::Error>),
}
impl<T> From<BoxerError> for core::result::Result<T, BoxerError> {
fn from(error: BoxerError) -> Self {
Err(error)
}
}
pub type Result<T> = core::result::Result<T, BoxerError>;
pub trait ReturnBoxerResult<Return> {
fn into_raw(self) -> *mut ValueBox<Return>;
fn log(self);
fn or_log(self, value: Return) -> Return;
fn or_print(self, value: Return) -> Return;
}
impl<Return> ReturnBoxerResult<Return> for Result<Return> {
fn into_raw(self) -> *mut ValueBox<Return> {
self.map(|value| ValueBox::new(value).into_raw())
.or_log(std::ptr::null_mut())
}
fn log(self) {
if let Err(error) = self {
log_boxer_error(error);
}
}
fn or_log(self, value: Return) -> Return {
self.unwrap_or_else(|error| {
log_boxer_error(error);
value
})
}
fn or_print(self, value: Return) -> Return {
self.map_err(|error| {
let error: Box<dyn std::error::Error> = Box::new(error);
let user_facing_error: UserFacingError = error.into();
user_facing_error
})
.unwrap_or_else(|error| {
println!("{}", pretty_summary(error.summary().as_str()));
if let Some(reasons) = pretty_reasons(error.reasons()) {
println!("{}", reasons);
}
value
})
}
}
fn log_boxer_error(error: BoxerError) {
match &error {
BoxerError::NullPointer(_) => warn_user_facing_error(to_user_facing_error(error)),
BoxerError::NoValue(_) => warn_user_facing_error(to_user_facing_error(error)),
_ => error_user_facing_error(to_user_facing_error(error)),
};
}
fn warn_user_facing_error(error: UserFacingError) {
warn!("{}", pretty_summary(error.summary().as_str()));
if let Some(reasons) = pretty_reasons(error.reasons()) {
warn!("{}", reasons);
}
}
fn error_user_facing_error(error: UserFacingError) {
error!("{}", pretty_summary(error.summary().as_str()));
if let Some(reasons) = pretty_reasons(error.reasons()) {
error!("{}", reasons);
}
}
fn to_user_facing_error(error: BoxerError) -> UserFacingError {
let error: Box<dyn std::error::Error> = Box::new(error);
let user_facing_error: UserFacingError = error.into();
user_facing_error
}
fn pretty_summary(summary: &str) -> String {
[SUMMARY_PREFIX, summary, RESET].concat()
}
fn pretty_reasons(reasons: Option<Vec<String>>) -> Option<String> {
reasons.map(|reasons| {
let mut reason_strings = Vec::with_capacity(reasons.len());
for reason in reasons {
let bullet_point = [REASON_PREFIX, &reason].concat();
reason_strings.push(bullet_point);
}
[&reason_strings.join("\n"), RESET].concat()
})
}