1use crate::service_protocol::messages::ErrorBehavior;
2use crate::service_protocol::MessageType;
3use crate::CommandType;
4use std::borrow::Cow;
5use std::fmt;
6use std::time::Duration;
7
8pub use crate::vm::errors::{codes, InvocationErrorCode};
10
11#[derive(Debug, Clone, Eq, PartialEq)]
14pub(crate) struct CommandMetadata {
15 pub(crate) index: u32,
16 pub(crate) ty: MessageType,
17 pub(crate) name: Option<Cow<'static, str>>,
18}
19
20impl fmt::Display for CommandMetadata {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 write!(f, "{} ", self.ty)?;
23 if let Some(name) = &self.name {
24 write!(f, "[{name}]")?;
25 } else {
26 write!(f, "[{}]", self.index)?;
27 }
28 Ok(())
29 }
30}
31
32impl CommandMetadata {
33 pub(crate) fn new_named(
34 name: impl Into<Cow<'static, str>>,
35 index: u32,
36 ty: MessageType,
37 ) -> Self {
38 Self {
39 name: Some(name.into()),
40 index,
41 ty,
42 }
43 }
44
45 #[allow(unused)]
46 pub(crate) fn new(index: u32, ty: MessageType) -> Self {
47 Self {
48 name: None,
49 index,
50 ty,
51 }
52 }
53}
54
55#[derive(Debug, Clone, Eq, PartialEq)]
56pub(crate) enum NotificationMetadata {
57 RelatedToCommand(CommandMetadata),
58 Awakeable(String),
59 Cancellation,
60}
61
62impl fmt::Display for NotificationMetadata {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match self {
65 NotificationMetadata::RelatedToCommand(cmd) => write!(f, "{}", cmd),
66 NotificationMetadata::Awakeable(awk_id) => write!(f, "Awakeable {}", awk_id),
67 NotificationMetadata::Cancellation => write!(f, "Cancellation"),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Eq, PartialEq)]
73pub struct Error {
74 pub(crate) code: u16,
75 pub(crate) message: Cow<'static, str>,
76 pub(crate) stacktrace: String,
77 pub(crate) related_command: Option<CommandMetadata>,
78 pub(crate) next_retry_delay: Option<Duration>,
79 pub(crate) behavior: ErrorBehavior,
80}
81
82impl fmt::Display for Error {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "({}) {}", self.code, self.message)?;
85 if !self.stacktrace.is_empty() {
86 write!(f, "\nStacktrace: {}", self.stacktrace)?;
87 }
88 if let Some(related_command) = &self.related_command {
89 write!(f, "\nRelated command: {related_command}")?;
90 }
91
92 Ok(())
93 }
94}
95
96impl std::error::Error for Error {}
97
98impl Error {
99 pub fn new(code: impl Into<u16>, message: impl Into<Cow<'static, str>>) -> Self {
100 Error {
101 code: code.into(),
102 message: message.into(),
103 stacktrace: Default::default(),
104 related_command: None,
105 next_retry_delay: None,
106 behavior: ErrorBehavior::Retry,
107 }
108 }
109
110 pub fn internal(message: impl Into<Cow<'static, str>>) -> Self {
111 Self::new(codes::INTERNAL, message)
112 }
113
114 pub fn code(&self) -> u16 {
115 self.code
116 }
117
118 pub fn message(&self) -> &str {
119 &self.message
120 }
121
122 pub fn description(&self) -> &str {
123 &self.stacktrace
124 }
125
126 pub fn with_stacktrace(mut self, stacktrace: impl ToString) -> Self {
127 self.stacktrace = stacktrace.to_string();
128 self
129 }
130
131 pub fn with_next_retry_delay_override(mut self, delay: Duration) -> Self {
132 self.next_retry_delay = Some(delay);
133 self
134 }
135
136 pub fn with_should_pause(mut self, should_pause: bool) -> Self {
139 self.behavior = if should_pause {
140 ErrorBehavior::Pause
141 } else {
142 ErrorBehavior::Retry
143 };
144 self
145 }
146
147 pub fn is_suspended_error(&self) -> bool {
148 self == &crate::vm::errors::SUSPENDED
149 }
150
151 pub(crate) fn with_related_command_metadata(
152 mut self,
153 related_command: CommandMetadata,
154 ) -> Self {
155 self.related_command = Some(related_command);
156 self
157 }
158}
159
160impl From<CommandType> for MessageType {
161 fn from(value: CommandType) -> Self {
162 match value {
163 CommandType::Input => MessageType::InputCommand,
164 CommandType::Output => MessageType::OutputCommand,
165 CommandType::GetState => MessageType::GetLazyStateCommand,
166 CommandType::GetStateKeys => MessageType::GetLazyStateKeysCommand,
167 CommandType::SetState => MessageType::SetStateCommand,
168 CommandType::ClearState => MessageType::ClearStateCommand,
169 CommandType::ClearAllState => MessageType::ClearAllStateCommand,
170 CommandType::GetPromise => MessageType::GetPromiseCommand,
171 CommandType::PeekPromise => MessageType::PeekPromiseCommand,
172 CommandType::CompletePromise => MessageType::CompletePromiseCommand,
173 CommandType::Sleep => MessageType::SleepCommand,
174 CommandType::Call => MessageType::CallCommand,
175 CommandType::OneWayCall => MessageType::OneWayCallCommand,
176 CommandType::SendSignal => MessageType::SendSignalCommand,
177 CommandType::Run => MessageType::RunCommand,
178 CommandType::AttachInvocation => MessageType::AttachInvocationCommand,
179 CommandType::GetInvocationOutput => MessageType::GetInvocationOutputCommand,
180 CommandType::CompleteAwakeable => MessageType::CompleteAwakeableCommand,
181 CommandType::CancelInvocation => MessageType::SendSignalCommand,
182 }
183 }
184}
185
186impl TryFrom<MessageType> for CommandType {
187 type Error = MessageType;
188
189 fn try_from(value: MessageType) -> Result<Self, Self::Error> {
190 match value {
191 MessageType::InputCommand => Ok(CommandType::Input),
192 MessageType::OutputCommand => Ok(CommandType::Output),
193 MessageType::GetLazyStateCommand | MessageType::GetEagerStateCommand => {
194 Ok(CommandType::GetState)
195 }
196 MessageType::GetLazyStateKeysCommand | MessageType::GetEagerStateKeysCommand => {
197 Ok(CommandType::GetStateKeys)
198 }
199 MessageType::SetStateCommand => Ok(CommandType::SetState),
200 MessageType::ClearStateCommand => Ok(CommandType::ClearState),
201 MessageType::ClearAllStateCommand => Ok(CommandType::ClearAllState),
202 MessageType::GetPromiseCommand => Ok(CommandType::GetPromise),
203 MessageType::PeekPromiseCommand => Ok(CommandType::PeekPromise),
204 MessageType::CompletePromiseCommand => Ok(CommandType::CompletePromise),
205 MessageType::SleepCommand => Ok(CommandType::Sleep),
206 MessageType::CallCommand => Ok(CommandType::Call),
207 MessageType::OneWayCallCommand => Ok(CommandType::OneWayCall),
208 MessageType::SendSignalCommand => Ok(CommandType::SendSignal),
209 MessageType::RunCommand => Ok(CommandType::Run),
210 MessageType::AttachInvocationCommand => Ok(CommandType::AttachInvocation),
211 MessageType::GetInvocationOutputCommand => Ok(CommandType::GetInvocationOutput),
212 MessageType::CompleteAwakeableCommand => Ok(CommandType::CompleteAwakeable),
213 _ => Err(value),
214 }
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn test_message_type_to_command_type_conversion() {
224 assert_eq!(
226 CommandType::try_from(MessageType::InputCommand).unwrap(),
227 CommandType::Input
228 );
229 assert_eq!(
230 CommandType::try_from(MessageType::OutputCommand).unwrap(),
231 CommandType::Output
232 );
233 assert_eq!(
234 CommandType::try_from(MessageType::GetLazyStateCommand).unwrap(),
235 CommandType::GetState
236 );
237 assert_eq!(
238 CommandType::try_from(MessageType::GetLazyStateKeysCommand).unwrap(),
239 CommandType::GetStateKeys
240 );
241 assert_eq!(
242 CommandType::try_from(MessageType::SetStateCommand).unwrap(),
243 CommandType::SetState
244 );
245 assert_eq!(
246 CommandType::try_from(MessageType::ClearStateCommand).unwrap(),
247 CommandType::ClearState
248 );
249 assert_eq!(
250 CommandType::try_from(MessageType::ClearAllStateCommand).unwrap(),
251 CommandType::ClearAllState
252 );
253 assert_eq!(
254 CommandType::try_from(MessageType::GetPromiseCommand).unwrap(),
255 CommandType::GetPromise
256 );
257 assert_eq!(
258 CommandType::try_from(MessageType::PeekPromiseCommand).unwrap(),
259 CommandType::PeekPromise
260 );
261 assert_eq!(
262 CommandType::try_from(MessageType::CompletePromiseCommand).unwrap(),
263 CommandType::CompletePromise
264 );
265 assert_eq!(
266 CommandType::try_from(MessageType::SleepCommand).unwrap(),
267 CommandType::Sleep
268 );
269 assert_eq!(
270 CommandType::try_from(MessageType::CallCommand).unwrap(),
271 CommandType::Call
272 );
273 assert_eq!(
274 CommandType::try_from(MessageType::OneWayCallCommand).unwrap(),
275 CommandType::OneWayCall
276 );
277 assert_eq!(
278 CommandType::try_from(MessageType::SendSignalCommand).unwrap(),
279 CommandType::SendSignal
280 );
281 assert_eq!(
282 CommandType::try_from(MessageType::RunCommand).unwrap(),
283 CommandType::Run
284 );
285 assert_eq!(
286 CommandType::try_from(MessageType::AttachInvocationCommand).unwrap(),
287 CommandType::AttachInvocation
288 );
289 assert_eq!(
290 CommandType::try_from(MessageType::GetInvocationOutputCommand).unwrap(),
291 CommandType::GetInvocationOutput
292 );
293 assert_eq!(
294 CommandType::try_from(MessageType::CompleteAwakeableCommand).unwrap(),
295 CommandType::CompleteAwakeable
296 );
297
298 assert_eq!(
300 CommandType::try_from(MessageType::Start).err().unwrap(),
301 MessageType::Start
302 );
303 assert_eq!(
304 CommandType::try_from(MessageType::End).err().unwrap(),
305 MessageType::End
306 );
307 assert_eq!(
308 CommandType::try_from(MessageType::GetLazyStateCompletionNotification)
309 .err()
310 .unwrap(),
311 MessageType::GetLazyStateCompletionNotification
312 );
313 }
314}