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