#[derive(Debug, thiserror::Error, Clone, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
pub enum ServiceError {
#[error("Service is already running")]
AlreadyRunning,
#[error("Service is not running")]
NotRunning,
#[error("Initialisation failed: {0}")]
Init(String),
#[error("Runtime error: {0}")]
Runtime(String),
#[error("Platform error: {0}")]
Platform(String),
#[cfg(mobile)]
#[error("Plugin invoke error: {0}")]
PluginInvoke(String),
#[cfg(feature = "desktop-service")]
#[error("Service installation failed: {0}")]
ServiceInstall(String),
#[cfg(feature = "desktop-service")]
#[error("Service uninstallation failed: {0}")]
ServiceUninstall(String),
#[cfg(feature = "desktop-service")]
#[error("IPC error: {0}")]
Ipc(String),
#[cfg(feature = "desktop-service")]
#[error("Service start failed: {0}")]
ServiceStart(String),
#[cfg(feature = "desktop-service")]
#[error("Service stop failed: {0}")]
ServiceStop(String),
}
#[cfg(mobile)]
impl From<tauri::plugin::mobile::PluginInvokeError> for ServiceError {
fn from(e: tauri::plugin::mobile::PluginInvokeError) -> Self {
Self::PluginInvoke(e.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_already_running() {
assert_eq!(
ServiceError::AlreadyRunning.to_string(),
"Service is already running"
);
}
#[test]
fn display_not_running() {
assert_eq!(
ServiceError::NotRunning.to_string(),
"Service is not running"
);
}
#[test]
fn display_init() {
let msg = "db connection failed".to_string();
assert_eq!(
ServiceError::Init(msg.clone()).to_string(),
format!("Initialisation failed: {msg}")
);
}
#[test]
fn display_runtime() {
let msg = "network timeout".to_string();
assert_eq!(
ServiceError::Runtime(msg.clone()).to_string(),
format!("Runtime error: {msg}")
);
}
#[test]
fn display_platform() {
let msg = "foreground service denied".to_string();
assert_eq!(
ServiceError::Platform(msg.clone()).to_string(),
format!("Platform error: {msg}")
);
}
#[test]
fn convert_to_invoke_error_via_serialize() {
let err = ServiceError::Init("test".into());
let invoke_err: tauri::ipc::InvokeError = err.into();
let _val = &invoke_err.0;
assert!(!invoke_err.0.is_null());
}
#[test]
fn clone_roundtrip() {
let err = ServiceError::Init("test".into());
let cloned = err.clone();
assert_eq!(err.to_string(), cloned.to_string());
}
#[test]
fn serde_roundtrip_already_running() {
let err = ServiceError::AlreadyRunning;
let json = serde_json::to_string(&err).unwrap();
let de: ServiceError = serde_json::from_str(&json).unwrap();
assert!(matches!(de, ServiceError::AlreadyRunning));
}
#[test]
fn serde_roundtrip_init() {
let err = ServiceError::Init("boom".into());
let json = serde_json::to_string(&err).unwrap();
let de: ServiceError = serde_json::from_str(&json).unwrap();
assert!(matches!(de, ServiceError::Init(ref s) if s == "boom"));
}
#[cfg(feature = "desktop-service")]
mod desktop_service {
use super::*;
#[test]
fn display_service_install() {
let msg = "permission denied".to_string();
assert_eq!(
ServiceError::ServiceInstall(msg.clone()).to_string(),
format!("Service installation failed: {msg}")
);
}
#[test]
fn display_service_uninstall() {
let msg = "not found".to_string();
assert_eq!(
ServiceError::ServiceUninstall(msg.clone()).to_string(),
format!("Service uninstallation failed: {msg}")
);
}
#[test]
fn display_ipc_error() {
let msg = "connection lost".to_string();
assert_eq!(
ServiceError::Ipc(msg.clone()).to_string(),
format!("IPC error: {msg}")
);
}
#[test]
fn serde_roundtrip_service_install() {
let err = ServiceError::ServiceInstall("fail".into());
let json = serde_json::to_string(&err).unwrap();
let de: ServiceError = serde_json::from_str(&json).unwrap();
assert!(matches!(de, ServiceError::ServiceInstall(ref s) if s == "fail"));
}
#[test]
fn serde_roundtrip_service_uninstall() {
let err = ServiceError::ServiceUninstall("fail".into());
let json = serde_json::to_string(&err).unwrap();
let de: ServiceError = serde_json::from_str(&json).unwrap();
assert!(matches!(de, ServiceError::ServiceUninstall(ref s) if s == "fail"));
}
#[test]
fn serde_roundtrip_ipc() {
let err = ServiceError::Ipc("socket closed".into());
let json = serde_json::to_string(&err).unwrap();
let de: ServiceError = serde_json::from_str(&json).unwrap();
assert!(matches!(de, ServiceError::Ipc(ref s) if s == "socket closed"));
}
#[test]
fn clone_roundtrip_service_install() {
let err = ServiceError::ServiceInstall("fail".into());
let cloned = err.clone();
assert_eq!(err.to_string(), cloned.to_string());
}
#[test]
fn clone_roundtrip_service_uninstall() {
let err = ServiceError::ServiceUninstall("fail".into());
let cloned = err.clone();
assert_eq!(err.to_string(), cloned.to_string());
}
#[test]
fn clone_roundtrip_ipc() {
let err = ServiceError::Ipc("timeout".into());
let cloned = err.clone();
assert_eq!(err.to_string(), cloned.to_string());
}
#[test]
fn display_service_start() {
let msg = "systemd failed".to_string();
assert_eq!(
ServiceError::ServiceStart(msg.clone()).to_string(),
format!("Service start failed: {msg}")
);
}
#[test]
fn display_service_stop() {
let msg = "not running".to_string();
assert_eq!(
ServiceError::ServiceStop(msg.clone()).to_string(),
format!("Service stop failed: {msg}")
);
}
#[test]
fn serde_roundtrip_service_start() {
let err = ServiceError::ServiceStart("fail".into());
let json = serde_json::to_string(&err).unwrap();
let de: ServiceError = serde_json::from_str(&json).unwrap();
assert!(matches!(de, ServiceError::ServiceStart(ref s) if s == "fail"));
}
#[test]
fn serde_roundtrip_service_stop() {
let err = ServiceError::ServiceStop("fail".into());
let json = serde_json::to_string(&err).unwrap();
let de: ServiceError = serde_json::from_str(&json).unwrap();
assert!(matches!(de, ServiceError::ServiceStop(ref s) if s == "fail"));
}
}
}