1#[cfg(feature = "alloc")]
8use alloc::{boxed::Box, string::String};
9
10pub type Result<T = ()> = core::result::Result<T, Error>;
12
13#[derive(Debug, thiserror::Error)]
15#[non_exhaustive]
16pub enum Error {
17 #[error("Attempted to use an invalid accidental")]
18 InvalidAccidental,
19 #[error("Attempted to name an invalid pitch class: {0}")]
20 InvalidPitchClass(isize),
21 #[error("Mismatched pitch classes, received {0} while expecting {1}")]
22 MismatchedPitchClasses(isize, isize),
23 #[error("Unable to parse the string into the configured type")]
24 FromStrParseError,
25 #[error("Unable to parse the string into the desired pitch class: {0}")]
26 InvalidPitchClassParse(&'static str),
27 #[error("Mismatched symbols")]
28 MismatchedSymbols,
29 #[error("Invalid Chord")]
30 InvalidChord,
31 #[cfg(feature = "alloc")]
32 #[error("Invalid Intervals: {0}")]
33 IncompatibleIntervals(String),
34 #[error("Invalid Note")]
35 InvalidNote,
36 #[error(transparent)]
38 AnyError(#[from] anyhow::Error),
39 #[error(transparent)]
40 #[cfg(feature = "serde_json")]
41 JsonError(#[from] serde_json::Error),
42 #[error(transparent)]
44 AddrParseError(#[from] core::net::AddrParseError),
45 #[error("The impossible has occurred")]
46 Infallible(#[from] core::convert::Infallible),
47 #[error(transparent)]
48 FmtError(#[from] core::fmt::Error),
49 #[error(transparent)]
50 Utf8Error(#[from] core::str::Utf8Error),
51 #[cfg(feature = "std")]
53 #[error(transparent)]
54 IOError(#[from] std::io::Error),
55 #[cfg(feature = "alloc")]
57 #[error(transparent)]
58 BoxError(#[from] Box<dyn core::error::Error + Send + Sync>),
59 #[cfg(feature = "alloc")]
60 #[error("Unknown Error: {0}")]
61 Unknown(String),
62}
63
64impl Error {
65 #[cfg(feature = "alloc")]
67 pub fn boxed<E>(err: E) -> Self
68 where
69 E: core::error::Error + Send + Sync + 'static,
70 {
71 Error::BoxError(Box::new(err))
72 }
73 #[cfg(feature = "alloc")]
75 pub fn unknown<E>(err: E) -> Self
76 where
77 E: alloc::string::ToString,
78 {
79 Error::Unknown(err.to_string())
80 }
81}
82
83#[cfg(feature = "alloc")]
84impl From<&str> for Error {
85 fn from(s: &str) -> Self {
86 Error::unknown(s)
87 }
88}
89
90#[cfg(feature = "alloc")]
91impl From<String> for Error {
92 fn from(s: String) -> Self {
93 Error::Unknown(s)
94 }
95}
96
97#[doc(hidden)]
98pub mod custom {
99
100 #[cfg(feature = "alloc")]
101 use alloc::boxed::Box;
102
103 #[derive(
104 Clone,
105 Copy,
106 Debug,
107 Default,
108 Eq,
109 Hash,
110 Ord,
111 PartialEq,
112 PartialOrd,
113 strum::AsRefStr,
114 strum::Display,
115 strum::EnumCount,
116 strum::EnumString,
117 strum::VariantNames,
118 )]
119 #[cfg_attr(
120 feature = "serde",
121 derive(serde::Deserialize, serde::Serialize),
122 serde(rename_all = "snake_case")
123 )]
124 #[non_exhaustive]
125 pub enum ErrorKind {
126 InvalidAccidental,
127 ChordError,
128 NoteError,
129 PitchError,
130 InvalidPitchClass,
131 MismatchedPitchClasses,
132 FromStrParseError,
133 InvalidPitchClassParse,
134 MismatchedSymbols,
135 InvalidChord,
136 IncompatibleIntervals,
137 InvalidNote,
138 Utf8Error,
139 AnyError,
140 JsonError,
141 AddrParseError,
142 Infallible,
143 FmtError,
144 None,
145 IOError,
146 #[default]
147 Unknown,
148 }
149
150 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
151 pub struct ErrorBase<E = Box<dyn core::error::Error + 'static>> {
152 pub(crate) kind: ErrorKind,
153 pub(crate) source: Option<E>,
154 }
155
156 impl<E> ErrorBase<E>
157 where
158 E: core::error::Error,
159 {
160 pub const fn new(kind: ErrorKind, source: Option<E>) -> Self {
161 Self { kind, source }
162 }
163
164 pub const fn unknown(source: E) -> Self {
165 Self {
166 kind: ErrorKind::Unknown,
167 source: Some(source),
168 }
169 }
170 pub fn with_kind(self, kind: ErrorKind) -> ErrorBase<E> {
172 ErrorBase {
173 kind,
174 source: self.source,
175 }
176 }
177 pub fn with_source<E2>(self, source: E2) -> ErrorBase<E2>
179 where
180 E2: core::error::Error,
181 {
182 ErrorBase {
183 kind: self.kind,
184 source: Some(source),
185 }
186 }
187 }
188
189 impl<E> core::fmt::Display for ErrorBase<E>
190 where
191 E: 'static + core::error::Error,
192 {
193 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
194 if let Some(source) = &self.source {
195 write!(f, "{}: {}", self.kind, source)
196 } else {
197 write!(f, "{}", self.kind)
198 }
199 }
200 }
201
202 impl<E> core::error::Error for ErrorBase<E>
203 where
204 E: 'static + core::error::Error,
205 {
206 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
207 self.source
208 .as_ref()
209 .map(|e| e as &(dyn core::error::Error + 'static))
210 }
211 }
212
213 impl<T> From<Option<T>> for ErrorBase
214 where
215 T: core::error::Error + 'static,
216 {
217 fn from(opt: Option<T>) -> Self {
218 match opt {
219 Some(err) => Self {
220 kind: ErrorKind::Unknown,
221 source: Some(Box::new(err)),
222 },
223 None => Self {
224 kind: ErrorKind::None,
225 source: None,
226 },
227 }
228 }
229 }
230
231 impl From<core::str::Utf8Error> for ErrorBase {
232 fn from(err: core::str::Utf8Error) -> Self {
233 Self {
234 kind: ErrorKind::Utf8Error,
235 #[cfg(feature = "alloc")]
236 source: Some(Box::new(err)),
237 }
238 }
239 }
240
241 #[cfg(feature = "serde_json")]
242 impl From<serde_json::Error> for ErrorBase {
243 fn from(err: serde_json::Error) -> Self {
244 Self {
245 kind: ErrorKind::JsonError,
246 source: Some(Box::new(err)),
247 }
248 }
249 }
250}