Skip to main content

glycin_utils/
error.rs

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