Skip to main content

edgefirst_image/
error.rs

1// SPDX-FileCopyrightText: Copyright 2025 Au-Zone Technologies
2// SPDX-License-Identifier: Apache-2.0
3
4pub type Result<T, E = Error> = std::result::Result<T, E>;
5
6#[derive(Debug)]
7pub enum Error {
8    Io(std::io::Error),
9    NotFound(String),
10    Library(libloading::Error),
11    JpegEncoding(jpeg_encoder::EncodingError),
12    Codec(edgefirst_codec::CodecError),
13    ResizeImageBuffer(fast_image_resize::ImageBufferError),
14    Resize(fast_image_resize::ResizeError),
15    Yuv(yuv::YuvError),
16    #[cfg(target_os = "linux")]
17    G2D(g2d_sys::Error),
18    Tensor(edgefirst_tensor::Error),
19    NotImplemented(String),
20    NotSupported(String),
21    InvalidShape(String),
22    /// Two tensor arguments that must be distinct reference the same
23    /// underlying buffer (e.g. `dst` and `background` in a draw call).
24    AliasedBuffers(String),
25    #[cfg(feature = "opengl")]
26    EGL(khronos_egl::Error),
27    GLVersion(String),
28    #[cfg(feature = "opengl")]
29    EGLLoad(khronos_egl::LoadError<libloading::Error>),
30    #[cfg(target_os = "linux")]
31    #[cfg(feature = "opengl")]
32    GbmInvalidFd(gbm::InvalidFdError),
33    #[cfg(target_os = "linux")]
34    #[cfg(feature = "opengl")]
35    GbmFrontBuffer(gbm::FrontBufferError),
36    OpenGl(String),
37    Internal(String),
38    CropInvalid(String),
39    ForcedBackendUnavailable(String),
40    NoConverter,
41    NotAnImage,
42    UnsupportedFormat(String),
43}
44
45impl From<std::io::Error> for Error {
46    fn from(err: std::io::Error) -> Self {
47        Error::Io(err)
48    }
49}
50
51impl From<libloading::Error> for Error {
52    fn from(err: libloading::Error) -> Self {
53        Error::Library(err)
54    }
55}
56
57impl From<jpeg_encoder::EncodingError> for Error {
58    fn from(err: jpeg_encoder::EncodingError) -> Self {
59        Error::JpegEncoding(err)
60    }
61}
62
63impl From<edgefirst_codec::CodecError> for Error {
64    fn from(err: edgefirst_codec::CodecError) -> Self {
65        Error::Codec(err)
66    }
67}
68
69impl From<fast_image_resize::ImageBufferError> for Error {
70    fn from(err: fast_image_resize::ImageBufferError) -> Self {
71        Error::ResizeImageBuffer(err)
72    }
73}
74
75impl From<fast_image_resize::ResizeError> for Error {
76    fn from(err: fast_image_resize::ResizeError) -> Self {
77        Error::Resize(err)
78    }
79}
80
81impl From<yuv::YuvError> for Error {
82    fn from(err: yuv::YuvError) -> Self {
83        Error::Yuv(err)
84    }
85}
86
87#[cfg(target_os = "linux")]
88impl From<g2d_sys::Error> for Error {
89    fn from(err: g2d_sys::Error) -> Self {
90        Error::G2D(err)
91    }
92}
93
94impl From<edgefirst_tensor::Error> for Error {
95    fn from(err: edgefirst_tensor::Error) -> Self {
96        Error::Tensor(err)
97    }
98}
99
100#[cfg(feature = "opengl")]
101impl From<khronos_egl::Error> for Error {
102    fn from(err: khronos_egl::Error) -> Self {
103        Error::EGL(err)
104    }
105}
106
107#[cfg(feature = "opengl")]
108impl From<khronos_egl::LoadError<libloading::Error>> for Error {
109    fn from(err: khronos_egl::LoadError<libloading::Error>) -> Self {
110        Error::EGLLoad(err)
111    }
112}
113
114#[cfg(target_os = "linux")]
115#[cfg(feature = "opengl")]
116impl From<gbm::InvalidFdError> for Error {
117    fn from(err: gbm::InvalidFdError) -> Self {
118        Error::GbmInvalidFd(err)
119    }
120}
121
122#[cfg(target_os = "linux")]
123#[cfg(feature = "opengl")]
124impl From<gbm::FrontBufferError> for Error {
125    fn from(err: gbm::FrontBufferError) -> Self {
126        Error::GbmFrontBuffer(err)
127    }
128}
129
130#[cfg(target_os = "linux")]
131#[cfg(feature = "opengl")]
132impl From<gls::Error> for Error {
133    fn from(err: gls::Error) -> Self {
134        Error::OpenGl(err.to_string())
135    }
136}
137
138impl From<ndarray::ShapeError> for Error {
139    fn from(err: ndarray::ShapeError) -> Self {
140        Error::Internal(format!("{err}"))
141    }
142}
143
144impl From<fast_image_resize::CropBoxError> for Error {
145    fn from(err: fast_image_resize::CropBoxError) -> Self {
146        Error::Internal(format!("{err}"))
147    }
148}
149
150impl std::fmt::Display for Error {
151    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
152        write!(f, "{self:?}")
153    }
154}
155
156impl std::error::Error for Error {}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_image_error_display() {
164        let e = Error::Io(std::io::Error::new(
165            std::io::ErrorKind::NotFound,
166            "file missing",
167        ));
168        let msg = e.to_string();
169        assert!(!msg.is_empty());
170        assert!(
171            msg.contains("Io") && msg.contains("file missing"),
172            "unexpected Io message: {msg}"
173        );
174
175        let e = Error::NotFound("texture.png".to_string());
176        let msg = e.to_string();
177        assert!(!msg.is_empty());
178        assert!(
179            msg.contains("NotFound") && msg.contains("texture.png"),
180            "unexpected NotFound message: {msg}"
181        );
182
183        let e = Error::Tensor(edgefirst_tensor::Error::InvalidSize(42));
184        let msg = e.to_string();
185        assert!(!msg.is_empty());
186        assert!(
187            msg.contains("Tensor") && msg.contains("InvalidSize"),
188            "unexpected Tensor message: {msg}"
189        );
190
191        let e = Error::NotImplemented("webp".to_string());
192        let msg = e.to_string();
193        assert!(!msg.is_empty());
194        assert!(
195            msg.contains("NotImplemented") && msg.contains("webp"),
196            "unexpected NotImplemented message: {msg}"
197        );
198
199        let e = Error::NotSupported("bmp format".to_string());
200        let msg = e.to_string();
201        assert!(!msg.is_empty());
202        assert!(
203            msg.contains("NotSupported") && msg.contains("bmp format"),
204            "unexpected NotSupported message: {msg}"
205        );
206
207        let e = Error::InvalidShape("wrong dims".to_string());
208        let msg = e.to_string();
209        assert!(!msg.is_empty());
210        assert!(
211            msg.contains("InvalidShape") && msg.contains("wrong dims"),
212            "unexpected InvalidShape message: {msg}"
213        );
214
215        let e = Error::GLVersion("4.5 required".to_string());
216        let msg = e.to_string();
217        assert!(!msg.is_empty());
218        assert!(
219            msg.contains("GLVersion") && msg.contains("4.5 required"),
220            "unexpected GLVersion message: {msg}"
221        );
222
223        let e = Error::OpenGl("shader compile failed".to_string());
224        let msg = e.to_string();
225        assert!(!msg.is_empty());
226        assert!(
227            msg.contains("OpenGl") && msg.contains("shader compile failed"),
228            "unexpected OpenGl message: {msg}"
229        );
230
231        let e = Error::Internal("unexpected state".to_string());
232        let msg = e.to_string();
233        assert!(!msg.is_empty());
234        assert!(
235            msg.contains("Internal") && msg.contains("unexpected state"),
236            "unexpected Internal message: {msg}"
237        );
238
239        let e = Error::CropInvalid("out of bounds".to_string());
240        let msg = e.to_string();
241        assert!(!msg.is_empty());
242        assert!(
243            msg.contains("CropInvalid") && msg.contains("out of bounds"),
244            "unexpected CropInvalid message: {msg}"
245        );
246
247        let e = Error::ForcedBackendUnavailable("g2d".to_string());
248        let msg = e.to_string();
249        assert!(!msg.is_empty());
250        assert!(
251            msg.contains("ForcedBackendUnavailable") && msg.contains("g2d"),
252            "unexpected ForcedBackendUnavailable message: {msg}"
253        );
254
255        let e = Error::NoConverter;
256        let msg = e.to_string();
257        assert!(!msg.is_empty());
258        assert!(
259            msg.contains("NoConverter"),
260            "unexpected NoConverter message: {msg}"
261        );
262
263        let e = Error::NotAnImage;
264        let msg = e.to_string();
265        assert!(!msg.is_empty());
266        assert!(
267            msg.contains("NotAnImage"),
268            "unexpected NotAnImage message: {msg}"
269        );
270
271        let e = Error::UnsupportedFormat("tiff".to_string());
272        let msg = e.to_string();
273        assert!(!msg.is_empty());
274        assert!(
275            msg.contains("UnsupportedFormat") && msg.contains("tiff"),
276            "unexpected UnsupportedFormat message: {msg}"
277        );
278    }
279}