client.rs: changed PackageType parsing to fix compilation and removed reqwest-based code; main.rs, args.rs: cleanup

This commit is contained in:
Emma Tebibyte 2023-03-24 14:50:10 -04:00
parent 8d0b82149d
commit 261b40cc0b
Signed by: emma
GPG Key ID: 6D661C738815E7DD
3 changed files with 16 additions and 119 deletions

View File

@ -85,7 +85,7 @@ pub enum Command {
Update(HopArgs),
}
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub enum PackageType {
Mod(Loader),
Pack(Loader),
@ -93,14 +93,14 @@ pub enum PackageType {
ResourcePack,
}
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub enum Loader {
Fabric,
Forge,
Quilt,
}
#[derive(Debug)]
#[derive(Clone, Copy, Debug)]
pub enum Server {
Bukkit,
Paper,
@ -109,7 +109,7 @@ pub enum Server {
Sponge,
}
#[derive(Debug)]
#[derive(Clone, Debug)]
pub enum PackageParseError {
Invalid(String),
}

View File

@ -42,29 +42,30 @@ use crate::{
use std::cmp::min;
use std::io::Write;
use curl::easy::{ Easy2, Handler };
use curl::easy::{ Easy };
use futures_util::StreamExt;
pub struct HopperClient {
config: Config,
client: Easy2<Handler>,
client: Easy,
}
impl HopperClient {
pub fn new(config: Config) -> Self {
curl::init();
Self {
config: config,
client: Easy2::new()
client: Easy::new()
}
}
pub async fn search_mods(
&self,
&mut self,
search_args: &SearchArgs,
) -> Result<SearchResponse, (String, u32)> {
) /*-> Result<SearchResponse, (String, u32)>*/ {
println!("Searching with query “{}”...", search_args.package_name);
let urls = Vec::new();
let mut urls = Vec::new();
for entry in self.config.sources.drain() {
let (source, domain) = entry;
@ -81,7 +82,7 @@ impl HopperClient {
.join(",");
facets.push(format!("{}", versions_facets));
}
if let Some(package_type) = &search_args.package_type {
if let Some(package_type) = search_args.package_type {
let project_type = match package_type {
PackageType::Mod(_) => "[\"project_type:mod\"]",
PackageType::Pack(_) => "[\"project_type:modpack\"]",
@ -106,6 +107,7 @@ impl HopperClient {
Server::Sponge => "[\"categories:sponge\"]",
}
},
PackageType::ResourcePack => "",
};
let package_type_facet = format!(
@ -120,110 +122,5 @@ impl HopperClient {
if !facets.is_empty() {
params.push(("facets", format!("[{}]", facets.join(","))));
}
// TODO: Rewrite using curl
let url = reqwest::Url::parse_with_params(url.as_str(), &params)?;
info!("GET {}", url);
let response = self.client.get(url).send().await?;
if response.status().is_success() {
Ok(response.json::<SearchResponse>().await?)
} else {
Err(response.json::<APIError>().await?.into())
}
}
pub async fn fetch_mod_info(&self, mod_result: &ModResult) -> anyhow::Result<ModInfo> {
let mod_id = &mod_result.project_id;
println!(
"Fetching mod info for {} (ID: {})...",
mod_result.title, mod_id
);
let url = format!(
"https://{}/v2/project/{}",
self.config.upstream.server_address, mod_id
);
info!("GET {}", url);
let response = self.client.get(url).send().await?;
if response.status().is_success() {
Ok(response.json::<ModInfo>().await?)
} else {
Err(response.json::<APIError>().await?.into())
}
}
pub async fn fetch_mod_version(&self, version_id: &String) -> anyhow::Result<ModVersion> {
println!("Fetching mod version {}...", version_id);
let url = format!(
"https://{}/v2/version/{}",
self.config.upstream.server_address, version_id
);
info!("GET {}", url);
let response = self.client.get(url).send().await?;
if response.status().is_success() {
Ok(response.json::<ModVersion>().await?)
} else {
Err(response.json::<APIError>().await?.into())
}
}
pub async fn download_version_file(
&self,
args: &Args,
file: &ModVersionFile,
) -> anyhow::Result<()> {
// TODO replace all uses of .unwrap() with proper error codes
let filename = &file.filename;
// TODO make confirmation skippable with flag argument
if !args.auto_accept {
use dialoguer::Confirm;
let prompt = format!("Download to {}?", filename);
let confirm = Confirm::new()
.with_prompt(prompt)
.default(true)
.interact()?;
if !confirm {
println!("Skipping downloading {}...", filename);
return Ok(());
}
}
let url = &file.url;
info!("GET {}", url);
let response = self.client.get(url).send().await?;
if !response.status().is_success() {
return Err(response.json::<APIError>().await?.into())
}
let total_size = response.content_length().unwrap();
// TODO better colors and styling!
// TODO square colored creeper face progress indicator (from top-left clockwise spiral in)
use indicatif::{ProgressBar, ProgressStyle};
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_message(&format!("Downloading {}", url));
let filename = &file.filename;
let mut file = std::fs::File::create(filename)?;
let mut downloaded: u64 = 0;
let mut stream = response.bytes_stream();
// TODO check hashes while streaming
while let Some(item) = stream.next().await {
let chunk = &item.unwrap();
file.write(&chunk)?;
let new = min(downloaded + (chunk.len() as u64), total_size);
downloaded = new;
pb.set_position(new);
}
pb.finish_with_message(&format!("Downloaded {} to {}", url, filename));
Ok(())
}
}

View File

@ -45,8 +45,8 @@ struct AppContext {
#[tokio::main]
#[no_mangle]
async fn rust_main(args: c_main::Args) {
let arguments = Arguments::from_args(args.into_iter());
async fn rust_main(arguments: c_main::Args) {
let args = Arguments::from_args(arguments.into_iter()).unwrap();
let xdg_dirs = match xdg::BaseDirectories::with_prefix("hopper") {
Ok(dirs) => dirs,
@ -74,7 +74,7 @@ async fn rust_main(args: c_main::Args) {
let ctx = AppContext { args, config };
match ctx.arguments.command {
match ctx.args.sub {
// Command::Get(search_args) => cmd_get(&ctx, search_args).await,
// Command::Init(hopfile_args) => cmd_init(hopfile_args).await,
_ => unimplemented!("unimplemented subcommand"),