Skip to content

Commit

Permalink
handle tags
Browse files Browse the repository at this point in the history
  • Loading branch information
QaidVoid committed Dec 5, 2024
1 parent e59ceb5 commit e25124e
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 27 deletions.
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# soar-dl
A lightning-fast, feature-rich release asset downloader for GitHub and GitLab repositories with support for direct downloads.

# Installation
```sh
cargo install soar-dl -F binary
```

# Usage
## Examples

> [!note]
> Any filter or output path you specify applies to all the assets.
```sh
# Download from github, using specific tag
soar-dl --github "pkgforge/soar@nightly"

# Download from gitlab
soar-dl --gitlab "inkscape/inkscape"

# Download using gitlab project id
soar-dl --github "18817634"

# Direct download
soar-dl "https://github.com/pkgforge/soar/releases/download/nightly/soar-nightly-x86_64-linux"

# Filter assets
soar-dl --github "pkgforge/soar" --regex ".*x86_64" --exclude "tar,b3sum"
soar-dl --github "pkgforge/soar" --match "x86_64,tar" --exclude "b3sum"

# Specify output path. Trailing / means it's a directory
soar-dl --github "pkgforge/soar" --gitlab "18817634" --output "final/"

# Don't do this. The last download will replace the existing file
# Only use file in output path if you're downloading single file.
soar-dl --github "pkgforge/soar" --gitlab "18817634" --output "final"
```

## Command Line Options
```
Usage: soar-dl [OPTIONS] [LINKS]...
Arguments:
[LINKS]... Links to files
Options:
--github <GITHUB> Github project
--gitlab <GITLAB> Gitlab project
-r, --regex <REGEX_PATTERNS> Regex to select the asset. Only works for github downloads
-m, --match <MATCH_KEYWORDS> Check if the asset contains given string
-e, --exclude <EXCLUDE_KEYWORDS> Check if the asset contains given string
-y, --yes Skip all prompts and use first
-o, --output <OUTPUT> Output file path
-h, --help Print help
-V, --version Print version
```
2 changes: 1 addition & 1 deletion src/bin/soar-dl/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub struct Args {
#[arg(required = false)]
pub links: Vec<String>,

/// Regex to select the asset. Only works for github downloads
/// Regex to select the asset.
#[arg(required = false, short = 'r', long = "regex")]
pub regex_patterns: Option<Vec<String>>,

Expand Down
37 changes: 15 additions & 22 deletions src/bin/soar-dl/download_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@ impl DownloadManager {
}

pub async fn execute(&self) {
let options = self.create_platform_options();

let _ = self.handle_github_downloads(&options).await;
let _ = self.handle_gitlab_downloads(&options).await;
let _ = self.handle_github_downloads().await;
let _ = self.handle_gitlab_downloads().await;
let _ = self.handle_direct_downloads().await;
}

fn create_platform_options(&self) -> PlatformDownloadOptions {
fn create_platform_options(&self, tag: Option<String>) -> PlatformDownloadOptions {
let asset_regexes = self
.args
.regex_patterns
Expand All @@ -52,7 +50,7 @@ impl DownloadManager {
PlatformDownloadOptions {
output_path: self.args.output.clone(),
progress_callback: Some(self.progress_callback.clone()),
tag: None,
tag,
regex_patterns: asset_regexes,
match_keywords: self.args.match_keywords.clone().unwrap_or_default(),
exclude_keywords: self.args.exclude_keywords.clone().unwrap_or_default(),
Expand All @@ -64,24 +62,26 @@ impl DownloadManager {
&self,
handler: &ReleaseHandler<P>,
project: &str,
options: &PlatformDownloadOptions,
) -> Result<(), PlatformError>
where
R: Release<A> + for<'de> Deserialize<'de>,
A: ReleaseAsset + Clone,
{
let (project, tag) = match project.trim().split_once('@') {
Some((proj, tag)) if !tag.trim().is_empty() => (proj, Some(tag.trim())),
_ => (project.trim_end_matches('@'), None),
};

let options = self.create_platform_options(tag.map(String::from));
let releases = handler.fetch_releases::<R>(project).await?;
let assets = handler.filter_releases(&releases, options).await?;
let assets = handler.filter_releases(&releases, &options).await?;

let selected_asset = self.select_asset(&assets)?;
handler.download(&selected_asset, options.clone()).await?;
Ok(())
}

async fn handle_github_downloads(
&self,
options: &PlatformDownloadOptions,
) -> Result<(), PlatformError> {
async fn handle_github_downloads(&self) -> Result<(), PlatformError> {
if self.args.github.is_empty() {
return Ok(());
}
Expand All @@ -90,9 +90,7 @@ impl DownloadManager {
for project in &self.args.github {
println!("Fetching releases from GitHub: {}", project);
if let Err(e) = self
.handle_platform_download::<Github, GithubRelease, GithubAsset>(
&handler, project, options,
)
.handle_platform_download::<Github, GithubRelease, GithubAsset>(&handler, project)
.await
{
eprintln!("{}", e);
Expand All @@ -101,10 +99,7 @@ impl DownloadManager {
Ok(())
}

async fn handle_gitlab_downloads(
&self,
options: &PlatformDownloadOptions,
) -> Result<(), PlatformError> {
async fn handle_gitlab_downloads(&self) -> Result<(), PlatformError> {
if self.args.gitlab.is_empty() {
return Ok(());
}
Expand All @@ -113,9 +108,7 @@ impl DownloadManager {
for project in &self.args.gitlab {
println!("Fetching releases from GitLab: {}", project);
if let Err(e) = self
.handle_platform_download::<Gitlab, GitlabRelease, GitlabAsset>(
&handler, project, options,
)
.handle_platform_download::<Gitlab, GitlabRelease, GitlabAsset>(&handler, project)
.await
{
eprintln!("{}", e);
Expand Down
10 changes: 6 additions & 4 deletions src/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ impl ReleasePlatform for Github {

const TOKEN_ENV_VAR: &'static str = "GITHUB_TOKEN";

fn format_project_path(project: &str) -> Result<(String, String), crate::error::PlatformError> {
fn format_project_path(project: &str) -> Result<(String, String), PlatformError> {
match project.split_once('/') {
Some((owner, repo)) => Ok((owner.to_string(), repo.to_string())),
None => Err(PlatformError::InvalidInput(format!(
Some((owner, repo)) if !owner.trim().is_empty() && !repo.trim().is_empty() => {
Ok((owner.to_string(), repo.to_string()))
}
_ => Err(PlatformError::InvalidInput(format!(
"Github project '{}' must be in 'owner/repo' format",
project
))),
}
}

fn format_api_path(project: &str) -> Result<String, crate::error::PlatformError> {
fn format_api_path(project: &str) -> Result<String, PlatformError> {
let (owner, repo) = Self::format_project_path(project)?;
Ok(format!("/repos/{}/{}/releases?per_page=100", owner, repo))
}
Expand Down

0 comments on commit e25124e

Please sign in to comment.