ohea_lock/
error.rs

1//! Error types for the Ohea Lock library.
2
3use thiserror::Error;
4
5/// Result type alias for Ohea Lock operations.
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// Errors that can occur when interacting with an Ohea Lock device.
9#[derive(Debug, Error)]
10#[non_exhaustive]
11pub enum Error {
12    /// The requested characteristic was not found on the device.
13    #[error("characteristic not found: {0}")]
14    CharacteristicNotFound(&'static str),
15
16    /// The requested service was not found on the device.
17    #[error("service not found: {0}")]
18    ServiceNotFound(&'static str),
19
20    /// Authentication is required but the device is not paired.
21    #[error("insufficient authentication - pairing required")]
22    InsufficientAuthentication,
23
24    /// The device is not connected.
25    #[error("device not connected")]
26    NotConnected,
27
28    /// A BLE transport error occurred.
29    #[error("transport error: {0}")]
30    Transport(String),
31
32    /// Invalid response received from the device.
33    #[error("invalid response: {0}")]
34    InvalidResponse(String),
35
36    /// The operation timed out.
37    #[error("operation timed out")]
38    Timeout,
39
40    /// An I/O error occurred.
41    #[error("io error: {0}")]
42    Io(#[from] std::io::Error),
43
44    /// btleplug-specific error (only available with `btleplug-support` feature).
45    #[cfg(feature = "btleplug-support")]
46    #[error("btleplug error: {0}")]
47    Btleplug(#[from] btleplug::Error),
48}
49
50impl Error {
51    /// Returns `true` if this error indicates that pairing is required.
52    #[must_use]
53    pub const fn requires_pairing(&self) -> bool {
54        matches!(self, Self::InsufficientAuthentication)
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn error_display_messages() {
64        let cases = [
65            (Error::CharacteristicNotFound("lock_state"), "characteristic not found: lock_state"),
66            (Error::ServiceNotFound("battery"), "service not found: battery"),
67            (Error::InsufficientAuthentication, "insufficient authentication - pairing required"),
68            (Error::NotConnected, "device not connected"),
69            (Error::Transport("timeout".into()), "transport error: timeout"),
70            (Error::InvalidResponse("empty".into()), "invalid response: empty"),
71            (Error::Timeout, "operation timed out"),
72        ];
73        for (error, expected) in cases {
74            assert_eq!(error.to_string(), expected);
75        }
76    }
77
78    #[test]
79    fn requires_pairing_returns_true_for_auth_error() {
80        assert!(Error::InsufficientAuthentication.requires_pairing());
81    }
82
83    #[test]
84    fn requires_pairing_returns_false_for_other_errors() {
85        let non_auth_errors = [
86            Error::CharacteristicNotFound("test"),
87            Error::ServiceNotFound("test"),
88            Error::NotConnected,
89            Error::Transport("test".into()),
90            Error::InvalidResponse("test".into()),
91            Error::Timeout,
92        ];
93        for error in non_auth_errors {
94            assert!(!error.requires_pairing(), "unexpected true for {error:?}");
95        }
96    }
97
98    #[test]
99    fn io_error_conversion() {
100        let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
101        let err: Error = io_err.into();
102        assert!(matches!(err, Error::Io(_)));
103    }
104
105    #[test]
106    fn error_is_send_and_sync() {
107        fn assert_send_sync<T: Send + Sync>() {}
108        // This will fail to compile if Error is not Send + Sync
109        assert_send_sync::<Error>();
110    }
111}