hyperdb_bootstrap/error.rs
1// Copyright (c) 2026, Salesforce, Inc. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! Error types returned by the `hyperd-bootstrap` crate.
5
6use thiserror::Error;
7
8/// Errors produced while downloading, verifying, and installing `hyperd`.
9///
10/// Every fallible function in this crate returns a `Result<T, Error>`. The
11/// variants line up with the phases of bootstrap: platform detection,
12/// HTTP/curl fetching, TOML parsing, archive extraction, and checksum
13/// verification.
14#[derive(Debug, Error)]
15pub enum Error {
16 /// The host (`os` / `arch` combination) has no published `hyperd` build.
17 #[error("unsupported platform: os={os} arch={arch}")]
18 UnsupportedPlatform {
19 /// Operating-system identifier returned by `std::env::consts::OS`.
20 os: String,
21 /// Architecture identifier returned by `std::env::consts::ARCH`.
22 arch: String,
23 },
24
25 /// A platform slug (e.g. `"macos-arm64"`) did not match any known target.
26 #[error("unknown platform slug: {0}")]
27 UnknownPlatformSlug(String),
28
29 /// A filesystem or I/O operation failed, enriched with contextual text.
30 #[error("{context}: {source}")]
31 Io {
32 /// Human-readable description of the operation that was attempted.
33 context: String,
34 /// Underlying `std::io::Error` returned by the OS.
35 #[source]
36 source: std::io::Error,
37 },
38
39 /// A `reqwest` HTTP client error (connection failure, TLS issue, etc.).
40 #[error("HTTP error: {0}")]
41 Http(#[source] reqwest::Error),
42
43 /// A server returned a non-success HTTP status while fetching `url`.
44 #[error("HTTP {status} when fetching {url}")]
45 HttpStatus {
46 /// URL that was being fetched when the failure occurred.
47 url: String,
48 /// HTTP response status code.
49 status: u16,
50 },
51
52 /// The fallback `curl` subprocess exited with a non-zero status.
53 #[error("curl exited with code {code} when fetching {url}")]
54 CurlFailed {
55 /// URL passed to `curl`.
56 url: String,
57 /// `curl` exit code.
58 code: i32,
59 },
60
61 /// The downloaded archive did not match the expected SHA-256 checksum.
62 #[error("sha256 mismatch: expected {expected}, got {actual}")]
63 ChecksumMismatch {
64 /// Hex-encoded expected digest (from `hyperd-version.toml`).
65 expected: String,
66 /// Hex-encoded digest computed from the downloaded bytes.
67 actual: String,
68 },
69
70 /// The `hyperd-version.toml` file could not be parsed.
71 #[error("failed to parse version TOML: {0}")]
72 TomlParse(#[source] toml::de::Error),
73
74 /// The downloaded ZIP archive was malformed or could not be extracted.
75 #[error("zip error: {0}")]
76 Zip(#[source] zip::result::ZipError),
77
78 /// The archive did not contain a recognizable `hyperd` executable.
79 #[error("hyperd executable not found in extracted archive")]
80 HyperdNotInArchive,
81
82 /// Scraping the public releases page for the latest version failed.
83 #[error("failed to scrape latest release: {0}")]
84 ScrapeFailed(&'static str),
85}
86
87impl Error {
88 /// Constructs an [`Self::UnsupportedPlatform`] error.
89 pub fn unsupported_platform(os: impl Into<String>, arch: impl Into<String>) -> Self {
90 Error::UnsupportedPlatform {
91 os: os.into(),
92 arch: arch.into(),
93 }
94 }
95
96 /// Constructs an [`Self::UnknownPlatformSlug`] error.
97 pub fn unknown_platform_slug(slug: impl Into<String>) -> Self {
98 Error::UnknownPlatformSlug(slug.into())
99 }
100
101 /// Constructs an [`Self::Io`] error.
102 pub fn io(context: impl Into<String>, source: std::io::Error) -> Self {
103 Error::Io {
104 context: context.into(),
105 source,
106 }
107 }
108
109 /// Constructs an [`Self::HttpStatus`] error.
110 pub fn http_status(url: impl Into<String>, status: u16) -> Self {
111 Error::HttpStatus {
112 url: url.into(),
113 status,
114 }
115 }
116
117 /// Constructs an [`Self::CurlFailed`] error.
118 pub fn curl_failed(url: impl Into<String>, code: i32) -> Self {
119 Error::CurlFailed {
120 url: url.into(),
121 code,
122 }
123 }
124
125 /// Constructs an [`Self::ChecksumMismatch`] error.
126 pub fn checksum_mismatch(expected: impl Into<String>, actual: impl Into<String>) -> Self {
127 Error::ChecksumMismatch {
128 expected: expected.into(),
129 actual: actual.into(),
130 }
131 }
132}