//! Maps a package's configured `source` to the endpoints the pipeline //! stages talk to. The only module that knows how each hosting service //! lays out its URLs; `checker` and `fetcher` take what it hands them. use crate::config::{Package, Source}; use crate::github::GithubEndpoints; use anyhow::{Context, Result}; #[derive(Debug, Clone)] pub enum Endpoints { Github(GithubEndpoints), /// A Forgejo/Gitea instance's API root, i.e. `/api/v1`. Forgejo { api: String, }, } impl Endpoints { /// Errors only if a `forgejo-release` package has no `base_url`, which /// `config::load_packages_dir` already rejects — this is the same check /// again for a `Package` built some other way, not a second source of /// truth. pub fn for_package(pkg: &Package) -> Result { match pkg.source { Source::GithubRelease => Ok(Endpoints::Github(GithubEndpoints::default())), Source::ForgejoRelease => { let base_url = pkg .base_url .as_deref() .context("source = \"forgejo-release\" needs a base_url")?; Ok(Endpoints::Forgejo { api: format!("{}/api/v1", base_url.trim_end_matches('/')), }) } } } /// The releases API root. GitHub and Forgejo both serve /// `/repos/{owner}/{repo}/releases/tags/{tag}` under it with the same /// `assets[].{name, browser_download_url}` shape, which is why /// `fetcher` and `verifier` need only this and not the enum. pub fn api(&self) -> &str { match self { Endpoints::Github(github) => &github.api, Endpoints::Forgejo { api } => api, } } } #[cfg(test)] mod tests { use super::*; fn package(extra: &str) -> Package { toml::from_str(&format!( r#" repo = "o/r" asset_pattern = "x" {extra} [verification] method = "same-origin-sha256" checksum_asset_pattern = "SUMS" "# )) .unwrap() } #[test] fn github_source_uses_real_github() { let endpoints = Endpoints::for_package(&package("")).unwrap(); assert_eq!(endpoints.api(), "https://api.github.com"); assert!(matches!(endpoints, Endpoints::Github(_))); } #[test] fn forgejo_source_appends_api_v1() { let pkg = package("source = \"forgejo-release\"\nbase_url = \"https://code.example.com\""); let endpoints = Endpoints::for_package(&pkg).unwrap(); assert_eq!(endpoints.api(), "https://code.example.com/api/v1"); } #[test] fn forgejo_source_tolerates_trailing_slash() { let pkg = package("source = \"forgejo-release\"\nbase_url = \"https://code.example.com/\""); let endpoints = Endpoints::for_package(&pkg).unwrap(); assert_eq!(endpoints.api(), "https://code.example.com/api/v1"); } #[test] fn forgejo_source_without_base_url_errors() { let err = Endpoints::for_package(&package("source = \"forgejo-release\"")).unwrap_err(); assert!(err.to_string().contains("needs a base_url")); } }