1use std::{error::Error as StdError, fmt};
2pub type Result<T> = std::result::Result<T, Error>;
3
4#[derive(Debug)]
5pub enum Error {
6 DeviceNotOpen,
7 DeviceNotFound {
8 vid: u16,
9 pid: u16,
10 },
11 BufferTooLarge {
12 context: &'static str,
13 max_words: usize,
14 actual_words: usize,
15 },
16 FeatureUnavailable(&'static str),
17 InvalidBitfile(&'static str),
18 InvalidBitfileLine {
19 line: usize,
20 reason: &'static str,
21 },
22 InvalidBufferLength {
23 context: &'static str,
24 expected: usize,
25 actual: usize,
26 },
27 InvalidMode {
28 expected: &'static str,
29 actual: &'static str,
30 },
31 PipelineEmpty,
32 PipelineFull {
33 capacity: usize,
34 },
35 NotProgrammed,
36 Timeout(&'static str),
37 UnexpectedResponse(&'static str),
38 VersionMismatch {
39 expected: u16,
40 actual: u16,
41 },
42 Usb {
43 source: Box<dyn StdError + Send + Sync>,
44 context: &'static str,
45 },
46 Io(std::io::Error),
47}
48
49impl fmt::Display for Error {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Error::DeviceNotOpen => write!(f, "device is not open"),
53 Error::DeviceNotFound { vid, pid } => {
54 write!(f, "device {vid:#06x}:{pid:#06x} not found")
55 }
56 Error::BufferTooLarge {
57 context,
58 max_words,
59 actual_words,
60 } => write!(
61 f,
62 "{context} exceeds FIFO capacity ({actual_words} words > {max_words} words)"
63 ),
64 Error::FeatureUnavailable(feature) => write!(f, "feature `{feature}` is unavailable"),
65 Error::InvalidBitfile(reason) => write!(f, "invalid bitfile: {reason}"),
66 Error::InvalidBitfileLine { line, reason } => {
67 write!(f, "invalid bitfile line {line}: {reason}")
68 }
69 Error::InvalidBufferLength {
70 context,
71 expected,
72 actual,
73 } => write!(
74 f,
75 "invalid buffer length for `{context}` (expected {expected}, got {actual})"
76 ),
77 Error::InvalidMode { expected, actual } => {
78 write!(
79 f,
80 "invalid device mode (expected `{expected}`, got `{actual}`)"
81 )
82 }
83 Error::PipelineEmpty => write!(f, "transfer pipeline has no pending transfers"),
84 Error::PipelineFull { capacity } => write!(
85 f,
86 "transfer pipeline is full (capacity {capacity} outstanding transfers)"
87 ),
88 Error::NotProgrammed => write!(f, "FPGA is not programmed"),
89 Error::Timeout(context) => write!(f, "operation `{context}` timed out"),
90 Error::UnexpectedResponse(context) => {
91 write!(f, "unexpected response during `{context}`")
92 }
93 Error::VersionMismatch { expected, actual } => write!(
94 f,
95 "SMIMS version mismatch (expected {expected:#06x}, found {actual:#06x})"
96 ),
97 Error::Usb { source, context } => {
98 write!(f, "usb error {source} in `{context}`")
99 }
100 Error::Io(err) => err.fmt(f),
101 }
102 }
103}
104
105impl std::error::Error for Error {
106 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
107 match self {
108 Error::Usb { source, .. } => Some(source.as_ref()),
109 Error::Io(err) => Some(err),
110 _ => None,
111 }
112 }
113}
114
115impl From<std::io::Error> for Error {
116 fn from(value: std::io::Error) -> Self {
117 Self::Io(value)
118 }
119}