tauri_plugin_iap/
error.rs1use serde::{ser::Serializer, Serialize};
2
3pub type Result<T> = std::result::Result<T, Error>;
4
5#[cfg(desktop)]
7#[derive(Debug, thiserror::Error, Clone, serde::Deserialize)]
8pub struct ErrorResponse<T = ()> {
9 pub code: Option<String>,
11 pub message: Option<String>,
13 #[serde(flatten)]
15 pub data: T,
16}
17
18#[cfg(desktop)]
19impl<T> std::fmt::Display for ErrorResponse<T> {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 if let Some(code) = &self.code {
22 write!(f, "[{code}]")?;
23 if self.message.is_some() {
24 write!(f, " - ")?;
25 }
26 }
27 if let Some(message) = &self.message {
28 write!(f, "{message}")?;
29 }
30 Ok(())
31 }
32}
33
34#[cfg(desktop)]
36#[derive(Debug, thiserror::Error)]
37pub enum PluginInvokeError {
38 #[error(transparent)]
40 InvokeRejected(#[from] ErrorResponse),
41 #[error("failed to deserialize response: {0}")]
43 CannotDeserializeResponse(serde_json::Error),
44 #[error("failed to serialize payload: {0}")]
46 CannotSerializePayload(serde_json::Error),
47}
48
49#[derive(Debug, thiserror::Error)]
50pub enum Error {
51 #[error(transparent)]
52 Io(#[from] std::io::Error),
53 #[cfg(mobile)]
54 #[error(transparent)]
55 PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
56 #[cfg(desktop)]
57 #[error(transparent)]
58 PluginInvoke(#[from] crate::error::PluginInvokeError),
59 #[cfg(target_os = "windows")]
60 #[error(transparent)]
61 WindowsApi(#[from] windows::core::Error),
62}
63
64impl Serialize for Error {
65 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
66 where
67 S: Serializer,
68 {
69 serializer.serialize_str(self.to_string().as_ref())
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76
77 #[test]
78 fn test_error_io_display() {
79 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
80 let error = Error::Io(io_error);
81 let display = error.to_string();
82 assert!(display.contains("file not found"));
83 }
84
85 #[test]
86 fn test_error_serialize() {
87 let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "test error");
88 let error = Error::Io(io_error);
89 let serialized = serde_json::to_string(&error).expect("Failed to serialize Error");
90 assert!(serialized.contains("test error"));
91 }
92
93 #[test]
94 fn test_error_from_io_error() {
95 let io_error = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
96 let error: Error = io_error.into();
97 assert!(error.to_string().contains("access denied"));
98 }
99
100 #[cfg(desktop)]
101 mod desktop_tests {
102 use super::*;
103
104 #[test]
105 fn test_error_response_display_code_only() {
106 let response = ErrorResponse {
107 code: Some("ERR001".to_string()),
108 message: None,
109 data: (),
110 };
111 assert_eq!(response.to_string(), "[ERR001]");
112 }
113
114 #[test]
115 fn test_error_response_display_message_only() {
116 let response = ErrorResponse {
117 code: None,
118 message: Some("Something went wrong".to_string()),
119 data: (),
120 };
121 assert_eq!(response.to_string(), "Something went wrong");
122 }
123
124 #[test]
125 fn test_error_response_display_both() {
126 let response = ErrorResponse {
127 code: Some("ERR001".to_string()),
128 message: Some("Something went wrong".to_string()),
129 data: (),
130 };
131 assert_eq!(response.to_string(), "[ERR001] - Something went wrong");
132 }
133
134 #[test]
135 fn test_error_response_display_neither() {
136 let response: ErrorResponse = ErrorResponse {
137 code: None,
138 message: None,
139 data: (),
140 };
141 assert_eq!(response.to_string(), "");
142 }
143
144 #[test]
145 fn test_error_response_deserialize() {
146 let json = r#"{"code":"testCode","message":"test message"}"#;
147 let response: ErrorResponse =
148 serde_json::from_str(json).expect("Failed to deserialize ErrorResponse");
149 assert_eq!(response.code, Some("testCode".to_string()));
150 assert_eq!(response.message, Some("test message".to_string()));
151 }
152
153 #[test]
154 fn test_error_response_deserialize_partial() {
155 let json = r#"{"code":"testCode"}"#;
156 let response: ErrorResponse =
157 serde_json::from_str(json).expect("Failed to deserialize partial ErrorResponse");
158 assert_eq!(response.code, Some("testCode".to_string()));
159 assert_eq!(response.message, None);
160 }
161
162 #[test]
163 fn test_plugin_invoke_error_invoke_rejected() {
164 let response = ErrorResponse {
165 code: Some("rejected".to_string()),
166 message: Some("Request rejected".to_string()),
167 data: (),
168 };
169 let error = PluginInvokeError::InvokeRejected(response);
170 let display = error.to_string();
171 assert!(display.contains("rejected"));
172 assert!(display.contains("Request rejected"));
173 }
174
175 #[test]
176 fn test_plugin_invoke_error_deserialize_failed() {
177 let json_error =
178 serde_json::from_str::<i32>("not a number").expect_err("Expected JSON parse error");
179 let error = PluginInvokeError::CannotDeserializeResponse(json_error);
180 let display = error.to_string();
181 assert!(display.contains("failed to deserialize response"));
182 }
183
184 #[test]
185 fn test_plugin_invoke_error_serialize_failed() {
186 let json_error = serde_json::from_str::<serde_json::Value>("{invalid}")
189 .expect_err("Expected JSON parse error");
190 let error = PluginInvokeError::CannotSerializePayload(json_error);
191 let display = error.to_string();
192 assert!(display.contains("failed to serialize payload"));
193 }
194
195 #[test]
196 fn test_error_from_plugin_invoke_error() {
197 let response = ErrorResponse {
198 code: Some("test".to_string()),
199 message: Some("test".to_string()),
200 data: (),
201 };
202 let plugin_error = PluginInvokeError::InvokeRejected(response);
203 let error: Error = plugin_error.into();
204 assert!(error.to_string().contains("test"));
205 }
206 }
207}