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: 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    /// Append the given description to the original one, in case the code is the same
117    #[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        // Test successful conversions
214        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        // Test failed conversions
288        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}