Skip to main content

obniz_rust/
error.rs

1use std::fmt;
2
3/// Custom error types for the obniz library
4#[derive(Debug)]
5pub enum ObnizError {
6    /// Connection errors
7    Connection(String),
8
9    /// WebSocket communication errors
10    WebSocket(String),
11
12    /// IO operation errors
13    IoOperation(String),
14
15    /// Invalid pin number (valid range: 0-11)
16    InvalidPin(u8),
17
18    /// JSON parsing errors
19    JsonParse(String),
20
21    /// Response timeout
22    Timeout,
23
24    /// Callback registration errors
25    CallbackError(String),
26
27    /// Device not found or invalid device ID
28    DeviceNotFound(String),
29
30    /// Permission denied
31    PermissionDenied,
32
33    /// Generic error with message
34    Generic(String),
35}
36
37impl fmt::Display for ObnizError {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            ObnizError::Connection(msg) => write!(f, "Connection error: {msg}"),
41            ObnizError::WebSocket(msg) => write!(f, "WebSocket error: {msg}"),
42            ObnizError::IoOperation(msg) => write!(f, "IO operation error: {msg}"),
43            ObnizError::InvalidPin(pin) => {
44                write!(f, "Invalid pin number: {pin}. Valid range is 0-11")
45            }
46            ObnizError::JsonParse(msg) => write!(f, "JSON parse error: {msg}"),
47            ObnizError::Timeout => write!(f, "Operation timed out"),
48            ObnizError::CallbackError(msg) => write!(f, "Callback error: {msg}"),
49            ObnizError::DeviceNotFound(id) => write!(f, "Device not found: {id}"),
50            ObnizError::PermissionDenied => write!(f, "Permission denied"),
51            ObnizError::Generic(msg) => write!(f, "Error: {msg}"),
52        }
53    }
54}
55
56impl std::error::Error for ObnizError {}
57
58impl From<anyhow::Error> for ObnizError {
59    fn from(err: anyhow::Error) -> Self {
60        ObnizError::Generic(err.to_string())
61    }
62}
63
64impl From<serde_json::Error> for ObnizError {
65    fn from(err: serde_json::Error) -> Self {
66        ObnizError::JsonParse(err.to_string())
67    }
68}
69
70impl From<tokio_tungstenite::tungstenite::Error> for ObnizError {
71    fn from(err: tokio_tungstenite::tungstenite::Error) -> Self {
72        ObnizError::WebSocket(err.to_string())
73    }
74}
75
76impl From<tokio::sync::oneshot::error::RecvError> for ObnizError {
77    fn from(_: tokio::sync::oneshot::error::RecvError) -> Self {
78        ObnizError::Timeout
79    }
80}
81
82impl From<tokio::sync::mpsc::error::SendError<crate::obniz::ObnizCommand>> for ObnizError {
83    fn from(err: tokio::sync::mpsc::error::SendError<crate::obniz::ObnizCommand>) -> Self {
84        ObnizError::Connection(format!("Failed to send command: {err}"))
85    }
86}
87
88/// Result type alias for obniz operations
89pub type ObnizResult<T> = Result<T, ObnizError>;
90
91/// Validation helpers
92pub fn validate_pin(pin: u8) -> ObnizResult<()> {
93    if pin > 11 {
94        Err(ObnizError::InvalidPin(pin))
95    } else {
96        Ok(())
97    }
98}
99
100/// Timeout helper for operations
101pub async fn with_timeout<F, T>(future: F, timeout_duration: std::time::Duration) -> ObnizResult<T>
102where
103    F: std::future::Future<Output = ObnizResult<T>>,
104{
105    match tokio::time::timeout(timeout_duration, future).await {
106        Ok(result) => result,
107        Err(_) => Err(ObnizError::Timeout),
108    }
109}