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