-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #823 from winpax/refactor-app-commands
refactor app commands
- Loading branch information
Showing
41 changed files
with
553 additions
and
497 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
pub mod cat; | ||
#[cfg(feature = "download")] | ||
pub mod download; | ||
pub mod home; | ||
pub mod info; | ||
pub mod list; | ||
pub mod purge; | ||
|
||
use clap::{Parser, Subcommand}; | ||
|
||
use sfsu_macros::Runnable; | ||
use sprinkles::{config, contexts::ScoopContext}; | ||
|
||
use super::{Command, CommandRunner}; | ||
|
||
#[derive(Debug, Clone, Subcommand, Runnable)] | ||
pub enum Commands { | ||
Cat(cat::Args), | ||
#[cfg(feature = "download")] | ||
Download(download::Args), | ||
Home(home::Args), | ||
Info(info::Args), | ||
List(list::Args), | ||
Purge(purge::Args), | ||
} | ||
|
||
#[derive(Debug, Clone, Parser)] | ||
/// Commands for managing apps | ||
pub struct Args { | ||
#[command(subcommand)] | ||
command: Commands, | ||
} | ||
|
||
impl Command for Args { | ||
#[inline] | ||
async fn runner(self, ctx: &impl ScoopContext<config::Scoop>) -> Result<(), anyhow::Error> { | ||
self.command.run(ctx).await | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
use std::{fs::File, io::Read, sync::atomic::Ordering}; | ||
|
||
use clap::Parser; | ||
use sprinkles::{config, contexts::ScoopContext, packages::reference::package}; | ||
|
||
use crate::{abandon, COLOR_ENABLED}; | ||
|
||
#[derive(Debug, Clone, Parser)] | ||
/// Show content of specified manifest | ||
pub struct Args { | ||
#[clap(help = "The manifest to display")] | ||
package: package::Reference, | ||
} | ||
|
||
impl super::Command for Args { | ||
async fn runner(self, ctx: &impl ScoopContext<config::Scoop>) -> Result<(), anyhow::Error> { | ||
let manifests = self.package.list_manifest_paths(ctx); | ||
|
||
if manifests.is_empty() { | ||
abandon!("No manifests found for {}", self.package); | ||
} | ||
|
||
let manifest = &manifests[0]; | ||
|
||
let manifest_content = { | ||
let mut buf = vec![]; | ||
|
||
let mut file = File::open(manifest)?; | ||
file.read_to_end(&mut buf)?; | ||
|
||
buf | ||
}; | ||
|
||
if COLOR_ENABLED.load(Ordering::Relaxed) { | ||
use bat::PrettyPrinter; | ||
|
||
PrettyPrinter::new() | ||
.input_from_bytes(&manifest_content) | ||
.language("json") | ||
.print()?; | ||
} else { | ||
print!("{}", String::from_utf8_lossy(&manifest_content)); | ||
} | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
use clap::Parser; | ||
|
||
use sprinkles::{ | ||
cache::{Downloader, Handle}, | ||
config, | ||
contexts::ScoopContext, | ||
packages::reference::package, | ||
progress::indicatif::MultiProgress, | ||
requests::AsyncClient, | ||
Architecture, | ||
}; | ||
|
||
use crate::{abandon, output::colours::eprintln_yellow}; | ||
|
||
#[derive(Debug, Clone, Parser)] | ||
/// Download the specified app. | ||
pub struct Args { | ||
#[clap(short, long, help = "Use the specified architecture, if the app supports it", default_value_t = Architecture::ARCH)] | ||
arch: Architecture, | ||
|
||
#[clap(short = 'H', long, help = "Disable hash validation")] | ||
no_hash_check: bool, | ||
|
||
#[clap(help = "The packages to download")] | ||
packages: Vec<package::Reference>, | ||
|
||
#[clap(from_global)] | ||
json: bool, | ||
} | ||
|
||
impl super::Command for Args { | ||
const BETA: bool = true; | ||
|
||
async fn runner(self, ctx: &impl ScoopContext<config::Scoop>) -> Result<(), anyhow::Error> { | ||
if self.packages.is_empty() { | ||
abandon!("No packages provided") | ||
} | ||
|
||
if self.no_hash_check { | ||
eprintln_yellow!( | ||
"Hash check has been disabled! This may allow modified files to be downloaded" | ||
); | ||
} | ||
|
||
let mp = MultiProgress::new(); | ||
|
||
eprint!("Attempting to generate manifest(s)"); | ||
let downloaders: Vec<Downloader> = | ||
futures::future::try_join_all(self.packages.into_iter().map(|package| { | ||
let mp = mp.clone(); | ||
async move { | ||
let manifest = match package.manifest(ctx).await { | ||
Ok(manifest) => manifest, | ||
Err(e) => abandon!("\rFailed to generate manifest: {e}"), | ||
}; | ||
|
||
let dl = Handle::open_manifest(ctx.cache_path(), &manifest, self.arch)?; | ||
|
||
let downloaders = dl.into_iter().map(|dl| { | ||
let mp = mp.clone(); | ||
async move { | ||
match Downloader::new::<AsyncClient>(dl, Some(&mp)).await { | ||
Ok(dl) => anyhow::Ok(dl), | ||
Err(e) => match e { | ||
sprinkles::cache::Error::ErrorCode(status) => { | ||
abandon!("Found {status} error while downloading") | ||
} | ||
_ => Err(e.into()), | ||
}, | ||
} | ||
} | ||
}); | ||
let downloaders = futures::future::try_join_all(downloaders).await?; | ||
|
||
anyhow::Ok(downloaders) | ||
} | ||
})) | ||
.await? | ||
.into_iter() | ||
.flatten() | ||
.collect(); | ||
eprintln!("\r📜 Generated manifest for any and all mismatched versions"); | ||
|
||
let threads = downloaders | ||
.into_iter() | ||
.map(|dl| tokio::spawn(async move { dl.download().await })); | ||
|
||
let results = futures::future::try_join_all(threads).await?; | ||
|
||
for result in results { | ||
let result = result?; | ||
|
||
if !self.no_hash_check { | ||
eprint!("🔓 Checking {} hash...", result.file_name.url); | ||
|
||
let actual_hash = result.actual_hash.no_prefix(); | ||
|
||
if result.actual_hash == result.computed_hash { | ||
eprintln!("\r🔒 Hash matched: {actual_hash}"); | ||
} else { | ||
eprintln!(); | ||
abandon!( | ||
"🔓 Hash mismatch: expected {actual_hash}, found {}", | ||
result.computed_hash.no_prefix() | ||
); | ||
} | ||
// } else { | ||
// eprintln!(); | ||
// warn!("🔓 No hash provided, skipping hash check"); | ||
// } | ||
} | ||
|
||
eprintln!("✅ Downloaded {}", result.file_name.url); | ||
} | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use clap::Parser; | ||
use sprinkles::{config, contexts::ScoopContext, packages::reference::package}; | ||
|
||
use crate::abandon; | ||
|
||
#[derive(Debug, Clone, Parser)] | ||
/// Opens the app homepage | ||
pub struct Args { | ||
#[clap(help = "The package to open the homepage for")] | ||
package: package::Reference, | ||
} | ||
|
||
impl super::Command for Args { | ||
async fn runner(self, ctx: &impl ScoopContext<config::Scoop>) -> Result<(), anyhow::Error> { | ||
let manifest = self | ||
.package | ||
.first(ctx) | ||
.ok_or(anyhow::anyhow!("Package not found"))?; | ||
|
||
let Some(homepage) = manifest.homepage else { | ||
abandon!("No homepage found for package"); | ||
}; | ||
|
||
open::that_detached(homepage)?; | ||
|
||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.