Skip to main content

rtb_update/
error.rs

1//! The `UpdateError` enum.
2
3use std::sync::Arc;
4
5/// Every failure mode the self-update flow can surface.
6///
7/// `Clone` is derived so callers can route errors through retry
8/// policies or embed them in progress events without losing the
9/// underlying `io::Error`. The `Io` variant wraps in `Arc` — same
10/// pattern as `rtb-forge::ProviderError` and `rtb-credentials::CredentialError`.
11#[derive(Debug, thiserror::Error, miette::Diagnostic, Clone)]
12#[non_exhaustive]
13pub enum UpdateError {
14    /// The upstream [`rtb_forge::ProviderError`] surfaced a failure.
15    #[error(transparent)]
16    #[diagnostic(transparent)]
17    Provider(#[from] rtb_forge::ProviderError),
18
19    /// No asset on the release matched the host platform.
20    #[error("no asset found for target {target}")]
21    #[diagnostic(
22        code(rtb::update::no_matching_asset),
23        help("the release exists but has no asset for this platform; a rebuild may be needed")
24    )]
25    NoMatchingAsset {
26        /// The host target triple we tried to match.
27        target: String,
28    },
29
30    /// Required signature file was absent from the release.
31    #[error("asset signature file missing (expected `{asset}.sig` or `{asset}.minisig`)")]
32    #[diagnostic(
33        code(rtb::update::missing_signature),
34        help(
35            "every published release must ship a detached signature; re-run the release pipeline"
36        )
37    )]
38    MissingSignature {
39        /// The asset filename we looked for a signature for.
40        asset: String,
41    },
42
43    /// Ed25519 signature did not verify against any trusted public key.
44    #[error("signature verification failed for `{asset}`")]
45    #[diagnostic(
46        code(rtb::update::bad_signature),
47        help(
48            "the downloaded bytes do not match the vendor's public key — treat as a potential tampering event"
49        )
50    )]
51    BadSignature {
52        /// The asset filename whose signature failed.
53        asset: String,
54    },
55
56    /// SHA-256 checksum did not match the checksums asset.
57    #[error("SHA-256 checksum mismatch for `{asset}`")]
58    #[diagnostic(code(rtb::update::bad_checksum))]
59    BadChecksum {
60        /// The asset filename whose checksum failed.
61        asset: String,
62    },
63
64    /// The staged binary refused `--version` (or did not match the
65    /// release tag). Swap is refused.
66    #[error("downloaded binary failed the runnable-self-test")]
67    #[diagnostic(
68        code(rtb::update::self_test_failed),
69        help("the new binary refused `--version`; refusing to swap")
70    )]
71    SelfTestFailed,
72
73    /// `self-replace` failed to swap.
74    #[error("atomic swap failed: {0}")]
75    #[diagnostic(code(rtb::update::swap_failed))]
76    SwapFailed(String),
77
78    /// `ToolMetadata::release_source` is `None` — the tool has not
79    /// been configured for self-update.
80    #[error("tool metadata carries no release source; update disabled")]
81    #[diagnostic(code(rtb::update::no_source))]
82    NoReleaseSource,
83
84    /// `ToolMetadata::update_public_keys` is empty — signatures cannot
85    /// be verified so updates are refused as a security policy.
86    #[error("tool metadata carries no public key; signatures cannot be verified")]
87    #[diagnostic(
88        code(rtb::update::no_public_key),
89        help("populate `ToolMetadata::update_public_keys` at compile time")
90    )]
91    NoPublicKey,
92
93    /// The caller asked for a downgrade (`target < current`) without
94    /// `--force`. Guards against a bad `--to` value turning into a
95    /// permanent regression.
96    #[error("downgrade refused: target {target} is older than current {current}")]
97    #[diagnostic(
98        code(rtb::update::downgrade_refused),
99        help("pass `--force` to explicitly downgrade")
100    )]
101    DowngradeRefused {
102        /// The version the caller requested.
103        target: semver::Version,
104        /// The version currently installed.
105        current: semver::Version,
106    },
107
108    /// Archive extraction failed. Includes tar/gzip errors.
109    #[error("archive extraction failed: {0}")]
110    #[diagnostic(code(rtb::update::archive))]
111    Archive(String),
112
113    /// Asset pattern had a `{version}` placeholder but no value to
114    /// fill, or matched zero assets.
115    #[error("asset pattern invalid or unmatched: {0}")]
116    #[diagnostic(code(rtb::update::pattern))]
117    Pattern(String),
118
119    /// I/O error during cache-dir or swap step.
120    #[error("I/O error: {0}")]
121    #[diagnostic(code(rtb::update::io))]
122    Io(#[from] Arc<std::io::Error>),
123}
124
125impl From<std::io::Error> for UpdateError {
126    fn from(err: std::io::Error) -> Self {
127        Self::Io(Arc::new(err))
128    }
129}
130
131/// `Result<T, UpdateError>`.
132pub type Result<T> = std::result::Result<T, UpdateError>;