hopper/src/main.rs

122 lines
3.4 KiB
Rust
Raw Normal View History

2021-12-11 04:20:26 +00:00
mod api;
mod client;
2021-12-11 04:20:26 +00:00
mod config;
2021-11-21 18:41:39 +00:00
2021-12-11 04:20:26 +00:00
use api::*;
2022-09-06 21:12:00 +00:00
use clap::Parser;
use client::*;
2021-12-11 04:20:26 +00:00
use config::*;
2021-11-21 18:18:26 +00:00
fn display_search_results(ctx: &AppContext, response: &SearchResponse) {
2021-11-22 02:00:53 +00:00
let iter = response.hits.iter().enumerate();
if ctx.config.options.reverse_search {
2021-11-22 02:00:53 +00:00
for (i, result) in iter.rev() {
result.display(i + 1);
}
} else {
for (i, result) in iter {
result.display(i + 1);
}
2021-11-21 18:18:26 +00:00
}
2021-11-22 01:50:59 +00:00
}
2021-11-21 18:18:26 +00:00
2021-11-22 03:50:55 +00:00
// TODO implement enum for more graceful exiting
async fn select_from_results(
_ctx: &AppContext,
response: &SearchResponse,
) -> anyhow::Result<Vec<usize>> {
2021-11-29 19:30:30 +00:00
let input: String = dialoguer::Input::new()
.with_prompt("Mods to install (eg: 1 2 3-5)")
2021-11-29 19:30:30 +00:00
.interact_text()?;
let mut selected: Vec<usize> = Vec::new();
for token in input.split(" ") {
let terms: Vec<&str> = token.split("-").collect();
match terms.len() {
1 => selected.push(terms[0].parse().expect("Token must be an integer")),
2 => {
let terms: Vec<usize> = terms
.iter()
.map(|term| term.parse().expect("Term must be an integer"))
.collect();
let from = terms[0];
let to = terms[1];
for index in from..=to {
selected.push(index);
}
}
_ => panic!("Invalid selection token {}", token),
2021-11-29 19:30:30 +00:00
}
}
2021-11-29 19:30:30 +00:00
selected.dedup();
2021-11-29 19:30:30 +00:00
let selected = selected
.iter()
.map(|index| {
if *index < 1 || *index > response.hits.len() {
// TODO return useful error instead of panicking
panic!("Index {} is out of bounds", index);
}
// input is indexed from 1, but results are indexed from 0
let index = index - 1;
index
})
.collect();
2021-11-29 19:30:30 +00:00
Ok(selected)
2021-11-22 03:50:55 +00:00
}
async fn cmd_get(ctx: &AppContext, search_args: SearchArgs) -> anyhow::Result<()> {
let client = HopperClient::new(ctx.config.clone());
let response = client.search_mods(&search_args).await?;
2021-11-22 03:50:55 +00:00
if response.hits.is_empty() {
// TODO formatting
println!("No results; nothing to do...");
return Ok(());
}
display_search_results(ctx, &response);
let selected = select_from_results(ctx, &response).await?;
2021-11-22 03:50:55 +00:00
if selected.is_empty() {
// TODO formatting
println!("No packages selected; nothing to do...");
return Ok(());
}
for selection in selected.iter() {
let to_get = &response.hits[*selection];
let mod_info = client.fetch_mod_info(to_get).await?;
2021-11-29 00:21:18 +00:00
// TODO allow the user to select multiple versions
if let Some(version_id) = mod_info.versions.first() {
println!("fetching version {}", version_id);
let version = client.fetch_mod_version(version_id).await?;
2021-11-29 00:33:38 +00:00
for file in version.files.iter() {
client.download_version_file(&ctx.args, file).await?;
2021-11-29 00:33:38 +00:00
}
2021-11-29 00:21:18 +00:00
}
2021-11-22 03:50:55 +00:00
}
2021-11-21 01:09:56 +00:00
Ok(())
2021-11-20 17:00:38 +00:00
}
2021-11-21 04:35:29 +00:00
#[tokio::main]
async fn main() -> anyhow::Result<()> {
env_logger::init();
2022-09-06 21:12:00 +00:00
let args = Args::parse();
2021-11-21 04:35:29 +00:00
let config = args.load_config()?;
let ctx = AppContext { args, config };
match ctx.args.to_owned().command {
Command::Get(search_args) => cmd_get(&ctx, search_args).await,
2021-11-21 04:35:29 +00:00
_ => unimplemented!("unimplemented subcommand"),
}
}