1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use thiserror::Error;
4
5#[derive(Debug, Error, Clone, Serialize, JsonSchema)]
7pub enum Error {
8 #[error("iii is not connected")]
9 NotConnected,
10 #[error("invocation timed out")]
11 Timeout,
12 #[error("runtime error: {0}")]
13 Runtime(String),
14 #[error("remote error ({code}): {message}")]
15 Remote {
16 code: String,
17 message: String,
18 stacktrace: Option<String>,
19 },
20 #[error("handler error: {0}")]
21 Handler(String),
22 #[error("serialization error: {0}")]
23 Serde(String),
24 #[error("websocket error: {0}")]
25 WebSocket(String),
26 #[error(
32 "registration rejected ({code}) in namespace '{namespace}' by worker '{owner_worker_id}'"
33 )]
34 RegistrationRejected {
35 code: String,
36 namespace: String,
37 worker_name: Option<String>,
38 function_id: Option<String>,
39 owner_worker_id: String,
40 },
41}
42
43impl From<serde_json::Error> for Error {
44 fn from(err: serde_json::Error) -> Self {
45 Error::Serde(err.to_string())
46 }
47}
48
49impl From<String> for Error {
50 fn from(msg: String) -> Self {
51 Error::Handler(msg)
52 }
53}
54
55impl From<&str> for Error {
56 fn from(msg: &str) -> Self {
57 Error::Handler(msg.to_string())
58 }
59}
60
61impl From<tokio_tungstenite::tungstenite::Error> for Error {
62 fn from(err: tokio_tungstenite::tungstenite::Error) -> Self {
63 Error::WebSocket(err.to_string())
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
73pub struct InvocationError {
74 pub code: String,
75 pub message: String,
76 #[serde(skip_serializing_if = "Option::is_none")]
77 pub function_id: Option<String>,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub stacktrace: Option<String>,
80}
81
82impl std::fmt::Display for InvocationError {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 write!(f, "{}: {}", self.code, self.message)
85 }
86}
87
88impl std::error::Error for InvocationError {}
89
90impl Error {
91 pub fn invocation_error(&self) -> Option<InvocationError> {
94 match self {
95 Error::Remote {
96 code,
97 message,
98 stacktrace,
99 } => Some(InvocationError {
100 code: code.clone(),
101 message: message.clone(),
102 function_id: None,
103 stacktrace: stacktrace.clone(),
104 }),
105 _ => None,
106 }
107 }
108}
109
110#[cfg(test)]
111mod invocation_error_tests {
112 use super::*;
113
114 #[test]
115 fn remote_error_yields_invocation_error() {
116 let err = Error::Remote {
117 code: "FORBIDDEN".into(),
118 message: "nope".into(),
119 stacktrace: Some("trace".into()),
120 };
121 let inv = err.invocation_error().expect("remote -> invocation");
122 assert_eq!(inv.code, "FORBIDDEN");
123 assert_eq!(inv.message, "nope");
124 assert_eq!(inv.stacktrace.as_deref(), Some("trace"));
125 assert_eq!(inv.to_string(), "FORBIDDEN: nope");
126 }
127
128 #[test]
129 fn non_remote_error_yields_none() {
130 assert!(Error::Timeout.invocation_error().is_none());
131 }
132}