1use std::str::Utf8Error;
2
3use log::error;
4use snafu::Snafu;
5
6pub use crate::errors::Error::*;
7use crate::io::{PinIdOrName, PinModeId};
8
9#[derive(Debug, Snafu)]
10#[snafu(visibility(pub))]
11pub enum Error {
12 RuntimeError,
14 StateError,
16 ProtocolError { source: ProtocolError },
18 HardwareError { source: HardwareError },
20 UnknownError { info: String },
22}
23
24impl From<std::io::Error> for Error {
25 fn from(error: std::io::Error) -> Self {
26 error!("std::io error {:?}", error);
27 let info = match error.kind() {
28 std::io::ErrorKind::NotFound => String::from("Board not found or already in use"),
29 std::io::ErrorKind::PermissionDenied => String::from("Board access denied"),
30 _ => error.to_string(),
31 };
32 Self::ProtocolError {
33 source: ProtocolError::IoException { info },
34 }
35 }
36}
37
38impl From<ProtocolError> for Error {
39 fn from(value: ProtocolError) -> Self {
40 Self::ProtocolError { source: value }
41 }
42}
43
44impl From<HardwareError> for Error {
45 fn from(value: HardwareError) -> Self {
46 Self::HardwareError { source: value }
47 }
48}
49
50impl From<Utf8Error> for Error {
51 fn from(value: Utf8Error) -> Self {
52 UnknownError {
53 info: value.to_string(),
54 }
55 }
56}
57
58#[derive(Debug, Snafu)]
59#[snafu(visibility(pub))]
60pub enum ProtocolError {
61 IoException { info: String },
63 NotInitialized,
65 MessageTooShort {
67 operation: &'static str,
68 expected: usize,
69 received: usize,
70 },
71 UnexpectedData,
73}
74
75#[derive(Debug, Snafu)]
76#[snafu(visibility(pub))]
77pub enum HardwareError {
78 IncompatiblePin {
80 pin: u8,
81 mode: PinModeId,
82 context: &'static str,
83 },
84 UnknownPin { pin: PinIdOrName },
86}
87
88#[cfg(test)]
89mod tests {
90 use std::io;
91
92 use crate::errors::HardwareError::{IncompatiblePin, UnknownPin};
93
94 use super::*;
95
96 #[test]
97 fn test_error_display() {
98 let runtime_error = RuntimeError;
99 assert_eq!(
100 format!("{}", runtime_error),
101 "Runtime error: Are you sure your code runs inside `#[hermes_five::runtime]`?"
102 );
103
104 let protocol_error = Error::from(ProtocolError::IoException {
105 info: "I/O error message".to_string(),
106 });
107 assert_eq!(
108 format!("{}", protocol_error),
109 "Protocol error: I/O error message."
110 );
111
112 let hardware_error = Error::from(IncompatiblePin {
113 pin: 1,
114 mode: PinModeId::SERVO,
115 context: "test context",
116 });
117 assert_eq!(
118 format!("{}", hardware_error),
119 "Hardware error: Pin (1) not compatible with mode (SERVO) - test context."
120 );
121
122 let unknown_error = UnknownError {
123 info: "Some unknown error".to_string(),
124 };
125 assert_eq!(
126 format!("{}", unknown_error),
127 "Unknown error: Some unknown error."
128 );
129 }
130
131 #[test]
132 fn test_from_io_error() {
133 let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found");
134 let error: Error = io_error.into();
135 assert_eq!(
136 format!("{}", error),
137 "Protocol error: Board not found or already in use."
138 );
139
140 let io_error = io::Error::new(io::ErrorKind::PermissionDenied, "error");
141 let error: Error = io_error.into();
142 assert_eq!(format!("{}", error), "Protocol error: Board access denied.");
143 }
144
145 #[test]
146 fn test_from_protocol_error() {
147 let protocol_error = ProtocolError::NotInitialized;
148 let error: Error = protocol_error.into();
149 assert_eq!(
150 format!("{}", error),
151 "Protocol error: Connection has not been initialized."
152 );
153 }
154
155 #[test]
156 fn test_from_hardware_error() {
157 let hardware_error = UnknownPin {
158 pin: PinIdOrName::Id(42),
159 };
160 let error: Error = hardware_error.into();
161 assert_eq!(format!("{}", error), "Hardware error: Unknown pin 42.");
162 }
163
164 #[test]
165 fn test_from_utf8_error() {
166 #[allow(invalid_from_utf8)]
167 let utf8_error = std::str::from_utf8(&[0x80]).err().unwrap(); let error: Error = utf8_error.into();
169 assert_eq!(
170 format!("{}", error),
171 "Unknown error: invalid utf-8 sequence of 1 bytes from index 0."
172 )
173 }
174}