1use std::{fmt, io};
2
3use crate::SharedString;
4
5pub type BoxedError = Box<dyn std::error::Error + Send + Sync + 'static>;
7
8#[derive(Debug)]
9pub(crate) enum ErrorKind {
10 NoDefaultValue,
12
13 Io(io::Error),
15
16 Conversion(BoxedError),
18
19 InvalidId,
21}
22
23impl From<io::Error> for ErrorKind {
24 fn from(err: io::Error) -> Self {
25 Self::Io(err)
26 }
27}
28
29impl From<BoxedError> for ErrorKind {
30 fn from(err: BoxedError) -> Self {
31 Self::Conversion(err)
32 }
33}
34
35impl From<ErrorKind> for BoxedError {
36 fn from(err: ErrorKind) -> Self {
37 match err {
38 ErrorKind::NoDefaultValue => Box::new(NoDefaultValueError),
39 ErrorKind::Io(err) => Box::new(err),
40 ErrorKind::Conversion(err) => err,
41 ErrorKind::InvalidId => Box::new(InvalidIdError),
42 }
43 }
44}
45
46impl ErrorKind {
47 pub fn or(self, other: Self) -> Self {
48 use ErrorKind::*;
49
50 match (self, other) {
51 (NoDefaultValue, other) => other,
52 (Io(_), other @ Conversion(_)) => other,
53 (Io(err), other @ Io(_)) if err.kind() == io::ErrorKind::NotFound => other,
54 (this, _) => this,
55 }
56 }
57}
58
59#[derive(Debug)]
60struct NoDefaultValueError;
61
62impl fmt::Display for NoDefaultValueError {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.write_str("the asset has neither extension nor default value")
65 }
66}
67
68impl std::error::Error for NoDefaultValueError {}
69
70#[derive(Debug)]
71struct InvalidIdError;
72
73impl fmt::Display for InvalidIdError {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 f.write_str("invalid id")
76 }
77}
78
79impl std::error::Error for InvalidIdError {}
80
81struct ErrorRepr {
82 id: SharedString,
83 error: BoxedError,
84}
85
86pub struct Error(Box<ErrorRepr>);
88
89impl Error {
90 #[cold]
91 pub(crate) fn new(id: SharedString, error: BoxedError) -> Self {
92 Self(Box::new(ErrorRepr { id, error }))
93 }
94
95 #[inline]
97 pub fn id(&self) -> &SharedString {
98 &self.0.id
99 }
100
101 #[inline]
103 pub fn reason(&self) -> &(dyn std::error::Error + 'static) {
104 &*self.0.error
105 }
106
107 #[inline]
109 pub fn into_inner(self) -> BoxedError {
110 self.0.error
111 }
112
113 #[inline]
115 pub fn downcast<E: std::error::Error + 'static>(mut self) -> Result<E, Self> {
116 match self.0.error.downcast() {
117 Ok(err) => Ok(*err),
118 Err(err) => {
119 self.0.error = err;
120 Err(self)
121 }
122 }
123 }
124}
125
126impl fmt::Debug for Error {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 f.debug_struct("Error")
129 .field("id", &self.0.id)
130 .field("error", &self.0.error)
131 .finish()
132 }
133}
134
135impl fmt::Display for Error {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 f.write_fmt(format_args!("failed to load \"{}\"", self.id()))
138 }
139}
140
141impl std::error::Error for Error {
142 #[inline]
143 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
144 Some(self.reason())
145 }
146}