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