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