1pub type Result<T, E = Error> = std::result::Result<T, E>;
5
6#[derive(Debug)]
7pub enum Error {
8 IoError(std::io::Error),
9 #[cfg(unix)]
10 NixError(nix::Error),
11 NotImplemented(String),
12 InvalidSize(usize),
13 ShapeMismatch(String),
14 #[cfg(target_os = "linux")]
15 UnknownDeviceType(u64, u64),
16 #[cfg(target_os = "linux")]
24 UnknownBufferType(u32),
25 InvalidMemoryType(String),
26 PboDisconnected,
28 PboMapped,
30 #[cfg(feature = "ndarray")]
31 NdArrayError(ndarray::ShapeError),
32 InvalidShape(String),
33 InvalidArgument(String),
34 InvalidOperation(String),
35 PartialWriteRequiresReadWrite(String),
43 QuantizationInvalid {
47 field: &'static str,
51 expected: String,
53 got: String,
55 },
56 InsufficientCapacity {
59 needed: usize,
61 capacity: usize,
63 },
64 RegionOutOfBounds {
67 region: crate::Region,
69 bounds: (usize, usize),
71 },
72 BatchIndexOutOfBounds {
74 index: usize,
76 batch: usize,
78 },
79 CpuAccessDenied {
82 declared: crate::CpuAccess,
85 requested: crate::CpuAccess,
87 },
88}
89
90impl Error {
91 pub fn cpu_access_denied(&self) -> Option<&Error> {
94 match self {
95 Error::CpuAccessDenied { .. } => Some(self),
96 _ => None,
97 }
98 }
99}
100
101impl From<std::io::Error> for Error {
102 fn from(err: std::io::Error) -> Self {
103 Error::IoError(err)
104 }
105}
106#[cfg(unix)]
107impl From<nix::Error> for Error {
108 fn from(err: nix::Error) -> Self {
109 Error::NixError(err)
110 }
111}
112
113#[cfg(feature = "ndarray")]
114impl From<ndarray::ShapeError> for Error {
115 fn from(err: ndarray::ShapeError) -> Self {
116 Error::NdArrayError(err)
117 }
118}
119
120impl std::fmt::Display for Error {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 match self {
123 Error::InsufficientCapacity { needed, capacity } => write!(
124 f,
125 "insufficient tensor capacity: need {needed} bytes, have {capacity}"
126 ),
127 Error::RegionOutOfBounds { region, bounds } => write!(
128 f,
129 "region {region:?} out of bounds for {}x{} frame",
130 bounds.0, bounds.1
131 ),
132 Error::BatchIndexOutOfBounds { index, batch } => write!(
133 f,
134 "batch index {index} out of bounds for batch size {batch}"
135 ),
136 Error::CpuAccessDenied {
137 declared,
138 requested,
139 } => {
140 let needed = declared.union(*requested);
141 write!(
142 f,
143 "CPU access denied: {} access requested on a tensor declared \
144 CpuAccess::{declared:?}; declare CpuAccess::{needed:?}",
145 requested.as_str()
146 )?;
147 if needed == crate::CpuAccess::ReadWrite {
148 f.write_str(" (the default)")
149 } else {
150 f.write_str(" or CpuAccess::ReadWrite (the default)")
151 }
152 }
153 #[cfg(target_os = "linux")]
154 Error::UnknownBufferType(magic) => write!(
155 f,
156 "UnknownBufferType: fd is on an unrecognized filesystem \
157 (magic {magic:#010x}); expected a DMA-BUF or tmpfs/shm fd"
158 ),
159 _ => write!(f, "{self:?}"),
160 }
161 }
162}
163
164impl std::error::Error for Error {}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn test_error_display() {
172 let e = Error::InvalidSize(0);
173 let msg = e.to_string();
174 assert!(!msg.is_empty());
175 assert!(
176 msg.contains("InvalidSize"),
177 "unexpected InvalidSize message: {msg}"
178 );
179
180 let e = Error::NotImplemented("foo".to_string());
181 let msg = e.to_string();
182 assert!(!msg.is_empty());
183 assert!(
184 msg.contains("NotImplemented") && msg.contains("foo"),
185 "unexpected NotImplemented message: {msg}"
186 );
187
188 let e = Error::ShapeMismatch("expected 3, got 4".to_string());
189 let msg = e.to_string();
190 assert!(!msg.is_empty());
191 assert!(
192 msg.contains("ShapeMismatch") && msg.contains("expected 3"),
193 "unexpected ShapeMismatch message: {msg}"
194 );
195
196 let e = Error::InvalidMemoryType("dma".to_string());
197 let msg = e.to_string();
198 assert!(!msg.is_empty());
199 assert!(
200 msg.contains("InvalidMemoryType") && msg.contains("dma"),
201 "unexpected InvalidMemoryType message: {msg}"
202 );
203
204 let e = Error::PboDisconnected;
205 let msg = e.to_string();
206 assert!(!msg.is_empty());
207 assert!(
208 msg.contains("PboDisconnected"),
209 "unexpected PboDisconnected message: {msg}"
210 );
211
212 let e = Error::PboMapped;
213 let msg = e.to_string();
214 assert!(!msg.is_empty());
215 assert!(
216 msg.contains("PboMapped"),
217 "unexpected PboMapped message: {msg}"
218 );
219
220 let e = Error::InvalidShape("bad shape".to_string());
221 let msg = e.to_string();
222 assert!(!msg.is_empty());
223 assert!(
224 msg.contains("InvalidShape") && msg.contains("bad shape"),
225 "unexpected InvalidShape message: {msg}"
226 );
227
228 let e = Error::InvalidArgument("negative".to_string());
229 let msg = e.to_string();
230 assert!(!msg.is_empty());
231 assert!(
232 msg.contains("InvalidArgument") && msg.contains("negative"),
233 "unexpected InvalidArgument message: {msg}"
234 );
235
236 let e = Error::PartialWriteRequiresReadWrite("retry with ReadWrite".to_string());
237 let msg = e.to_string();
238 assert!(!msg.is_empty());
239 assert!(
240 msg.contains("PartialWriteRequiresReadWrite") && msg.contains("retry with ReadWrite"),
241 "unexpected PartialWriteRequiresReadWrite message: {msg}"
242 );
243
244 let e = Error::InvalidOperation("read-only".to_string());
245 let msg = e.to_string();
246 assert!(!msg.is_empty());
247 assert!(
248 msg.contains("InvalidOperation") && msg.contains("read-only"),
249 "unexpected InvalidOperation message: {msg}"
250 );
251
252 let e = Error::IoError(std::io::Error::new(
253 std::io::ErrorKind::NotFound,
254 "file missing",
255 ));
256 let msg = e.to_string();
257 assert!(!msg.is_empty());
258 assert!(
259 msg.contains("IoError") && msg.contains("file missing"),
260 "unexpected IoError message: {msg}"
261 );
262 }
263
264 #[test]
265 fn cpu_access_denied_message_names_both_accesses_and_the_fix() {
266 let e = Error::CpuAccessDenied {
267 declared: crate::CpuAccess::Write,
268 requested: crate::CpuAccess::Read,
269 };
270 let msg = e.to_string();
271 assert_eq!(
272 msg,
273 "CPU access denied: read access requested on a tensor declared \
274 CpuAccess::Write; declare CpuAccess::ReadWrite (the default)"
275 );
276
277 let e = Error::CpuAccessDenied {
278 declared: crate::CpuAccess::None,
279 requested: crate::CpuAccess::Read,
280 };
281 assert_eq!(
282 e.to_string(),
283 "CPU access denied: read access requested on a tensor declared \
284 CpuAccess::None; declare CpuAccess::Read or CpuAccess::ReadWrite (the default)"
285 );
286 }
287
288 #[test]
289 fn insufficient_capacity_message() {
290 let e = Error::InsufficientCapacity {
291 needed: 100,
292 capacity: 64,
293 };
294 assert_eq!(
295 e.to_string(),
296 "insufficient tensor capacity: need 100 bytes, have 64"
297 );
298 }
299
300 #[test]
301 fn region_out_of_bounds_message_names_the_frame() {
302 let region = crate::Region::new(2, 3, 8, 8);
303 let e = Error::RegionOutOfBounds {
304 region,
305 bounds: (4, 5),
306 };
307 assert_eq!(
308 e.to_string(),
309 format!("region {region:?} out of bounds for 4x5 frame")
310 );
311 }
312
313 #[test]
314 fn batch_index_out_of_bounds_message_names_index_and_size() {
315 let e = Error::BatchIndexOutOfBounds { index: 9, batch: 4 };
316 assert_eq!(
317 e.to_string(),
318 "batch index 9 out of bounds for batch size 4"
319 );
320 }
321
322 #[cfg(target_os = "linux")]
323 #[test]
324 fn unknown_buffer_type_message_names_the_magic() {
325 let e = Error::UnknownBufferType(0x1234);
326 assert_eq!(
327 e.to_string(),
328 "UnknownBufferType: fd is on an unrecognized filesystem (magic 0x00001234); \
329 expected a DMA-BUF or tmpfs/shm fd"
330 );
331 }
332}