use serde::{Serialize, ser::Serializer};
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Io(#[from] std::io::Error),
#[cfg(mobile)]
#[error(transparent)]
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
#[cfg(desktop)]
#[error("Biometric authentication failed: {message}")]
AuthenticationFailed {
message: String,
code: Option<String>,
},
#[cfg(desktop)]
#[error("Biometric unavailable: {message}")]
Unavailable {
message: String,
code: Option<String>,
},
}
impl Serialize for Error {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
#[cfg(desktop)]
Self::AuthenticationFailed { message, code } | Self::Unavailable { message, code } => {
#[derive(Serialize)]
struct Payload<'a> {
message: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
code: Option<&'a str>,
}
Payload {
message,
code: code.as_deref(),
}
.serialize(serializer)
}
_ => serializer.serialize_str(self.to_string().as_ref()),
}
}
}
#[cfg(all(test, desktop))]
mod tests {
use super::Error;
#[test]
fn desktop_errors_serialize_like_mobile_rejections() {
let err = Error::AuthenticationFailed {
message: "Canceled by user.".into(),
code: Some("userCancel".into()),
};
assert_eq!(
serde_json::to_value(&err).unwrap(),
serde_json::json!({ "message": "Canceled by user.", "code": "userCancel" })
);
let err = Error::Unavailable {
message: "No biometry".into(),
code: None,
};
assert_eq!(
serde_json::to_value(&err).unwrap(),
serde_json::json!({ "message": "No biometry" })
);
}
}