1use std::{backtrace::Backtrace, io::Error as ioError, path::Path};
6
7pub type Result<T> = std::result::Result<T, Error>;
8
9macro_rules! fn_string {
11 ($fn_name:ident, $fortype:expr) => {
12 #[doc = concat!("Create a new [Self] as [", stringify!($fortype), "]")]
13 pub fn $fn_name<M>(msg: M) -> Self
14 where
15 M: Into<String>,
16 {
17 return Self::new($fortype(msg.into()));
18 }
19 };
20}
21
22#[derive(Debug)]
24pub struct Error {
25 source: ErrorEnum,
27 #[cfg(feature = "backtrace")]
28 backtrace: Backtrace,
30}
31
32impl Error {
33 pub fn new(source: ErrorEnum) -> Self {
35 Self {
36 source,
37 #[cfg(feature = "backtrace")]
38 backtrace: Backtrace::capture(),
39 }
40 }
41
42 #[cfg(feature = "backtrace")]
43 pub fn get_backtrace(&self) -> &Backtrace {
45 &self.backtrace
46 }
47
48 fn_string!(other, ErrorEnum::Other);
49 fn_string!(
50 unsupported_schema_format,
51 ErrorEnum::UnsupportedSchemaFormat
52 );
53 fn_string!(unsupported_type, ErrorEnum::UnsupportedType);
54 fn_string!(no_file_signature, ErrorEnum::NoFileSignature);
55
56 pub fn custom_ioerror_path<M, P>(kind: std::io::ErrorKind, msg: M, path: P) -> Self
58 where
59 M: Into<String>,
60 P: AsRef<Path>,
61 {
62 return Self::new(ErrorEnum::IoError(
63 ioError::new(kind, msg.into()),
64 format_path(path.as_ref().to_string_lossy().to_string()),
65 ));
66 }
67
68 pub fn not_a_directory<M, P>(msg: M, path: P) -> Self
69 where
70 M: Into<String>,
71 P: AsRef<Path>,
72 {
73 return Self::new(ErrorEnum::NotADirectory(
74 msg.into(),
75 path.as_ref().to_string_lossy().to_string(),
76 ));
77 }
78}
79
80impl std::fmt::Display for Error {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 self.source.fmt(f)
83 }
84}
85
86impl std::error::Error for Error {
87 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
88 return self.source.source();
89 }
90}
91
92impl<T> From<T> for Error
94where
95 T: Into<ErrorEnum>,
96{
97 fn from(value: T) -> Self {
98 Self::new(value.into())
99 }
100}
101
102#[derive(thiserror::Error, Debug)]
104pub enum ErrorEnum {
105 #[error("IoError: {0}; {1}")]
108 IoError(std::io::Error, String),
109 #[error("NotADirectory: {0}; Path: \"{1}\"")]
112 NotADirectory(String, String),
113 #[error("UnsupportedSchemaFormat: {0}")]
115 UnsupportedSchemaFormat(String),
116 #[error("UnsupportedType: {0}")]
118 UnsupportedType(String),
119 #[error("NoFileSignature: {0}")]
121 NoFileSignature(String),
122
123 #[error("Other: {0}")]
125 Other(String),
126}
127
128#[inline]
130fn format_path(msg: String) -> String {
131 format!("Path \"{}\"", msg)
132}
133
134pub trait IOErrorToError<T> {
136 fn attach_path_err<P: AsRef<Path>>(self, path: P) -> Result<T>;
138
139 fn attach_path_msg<P: AsRef<Path>, M: AsRef<str>>(self, path: P, msg: M) -> Result<T>;
141}
142
143impl<T> IOErrorToError<T> for std::result::Result<T, std::io::Error> {
144 fn attach_path_err<P: AsRef<Path>>(self, path: P) -> Result<T> {
145 return match self {
146 Ok(v) => Ok(v),
147 Err(e) => Err(crate::Error::new(ErrorEnum::IoError(
148 e,
149 format_path(path.as_ref().to_string_lossy().to_string()),
150 ))),
151 };
152 }
153
154 fn attach_path_msg<P: AsRef<Path>, M: AsRef<str>>(self, path: P, msg: M) -> Result<T> {
155 match self {
156 Ok(v) => Ok(v),
157 Err(e) => Err(crate::Error::new(ErrorEnum::IoError(
158 e,
159 format!(
160 "{msg} {path}",
161 msg = msg.as_ref(),
162 path = format_path(path.as_ref().to_string_lossy().to_string())
163 ),
164 ))),
165 }
166 }
167}