Skip to main content

tauri_plugin_configurate/
error.rs

1use serde::{ser::Serializer, Serialize};
2
3pub type Result<T> = std::result::Result<T, Error>;
4
5#[derive(Debug, thiserror::Error)]
6pub enum Error {
7    #[error(transparent)]
8    Io(#[from] std::io::Error),
9
10    /// Errors originating from storage serialization/deserialization.
11    #[error("storage error: {0}")]
12    Storage(String),
13
14    /// Errors originating from the OS keyring.
15    #[error("keyring error: {0}")]
16    Keyring(String),
17
18    /// Errors when resolving a dotpath inside a JSON value.
19    #[error("dotpath error: {0}")]
20    Dotpath(String),
21
22    /// Invalid payload sent from the frontend (wrong field combination, bad value, etc.).
23    #[error("invalid payload: {0}")]
24    InvalidPayload(String),
25
26    /// Errors from serde_json.
27    #[error(transparent)]
28    Json(#[from] serde_json::Error),
29
30    #[cfg(mobile)]
31    #[error(transparent)]
32    PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
33}
34
35impl From<keyring::Error> for Error {
36    fn from(e: keyring::Error) -> Self {
37        Error::Keyring(e.to_string())
38    }
39}
40
41impl Serialize for Error {
42    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
43    where
44        S: Serializer,
45    {
46        use serde::ser::SerializeMap;
47
48        // For IO errors, include a stable `io_kind` sub-field so the frontend
49        // can distinguish "not found" from "permission denied" without parsing
50        // the human-readable message string.
51        let io_kind: Option<&'static str> = if let Error::Io(e) = self {
52            Some(match e.kind() {
53                std::io::ErrorKind::NotFound => "not_found",
54                std::io::ErrorKind::PermissionDenied => "permission_denied",
55                std::io::ErrorKind::AlreadyExists => "already_exists",
56                std::io::ErrorKind::WouldBlock => "would_block",
57                std::io::ErrorKind::InvalidInput => "invalid_input",
58                std::io::ErrorKind::TimedOut => "timed_out",
59                std::io::ErrorKind::Interrupted => "interrupted",
60                std::io::ErrorKind::OutOfMemory => "out_of_memory",
61                _ => "other",
62            })
63        } else {
64            None
65        };
66
67        let field_count = if io_kind.is_some() { 3 } else { 2 };
68        let mut map = serializer.serialize_map(Some(field_count))?;
69        let kind = match self {
70            Error::Io(_) => "io",
71            Error::Storage(_) => "storage",
72            Error::Keyring(_) => "keyring",
73            Error::Dotpath(_) => "dotpath",
74            Error::InvalidPayload(_) => "invalid_payload",
75            Error::Json(_) => "json",
76            #[cfg(mobile)]
77            Error::PluginInvoke(_) => "plugin_invoke",
78        };
79        map.serialize_entry("kind", kind)?;
80        map.serialize_entry("message", &self.to_string())?;
81        if let Some(ik) = io_kind {
82            map.serialize_entry("io_kind", ik)?;
83        }
84        map.end()
85    }
86}