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