Skip to main content

hyperdb_bootstrap/
release.rs

1// Copyright (c) 2026, Salesforce, Inc. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! A pinned `hyperd` release descriptor loaded from `hyperd-version.toml`.
5//!
6//! Each `PinnedRelease` records a specific `version` + `build_id` pair
7//! (the two components that make up a Hyper release tag) and the expected
8//! SHA-256 checksums for each platform.
9
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::path::Path;
13
14use crate::platform::Platform;
15use crate::Error;
16
17const BUILTIN_TOML: &str = include_str!("../hyperd-version.toml");
18
19/// A concrete `hyperd` release pinned to a specific version and build, with
20/// optional per-platform SHA-256 checksums.
21///
22/// The "built-in" pin shipped with the crate lives in
23/// `hyperd-bootstrap/hyperd-version.toml` and is available via
24/// [`PinnedRelease::builtin`]. Callers can override it by loading an
25/// external TOML file (see [`PinnedRelease::from_toml_file`]) or by passing
26/// a literal TOML string to [`PinnedRelease::from_toml_str`].
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct PinnedRelease {
29    /// Upstream release version (for example, `"0.0.24457"`).
30    pub version: String,
31    /// Upstream build identifier suffix (for example, `"rc36858b6"`).
32    pub build_id: String,
33    /// Expected SHA-256 digests keyed by platform. Empty strings are treated
34    /// as "no digest" so that partially-filled tables skip verification for
35    /// the missing targets instead of failing outright.
36    #[serde(default)]
37    pub sha256: HashMap<Platform, String>,
38}
39
40impl PinnedRelease {
41    /// Returns the `PinnedRelease` baked into the crate at build time.
42    ///
43    /// # Panics
44    ///
45    /// Panics if the shipped `hyperd-version.toml` fails to parse. This is
46    /// treated as a programmer error — the file is validated by the build
47    /// script and release CI.
48    #[must_use]
49    pub fn builtin() -> Self {
50        toml::from_str(BUILTIN_TOML).expect("baked-in hyperd-version.toml must parse")
51    }
52
53    /// Parses a `PinnedRelease` from an in-memory TOML string.
54    ///
55    /// # Errors
56    ///
57    /// Returns [`Error::TomlParse`] if the text is not valid TOML or the
58    /// document does not match the `PinnedRelease` schema.
59    pub fn from_toml_str(s: &str) -> Result<Self, Error> {
60        toml::from_str(s).map_err(Error::TomlParse)
61    }
62
63    /// Loads a `PinnedRelease` from a TOML file on disk.
64    ///
65    /// # Errors
66    ///
67    /// Returns [`Error::Io`] if the file cannot be read, or
68    /// [`Error::TomlParse`] if the content is not a valid `PinnedRelease`.
69    pub fn from_toml_file(path: &Path) -> Result<Self, Error> {
70        let text = std::fs::read_to_string(path).map_err(|source| {
71            Error::io(format!("reading version file {}", path.display()), source)
72        })?;
73        Self::from_toml_str(&text)
74    }
75
76    /// Returns the expected SHA-256 digest for `platform`, or `None` if the
77    /// release metadata does not pin a digest for that platform. Empty
78    /// strings (common in pre-release metadata) are treated as absent.
79    #[must_use]
80    pub fn sha256_for(&self, platform: Platform) -> Option<&str> {
81        self.sha256
82            .get(&platform)
83            .map(|s| s.trim())
84            .filter(|s| !s.is_empty())
85    }
86
87    /// Returns the full Hyper release tag — `version.build_id` — used in
88    /// download URLs and install directory names.
89    #[must_use]
90    pub fn version_tag(&self) -> String {
91        format!("{}.{}", self.version, self.build_id)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98
99    #[test]
100    fn builtin_parses() {
101        let r = PinnedRelease::builtin();
102        assert!(!r.version.is_empty());
103        assert!(!r.build_id.is_empty());
104    }
105
106    #[test]
107    fn empty_sha_is_ignored() {
108        let toml_str = r#"
109version = "0.0.1"
110build_id = "rc1"
111[sha256]
112"macos-arm64" = ""
113"linux-x86_64" = "abc"
114"#;
115        let r = PinnedRelease::from_toml_str(toml_str).unwrap();
116        assert!(r.sha256_for(Platform::MacosArm64).is_none());
117        assert_eq!(r.sha256_for(Platform::LinuxX86_64), Some("abc"));
118    }
119
120    #[test]
121    fn version_tag_format() {
122        let r = PinnedRelease {
123            version: "0.0.24457".to_string(),
124            build_id: "rc36858b6".to_string(),
125            sha256: HashMap::new(),
126        };
127        assert_eq!(r.version_tag(), "0.0.24457.rc36858b6");
128    }
129}