Skip to main content

tauri_plugin_hot_update/
error.rs

1use serde::{Serialize, Serializer};
2
3/// Errors surfaced by the hot-update plugin.
4///
5/// Every verification gate in the update pipeline is a hard stop with its own
6/// variant — a failed signature, hash, or size check aborts the update; there
7/// is no warn-and-continue path anywhere in this module.
8#[derive(Debug, thiserror::Error)]
9#[non_exhaustive]
10pub enum Error {
11    #[error(transparent)]
12    Io(#[from] std::io::Error),
13
14    /// The plugin was not initialized: either `install()` was not called on
15    /// the context, or `.plugin(init(handle))` was not registered, or
16    /// initialization failed at boot (in which case the app serves the
17    /// embedded bundle — the fail-safe floor).
18    #[error("hot-update is not active (plugin not initialized); serving embedded assets")]
19    NotActive,
20
21    /// A staging request was refused by the state machine gates.
22    #[error("stage refused: {0}")]
23    StageRefused(#[from] crate::machine::StageError),
24
25    /// Invalid `plugins.hot-update` configuration. Raised from the plugin's
26    /// setup hook, so it aborts app startup — config ships inside the store
27    /// binary and must be caught on the developer's first run.
28    #[error("hot-update config invalid: {0}")]
29    Config(String),
30
31    /// `check`/`download` was invoked while the plugin is disabled by
32    /// config (`plugins.hot-update.enabled` is false).
33    #[error("hot-update is disabled by config (`plugins.hot-update.enabled` is false)")]
34    Disabled,
35
36    /// A configured trusted public key is not valid minisign key material.
37    /// This is a hard stop even when other keys in the list would verify:
38    /// silently skipping a malformed trust anchor would weaken the key list
39    /// without anyone noticing.
40    #[error("invalid minisign public key in the trusted key list")]
41    InvalidPublicKey,
42
43    /// The manifest signature failed: undecodable `.minisig` data, or no key
44    /// in the trusted list verified the manifest bytes.
45    #[error("manifest signature rejected: {0}")]
46    ManifestSignature(String),
47
48    /// The signed manifest bytes are not valid JSON for the manifest schema.
49    #[error("manifest is not valid manifest JSON: {0}")]
50    ManifestParse(#[from] serde_json::Error),
51
52    /// The manifest parsed but declares nonsensical values (malformed
53    /// sha256, zero or over-cap archive size).
54    #[error("manifest invalid: {0}")]
55    ManifestInvalid(String),
56
57    /// Transport-level HTTP failure (connect, TLS, read).
58    #[error("update request failed: {0}")]
59    Http(#[from] reqwest::Error),
60
61    /// The update server answered with a non-success status.
62    #[error("update server returned HTTP {status} for {url}")]
63    HttpStatus { status: u16, url: String },
64
65    /// A response body exceeded its sanity cap (manifest or signature).
66    #[error("response for {url} exceeds the {limit}-byte cap")]
67    ResponseTooLarge { url: String, limit: u64 },
68
69    /// The downloaded archive's byte count diverged from the signed
70    /// manifest's `archive.size` (short body, or a stream that kept going).
71    #[error("archive size mismatch: manifest declares {declared} bytes, got {actual}")]
72    ArchiveSize { declared: u64, actual: u64 },
73
74    /// The downloaded archive's sha256 diverged from the signed manifest.
75    #[error("archive sha256 mismatch: manifest declares {declared}, got {actual}")]
76    ArchiveSha256 { declared: String, actual: String },
77
78    /// Archive extraction was refused (hostile entry) or failed.
79    #[error(transparent)]
80    Extract(#[from] crate::extract::ExtractError),
81}
82
83impl Serialize for Error {
84    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
85    where
86        S: Serializer,
87    {
88        serializer.serialize_str(self.to_string().as_ref())
89    }
90}
91
92pub type Result<T> = std::result::Result<T, Error>;