use crate::error::FetcherError;
use crate::manifest::{ChannelsResponse, KnownGoodVersionsResponse};
use crate::platform::Platform;
use crate::version::{Channel, VersionSpec};
pub(crate) async fn resolve_download_url(
manifest: &KnownGoodVersionsResponse,
spec: &VersionSpec,
platform: Platform,
) -> Result<(String, String), FetcherError> {
let entry = match spec {
VersionSpec::Latest | VersionSpec::Stable | VersionSpec::Channel(Channel::Stable) => {
manifest
.versions
.last()
.ok_or_else(|| FetcherError::VersionNotFound("manifest is empty".to_string()))?
}
VersionSpec::Channel(Channel::Beta | Channel::Dev | Channel::Canary) => {
return Err(FetcherError::UnsupportedPlatform);
}
VersionSpec::Explicit(want) => manifest
.versions
.iter()
.find(|v| &v.version == want)
.ok_or_else(|| FetcherError::VersionNotFound(want.clone()))?,
};
let key = platform.as_cft_str();
let download = entry
.downloads
.chrome
.iter()
.find(|d| d.platform == key)
.ok_or(FetcherError::UnsupportedPlatform)?;
Ok((entry.version.clone(), download.url.clone()))
}
pub(crate) async fn resolve_channel_download_url(
manifest: &ChannelsResponse,
channel: Channel,
platform: Platform,
) -> Result<(String, String), FetcherError> {
let channel_key = channel.as_cft_str();
let entry = manifest.channels.get(channel_key).ok_or_else(|| {
FetcherError::VersionNotFound(format!("channel {channel_key} not present in manifest"))
})?;
let platform_key = platform.as_cft_str();
let download = entry
.downloads
.chrome
.iter()
.find(|d| d.platform == platform_key)
.ok_or(FetcherError::UnsupportedPlatform)?;
Ok((entry.version.clone(), download.url.clone()))
}
#[cfg(test)]
#[allow(clippy::panic, clippy::unwrap_used)]
mod tests {
use super::*;
fn fixture_manifest() -> KnownGoodVersionsResponse {
let json = r#"{
"versions": [
{
"version": "120.0.6099.234",
"revision": "1234",
"downloads": {
"chrome": [
{"platform": "linux64", "url": "https://example.com/chrome-linux64.zip"},
{"platform": "mac-x64", "url": "https://example.com/chrome-mac-x64.zip"}
]
}
}
]
}"#;
serde_json::from_str(json).unwrap()
}
#[tokio::test]
async fn latest_returns_last_entry_url_for_matching_platform() {
let manifest = fixture_manifest();
let (version, url) =
resolve_download_url(&manifest, &VersionSpec::Latest, Platform::LinuxX64)
.await
.unwrap();
assert_eq!(version, "120.0.6099.234");
assert_eq!(url, "https://example.com/chrome-linux64.zip");
}
#[tokio::test]
async fn explicit_unknown_version_returns_version_not_found() {
let manifest = fixture_manifest();
let err = resolve_download_url(
&manifest,
&VersionSpec::Explicit("999.0".to_string()),
Platform::LinuxX64,
)
.await
.unwrap_err();
match err {
FetcherError::VersionNotFound(v) => assert_eq!(v, "999.0"),
other => panic!("expected VersionNotFound, got {other:?}"),
}
}
#[tokio::test]
async fn beta_channel_returns_unsupported_platform_against_flat_manifest() {
let manifest = fixture_manifest();
let err = resolve_download_url(
&manifest,
&VersionSpec::Channel(Channel::Beta),
Platform::LinuxX64,
)
.await
.unwrap_err();
assert!(matches!(err, FetcherError::UnsupportedPlatform));
}
fn fixture_channels_manifest() -> ChannelsResponse {
let json = r#"{
"timestamp": "2026-07-16T00:00:00.000Z",
"channels": {
"Stable": {
"channel": "Stable",
"version": "120.0.6099.234",
"revision": "1234",
"downloads": {
"chrome": [
{"platform": "linux64", "url": "https://example.com/stable-linux64.zip"}
]
}
},
"Beta": {
"channel": "Beta",
"version": "121.0.6100.10",
"revision": "1235",
"downloads": {
"chrome": [
{"platform": "linux64", "url": "https://example.com/beta-linux64.zip"}
]
}
},
"Dev": {
"channel": "Dev",
"version": "122.0.6101.5",
"revision": "1236",
"downloads": {
"chrome": [
{"platform": "mac-x64", "url": "https://example.com/dev-mac-x64.zip"}
]
}
},
"Canary": {
"channel": "Canary",
"version": "123.0.6102.1",
"revision": "1237",
"downloads": {
"chrome": [
{"platform": "linux64", "url": "https://example.com/canary-linux64.zip"}
]
}
}
}
}"#;
serde_json::from_str(json).unwrap()
}
#[tokio::test]
async fn beta_channel_resolves_from_channels_manifest() {
let manifest = fixture_channels_manifest();
let (version, url) =
resolve_channel_download_url(&manifest, Channel::Beta, Platform::LinuxX64)
.await
.unwrap();
assert_eq!(version, "121.0.6100.10");
assert_eq!(url, "https://example.com/beta-linux64.zip");
}
#[tokio::test]
async fn dev_and_canary_channels_resolve_from_channels_manifest() {
let manifest = fixture_channels_manifest();
let (version, url) =
resolve_channel_download_url(&manifest, Channel::Dev, Platform::MacX64)
.await
.unwrap();
assert_eq!(version, "122.0.6101.5");
assert_eq!(url, "https://example.com/dev-mac-x64.zip");
let (version, url) =
resolve_channel_download_url(&manifest, Channel::Canary, Platform::LinuxX64)
.await
.unwrap();
assert_eq!(version, "123.0.6102.1");
assert_eq!(url, "https://example.com/canary-linux64.zip");
}
#[tokio::test]
async fn channel_missing_platform_returns_unsupported_platform() {
let manifest = fixture_channels_manifest();
let err = resolve_channel_download_url(&manifest, Channel::Dev, Platform::LinuxX64)
.await
.unwrap_err();
assert!(matches!(err, FetcherError::UnsupportedPlatform));
}
#[tokio::test]
async fn channel_missing_from_manifest_returns_version_not_found() {
let json = r#"{ "timestamp": "2026-07-16T00:00:00.000Z", "channels": {} }"#;
let manifest: ChannelsResponse = serde_json::from_str(json).unwrap();
let err = resolve_channel_download_url(&manifest, Channel::Beta, Platform::LinuxX64)
.await
.unwrap_err();
match err {
FetcherError::VersionNotFound(msg) => assert!(msg.contains("Beta")),
other => panic!("expected VersionNotFound, got {other:?}"),
}
}
}