mithril_signer/runtime/
error.rs1use slog::{crit, error, Logger};
2use thiserror::Error;
3
4use mithril_common::entities::EpochError;
5use mithril_common::StdError;
6
7use crate::RunnerError;
8
9#[derive(Error, Debug)]
12pub enum RuntimeError {
13 #[error("An error occurred, runtime state kept. message = '{message}'")]
16 KeepState {
17 message: String,
19
20 #[source]
22 nested_error: Option<StdError>,
23 },
24 #[error("A critical error occurred, aborting runtime. message = '{message}'")]
27 Critical {
28 message: String,
30
31 #[source]
33 nested_error: Option<StdError>,
34 },
35}
36
37impl RuntimeError {
38 pub fn is_critical(&self) -> bool {
40 matches!(
41 self,
42 RuntimeError::Critical {
43 message: _,
44 nested_error: _
45 }
46 )
47 }
48
49 pub fn write_to_log(&self, logger: &Logger) {
51 match self {
52 Self::KeepState { nested_error, .. } => match nested_error {
53 None => error!(logger, "{self}"),
54 Some(err) => error!(logger, "{self}"; "nested_error" => ?err),
55 },
56 Self::Critical { nested_error, .. } => match nested_error {
57 None => crit!(logger, "{self}"),
58 Some(err) => crit!(logger, "{self}"; "nested_error" => ?err),
59 },
60 }
61 }
62}
63
64impl From<RunnerError> for RuntimeError {
65 fn from(value: RunnerError) -> Self {
66 Self::KeepState {
67 message: "runner failed".to_string(),
68 nested_error: Some(value.into()),
69 }
70 }
71}
72
73impl From<EpochError> for RuntimeError {
74 fn from(value: EpochError) -> Self {
75 Self::KeepState {
76 message: "Epoch offset conversion failed".to_string(),
77 nested_error: Some(value.into()),
78 }
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use anyhow::anyhow;
85
86 use crate::test_tools::TestLogger;
87
88 use super::*;
89
90 fn nested_error_debug_string(error: &RuntimeError) -> String {
91 let error = match error {
92 RuntimeError::KeepState { nested_error, .. } => nested_error,
93 RuntimeError::Critical { nested_error, .. } => nested_error,
94 };
95 match error {
96 None => String::new(),
97 Some(err) => {
98 format!("{err:?}")
99 }
100 }
101 }
102
103 #[test]
104 fn log_critical_without_nested_error() {
105 let (logger, log_inspector) = TestLogger::memory();
106
107 let error = RuntimeError::Critical {
108 message: "Critical error".to_string(),
109 nested_error: None,
110 };
111 error.write_to_log(&logger);
112
113 assert!(log_inspector.contains_log(&format!("{error}")));
114 assert!(!log_inspector.contains_log("nested_error"));
115 }
116
117 #[test]
118 fn log_critical_with_nested_error() {
119 let (logger, log_inspector) = TestLogger::memory();
120
121 let error = RuntimeError::Critical {
122 message: "Critical error".to_string(),
123 nested_error: Some(
124 anyhow!("Another context error")
125 .context("Context error")
126 .context("Critical nested error"),
127 ),
128 };
129 error.write_to_log(&logger);
130
131 assert!(log_inspector.contains_log(&format!("{error}")));
132 assert!(log_inspector.contains_log(&nested_error_debug_string(&error)));
133 }
134
135 #[test]
136 fn log_keep_state_without_nested_error() {
137 let (logger, log_inspector) = TestLogger::memory();
138
139 let error = RuntimeError::KeepState {
140 message: "KeepState error".to_string(),
141 nested_error: None,
142 };
143 error.write_to_log(&logger);
144
145 assert!(log_inspector.contains_log(&format!("{error}")));
146 assert!(!log_inspector.contains_log("nested_error"));
147 }
148
149 #[test]
150 fn log_keep_state_with_nested_error() {
151 let (logger, log_inspector) = TestLogger::memory();
152
153 let error = RuntimeError::KeepState {
154 message: "KeepState error".to_string(),
155 nested_error: Some(
156 anyhow!("Another context error")
157 .context("Context error")
158 .context("KeepState nested error"),
159 ),
160 };
161 error.write_to_log(&logger);
162
163 assert!(log_inspector.contains_log(&format!("{error}")));
164 assert!(log_inspector.contains_log(&nested_error_debug_string(&error)));
165 }
166}