Skip to main content

glycin_utils/
error.rs

1use std::any::Any;
2
3#[derive(zbus::DBusError, Debug, Clone)]
4#[zbus(prefix = "org.gnome.glycin.Error")]
5#[non_exhaustive]
6/// Error within the remote process.
7///
8/// Errors that appear within the loader or editor.
9pub enum RemoteError {
10    #[zbus(error)]
11    ZBus(zbus::Error),
12    LoadingError(String),
13    InternalLoaderError(String),
14    EditingError(String),
15    InternalEditorError(String),
16    UnsupportedImageFormat(String),
17    ConversionTooLargerError,
18    OutOfMemory(String),
19    Aborted,
20    NoMoreFrames,
21}
22
23type Location = std::panic::Location<'static>;
24
25impl ProcessError {
26    pub fn into_loader_error(self) -> RemoteError {
27        match self {
28            err @ ProcessError::ExpectedError { .. } => RemoteError::LoadingError(err.to_string()),
29            err @ ProcessError::InternalError { .. } => {
30                RemoteError::InternalLoaderError(err.to_string())
31            }
32            ProcessError::UnsupportedImageFormat(msg) => RemoteError::UnsupportedImageFormat(msg),
33            ProcessError::ConversionTooLargerError => RemoteError::ConversionTooLargerError,
34            err @ ProcessError::OutOfMemory { .. } => RemoteError::OutOfMemory(err.to_string()),
35            ProcessError::NoMoreFrames => RemoteError::NoMoreFrames,
36        }
37    }
38
39    pub fn into_editor_error(self) -> RemoteError {
40        match self {
41            err @ ProcessError::ExpectedError { .. } => RemoteError::EditingError(err.to_string()),
42            err @ ProcessError::InternalError { .. } => {
43                RemoteError::InternalEditorError(err.to_string())
44            }
45            ProcessError::UnsupportedImageFormat(msg) => RemoteError::UnsupportedImageFormat(msg),
46            ProcessError::ConversionTooLargerError => RemoteError::ConversionTooLargerError,
47            err @ ProcessError::OutOfMemory { .. } => RemoteError::OutOfMemory(err.to_string()),
48            ProcessError::NoMoreFrames => RemoteError::NoMoreFrames,
49        }
50    }
51}
52
53#[derive(thiserror::Error, Debug)]
54#[non_exhaustive]
55pub enum ProcessError {
56    #[error("{location}: {err}")]
57    ExpectedError { err: String, location: Location },
58    #[error("{location}: Internal error: {err}")]
59    InternalError { err: String, location: Location },
60    #[error("Unsupported image format: {0}")]
61    UnsupportedImageFormat(String),
62    #[error("Dimension too large for system")]
63    ConversionTooLargerError,
64    #[error("{location}: Not enough memory available")]
65    OutOfMemory { location: Location },
66    #[error("No more frames available")]
67    NoMoreFrames,
68}
69
70impl ProcessError {
71    #[track_caller]
72    pub fn expected(err: &impl ToString) -> Self {
73        Self::ExpectedError {
74            err: err.to_string(),
75            location: *Location::caller(),
76        }
77    }
78
79    #[track_caller]
80    pub fn out_of_memory() -> Self {
81        Self::OutOfMemory {
82            location: *Location::caller(),
83        }
84    }
85}
86
87impl From<DimensionTooLargerError> for ProcessError {
88    fn from(err: DimensionTooLargerError) -> Self {
89        eprintln!("Decoding error: {err:?}");
90        Self::ConversionTooLargerError
91    }
92}
93
94pub trait GenericContexts<T> {
95    fn expected_error(self) -> Result<T, ProcessError>;
96    fn internal_error(self) -> Result<T, ProcessError>;
97}
98
99impl<T, E> GenericContexts<T> for Result<T, E>
100where
101    E: std::error::Error + Any,
102{
103    #[track_caller]
104    fn expected_error(self) -> Result<T, ProcessError> {
105        match self {
106            Ok(x) => Ok(x),
107            Err(err) => Err(
108                if let Some(err) = ((&err) as &dyn Any).downcast_ref::<ProcessError>() {
109                    if matches!(err, ProcessError::OutOfMemory { .. }) {
110                        ProcessError::out_of_memory()
111                    } else {
112                        ProcessError::expected(err)
113                    }
114                } else if let Some(err) =
115                    ((&err) as &dyn Any).downcast_ref::<glycin_common::Error>()
116                {
117                    if matches!(err, glycin_common::Error::OutOfMemory) {
118                        ProcessError::out_of_memory()
119                    } else {
120                        ProcessError::expected(err)
121                    }
122                } else {
123                    ProcessError::expected(&err)
124                },
125            ),
126        }
127    }
128
129    #[track_caller]
130    fn internal_error(self) -> Result<T, ProcessError> {
131        match self {
132            Ok(x) => Ok(x),
133            Err(err) => Err(ProcessError::InternalError {
134                err: err.to_string(),
135                location: *Location::caller(),
136            }),
137        }
138    }
139}
140
141impl<T> GenericContexts<T> for Option<T> {
142    #[track_caller]
143    fn expected_error(self) -> Result<T, ProcessError> {
144        match self {
145            Some(x) => Ok(x),
146            None => Err(ProcessError::ExpectedError {
147                err: String::from("None"),
148                location: *Location::caller(),
149            }),
150        }
151    }
152
153    #[track_caller]
154    fn internal_error(self) -> Result<T, ProcessError> {
155        match self {
156            Some(x) => Ok(x),
157            None => Err(ProcessError::InternalError {
158                err: String::from("None"),
159                location: *Location::caller(),
160            }),
161        }
162    }
163}
164
165#[derive(Debug, Clone)]
166pub struct DimensionTooLargerError;
167
168impl std::fmt::Display for DimensionTooLargerError {
169    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
170        f.write_str("Dimension too large for system")
171    }
172}
173
174impl std::error::Error for DimensionTooLargerError {}