commit
af3f60be5d
|
@ -33,6 +33,8 @@ Features:
|
||||||
- zsh autocomplete
|
- zsh autocomplete
|
||||||
- nushell autocomplete
|
- nushell autocomplete
|
||||||
- manpage
|
- manpage
|
||||||
|
- configurable mod search result display like [Starship](https://starship.rs)
|
||||||
|
- `display` command or something that displays (cached?) mod info
|
||||||
- parallel mod downloading
|
- parallel mod downloading
|
||||||
|
|
||||||
Long-term/host-dependent:
|
Long-term/host-dependent:
|
||||||
|
@ -111,13 +113,6 @@ $ hopper add sodium --mc-version 1.17
|
||||||
:: ...
|
:: ...
|
||||||
```
|
```
|
||||||
|
|
||||||
- configurable mod search results like [Starship](https://starship.rs)
|
|
||||||
- pad mod indices based on largest number
|
|
||||||
- option in config to reverse sorting order
|
|
||||||
- add parameter to restrict target Minecraft version
|
|
||||||
- manually pick out Minecraft version
|
|
||||||
- square colored creeper face progress indicator (from top-left clockwise spiral in)
|
|
||||||
|
|
||||||
## `hopper get`
|
## `hopper get`
|
||||||
|
|
||||||
Just like `hopper add` but simply downloads a mod jar to the current directory.
|
Just like `hopper add` but simply downloads a mod jar to the current directory.
|
||||||
|
|
35
src/main.rs
35
src/main.rs
|
@ -7,18 +7,23 @@ use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
// TODO parameter to restrict target Minecraft version
|
||||||
|
#[derive(StructOpt, Clone, Debug)]
|
||||||
|
struct SearchArgs {
|
||||||
|
package_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO use ColoredHelp by default?
|
// TODO use ColoredHelp by default?
|
||||||
// TODO move each enum value to a dedicated struct
|
|
||||||
#[derive(StructOpt, Clone, Debug)]
|
#[derive(StructOpt, Clone, Debug)]
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Adds a mod to the current instance
|
/// Adds a mod to the current instance
|
||||||
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
||||||
Add { package_name: String },
|
Add(SearchArgs),
|
||||||
/// Removes a mod
|
/// Removes a mod
|
||||||
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
||||||
Remove { package_name: String },
|
Remove { package_name: String },
|
||||||
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
||||||
Get { package_name: String },
|
Get(SearchArgs),
|
||||||
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
||||||
Update,
|
Update,
|
||||||
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
#[structopt(setting = structopt::clap::AppSettings::ColoredHelp)]
|
||||||
|
@ -148,7 +153,7 @@ impl ModResult {
|
||||||
let index = style(index).magenta();
|
let index = style(index).magenta();
|
||||||
let info = self.format_info();
|
let info = self.format_info();
|
||||||
let description = self.format_description();
|
let description = self.format_description();
|
||||||
println!("{} {}\n {}", index, info, description);
|
println!("{:>2} {}\n {}", index, info, description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,8 +168,7 @@ struct ModInfo {
|
||||||
published: String, // TODO serialize datetime
|
published: String, // TODO serialize datetime
|
||||||
updated: String, // TODO serialize datetime
|
updated: String, // TODO serialize datetime
|
||||||
status: String,
|
status: String,
|
||||||
// TODO License object
|
license: License,
|
||||||
// license: String,
|
|
||||||
client_side: String, // TODO serialize as enum
|
client_side: String, // TODO serialize as enum
|
||||||
server_side: String, // TODO serialize as enum
|
server_side: String, // TODO serialize as enum
|
||||||
downloads: isize,
|
downloads: isize,
|
||||||
|
@ -179,6 +183,13 @@ struct ModInfo {
|
||||||
donation_urls: Vec<String>,
|
donation_urls: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct License {
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct ModVersion {
|
struct ModVersion {
|
||||||
id: String, // version id
|
id: String, // version id
|
||||||
|
@ -206,10 +217,10 @@ struct ModVersionFile {
|
||||||
filename: String,
|
filename: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn search_mods(ctx: &AppContext, query: String) -> anyhow::Result<SearchResponse> {
|
async fn search_mods(ctx: &AppContext, search_args: &SearchArgs) -> anyhow::Result<SearchResponse> {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let url = format!("https://{}/api/v1/mod", ctx.config.upstream.server_address);
|
let url = format!("https://{}/api/v1/mod", ctx.config.upstream.server_address);
|
||||||
let params = [("query", query.as_str())];
|
let params = [("query", search_args.package_name.as_str())];
|
||||||
let url = reqwest::Url::parse_with_params(url.as_str(), ¶ms)?;
|
let url = reqwest::Url::parse_with_params(url.as_str(), ¶ms)?;
|
||||||
let response = client
|
let response = client
|
||||||
.get(url)
|
.get(url)
|
||||||
|
@ -220,7 +231,6 @@ async fn search_mods(ctx: &AppContext, query: String) -> anyhow::Result<SearchRe
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO config flag to reverse search results order
|
|
||||||
fn display_search_results(ctx: &AppContext, response: &SearchResponse) {
|
fn display_search_results(ctx: &AppContext, response: &SearchResponse) {
|
||||||
let iter = response.hits.iter().enumerate();
|
let iter = response.hits.iter().enumerate();
|
||||||
if ctx.config.options.reverse_search {
|
if ctx.config.options.reverse_search {
|
||||||
|
@ -314,6 +324,7 @@ async fn download_version_file(ctx: &AppContext, file: &ModVersionFile) -> anyho
|
||||||
let total_size = response.content_length().unwrap();
|
let total_size = response.content_length().unwrap();
|
||||||
|
|
||||||
// TODO better colors and styling!
|
// TODO better colors and styling!
|
||||||
|
// TODO square colored creeper face progress indicator (from top-left clockwise spiral in)
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
let pb = ProgressBar::new(total_size);
|
let pb = ProgressBar::new(total_size);
|
||||||
pb.set_style(ProgressStyle::default_bar().template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})").progress_chars("#>-"));
|
pb.set_style(ProgressStyle::default_bar().template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})").progress_chars("#>-"));
|
||||||
|
@ -337,8 +348,8 @@ async fn download_version_file(ctx: &AppContext, file: &ModVersionFile) -> anyho
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cmd_get(ctx: &AppContext, package_name: String) -> anyhow::Result<()> {
|
async fn cmd_get(ctx: &AppContext, search_args: SearchArgs) -> anyhow::Result<()> {
|
||||||
let response = search_mods(ctx, package_name).await?;
|
let response = search_mods(ctx, &search_args).await?;
|
||||||
|
|
||||||
if response.hits.is_empty() {
|
if response.hits.is_empty() {
|
||||||
// TODO formatting
|
// TODO formatting
|
||||||
|
@ -378,7 +389,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
let config = args.load_config()?;
|
let config = args.load_config()?;
|
||||||
let ctx = AppContext { args, config };
|
let ctx = AppContext { args, config };
|
||||||
match ctx.args.to_owned().command {
|
match ctx.args.to_owned().command {
|
||||||
Command::Get { package_name } => cmd_get(&ctx, package_name).await,
|
Command::Get(search_args) => cmd_get(&ctx, search_args).await,
|
||||||
_ => unimplemented!("unimplemented subcommand"),
|
_ => unimplemented!("unimplemented subcommand"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue