tauri_plugin_iap/
error.rs

1use serde::{ser::Serializer, Serialize};
2
3pub type Result<T> = std::result::Result<T, Error>;
4
5/// Replica of the tauri::plugin::mobile::ErrorResponse for desktop platforms.
6#[cfg(desktop)]
7#[derive(Debug, thiserror::Error, Clone, serde::Deserialize)]
8pub struct ErrorResponse<T = ()> {
9    /// Error code.
10    pub code: Option<String>,
11    /// Error message.
12    pub message: Option<String>,
13    /// Optional error data.
14    #[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/// Replica of the tauri::plugin::mobile::PluginInvokeError for desktop platforms.
35#[cfg(desktop)]
36#[derive(Debug, thiserror::Error)]
37pub enum PluginInvokeError {
38    /// Error returned from direct desktop plugin.
39    #[error(transparent)]
40    InvokeRejected(#[from] ErrorResponse),
41    /// Failed to deserialize response.
42    #[error("failed to deserialize response: {0}")]
43    CannotDeserializeResponse(serde_json::Error),
44    /// Failed to serialize request payload.
45    #[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            // Create a serialization error by trying to serialize from invalid JSON
187            // We can reuse a deserialization error type since serde_json::Error is the same type
188            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}