release-dep 0.1.0

A Rust library for fetching and downloading release binaries from multiple Git repositories (GitHub and Gitee) with semver version matching.
Documentation
use anyhow::{bail, Result};
use reqwest::Client;
use semver::Version;
use serde::Deserialize;

use crate::{download_binary, OneConfig, ReleaseDep, RepoProvider};

#[allow(unused)]
#[derive(Debug, Deserialize)]
pub struct GithubRelease {
    id: u64,
    tag_name: String,
    name: Option<String>,
    body: Option<String>,
    draft: bool,
    prerelease: bool,
    created_at: String,
    published_at: Option<String>,
    assets: Vec<GithubAsset>,
}

#[allow(unused)]
#[derive(Debug, Deserialize)]
pub struct GithubAsset {
    id: u64,
    name: String,
    browser_download_url: String,
    size: u64,
    download_count: u64,
}

pub async fn github_get_release(config: OneConfig) -> Result<ReleaseDep> {
    let client = Client::new();
    let url_info = &config.url_info;

    // 按页倒序获取 releases
    let mut page = 1;
    let per_page = 30; // GitHub API 默认每页30个
    let mut has_next_page = true;

    while has_next_page {
        let url = format!(
            "https://api.github.com/repos/{}/{}/releases?page={}&per_page={}",
            url_info.owner, url_info.repo, page, per_page
        );

        println!("Fetching releases from: {url}");
        println!("Processing page {page}");

        let response = client
            .get(&url)
            .header("User-Agent", "release-dep/1.0")
            .header("Accept", "application/vnd.github.v3+json")
            .send()
            .await?;

        if !response.status().is_success() {
            bail!(
                "Failed to fetch releases from GitHub: {}",
                response.status()
            );
        }

        // 检查是否有下一页(GitHub 使用 Link 响应头)
        has_next_page = if let Some(link_header) = response.headers().get("link") {
            if let Ok(link_str) = link_header.to_str() {
                link_str.contains("rel=\"next\"")
            } else {
                false
            }
        } else {
            false
        };

        let releases: Vec<GithubRelease> = response.json().await?;

        if releases.is_empty() {
            println!("No more releases found on page {page}");
            break;
        }

        // 遍历当前页的所有 releases
        for release in releases {
            // 跳过草稿和预发布版本
            if release.draft || release.prerelease {
                continue;
            }

            // 解析 tag_name 作为版本号
            // tag_name 可能是 "v1.0.0" 或 "package-v1.0.0" 格式
            let version_str = if release.tag_name.contains("-v") {
                // 如果包含 "-v",取最后一个 "-v" 之后的部分
                release
                    .tag_name
                    .split("-v")
                    .last()
                    .unwrap_or(&release.tag_name)
            } else if release.tag_name.starts_with('v') {
                // 如果以 "v" 开头,去掉 "v"
                &release.tag_name[1..]
            } else {
                &release.tag_name
            };

            println!(
                "Parsing version from tag: {} -> {version_str}",
                release.tag_name
            );

            if let Ok(release_version) = Version::parse(version_str) {
                if config.version.matches(&release_version) {
                    println!(
                        "Found compatible version: {release_version} (requested: {})",
                        config.version
                    );

                    // 查找与 package 名称匹配的 binary asset
                    for asset in &release.assets {
                        if asset.name.contains(&config.package) {
                            println!("Found matching asset: {}", asset.name);

                            // 下载二进制文件
                            let download_dir =
                                config.download_dir.as_ref().map(|p| p.to_str().unwrap());
                            let binary_path = download_binary(
                                &client,
                                &asset.browser_download_url,
                                &asset.name,
                                download_dir,
                                RepoProvider::Github,
                            )
                            .await?;

                            println!("Successfully found and downloaded release on page {page}");
                            return Ok(ReleaseDep {
                                name: config.package.clone(),
                                version: release_version,
                                binary: binary_path,
                            });
                        }
                    }

                    // 如果没有找到匹配的 asset,尝试下一个 release
                    println!("No matching binary found for release {}", release.tag_name);
                }
            }
        }

        page += 1;
    }

    bail!(
        "No compatible release found for package {} with version {}",
        config.package,
        config.version
    );
}