1use thiserror::Error;
11
12#[derive(Error, Debug, Clone, PartialEq)]
21pub enum ProcessError {
22 #[error("Processing error: {0}")]
24 Processing(String),
25
26 #[error("Parameter error: {0}")]
28 Parameter(String),
29
30 #[error("Buffer error: {0}")]
32 Buffer(String),
33
34 #[error("Type mismatch: expected {expected}, got {got}")]
36 TypeMismatch {
37 expected: &'static str,
39 got: &'static str,
41 },
42
43 #[error("Sample rate mismatch: expected {expected}, got {got}")]
45 SampleRateMismatch {
46 expected: f32,
48 got: f32,
50 },
51
52 #[error("Configuration error: {0}")]
54 Config(String),
55
56 #[error("Not initialized")]
58 NotInitialized,
59
60 #[error("Already initialized")]
62 AlreadyInitialized,
63
64 #[error("Unsupported operation: {0}")]
66 Unsupported(String),
67
68 #[error("Operation timed out")]
70 Timeout,
71
72 #[error("Realtime violation: {0}")]
75 RealtimeViolation(String),
76
77 #[error("Internal error: {0}")]
79 Internal(String),
80}
81
82pub type ProcessResult<T> = Result<T, ProcessError>;
84
85impl ProcessError {
86 pub fn processing(msg: impl Into<String>) -> Self {
88 Self::Processing(msg.into())
89 }
90
91 pub fn parameter(msg: impl Into<String>) -> Self {
93 Self::Parameter(msg.into())
94 }
95
96 pub fn buffer(msg: impl Into<String>) -> Self {
98 Self::Buffer(msg.into())
99 }
100
101 pub fn type_mismatch(expected: &'static str, got: &'static str) -> Self {
103 Self::TypeMismatch { expected, got }
104 }
105
106 pub fn sample_rate_mismatch(expected: f32, got: f32) -> Self {
108 Self::SampleRateMismatch { expected, got }
109 }
110
111 pub fn config(msg: impl Into<String>) -> Self {
113 Self::Config(msg.into())
114 }
115
116 pub fn unsupported(msg: impl Into<String>) -> Self {
118 Self::Unsupported(msg.into())
119 }
120
121 pub fn internal(msg: impl Into<String>) -> Self {
123 Self::Internal(msg.into())
124 }
125
126 pub fn is_recoverable(&self) -> bool {
131 match self {
132 Self::Processing(_) => true,
133 Self::Parameter(_) => true,
134 Self::Buffer(_) => true,
135 Self::TypeMismatch { .. } => false,
136 Self::SampleRateMismatch { .. } => false,
137 Self::Config(_) => false,
138 Self::NotInitialized => true,
139 Self::AlreadyInitialized => true,
140 Self::Unsupported(_) => false,
141 Self::Timeout => true,
142 Self::RealtimeViolation(_) => false,
143 Self::Internal(_) => false,
144 }
145 }
146
147 pub fn code(&self) -> &'static str {
149 match self {
150 Self::Processing(_) => "ERR_PROCESSING",
151 Self::Parameter(_) => "ERR_PARAMETER",
152 Self::Buffer(_) => "ERR_BUFFER",
153 Self::TypeMismatch { .. } => "ERR_TYPE_MISMATCH",
154 Self::SampleRateMismatch { .. } => "ERR_SAMPLE_RATE",
155 Self::Config(_) => "ERR_CONFIG",
156 Self::NotInitialized => "ERR_NOT_INIT",
157 Self::AlreadyInitialized => "ERR_ALREADY_INIT",
158 Self::Unsupported(_) => "ERR_UNSUPPORTED",
159 Self::Timeout => "ERR_TIMEOUT",
160 Self::RealtimeViolation(_) => "ERR_RT_VIOLATION",
161 Self::Internal(_) => "ERR_INTERNAL",
162 }
163 }
164}
165
166#[derive(Error, Debug, Clone, PartialEq)]
172pub enum ParameterError {
173 #[error("Parameter name cannot be empty")]
175 Empty,
176
177 #[error("Parameter name cannot contain '{0}'")]
179 InvalidCharacter(char),
180
181 #[error("Parameter name too long (max {max} characters)")]
183 TooLong {
184 max: usize,
186 },
187
188 #[error("Parameter name must start with a letter")]
190 MustStartWithLetter,
191
192 #[error("Parameter '{0}' not found")]
194 NotFound(String),
195
196 #[error("Parameter type mismatch: expected {expected:?}, got {got:?}")]
198 TypeMismatch {
199 expected: crate::traits::ParamType,
201 got: crate::traits::ParamType,
203 },
204
205 #[error("Value {value} out of range [{min}, {max}]")]
207 OutOfRange {
208 value: f32,
210 min: f32,
212 max: f32,
214 },
215
216 #[error("Invalid choice '{0}'")]
218 InvalidChoice(String),
219
220 #[error("Parameter '{0}' already exists")]
222 Duplicate(String),
223
224 #[error("Parameter '{0}' is read-only")]
226 ReadOnly(String),
227}
228
229pub type ParameterResult<T> = Result<T, ParameterError>;
231
232impl ParameterError {
233 pub fn not_found(name: impl Into<String>) -> Self {
235 Self::NotFound(name.into())
236 }
237
238 pub fn type_mismatch(
240 expected: crate::traits::ParamType,
241 got: crate::traits::ParamType,
242 ) -> Self {
243 Self::TypeMismatch { expected, got }
244 }
245
246 pub fn out_of_range(value: f32, min: f32, max: f32) -> Self {
248 Self::OutOfRange { value, min, max }
249 }
250
251 pub fn invalid_choice(choice: impl Into<String>) -> Self {
253 Self::InvalidChoice(choice.into())
254 }
255
256 pub fn duplicate(name: impl Into<String>) -> Self {
258 Self::Duplicate(name.into())
259 }
260
261 pub fn read_only(name: impl Into<String>) -> Self {
263 Self::ReadOnly(name.into())
264 }
265}
266
267#[derive(Error, Debug, Clone, PartialEq)]
273pub enum ClockError {
274 #[error("Hardware error: {0}")]
276 Hardware(String),
277
278 #[error("Invalid sample rate: {0}")]
280 InvalidSampleRate(f32),
281
282 #[error("Clock not started")]
284 NotStarted,
285
286 #[error("Clock already started")]
288 AlreadyStarted,
289
290 #[error("Clock underflow")]
292 Underflow,
293
294 #[error("Clock overflow")]
296 Overflow,
297}
298
299pub type ClockResult<T> = Result<T, ClockError>;
301
302impl From<ParameterError> for ProcessError {
307 fn from(err: ParameterError) -> Self {
308 match err {
309 ParameterError::NotFound(name) => {
310 Self::parameter(format!("Parameter not found: {}", name))
311 }
312 ParameterError::TypeMismatch { expected, got } => {
313 Self::type_mismatch(expected.name(), got.name())
314 }
315 ParameterError::OutOfRange { value, min, max } => {
316 Self::parameter(format!("Value {} out of range [{}, {}]", value, min, max))
317 }
318 ParameterError::InvalidChoice(choice) => {
319 Self::parameter(format!("Invalid choice: {}", choice))
320 }
321 ParameterError::Duplicate(name) => {
322 Self::parameter(format!("Duplicate parameter: {}", name))
323 }
324 ParameterError::ReadOnly(name) => {
325 Self::parameter(format!("Parameter is read-only: {}", name))
326 }
327 _ => Self::parameter(err.to_string()),
328 }
329 }
330}
331
332impl From<ClockError> for ProcessError {
333 fn from(err: ClockError) -> Self {
334 match err {
335 ClockError::Hardware(msg) => Self::processing(format!("Hardware error: {}", msg)),
336 ClockError::InvalidSampleRate(sr) => {
337 Self::config(format!("Invalid sample rate: {}", sr))
338 }
339 ClockError::NotStarted => Self::processing("Clock not started"),
340 ClockError::AlreadyStarted => Self::processing("Clock already started"),
341 ClockError::Underflow => Self::buffer("Clock underflow"),
342 ClockError::Overflow => Self::buffer("Clock overflow"),
343 }
344 }
345}
346
347impl From<std::io::Error> for ProcessError {
348 fn from(err: std::io::Error) -> Self {
349 Self::Processing(format!("IO error: {}", err))
350 }
351}
352
353impl From<crate::error::Error> for ProcessError {
354 fn from(err: crate::error::Error) -> Self {
355 Self::Processing(err.to_string())
356 }
357}
358
359#[cfg(test)]
364mod tests {
365 use super::*;
366
367 #[test]
368 fn test_process_error_creation() {
369 let err = ProcessError::processing("test error");
370 assert!(matches!(err, ProcessError::Processing(_)));
371 assert_eq!(err.code(), "ERR_PROCESSING");
372 assert!(err.is_recoverable());
373 }
374
375 #[test]
376 fn test_parameter_error_creation() {
377 let err = ParameterError::not_found("gain");
378 assert!(matches!(err, ParameterError::NotFound(_)));
379
380 let err = ParameterError::out_of_range(2.0, 0.0, 1.0);
381 assert!(matches!(err, ParameterError::OutOfRange { value: 2.0, .. }));
382 }
383
384 #[test]
385 fn test_error_conversions() {
386 let param_err = ParameterError::not_found("test");
387 let proc_err: ProcessError = param_err.into();
388 assert!(matches!(proc_err, ProcessError::Parameter(_)));
389
390 let clock_err = ClockError::Underflow;
391 let proc_err: ProcessError = clock_err.into();
392 assert!(matches!(proc_err, ProcessError::Buffer(_)));
393 }
394
395 #[test]
396 fn test_recoverable_flags() {
397 assert!(ProcessError::processing("test").is_recoverable());
398 assert!(ProcessError::parameter("test").is_recoverable());
399 assert!(ProcessError::buffer("test").is_recoverable());
400 }
401
402 #[test]
403 fn test_error_codes() {
404 assert_eq!(ProcessError::processing("").code(), "ERR_PROCESSING");
405 }
406
407 #[test]
408 fn test_parameter_error_details() {
409 let err = ParameterError::out_of_range(1.5, 0.0, 1.0);
410 match err {
411 ParameterError::OutOfRange { value, min, max } => {
412 assert_eq!(value, 1.5);
413 assert_eq!(min, 0.0);
414 assert_eq!(max, 1.0);
415 }
416 _ => panic!("Wrong error type"),
417 }
418 }
419}