pkgwatch/src/checker.rs
Austin Schaefer ba2c5c2d02
All checks were successful
CI / build (pull_request) Successful in 35s
CI / test (pull_request) Successful in 2m27s
CI / audit (pull_request) Successful in 10s
CI / coverage (pull_request) Successful in 5m8s
Make release sources polymorphic via a ReleaseSource trait
Replaces the Endpoints enum and checker's per-host dispatch: checker.rs now
holds only the ReleaseSource trait (latest release + API root), each host
implements it in its own module (github.rs, forgejo.rs), and source.rs is a
factory returning a Box<dyn ReleaseSource> per package. Adding a host no
longer touches existing ones, and the pipeline only sees the trait.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 10:58:26 +02:00

48 lines
1.9 KiB
Rust

//! The check stage's contract: a `ReleaseSource` says what a package's
//! latest release is and where its releases API lives, and
//! `version_from_tag` turns the resulting tag into a version string. Each
//! hosting service implements the trait in its own module (`github`,
//! `forgejo`) so per-host logic doesn't accumulate here;
//! `source::for_package` picks the implementation for a package.
use anyhow::Result;
/// A hosting service a package's releases are published on. `Debug` so a
/// `Box<dyn ReleaseSource>` can sit in a `Result` that tests unwrap.
pub trait ReleaseSource: std::fmt::Debug {
/// The latest release tag for `repo`.
fn latest_release(&self, client: &reqwest::blocking::Client, repo: &str) -> Result<String>;
/// The releases API root, which `fetcher` and `verifier` build their
/// own paths under. GitHub and Forgejo both serve
/// `/repos/{owner}/{repo}/releases/tags/{tag}` there with the same
/// `assets[].{name, browser_download_url}` shape, which is why those
/// stages need only this and not the source itself.
fn api(&self) -> &str;
}
/// Strips a leading `v` from a release tag, e.g. `v2.62.0` -> `2.62.0`.
///
/// Some projects (uv) tag releases with the bare version and use it
/// verbatim in asset filenames; others (scaleway-cli) tag `vX.Y.Z` but
/// still use the bare version in filenames. This is the version string
/// substituted into `{version}` placeholders in `asset_pattern` /
/// `checksum_asset_pattern`, not the tag used for API/attestation calls.
pub fn version_from_tag(tag: &str) -> &str {
tag.strip_prefix('v').unwrap_or(tag)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_from_tag_strips_leading_v() {
assert_eq!(version_from_tag("v2.62.0"), "2.62.0");
}
#[test]
fn version_from_tag_leaves_bare_version_unchanged() {
assert_eq!(version_from_tag("0.12.15"), "0.12.15");
}
}