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>;