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 struct Error {
56    pub(crate) code: u16,
57    pub(crate) message: Cow<'static, str>,
58    pub(crate) stacktrace: String,
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 ToString) -> Self {
107        self.stacktrace = stacktrace.to_string();
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    pub fn is_suspended_error(&self) -> bool {
117        self == &crate::vm::errors::SUSPENDED
118    }
119
120    pub(crate) fn with_related_command_metadata(
121        mut self,
122        related_command: CommandMetadata,
123    ) -> Self {
124        self.related_command = Some(related_command);
125        self
126    }
127}
128
129impl From<CommandType> for MessageType {
130    fn from(value: CommandType) -> Self {
131        match value {
132            CommandType::Input => MessageType::InputCommand,
133            CommandType::Output => MessageType::OutputCommand,
134            CommandType::GetState => MessageType::GetLazyStateCommand,
135            CommandType::GetStateKeys => MessageType::GetLazyStateKeysCommand,
136            CommandType::SetState => MessageType::SetStateCommand,
137            CommandType::ClearState => MessageType::ClearStateCommand,
138            CommandType::ClearAllState => MessageType::ClearAllStateCommand,
139            CommandType::GetPromise => MessageType::GetPromiseCommand,
140            CommandType::PeekPromise => MessageType::PeekPromiseCommand,
141            CommandType::CompletePromise => MessageType::CompletePromiseCommand,
142            CommandType::Sleep => MessageType::SleepCommand,
143            CommandType::Call => MessageType::CallCommand,
144            CommandType::OneWayCall => MessageType::OneWayCallCommand,
145            CommandType::SendSignal => MessageType::SendSignalCommand,
146            CommandType::Run => MessageType::RunCommand,
147            CommandType::AttachInvocation => MessageType::AttachInvocationCommand,
148            CommandType::GetInvocationOutput => MessageType::GetInvocationOutputCommand,
149            CommandType::CompleteAwakeable => MessageType::CompleteAwakeableCommand,
150            CommandType::CancelInvocation => MessageType::SendSignalCommand,
151        }
152    }
153}
154
155impl TryFrom<MessageType> for CommandType {
156    type Error = MessageType;
157
158    fn try_from(value: MessageType) -> Result<Self, Self::Error> {
159        match value {
160            MessageType::InputCommand => Ok(CommandType::Input),
161            MessageType::OutputCommand => Ok(CommandType::Output),
162            MessageType::GetLazyStateCommand | MessageType::GetEagerStateCommand => {
163                Ok(CommandType::GetState)
164            }
165            MessageType::GetLazyStateKeysCommand | MessageType::GetEagerStateKeysCommand => {
166                Ok(CommandType::GetStateKeys)
167            }
168            MessageType::SetStateCommand => Ok(CommandType::SetState),
169            MessageType::ClearStateCommand => Ok(CommandType::ClearState),
170            MessageType::ClearAllStateCommand => Ok(CommandType::ClearAllState),
171            MessageType::GetPromiseCommand => Ok(CommandType::GetPromise),
172            MessageType::PeekPromiseCommand => Ok(CommandType::PeekPromise),
173            MessageType::CompletePromiseCommand => Ok(CommandType::CompletePromise),
174            MessageType::SleepCommand => Ok(CommandType::Sleep),
175            MessageType::CallCommand => Ok(CommandType::Call),
176            MessageType::OneWayCallCommand => Ok(CommandType::OneWayCall),
177            MessageType::SendSignalCommand => Ok(CommandType::SendSignal),
178            MessageType::RunCommand => Ok(CommandType::Run),
179            MessageType::AttachInvocationCommand => Ok(CommandType::AttachInvocation),
180            MessageType::GetInvocationOutputCommand => Ok(CommandType::GetInvocationOutput),
181            MessageType::CompleteAwakeableCommand => Ok(CommandType::CompleteAwakeable),
182            _ => Err(value),
183        }
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190
191    #[test]
192    fn test_message_type_to_command_type_conversion() {
193        // Test successful conversions
194        assert_eq!(
195            CommandType::try_from(MessageType::InputCommand).unwrap(),
196            CommandType::Input
197        );
198        assert_eq!(
199            CommandType::try_from(MessageType::OutputCommand).unwrap(),
200            CommandType::Output
201        );
202        assert_eq!(
203            CommandType::try_from(MessageType::GetLazyStateCommand).unwrap(),
204            CommandType::GetState
205        );
206        assert_eq!(
207            CommandType::try_from(MessageType::GetLazyStateKeysCommand).unwrap(),
208            CommandType::GetStateKeys
209        );
210        assert_eq!(
211            CommandType::try_from(MessageType::SetStateCommand).unwrap(),
212            CommandType::SetState
213        );
214        assert_eq!(
215            CommandType::try_from(MessageType::ClearStateCommand).unwrap(),
216            CommandType::ClearState
217        );
218        assert_eq!(
219            CommandType::try_from(MessageType::ClearAllStateCommand).unwrap(),
220            CommandType::ClearAllState
221        );
222        assert_eq!(
223            CommandType::try_from(MessageType::GetPromiseCommand).unwrap(),
224            CommandType::GetPromise
225        );
226        assert_eq!(
227            CommandType::try_from(MessageType::PeekPromiseCommand).unwrap(),
228            CommandType::PeekPromise
229        );
230        assert_eq!(
231            CommandType::try_from(MessageType::CompletePromiseCommand).unwrap(),
232            CommandType::CompletePromise
233        );
234        assert_eq!(
235            CommandType::try_from(MessageType::SleepCommand).unwrap(),
236            CommandType::Sleep
237        );
238        assert_eq!(
239            CommandType::try_from(MessageType::CallCommand).unwrap(),
240            CommandType::Call
241        );
242        assert_eq!(
243            CommandType::try_from(MessageType::OneWayCallCommand).unwrap(),
244            CommandType::OneWayCall
245        );
246        assert_eq!(
247            CommandType::try_from(MessageType::SendSignalCommand).unwrap(),
248            CommandType::SendSignal
249        );
250        assert_eq!(
251            CommandType::try_from(MessageType::RunCommand).unwrap(),
252            CommandType::Run
253        );
254        assert_eq!(
255            CommandType::try_from(MessageType::AttachInvocationCommand).unwrap(),
256            CommandType::AttachInvocation
257        );
258        assert_eq!(
259            CommandType::try_from(MessageType::GetInvocationOutputCommand).unwrap(),
260            CommandType::GetInvocationOutput
261        );
262        assert_eq!(
263            CommandType::try_from(MessageType::CompleteAwakeableCommand).unwrap(),
264            CommandType::CompleteAwakeable
265        );
266
267        // Test failed conversions
268        assert_eq!(
269            CommandType::try_from(MessageType::Start).err().unwrap(),
270            MessageType::Start
271        );
272        assert_eq!(
273            CommandType::try_from(MessageType::End).err().unwrap(),
274            MessageType::End
275        );
276        assert_eq!(
277            CommandType::try_from(MessageType::GetLazyStateCompletionNotification)
278                .err()
279                .unwrap(),
280            MessageType::GetLazyStateCompletionNotification
281        );
282    }
283}