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