1use std::fmt;
2use std::io;
3
4use ipc_channel::ipc::{IpcError, TryRecvError};
5use ipc_channel::{Error as BincodeError, ErrorKind as BincodeErrorKind};
6use serde::{Deserialize, Serialize};
7
8#[derive(Serialize, Deserialize)]
15pub struct PanicInfo {
16 msg: String,
17 pub(crate) location: Option<Location>,
18 #[cfg(feature = "backtrace")]
19 pub(crate) backtrace: Option<backtrace::Backtrace>,
20}
21
22#[derive(Serialize, Deserialize, Debug)]
26pub struct Location {
27 file: String,
28 line: u32,
29 column: u32,
30}
31
32impl Location {
33 pub(crate) fn from_std(loc: &std::panic::Location) -> Location {
34 Location {
35 file: loc.file().into(),
36 line: loc.line(),
37 column: loc.column(),
38 }
39 }
40
41 pub fn file(&self) -> &str {
43 &self.file
44 }
45
46 pub fn line(&self) -> u32 {
48 self.line
49 }
50
51 pub fn column(&self) -> u32 {
53 self.column
54 }
55}
56
57impl PanicInfo {
58 pub(crate) fn new(s: &str) -> PanicInfo {
60 PanicInfo {
61 msg: s.into(),
62 location: None,
63 #[cfg(feature = "backtrace")]
64 backtrace: None,
65 }
66 }
67
68 pub fn message(&self) -> &str {
70 self.msg.as_str()
71 }
72
73 pub fn location(&self) -> Option<&Location> {
75 self.location.as_ref()
76 }
77
78 #[cfg(feature = "backtrace")]
83 pub fn backtrace(&self) -> Option<&backtrace::Backtrace> {
84 self.backtrace.as_ref()
85 }
86}
87
88impl fmt::Debug for PanicInfo {
89 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90 f.debug_struct("PanicInfo")
91 .field("message", &self.message())
92 .field("location", &self.location())
93 .field("backtrace", &{
94 #[cfg(feature = "backtrace")]
95 {
96 self.backtrace()
97 }
98 #[cfg(not(feature = "backtrace"))]
99 {
100 None::<()>
101 }
102 })
103 .finish()
104 }
105}
106
107impl fmt::Display for PanicInfo {
108 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109 write!(f, "{}", self.msg)
110 }
111}
112
113#[derive(Debug)]
117pub struct SpawnError {
118 kind: SpawnErrorKind,
119}
120
121#[derive(Debug)]
122enum SpawnErrorKind {
123 Bincode(BincodeError),
124 Io(io::Error),
125 Panic(PanicInfo),
126 IpcChannelClosed(io::Error),
127 Cancelled,
128 TimedOut,
129 Consumed,
130}
131
132impl SpawnError {
133 pub fn panic_info(&self) -> Option<&PanicInfo> {
135 if let SpawnErrorKind::Panic(ref info) = self.kind {
136 Some(info)
137 } else {
138 None
139 }
140 }
141
142 pub fn is_panic(&self) -> bool {
144 self.panic_info().is_some()
145 }
146
147 pub fn is_cancellation(&self) -> bool {
149 matches!(self.kind, SpawnErrorKind::Cancelled)
150 }
151
152 pub fn is_timeout(&self) -> bool {
154 matches!(self.kind, SpawnErrorKind::TimedOut)
155 }
156
157 pub fn is_remote_close(&self) -> bool {
159 matches!(self.kind, SpawnErrorKind::IpcChannelClosed(..))
160 }
161
162 pub(crate) fn new_remote_close() -> SpawnError {
163 SpawnError {
164 kind: SpawnErrorKind::IpcChannelClosed(io::Error::new(
165 io::ErrorKind::ConnectionReset,
166 "remote closed",
167 )),
168 }
169 }
170
171 pub(crate) fn new_cancelled() -> SpawnError {
172 SpawnError {
173 kind: SpawnErrorKind::Cancelled,
174 }
175 }
176
177 pub(crate) fn new_timeout() -> SpawnError {
178 SpawnError {
179 kind: SpawnErrorKind::TimedOut,
180 }
181 }
182
183 pub(crate) fn new_consumed() -> SpawnError {
184 SpawnError {
185 kind: SpawnErrorKind::Consumed,
186 }
187 }
188}
189
190impl std::error::Error for SpawnError {
191 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
192 match self.kind {
193 SpawnErrorKind::Bincode(ref err) => Some(err),
194 SpawnErrorKind::Io(ref err) => Some(err),
195 SpawnErrorKind::Panic(_) => None,
196 SpawnErrorKind::Cancelled => None,
197 SpawnErrorKind::TimedOut => None,
198 SpawnErrorKind::Consumed => None,
199 SpawnErrorKind::IpcChannelClosed(ref err) => Some(err),
200 }
201 }
202}
203
204impl fmt::Display for SpawnError {
205 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206 match self.kind {
207 SpawnErrorKind::Bincode(_) => write!(f, "process spawn error: bincode error"),
208 SpawnErrorKind::Io(_) => write!(f, "process spawn error: i/o error"),
209 SpawnErrorKind::Panic(ref p) => write!(f, "process spawn error: panic: {}", p),
210 SpawnErrorKind::Cancelled => write!(f, "process spawn error: call cancelled"),
211 SpawnErrorKind::TimedOut => write!(f, "process spawn error: timed out"),
212 SpawnErrorKind::Consumed => write!(f, "process spawn error: result already consumed"),
213 SpawnErrorKind::IpcChannelClosed(_) => write!(
214 f,
215 "process spawn error: remote side closed (might have panicked on serialization)"
216 ),
217 }
218 }
219}
220
221impl From<BincodeError> for SpawnError {
222 fn from(err: BincodeError) -> SpawnError {
223 if let BincodeErrorKind::Io(io_err) = *err {
225 return SpawnError::from(io_err);
226 }
227 SpawnError {
228 kind: SpawnErrorKind::Bincode(err),
229 }
230 }
231}
232
233impl From<TryRecvError> for SpawnError {
234 fn from(err: TryRecvError) -> SpawnError {
235 match err {
236 TryRecvError::Empty => SpawnError::new_remote_close(),
237 TryRecvError::IpcError(err) => SpawnError::from(err),
238 }
239 }
240}
241
242impl From<IpcError> for SpawnError {
243 fn from(err: IpcError) -> SpawnError {
244 match err {
246 IpcError::Io(err) => SpawnError::from(err),
247 IpcError::Bincode(err) => SpawnError::from(err),
248 IpcError::Disconnected => SpawnError::new_remote_close(),
249 }
250 }
251}
252
253impl From<io::Error> for SpawnError {
254 fn from(err: io::Error) -> SpawnError {
255 if let io::ErrorKind::ConnectionReset = err.kind() {
256 return SpawnError {
257 kind: SpawnErrorKind::IpcChannelClosed(err),
258 };
259 }
260 SpawnError {
261 kind: SpawnErrorKind::Io(err),
262 }
263 }
264}
265
266impl From<PanicInfo> for SpawnError {
267 fn from(panic: PanicInfo) -> SpawnError {
268 SpawnError {
269 kind: SpawnErrorKind::Panic(panic),
270 }
271 }
272}