use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{json, Value as JsonValue}; use std::{fmt, io}; /// The error type of `tauri-utils`. #[derive(Debug, thiserror::Error)] pub enum SidecarError { /// An IO error occurred on the underlying communication channel. #[error(transparent)] Io(#[from] io::Error), /// The peer returned an error. #[error("Remote error: {0}")] RemoteError(RemoteError), /// The peer closed the connection. #[error("Peer closed the connection.")] PeerDisconnect, /// The peer sent a response containing the id, but was malformed. #[error("Invalid response.")] InvalidResponse, #[error(transparent)] Internal(#[from] anyhow::Error), } #[derive(Debug)] pub enum ReadError { /// An error occurred in the underlying stream Io(io::Error), /// The message was not valid JSON. Json(serde_json::Error), /// The message was not a JSON object. NotObject(String), /// The the method and params were not recognized by the handler. UnknownRequest(serde_json::Error), /// The peer closed the connection. Disconnect, } #[derive(Debug, Clone, thiserror::Error)] pub enum RemoteError { /// The JSON was valid, but was not a correctly formed request. /// /// This Error is used internally, and should not be returned by /// clients. #[error("Invalid request: {0:?}")] InvalidRequest(Option), #[error("Invalid response: {0}")] InvalidResponse(JsonValue), #[error("Parse response: {0}")] ParseResponse(JsonValue), /// A custom error, defined by the client. #[error("Custom error: {message}")] Custom { code: i64, message: String, data: Option, }, /// An error that cannot be represented by an error object. /// /// This error is intended to accommodate clients that return arbitrary /// error values. It should not be used for new errors. #[error("Unknown error: {0}")] Unknown(JsonValue), } impl ReadError { /// Returns `true` iff this is the `ReadError::Disconnect` variant. pub fn is_disconnect(&self) -> bool { matches!(*self, ReadError::Disconnect) } } impl fmt::Display for ReadError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { ReadError::Io(ref err) => write!(f, "I/O Error: {:?}", err), ReadError::Json(ref err) => write!(f, "JSON Error: {:?}", err), ReadError::NotObject(s) => write!(f, "Expected JSON object, found: {}", s), ReadError::UnknownRequest(ref err) => write!(f, "Unknown request: {:?}", err), ReadError::Disconnect => write!(f, "Peer closed the connection."), } } } impl From for ReadError { fn from(err: serde_json::Error) -> ReadError { ReadError::Json(err) } } impl From for ReadError { fn from(err: io::Error) -> ReadError { ReadError::Io(err) } } impl From for RemoteError { fn from(err: serde_json::Error) -> RemoteError { RemoteError::InvalidRequest(Some(json!(err.to_string()))) } } impl From for SidecarError { fn from(err: RemoteError) -> SidecarError { SidecarError::RemoteError(err) } } #[derive(Deserialize, Serialize)] struct ErrorHelper { code: i64, message: String, #[serde(skip_serializing_if = "Option::is_none")] data: Option, } impl<'de> Deserialize<'de> for RemoteError { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let v = JsonValue::deserialize(deserializer)?; let resp = match ErrorHelper::deserialize(&v) { Ok(resp) => resp, Err(_) => return Ok(RemoteError::Unknown(v)), }; Ok(match resp.code { -32600 => RemoteError::InvalidRequest(resp.data), _ => RemoteError::Custom { code: resp.code, message: resp.message, data: resp.data, }, }) } } impl Serialize for RemoteError { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let (code, message, data) = match self { RemoteError::InvalidRequest(ref d) => (-32600, "Invalid request".to_string(), d.clone()), RemoteError::Custom { code, ref message, ref data, } => (*code, message.clone(), data.clone()), RemoteError::Unknown(_) => { panic!("The 'Unknown' error variant is not intended for client use.") }, RemoteError::InvalidResponse(resp) => ( -1, "Invalid response".to_string(), Some(json!(resp.to_string())), ), RemoteError::ParseResponse(resp) => ( -1, "Invalid response".to_string(), Some(json!(resp.to_string())), ), }; let err = ErrorHelper { code, message, data, }; err.serialize(serializer) } }