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