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]
10pub 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 {}