From 0128b793f2c58172e77d05d7fbf440be828312cb Mon Sep 17 00:00:00 2001 From: Azathothas Date: Tue, 12 Nov 2024 15:04:31 +0000 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Synced=20Soar=20?= =?UTF-8?q?=F0=9F=93=A6=20<--=20feat(download):=20add=20ergonomic=20flags?= =?UTF-8?q?=20for=20github=20asset=20matching=20=E2=8C=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/LATEST.txt | 2 +- build.rs | 5 +- src/cli.rs | 12 ++++- src/lib.rs | 15 +++++- src/misc/download.rs | 117 ++++++++++++++++++++++++++++--------------- 5 files changed, 104 insertions(+), 47 deletions(-) diff --git a/.github/LATEST.txt b/.github/LATEST.txt index d47f756..e0be06c 100755 --- a/.github/LATEST.txt +++ b/.github/LATEST.txt @@ -1 +1 @@ -e0b9a5886bcdafb27a2af0cae42f72ec6d5beda1 +e47083d3fc87b39fe938d035748de89f89161c45 diff --git a/build.rs b/build.rs index 868cb95..80de9aa 100644 --- a/build.rs +++ b/build.rs @@ -10,7 +10,10 @@ fn main() { .expect("Failed to get git commit SHA") .stdout; - let commit_sha = String::from_utf8(commit_sha).expect("Invalid UTF-8 output").trim().to_string(); + let commit_sha = String::from_utf8(commit_sha) + .expect("Invalid UTF-8 output") + .trim() + .to_string(); println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rustc-env=CARGO_PKG_VERSION=nightly-{}", commit_sha); diff --git a/src/cli.rs b/src/cli.rs index ca6a6e2..e4287ed 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -181,8 +181,16 @@ pub enum Commands { output: Option, /// Regex to select the asset. Only works for github downloads - #[arg(required = false, short, long)] - regex: Option, + #[arg(required = false, short = 'r', long = "regex")] + regex_patterns: Option>, + + /// Check if the asset contains given string + #[arg(required = false, short, long = "match")] + match_keywords: Option>, + + /// Check if the asset contains given string + #[arg(required = false, short, long = "exclude")] + exclude_keywords: Option>, }, /// Health check diff --git a/src/lib.rs b/src/lib.rs index 72860fc..a680037 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,9 +124,20 @@ async fn handle_cli() -> Result<()> { links, yes, output, - regex, + regex_patterns, + match_keywords, + exclude_keywords, } => { - download_and_save(registry.await?, links.as_ref(), yes, output, regex).await?; + download_and_save( + registry.await?, + links.as_ref(), + yes, + output, + regex_patterns.as_deref(), + match_keywords.as_deref(), + exclude_keywords.as_deref(), + ) + .await?; } Commands::Health => { check_health().await; diff --git a/src/misc/download.rs b/src/misc/download.rs index 24b003e..9c477b0 100644 --- a/src/misc/download.rs +++ b/src/misc/download.rs @@ -23,7 +23,7 @@ use crate::{ registry::{select_single_package, PackageRegistry}, }; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] struct GithubAsset { name: String, size: u64, @@ -168,16 +168,48 @@ async fn fetch_github_releases(user_repo: &str) -> Result> { Ok(releases) } +fn select_asset_idx(assets: &[&GithubAsset], max: usize) -> Result { + for (i, asset) in assets.iter().enumerate() { + info!( + " [{}] {:#?} ({})", + i + 1, + asset.name, + format_bytes(asset.size), + ); + } + let selection = loop { + let response = interactive_ask( + &format!("Select an asset (1-{}): ", assets.len()), + AskType::Normal, + )?; + + match response.parse::() { + Ok(n) if n > 0 && n <= max => break n - 1, + _ => error!("Invalid selection, please try again."), + } + }; + Ok(selection) +} + pub async fn download_and_save( registry: PackageRegistry, links: &[String], yes: bool, output: Option, - regex: Option, + regex_patterns: Option<&[String]>, + match_keywords: Option<&[String]>, + exclude_keywords: Option<&[String]>, ) -> Result<()> { let re = Regex::new(GITHUB_URL_REGEX).unwrap(); - let asset_re = regex.as_deref(); - let asset_re = Regex::new(asset_re.unwrap_or_default())?; + let asset_regexes = regex_patterns + .map(|patterns| { + patterns + .iter() + .map(|pattern| Regex::new(pattern)) + .collect::, regex::Error>>() + }) + .transpose()? + .unwrap_or_default(); for link in links { if re.is_match(link) { @@ -219,47 +251,50 @@ pub async fn download_and_save( continue; } - let selected_file = if regex.is_some() { - let Some(asset) = assets.iter().find(|asset| asset_re.is_match(&asset.name)) - else { - continue; - }; - asset - } else if assets.len() == 1 || yes { - &assets[0] - } else { - info!( - "Showing assets for {}{}", - release.tag_name, - if release.prerelease { - " [prerelease]".color(Color::BrightRed) - } else { - " [stable]".color(Color::BrightCyan) + let selected_asset = { + let assets: Vec<&GithubAsset> = assets + .iter() + .filter(|asset| { + asset_regexes + .iter() + .all(|regex| regex.is_match(&asset.name)) + && match_keywords.map_or(true, |keywords| { + keywords.iter().all(|keyword| asset.name.contains(keyword)) + }) + && exclude_keywords.map_or(true, |keywords| { + keywords.iter().all(|keyword| !asset.name.contains(keyword)) + }) + }) + .collect(); + + match assets.len() { + 0 => { + error!("No assets matched the provided criteria."); + continue; } - ); - for (i, asset) in assets.iter().enumerate() { - info!( - " [{}] {:#?} ({})", - i + 1, - asset.name, - format_bytes(asset.size), - ); - } - let selection = loop { - let response = interactive_ask( - &format!("Select a file (1-{}): ", assets.len()), - AskType::Normal, - )?; - - match response.parse::() { - Ok(n) if n > 0 && n <= releases.len() => break n - 1, - _ => error!("Invalid selection, please try again."), + 1 => assets[0], + _ => { + if yes { + assets[0] + } else { + info!( + "Multiple matching assets found for {}{}", + release.tag_name, + if release.prerelease { + " [prerelease]".color(Color::BrightRed) + } else { + " [stable]".color(Color::BrightCyan) + } + ); + + let asset_idx = select_asset_idx(&assets, releases.len())?; + assets[asset_idx] + } } - }; - &assets[selection] + } }; - let download_url = &selected_file.browser_download_url; + let download_url = &selected_asset.browser_download_url; download(download_url, output.clone()).await?; } } else if let Ok(url) = Url::parse(link) {