1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
//! Module containing runtime error types.

use std::any::Any;
use std::{fmt, io};

use crate::{coordinator, worker};

/// Error returned by running a [`Runtime`].
///
/// [`Runtime`]: crate::Runtime
pub struct Error {
    inner: ErrorInner,
}

/// Inside of `Error` error.
enum ErrorInner {
    /// User setup error, created via [`Error::setup`].
    Setup(StringError),
    /// Error setting up tracing infrastructure.
    SetupTrace(io::Error),

    /// Error initialising coordinator.
    InitCoordinator(io::Error),
    /// Error in coordinator.
    Coordinator(coordinator::Error),

    /// Error starting worker thread.
    StartWorker(io::Error),
    /// Error in a worker thread.
    Worker(worker::Error),
    /// Panic in a worker thread.
    WorkerPanic(StringError),

    /// Error starting synchronous actor thread.
    StartSyncActor(io::Error),
    /// Panic in a synchronous actor thread.
    SyncActorPanic(StringError),
}

impl Error {
    const DESC: &'static str = "error running Heph runtime";

    /// Create an error to act as user-defined setup error.
    ///
    /// The `err` will be converted into a [`String`] (using [`ToString`], which
    /// is implemented for all type that implement [`fmt::Display`]).
    #[allow(clippy::needless_pass_by_value)]
    pub fn setup<E>(err: E) -> Error
    where
        E: ToString,
    {
        Error {
            inner: ErrorInner::Setup(StringError(err.to_string())),
        }
    }

    pub(super) const fn setup_trace(err: io::Error) -> Error {
        Error {
            inner: ErrorInner::SetupTrace(err),
        }
    }

    pub(super) const fn init_coordinator(err: io::Error) -> Error {
        Error {
            inner: ErrorInner::InitCoordinator(err),
        }
    }

    pub(super) const fn coordinator(err: coordinator::Error) -> Error {
        Error {
            inner: ErrorInner::Coordinator(err),
        }
    }

    pub(super) const fn start_worker(err: io::Error) -> Error {
        Error {
            inner: ErrorInner::StartWorker(err),
        }
    }

    pub(super) const fn worker(err: worker::Error) -> Error {
        Error {
            inner: ErrorInner::Worker(err),
        }
    }

    pub(super) fn worker_panic(err: Box<dyn Any + Send + 'static>) -> Error {
        let msg = convert_panic(err);
        Error {
            inner: ErrorInner::WorkerPanic(msg),
        }
    }

    pub(super) const fn start_sync_actor(err: io::Error) -> Error {
        Error {
            inner: ErrorInner::StartSyncActor(err),
        }
    }

    pub(super) fn sync_actor_panic(err: Box<dyn Any + Send + 'static>) -> Error {
        let msg = convert_panic(err);
        Error {
            inner: ErrorInner::SyncActorPanic(msg),
        }
    }
}

/// Maps a boxed panic messages to a [`StringError`]
fn convert_panic(err: Box<dyn Any + Send + 'static>) -> StringError {
    let msg = match err.downcast::<&'static str>() {
        Ok(s) => (*s).to_owned(),
        Err(err) => match err.downcast::<String>() {
            Ok(s) => *s,
            Err(..) => {
                "unknown panic message (use `String` or `&'static str` in the future)".to_owned()
            }
        },
    };
    StringError(msg)
}

/// We implement [`Debug`] by using [`Display`] implementation because the
/// [`Termination`] trait uses `Debug` rather then `Display` when returning an
/// `Result`.
///
/// [`Termination`]: std::process::Termination
/// [`Debug`]: std::fmt::Debug
/// [`Display`]: std::fmt::Display
impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use ErrorInner::*;
        match self.inner {
            Setup(ref err) => {
                write!(f, "{}: error in user-defined setup: {}", Self::DESC, err)
            }
            SetupTrace(ref err) => write!(
                f,
                "{}: error setting up trace infrastructure: {}",
                Self::DESC,
                err
            ),
            InitCoordinator(ref err) => {
                write!(f, "{}: error creating coordinator: {}", Self::DESC, err)
            }
            Coordinator(ref err) => {
                write!(f, "{}: error in coordinator thread: {}", Self::DESC, err)
            }
            StartWorker(ref err) => {
                write!(f, "{}: error starting worker thread: {}", Self::DESC, err)
            }
            Worker(ref err) => write!(f, "{}: error in worker thread: {}", Self::DESC, err),
            WorkerPanic(ref msg) => write!(f, "{}: panic in worker thread: {}", Self::DESC, msg),
            StartSyncActor(ref err) => write!(
                f,
                "{}: error starting synchronous actor: {}",
                Self::DESC,
                err
            ),
            SyncActorPanic(ref msg) => write!(
                f,
                "{}: panic in synchronous actor thread: {}",
                Self::DESC,
                msg
            ),
        }
    }
}

impl std::error::Error for Error {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use ErrorInner::*;
        match self.inner {
            // All `io::Error`.
            SetupTrace(ref err)
            | InitCoordinator(ref err)
            | StartWorker(ref err)
            | StartSyncActor(ref err) => Some(err),
            Coordinator(ref err) => Some(err),
            Worker(ref err) => Some(err),
            // All `StringError`.
            Setup(ref err) | WorkerPanic(ref err) | SyncActorPanic(ref err) => Some(err),
        }
    }
}

/// Wrapper around `String` to implement the [`Error`] trait.
///
/// [`Error`]: std::error::Error
pub(crate) struct StringError(String);

impl From<String> for StringError {
    fn from(err: String) -> StringError {
        StringError(err)
    }
}

impl fmt::Debug for StringError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl fmt::Display for StringError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl std::error::Error for StringError {}