upstream-rs 2.6.0

Fetch package updates directly from the source.
Documentation
use crate::models::provider::{Asset, Release};
use std::path::Path;

pub fn find_signature_assets<'r>(release: &'r Release, asset_name: &str) -> Vec<&'r Asset> {
    let mut out: Vec<&Asset> = Vec::new();
    let mut seen: Vec<String> = Vec::new();
    let mut push_unique = |asset: &'r Asset| {
        let lowered = asset.name.to_ascii_lowercase();
        if seen.iter().any(|n| n == &lowered) {
            return;
        }
        seen.push(lowered);
        out.push(asset);
    };

    let basename = Path::new(asset_name)
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or(asset_name);

    let specific_candidates = [
        format!("{asset_name}.minisig"),
        format!("{basename}.minisig"),
        format!("{asset_name}.cosign.sig"),
        format!("{basename}.cosign.sig"),
        format!("{asset_name}.sig"),
        format!("{basename}.sig"),
    ];

    for candidate in &specific_candidates {
        if let Some(asset) = release.get_asset_by_name_invariant(candidate) {
            push_unique(asset);
        }
    }

    const COMMON_NAMES: &[&str] = &[
        "minisig",
        "signature.minisig",
        "cosign.sig",
        "signature.cosign.sig",
        "signature.sig",
        "signatures.txt",
    ];
    for name in COMMON_NAMES {
        if let Some(asset) = release.get_asset_by_name_invariant(name) {
            push_unique(asset);
        }
    }

    for asset in &release.assets {
        if is_signature_filename(&asset.name) {
            push_unique(asset);
        }
    }

    out
}

pub fn signature_target_name(signature_asset_name: &str) -> Option<&str> {
    let lowered = signature_asset_name.to_ascii_lowercase();
    for suffix in [".cosign.sig", ".minisig", ".sig"] {
        if lowered.ends_with(suffix) {
            let target_len = signature_asset_name.len().saturating_sub(suffix.len());
            let target = &signature_asset_name[..target_len];
            if !target.is_empty() {
                return Some(target);
            }
        }
    }
    None
}

fn is_signature_filename(name: &str) -> bool {
    let lowered = name.to_ascii_lowercase();
    lowered.ends_with(".minisig")
        || lowered.ends_with(".cosign.sig")
        || lowered.ends_with(".sig")
        || lowered.contains("signature")
}