This commit is contained in:
parent
6d44b60182
commit
1fe7ef0f2d
9 changed files with 82 additions and 24 deletions
|
@ -1,2 +1,3 @@
|
||||||
# 0.1.2
|
# 0.1.2
|
||||||
- optimize curseforge relation meta querying
|
- optimize curseforge relation meta querying
|
||||||
|
- add debug logging
|
||||||
|
|
|
@ -17,10 +17,12 @@ crossterm = "0.21.0"
|
||||||
futures = "0.3.16"
|
futures = "0.3.16"
|
||||||
indicatif = "0.16.2"
|
indicatif = "0.16.2"
|
||||||
json5 = "0.3.0"
|
json5 = "0.3.0"
|
||||||
|
log = "0.4.14"
|
||||||
percent-encoding = "2.1.0"
|
percent-encoding = "2.1.0"
|
||||||
reqwest = { version = "0.11.4", features = ["stream"] }
|
reqwest = { version = "0.11.4", features = ["stream"] }
|
||||||
serde = { version = "1.0.129", features = ["derive"] }
|
serde = { version = "1.0.129", features = ["derive"] }
|
||||||
serde_json = "1.0.67"
|
serde_json = "1.0.67"
|
||||||
|
simplelog = "0.10.0"
|
||||||
structopt = "0.3.22"
|
structopt = "0.3.22"
|
||||||
tera = "1.12.1"
|
tera = "1.12.1"
|
||||||
thiserror = "1.0.28"
|
thiserror = "1.0.28"
|
||||||
|
|
|
@ -24,6 +24,7 @@ use addonscript::manifest::{
|
||||||
use anyhow::{bail, Context};
|
use anyhow::{bail, Context};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use indicatif::ProgressBar;
|
use indicatif::ProgressBar;
|
||||||
|
use log::{debug, info};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use std::{collections::HashMap, io::Write, path::Path, sync::Arc};
|
use std::{collections::HashMap, io::Write, path::Path, sync::Arc};
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
|
@ -110,6 +111,7 @@ pub async fn run((config, mut manifest): (Config, Manifest), all: bool) -> anyho
|
||||||
|
|
||||||
match installer {
|
match installer {
|
||||||
Installer::Dir(dir) => {
|
Installer::Dir(dir) => {
|
||||||
|
info!("copying dir installer file {}", path.to_string_lossy());
|
||||||
tokio::fs::copy(config.locations.src.join(path), twitch_dir.join(dir))
|
tokio::fs::copy(config.locations.src.join(path), twitch_dir.join(dir))
|
||||||
.await?;
|
.await?;
|
||||||
},
|
},
|
||||||
|
@ -246,6 +248,7 @@ fn sort_file(
|
||||||
link_rels: &mut Vec<(Installer, Link)>,
|
link_rels: &mut Vec<(Installer, Link)>,
|
||||||
cf_rels: &mut Vec<(u32, u32)>,
|
cf_rels: &mut Vec<(u32, u32)>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
debug!("Sorting file {:?}", &file);
|
||||||
match file {
|
match file {
|
||||||
File::Link {
|
File::Link {
|
||||||
installer, link, ..
|
installer, link, ..
|
||||||
|
|
|
@ -3,7 +3,8 @@ use crate::{config::Config, util::CliStyle};
|
||||||
pub async fn run(config: Config) -> anyhow::Result<()> {
|
pub async fn run(config: Config) -> anyhow::Result<()> {
|
||||||
// These operations are so incredibly performance-critical that we absolutely
|
// These operations are so incredibly performance-critical that we absolutely
|
||||||
// must execute them in parallel!
|
// must execute them in parallel!
|
||||||
let (res1, res2) = tokio::join!(
|
// we ignore the results on purpose, so nothing fails when the dirs dont exist.
|
||||||
|
let (_, _) = tokio::join!(
|
||||||
async {
|
async {
|
||||||
println!(
|
println!(
|
||||||
"{}",
|
"{}",
|
||||||
|
@ -17,8 +18,5 @@ pub async fn run(config: Config) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
res1?;
|
|
||||||
res2?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use addonscript::manifest::{
|
||||||
RepositoryType,
|
RepositoryType,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use log::{debug, info};
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||||
|
@ -27,6 +28,7 @@ pub async fn run(
|
||||||
.context("Manifest has no versions!")?;
|
.context("Manifest has no versions!")?;
|
||||||
let repos = util::repo_map(manifest.repositories);
|
let repos = util::repo_map(manifest.repositories);
|
||||||
|
|
||||||
|
info!("instatiating tera engine");
|
||||||
let mut tera = Tera::default();
|
let mut tera = Tera::default();
|
||||||
tera.add_raw_template("modlist", TEMPLATE)?;
|
tera.add_raw_template("modlist", TEMPLATE)?;
|
||||||
|
|
||||||
|
@ -47,6 +49,11 @@ pub async fn run(
|
||||||
if !cf_rels.is_empty() {
|
if !cf_rels.is_empty() {
|
||||||
println!("{}", "Querying CF metas.".info());
|
println!("{}", "Querying CF metas.".info());
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Requesting addon info for {} curseforge mods",
|
||||||
|
cf_rels.len()
|
||||||
|
);
|
||||||
|
|
||||||
let res = http
|
let res = http
|
||||||
.post("https://addons-ecs.forgesvc.net/api/v2/addon")
|
.post("https://addons-ecs.forgesvc.net/api/v2/addon")
|
||||||
.body(
|
.body(
|
||||||
|
@ -64,6 +71,7 @@ pub async fn run(
|
||||||
let cf_metas = serde_json::from_slice::<Vec<AddonInfoResponse>>(&res)
|
let cf_metas = serde_json::from_slice::<Vec<AddonInfoResponse>>(&res)
|
||||||
.context("Failed deserializing CF relation response")?;
|
.context("Failed deserializing CF relation response")?;
|
||||||
|
|
||||||
|
info!("Converting CF metas to AS metas");
|
||||||
let cf_metas = cf_metas.into_iter().map(|m| Meta {
|
let cf_metas = cf_metas.into_iter().map(|m| Meta {
|
||||||
name: m.name,
|
name: m.name,
|
||||||
contributors: m
|
contributors: m
|
||||||
|
@ -108,6 +116,7 @@ pub async fn run(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_meta(rel: Relation, repos: &HashMap<String, Repository>) -> anyhow::Result<MetaInfo> {
|
fn get_meta(rel: Relation, repos: &HashMap<String, Repository>) -> anyhow::Result<MetaInfo> {
|
||||||
|
debug!("getting meta for {:?}", &rel);
|
||||||
if let Some(meta) = rel.meta {
|
if let Some(meta) = rel.meta {
|
||||||
return Ok(MetaInfo::Meta(meta));
|
return Ok(MetaInfo::Meta(meta));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ use addonscript::{
|
||||||
};
|
};
|
||||||
use anyhow::{bail, Context};
|
use anyhow::{bail, Context};
|
||||||
use crossterm::style::Stylize;
|
use crossterm::style::Stylize;
|
||||||
|
use log::info;
|
||||||
use twitch::manifest::Manifest as TwManifest;
|
use twitch::manifest::Manifest as TwManifest;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ pub async fn run(config: Config, infile: PathBuf) -> anyhow::Result<()> {
|
||||||
)
|
)
|
||||||
.context("Failed to parse twitch manifest")?;
|
.context("Failed to parse twitch manifest")?;
|
||||||
|
|
||||||
|
info!("converting twitch mods to AS relations");
|
||||||
let mut relations = data
|
let mut relations = data
|
||||||
.files
|
.files
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -56,6 +58,7 @@ pub async fn run(config: Config, infile: PathBuf) -> anyhow::Result<()> {
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if let Some(ml) = data.minecraft.mod_loaders.pop() {
|
if let Some(ml) = data.minecraft.mod_loaders.pop() {
|
||||||
|
info!("fount modloader {:?}", &ml);
|
||||||
let mut splits = ml.id.split('-');
|
let mut splits = ml.id.split('-');
|
||||||
if !matches!(splits.next(), Some("forge")) {
|
if !matches!(splits.next(), Some("forge")) {
|
||||||
bail!("Twitch manifest contains invalid or unknown modloader!");
|
bail!("Twitch manifest contains invalid or unknown modloader!");
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use crossterm::style::Stylize;
|
use crossterm::style::Stylize;
|
||||||
use futures::{stream, StreamExt};
|
use futures::{stream, StreamExt};
|
||||||
|
use log::info;
|
||||||
use reqwest::{Client, StatusCode};
|
use reqwest::{Client, StatusCode};
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -58,6 +59,8 @@ impl<C: Callback> Downloader<C> {
|
||||||
url: Url,
|
url: Url,
|
||||||
target: PathBuf,
|
target: PathBuf,
|
||||||
) -> Result<DownloadInfo, DownloadError> {
|
) -> Result<DownloadInfo, DownloadError> {
|
||||||
|
info!("Downloading {}", &url);
|
||||||
|
|
||||||
if let Some(parent) = target.parent() {
|
if let Some(parent) = target.parent() {
|
||||||
tokio::fs::create_dir_all(parent).await?;
|
tokio::fs::create_dir_all(parent).await?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
use anyhow::Context;
|
||||||
|
use log::{info, LevelFilter};
|
||||||
|
use simplelog::{ColorChoice, TermLogger, TerminalMode};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
@ -9,6 +12,9 @@ mod util;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
struct Opt {
|
struct Opt {
|
||||||
|
#[structopt(short, long, parse(from_occurrences), help = "enable verbose logging")]
|
||||||
|
verbose: u8,
|
||||||
|
|
||||||
#[structopt(subcommand)]
|
#[structopt(subcommand)]
|
||||||
cmd: Command,
|
cmd: Command,
|
||||||
}
|
}
|
||||||
|
@ -69,32 +75,54 @@ enum Command {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let Opt { cmd } = Opt::from_args();
|
let Opt { cmd, verbose } = Opt::from_args();
|
||||||
|
|
||||||
|
let log_level = match verbose {
|
||||||
|
0 => LevelFilter::Off,
|
||||||
|
1 => LevelFilter::Info,
|
||||||
|
2 => LevelFilter::Debug,
|
||||||
|
_ => LevelFilter::Trace,
|
||||||
|
};
|
||||||
|
|
||||||
|
TermLogger::init(
|
||||||
|
log_level,
|
||||||
|
simplelog::ConfigBuilder::new()
|
||||||
|
.set_time_to_local(true)
|
||||||
|
.set_target_level(LevelFilter::Error)
|
||||||
|
.build(),
|
||||||
|
TerminalMode::Stderr,
|
||||||
|
ColorChoice::Auto,
|
||||||
|
)
|
||||||
|
.context("Failed to init logger")?;
|
||||||
|
|
||||||
|
/// runs a given command, if the first arg is config or manifest, the
|
||||||
|
/// manifest or config file is passed to the comand respectively.
|
||||||
|
macro_rules! run_cmd {
|
||||||
|
(config: $cmd:ident $($args:expr),* $(,)?) => {
|
||||||
|
run_cmd!($cmd util::parse_config().await?, $($args),*)
|
||||||
|
};
|
||||||
|
|
||||||
|
(manifest: $cmd:ident $($args:expr),* $(,)?) => {
|
||||||
|
run_cmd!($cmd util::parse_config_and_manifest().await?, $($args),*)
|
||||||
|
};
|
||||||
|
|
||||||
|
($cmd:ident $($args:expr),* $(,)?) => {{
|
||||||
|
info!("Running command {}", stringify!($cmd));
|
||||||
|
commands::$cmd::run($($args),*).await?;
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
match cmd {
|
match cmd {
|
||||||
Command::Init {
|
Command::Init {
|
||||||
modpack_name,
|
modpack_name,
|
||||||
name,
|
name,
|
||||||
mcversion,
|
mcversion,
|
||||||
} => commands::init::run(modpack_name, name, mcversion).await?,
|
} => run_cmd!(init modpack_name, name, mcversion),
|
||||||
|
Command::DownloadMods { dir, all } => run_cmd!(manifest: downloadmods dir, all),
|
||||||
Command::DownloadMods { dir, all } => {
|
Command::BuildTwitch { all } => run_cmd!(manifest: buildtwitch all),
|
||||||
commands::downloadmods::run(util::parse_config_and_manifest().await?, dir, all).await?
|
Command::Clean => run_cmd!(config: clean),
|
||||||
},
|
Command::CreateModList { outfile } => run_cmd!(manifest: createmodlist outfile),
|
||||||
|
Command::Import { infile } => run_cmd!(config: import infile),
|
||||||
Command::BuildTwitch { all } => {
|
|
||||||
commands::buildtwitch::run(util::parse_config_and_manifest().await?, all).await?
|
|
||||||
},
|
|
||||||
|
|
||||||
Command::Clean => commands::clean::run(util::parse_config().await?).await?,
|
|
||||||
|
|
||||||
Command::CreateModList { outfile } => {
|
|
||||||
commands::createmodlist::run(util::parse_config_and_manifest().await?, outfile).await?
|
|
||||||
},
|
|
||||||
|
|
||||||
Command::Import { infile } => {
|
|
||||||
commands::import::run(util::parse_config().await?, infile).await?
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -2,6 +2,7 @@ use addonscript::manifest::{link::Link, Manifest, Repository};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use crossterm::style::{Attribute, Attributes, Color, ContentStyle, Stylize};
|
use crossterm::style::{Attribute, Attributes, Color, ContentStyle, Stylize};
|
||||||
use indicatif::ProgressStyle;
|
use indicatif::ProgressStyle;
|
||||||
|
use log::info;
|
||||||
use percent_encoding::percent_decode;
|
use percent_encoding::percent_decode;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -17,6 +18,7 @@ use crate::config::Config;
|
||||||
|
|
||||||
/// reads and parses the config from the current working directory
|
/// reads and parses the config from the current working directory
|
||||||
pub async fn parse_config() -> anyhow::Result<Config> {
|
pub async fn parse_config() -> anyhow::Result<Config> {
|
||||||
|
info!("reading config");
|
||||||
let conf = tokio::fs::read("modpacktoolsconfig.toml")
|
let conf = tokio::fs::read("modpacktoolsconfig.toml")
|
||||||
.await
|
.await
|
||||||
.context("Failed to read config")?;
|
.context("Failed to read config")?;
|
||||||
|
@ -36,6 +38,8 @@ pub async fn parse_config_and_manifest() -> anyhow::Result<(Config, Manifest)> {
|
||||||
src.join("modpack.json")
|
src.join("modpack.json")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
info!("reading manifest");
|
||||||
|
|
||||||
let data = tokio::fs::read(path)
|
let data = tokio::fs::read(path)
|
||||||
.await
|
.await
|
||||||
.context("Failed to read manifest")?;
|
.context("Failed to read manifest")?;
|
||||||
|
@ -109,6 +113,7 @@ pub fn progress_style() -> ProgressStyle {
|
||||||
|
|
||||||
/// creates the modpacktools temporary directory (set in the config)
|
/// creates the modpacktools temporary directory (set in the config)
|
||||||
pub async fn make_tmp_dir(config: &Config) -> anyhow::Result<()> {
|
pub async fn make_tmp_dir(config: &Config) -> anyhow::Result<()> {
|
||||||
|
info!("creating temporary directory");
|
||||||
tokio::fs::create_dir_all(&config.locations.temp_dir)
|
tokio::fs::create_dir_all(&config.locations.temp_dir)
|
||||||
.await
|
.await
|
||||||
.context("Failed to create temporary directory")?;
|
.context("Failed to create temporary directory")?;
|
||||||
|
@ -181,6 +186,12 @@ pub fn link_file_name(link: &Link) -> Result<String, LinkFileNameError> {
|
||||||
|
|
||||||
/// Copies a directory inclding all files
|
/// Copies a directory inclding all files
|
||||||
pub async fn copy_dir(from: PathBuf, to: PathBuf) -> io::Result<()> {
|
pub async fn copy_dir(from: PathBuf, to: PathBuf) -> io::Result<()> {
|
||||||
|
info!(
|
||||||
|
"Copying directory {} to {}",
|
||||||
|
from.to_string_lossy(),
|
||||||
|
to.to_string_lossy()
|
||||||
|
);
|
||||||
|
|
||||||
for file in WalkDir::new(&from) {
|
for file in WalkDir::new(&from) {
|
||||||
let file = file?;
|
let file = file?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue