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 GiteeRelease {
    id: u64,
    tag_name: String,
    name: String,
    body: String,
    draft: Option<bool>,
    prerelease: bool,
    created_at: Option<String>,
    published_at: Option<String>,
    assets: Vec<GiteeAsset>,
}

#[derive(Debug, Deserialize)]
pub struct GiteeAsset {
    name: String,
    browser_download_url: String,
}

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

    // 按页倒序获取 releases
    let mut page = 1;
    let per_page = 20; // Gitee API 默认每页20个
    let mut total_pages: Option<u32> = None;

    loop {
        let url = format!(
            "https://gitee.com/api/v5/repos/{}/{}/releases?page={}&per_page={}&direction=desc",
            url_info.owner, url_info.repo, page, per_page
        );

        println!("Fetching releases from: {url}");
        if let Some(total) = total_pages {
            println!("Processing page {page}/{total}");
        } else {
            println!("Processing page {page}");
        }

        let response = client.get(&url).send().await?;

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

        // 从响应头获取总页数信息
        if total_pages.is_none() {
            if let Some(total_page_header) = response.headers().get("total_page") {
                if let Ok(total_page_str) = total_page_header.to_str() {
                    if let Ok(pages) = total_page_str.parse::<u32>() {
                        total_pages = Some(pages);
                        println!("Total pages available: {pages}");
                    }
                }
            }
        }

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

        if releases.is_empty() {
            bail!("No releases found for {}/{}", url_info.owner, url_info.repo);
        }

        let releases_len = releases.len();

        // 遍历当前页的所有 releases
        for release in releases {
            // 跳过草稿和预发布版本
            if release.draft.unwrap_or(false) || 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) {
                // 检查版本是否匹配(使用 semver 兼容性匹配)
                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::Gitee,
                            )
                            .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);
                }
            }
        }

        // 检查是否已经遍历完所有页面
        if let Some(total) = total_pages {
            if page >= total {
                println!("Reached last page ({page}/{total})");
                break;
            }
        } else {
            // 如果没有总页数信息,使用原来的逻辑
            if releases_len < per_page {
                break;
            }
        }

        page += 1;
    }

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