Skip to main content

restate_sdk_shared_core/
error.rs

1use crate::service_protocol::MessageType;
2use crate::CommandType;
3use std::borrow::Cow;
4use std::fmt;
5use std::time::Duration;
6
7// Export some stuff we need from the internal package
8pub use crate::vm::errors::{codes, InvocationErrorCode};
9
10// -- Error type
11
12#[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        // Test successful conversions
211        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        // Test failed conversions
285        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}