1use core::fmt;
2use serde::{Deserialize, Serialize};
3use std::{io::ErrorKind, string::FromUtf8Error};
4use thiserror::Error;
5
6use crate::{Result, Vars};
7
8#[derive(Deserialize, Serialize, Error, Debug, Clone, PartialEq)]
9pub enum ActError {
10 #[error("{0}")]
11 Config(String),
12
13 #[error("{0}")]
14 Convert(String),
15
16 #[error("{0}")]
17 Script(String),
18
19 #[error("ecode: {ecode}, message: {message}")]
20 Exception { ecode: String, message: String },
21
22 #[error("{0}")]
23 Model(String),
24
25 #[error("{0}")]
26 Runtime(String),
27
28 #[error("{0}")]
29 Store(String),
30
31 #[error("{0}")]
32 Action(String),
33
34 #[error("{0}")]
35 IoError(String),
36
37 #[error("{0}")]
38 Package(String),
39}
40
41#[derive(Default, Debug, Clone, Deserialize, Serialize)]
42pub struct Error {
43 #[serde(default)]
44 pub ecode: String,
45 #[serde(default)]
46 pub message: String,
47}
48
49impl fmt::Display for Error {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 let text = serde_json::to_string(self).unwrap();
52 f.write_str(&text)
53 }
54}
55
56impl Error {
57 pub fn new(message: &str, ecode: &str) -> Self {
58 Self {
59 message: message.to_string(),
60 ecode: ecode.to_string(),
61 }
62 }
63
64 pub fn from_var(value: &Vars) -> Result<Self> {
65 serde_json::from_value::<Self>(value.clone().into()).map_err(|err| err.into())
66 }
67}
68
69impl From<ActError> for String {
70 fn from(val: ActError) -> Self {
71 val.to_string()
72 }
73}
74
75impl From<ActError> for Vars {
76 fn from(val: ActError) -> Self {
77 match val {
78 ActError::Exception { ecode, message } => {
79 Vars::new().with("ecode", ecode).with("message", message)
80 }
81 err => Vars::new()
82 .with("ecode", "")
83 .with("message", err.to_string()),
84 }
85 }
86}
87
88impl From<ActError> for Error {
89 fn from(val: ActError) -> Self {
90 match val {
91 ActError::Exception { ecode, message } => Error { ecode, message },
92 err => Error {
93 ecode: "".to_string(),
94 message: err.to_string(),
95 },
96 }
97 }
98}
99
100impl From<std::io::Error> for ActError {
101 fn from(error: std::io::Error) -> Self {
102 ActError::IoError(error.to_string())
103 }
104}
105
106impl From<ActError> for std::io::Error {
107 fn from(val: ActError) -> Self {
108 #[allow(clippy::io_other_error)]
109 std::io::Error::new(ErrorKind::Other, val.to_string())
110 }
111}
112
113impl From<rquickjs::Error> for ActError {
114 fn from(error: rquickjs::Error) -> Self {
115 ActError::Script(error.to_string())
116 }
117}
118
119impl From<ActError> for rquickjs::Error {
120 fn from(val: ActError) -> Self {
121 std::io::Error::other(val.to_string()).into()
122 }
123}
124
125impl From<FromUtf8Error> for ActError {
126 fn from(_: FromUtf8Error) -> Self {
127 ActError::Runtime("Error with utf-8 string convert".to_string())
128 }
129}
130
131impl From<serde_json::Error> for ActError {
132 fn from(error: serde_json::Error) -> Self {
133 ActError::Convert(error.to_string())
134 }
135}
136
137impl<'a> From<rquickjs::CaughtError<'a>> for ActError {
138 fn from(error: rquickjs::CaughtError<'a>) -> Self {
139 ActError::Script(error.to_string())
140 }
141}
142
143impl From<jsonschema::ValidationError<'_>> for ActError {
144 fn from(error: jsonschema::ValidationError<'_>) -> Self {
145 ActError::Runtime(error.to_string())
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use serde_json::json;
152
153 use crate::{ActError, Error, Vars};
154
155 #[test]
156 fn engine_error_default() {
157 let err = Error::default();
158 assert_eq!(err.message, "");
159 assert_eq!(err.ecode, "");
160 }
161
162 #[test]
163 fn engine_error_json_full() {
164 let err = Error::new("abc", "err1");
165 let v = serde_json::to_value(err).unwrap();
166 assert_eq!(v, json!({ "ecode": "err1", "message": "abc" }))
167 }
168
169 #[test]
170 fn engine_error_from_value() {
171 let err = Vars::new().with("ecode", "err1").with("message", "test");
172 let v = Error::from_var(&err).unwrap();
173 assert_eq!(v.ecode, "err1");
174 assert_eq!(v.message, "test");
175 }
176
177 #[test]
178 fn engine_act_error_into() {
179 let err = ActError::Action("error message".to_string());
180 let v: Error = err.into();
181 assert_eq!(v.message, "error message");
182 assert_eq!(v.ecode, "");
183 }
184
185 #[test]
186 fn engine_act_exception_into() {
187 let err = ActError::Exception {
188 ecode: "err1".to_string(),
189 message: "error message".to_string(),
190 };
191 let v: Error = err.into();
192 assert_eq!(v.message, "error message");
193 assert_eq!(v.ecode, "err1");
194 }
195}