This commit is contained in:
parent
5d3b2eb8b5
commit
4b2c701574
|
@ -11,7 +11,6 @@ path = "src/main.rs"
|
|||
|
||||
[dependencies]
|
||||
addonscript = { path = "../addonscript" }
|
||||
anyhow = "1.0.43"
|
||||
async-trait = "0.1.51"
|
||||
crossterm = "0.21.0"
|
||||
futures = "0.3.16"
|
||||
|
@ -19,6 +18,7 @@ heck = "0.3.3"
|
|||
indicatif = "0.16.2"
|
||||
json5 = "0.3.0"
|
||||
log = "0.4.14"
|
||||
miette = { version = "3.1.0", features = ["fancy"] }
|
||||
percent-encoding = "2.1.0"
|
||||
reqwest = { version = "0.11.4", features = ["stream"] }
|
||||
serde = { version = "1.0.129", features = ["derive"] }
|
||||
|
|
|
@ -21,10 +21,10 @@ use addonscript::manifest::{
|
|||
Repository,
|
||||
RepositoryType,
|
||||
};
|
||||
use anyhow::{bail, Context};
|
||||
use async_trait::async_trait;
|
||||
use indicatif::ProgressBar;
|
||||
use log::{debug, info};
|
||||
use miette::{bail, miette, IntoDiagnostic, WrapErr};
|
||||
use reqwest::Client;
|
||||
use std::{collections::HashMap, io::Write, path::Path, sync::Arc};
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
@ -32,14 +32,14 @@ use twitch::manifest::Manifest as TwManifest;
|
|||
use walkdir::WalkDir;
|
||||
use zip::{CompressionMethod, ZipWriter};
|
||||
|
||||
pub async fn run((config, mut manifest): (Config, Manifest), all: bool) -> anyhow::Result<()> {
|
||||
pub async fn run((config, mut manifest): (Config, Manifest), all: bool) -> miette::Result<()> {
|
||||
util::make_tmp_dir(&config).await?;
|
||||
let twitch_dir = config.locations.temp_dir.join("twitch");
|
||||
|
||||
let mut version = manifest
|
||||
.versions
|
||||
.pop()
|
||||
.context("Manifest has no versions!")?;
|
||||
.ok_or_else(|| miette!("Manifest has no versions!"))?;
|
||||
let repos = util::repo_map(manifest.repositories);
|
||||
|
||||
let mut modloader = None;
|
||||
|
@ -63,9 +63,9 @@ pub async fn run((config, mut manifest): (Config, Manifest), all: bool) -> anyho
|
|||
|
||||
let version = rel
|
||||
.versions
|
||||
.context("Modloader is missing `versions` field!")?;
|
||||
let version =
|
||||
forge::parse_version(&version).context("Couldn't parse forge version!")?;
|
||||
.ok_or_else(|| miette!("Modloader is missing `versions` field!"))?;
|
||||
let version = forge::parse_version(&version)
|
||||
.ok_or_else(|| miette!("Couldn't parse forge version!"))?;
|
||||
modloader = Some(format!("forge-{}", version));
|
||||
|
||||
continue;
|
||||
|
@ -113,7 +113,8 @@ pub async fn run((config, mut manifest): (Config, Manifest), all: bool) -> anyho
|
|||
Installer::Dir(dir) => {
|
||||
info!("copying dir installer file {}", path.to_string_lossy());
|
||||
tokio::fs::copy(config.locations.src.join(path), twitch_dir.join(dir))
|
||||
.await?;
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
},
|
||||
|
||||
Installer::Override => {
|
||||
|
@ -122,7 +123,9 @@ pub async fn run((config, mut manifest): (Config, Manifest), all: bool) -> anyho
|
|||
bail!("File with override installer is not directory!");
|
||||
}
|
||||
|
||||
util::copy_dir(path, twitch_dir.join("overrides")).await?;
|
||||
util::copy_dir(path, twitch_dir.join("overrides"))
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
},
|
||||
}
|
||||
},
|
||||
|
@ -192,15 +195,22 @@ pub async fn run((config, mut manifest): (Config, Manifest), all: bool) -> anyho
|
|||
.collect::<Vec<_>>(),
|
||||
manifest.meta.name.clone(),
|
||||
version.version.clone(),
|
||||
version.mcversion.pop().context("mcversion is empty!")?,
|
||||
version
|
||||
.mcversion
|
||||
.pop()
|
||||
.ok_or_else(|| miette!("mcversion is empty!"))?,
|
||||
modloader,
|
||||
);
|
||||
|
||||
let json = serde_json::to_vec(&tw_manifest).context("Failed to serialize twitch manifest")?;
|
||||
let json = serde_json::to_vec(&tw_manifest)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to serialize twitch manifest")?;
|
||||
|
||||
tokio::fs::write(twitch_dir.join("manifest.json"), json).await?;
|
||||
tokio::fs::write(twitch_dir.join("manifest.json"), json)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
tokio::fs::create_dir_all("build").await?;
|
||||
tokio::fs::create_dir_all("build").await.into_diagnostic()?;
|
||||
|
||||
println!("{}", "Zipping pack.".info());
|
||||
|
||||
|
@ -210,7 +220,8 @@ pub async fn run((config, mut manifest): (Config, Manifest), all: bool) -> anyho
|
|||
"build/{}-{}-twitch.zip",
|
||||
manifest.meta.name, version.version
|
||||
)))
|
||||
.context("Failed to open zip file!")?,
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to open zip file!")?,
|
||||
);
|
||||
|
||||
let options =
|
||||
|
@ -218,26 +229,30 @@ pub async fn run((config, mut manifest): (Config, Manifest), all: bool) -> anyho
|
|||
|
||||
let mut buf = vec![];
|
||||
for entry in WalkDir::new(&twitch_dir) {
|
||||
let entry = entry?;
|
||||
let entry = entry.into_diagnostic()?;
|
||||
let path = entry.path();
|
||||
let to = path.strip_prefix(&twitch_dir)?;
|
||||
let to = path.strip_prefix(&twitch_dir).into_diagnostic()?;
|
||||
|
||||
if path.is_file() {
|
||||
zip.start_file(to.to_string_lossy(), options)?;
|
||||
zip.start_file(to.to_string_lossy(), options)
|
||||
.into_diagnostic()?;
|
||||
|
||||
tokio::fs::File::open(path)
|
||||
.await?
|
||||
.await
|
||||
.into_diagnostic()?
|
||||
.read_to_end(&mut buf)
|
||||
.await?;
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
zip.write_all(&buf)?;
|
||||
zip.write_all(&buf).into_diagnostic()?;
|
||||
|
||||
buf.clear();
|
||||
} else if !to.as_os_str().is_empty() {
|
||||
zip.add_directory(to.to_string_lossy(), options)?;
|
||||
zip.add_directory(to.to_string_lossy(), options)
|
||||
.into_diagnostic()?;
|
||||
}
|
||||
}
|
||||
zip.finish()?;
|
||||
zip.finish().into_diagnostic()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -247,7 +262,7 @@ fn sort_file(
|
|||
repos: &HashMap<String, Repository>,
|
||||
link_rels: &mut Vec<(Installer, Link)>,
|
||||
cf_rels: &mut Vec<(u32, u32)>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> miette::Result<()> {
|
||||
debug!("Sorting file {:?}", &file);
|
||||
match file {
|
||||
File::Link {
|
||||
|
@ -261,11 +276,12 @@ fn sort_file(
|
|||
} => {
|
||||
let repo = repos
|
||||
.get(&repository)
|
||||
.with_context(|| format!("File references unknown repository {}", &repository))?;
|
||||
.ok_or_else(|| miette!("File references unknown repository {}", &repository))?;
|
||||
match repo.repo_type {
|
||||
RepositoryType::Maven => {
|
||||
let url = util::mvn_artifact_to_url(&artifact, repo)
|
||||
.context("Failed to convert maven artifact to url")?;
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to convert maven artifact to url")?;
|
||||
|
||||
link_rels.push((installer, Link::Http(url)));
|
||||
},
|
||||
|
@ -273,8 +289,11 @@ fn sort_file(
|
|||
let (p_id, f_id) = util::parse_curseforge_artifact(&artifact)?;
|
||||
cf_rels.push((
|
||||
p_id.parse()
|
||||
.context("Couldn't parse curseforge project ID!")?,
|
||||
f_id.parse().context("Couldn't parse curseforge file ID!")?,
|
||||
.into_diagnostic()
|
||||
.wrap_err("Couldn't parse curseforge project ID!")?,
|
||||
f_id.parse()
|
||||
.into_diagnostic()
|
||||
.wrap_err("Couldn't parse curseforge file ID!")?,
|
||||
));
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{config::Config, util::CliStyle};
|
||||
|
||||
pub async fn run(config: Config) -> anyhow::Result<()> {
|
||||
pub async fn run(config: Config) -> miette::Result<()> {
|
||||
// These operations are so incredibly performance-critical that we absolutely
|
||||
// must execute them in parallel!
|
||||
// we ignore the results on purpose, so nothing fails when the dirs dont exist.
|
||||
|
|
|
@ -8,8 +8,8 @@ use addonscript::manifest::{
|
|||
Repository,
|
||||
RepositoryType,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use log::{debug, info};
|
||||
use miette::{miette, IntoDiagnostic, WrapErr};
|
||||
use reqwest::Client;
|
||||
use serde::Serialize;
|
||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||
|
@ -21,16 +21,17 @@ const TEMPLATE: &str = include_str!("../../assets/modlist.html.tera");
|
|||
pub async fn run(
|
||||
(_config, mut manifest): (Config, Manifest),
|
||||
outfile: PathBuf,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> miette::Result<()> {
|
||||
let version = manifest
|
||||
.versions
|
||||
.pop()
|
||||
.context("Manifest has no versions!")?;
|
||||
.ok_or_else(|| miette!("Manifest has no versions!"))?;
|
||||
let repos = util::repo_map(manifest.repositories);
|
||||
|
||||
info!("instatiating tera engine");
|
||||
let mut tera = Tera::default();
|
||||
tera.add_raw_template("modlist", TEMPLATE)?;
|
||||
tera.add_raw_template("modlist", TEMPLATE)
|
||||
.into_diagnostic()?;
|
||||
|
||||
let http = Arc::new(Client::new());
|
||||
|
||||
|
@ -58,18 +59,22 @@ pub async fn run(
|
|||
.post("https://addons-ecs.forgesvc.net/api/v2/addon")
|
||||
.body(
|
||||
serde_json::to_string(&cf_rels)
|
||||
.context("Failed to serialize curseforge relation IDs")?,
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to serialize curseforge relation IDs")?,
|
||||
)
|
||||
.header("Content-Type", "application/json")
|
||||
.send()
|
||||
.await
|
||||
.context("Failed sending CF relation request")?
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed sending CF relation request")?
|
||||
.bytes()
|
||||
.await
|
||||
.context("Failed getting CF relation response body")?;
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed getting CF relation response body")?;
|
||||
|
||||
let cf_metas = serde_json::from_slice::<Vec<AddonInfoResponse>>(&res)
|
||||
.context("Failed deserializing CF relation response")?;
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed deserializing CF relation response")?;
|
||||
|
||||
info!("Converting CF metas to AS metas");
|
||||
let cf_metas = cf_metas.into_iter().map(|m| Meta {
|
||||
|
@ -98,24 +103,29 @@ pub async fn run(
|
|||
|
||||
println!("{}", "Rendering modlist.".info());
|
||||
|
||||
let rendered = tera.render(
|
||||
"modlist",
|
||||
&tera::Context::from_serialize(&ModListContent {
|
||||
metas,
|
||||
pack_meta: manifest.meta,
|
||||
})?,
|
||||
)?;
|
||||
let rendered = tera
|
||||
.render(
|
||||
"modlist",
|
||||
&tera::Context::from_serialize(&ModListContent {
|
||||
metas,
|
||||
pack_meta: manifest.meta,
|
||||
})
|
||||
.into_diagnostic()?,
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
|
||||
println!("{}", "Writing file.".info());
|
||||
if let Some(parent) = outfile.parent() {
|
||||
tokio::fs::create_dir_all(parent).await?;
|
||||
tokio::fs::create_dir_all(parent).await.into_diagnostic()?;
|
||||
}
|
||||
tokio::fs::write(outfile, rendered).await?;
|
||||
tokio::fs::write(outfile, rendered)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_meta(rel: Relation, repos: &HashMap<String, Repository>) -> anyhow::Result<MetaInfo> {
|
||||
fn get_meta(rel: Relation, repos: &HashMap<String, Repository>) -> miette::Result<MetaInfo> {
|
||||
debug!("getting meta for {:?}", &rel);
|
||||
if let Some(meta) = rel.meta {
|
||||
return Ok(MetaInfo::Meta(meta));
|
||||
|
@ -156,7 +166,7 @@ fn get_meta(rel: Relation, repos: &HashMap<String, Repository>) -> anyhow::Resul
|
|||
} => {
|
||||
let repo = repos
|
||||
.get(&repository)
|
||||
.context("File references unknown repository!")?;
|
||||
.ok_or_else(|| miette!("File references unknown repository!"))?;
|
||||
|
||||
match repo.repo_type {
|
||||
RepositoryType::Maven => Ok(MetaInfo::Meta(Meta {
|
||||
|
@ -170,7 +180,8 @@ fn get_meta(rel: Relation, repos: &HashMap<String, Repository>) -> anyhow::Resul
|
|||
let (p_id, _) = util::parse_curseforge_artifact(&artifact)?;
|
||||
let p_id = p_id
|
||||
.parse::<u32>()
|
||||
.context("Failed to parse curseforge ID")?;
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to parse curseforge ID")?;
|
||||
|
||||
Ok(MetaInfo::CfId(p_id))
|
||||
},
|
||||
|
|
|
@ -19,11 +19,11 @@ use addonscript::manifest::{
|
|||
Repository,
|
||||
RepositoryType,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use async_trait::async_trait;
|
||||
use crossterm::style::Stylize;
|
||||
use futures::{stream, StreamExt};
|
||||
use indicatif::ProgressBar;
|
||||
use miette::{miette, IntoDiagnostic, WrapErr};
|
||||
use percent_encoding::percent_decode;
|
||||
use reqwest::Client;
|
||||
use std::{
|
||||
|
@ -38,14 +38,16 @@ pub async fn run(
|
|||
(config, manifest): (Config, Manifest),
|
||||
target: PathBuf,
|
||||
all: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> miette::Result<()> {
|
||||
let http = Arc::new(Client::new());
|
||||
let Manifest {
|
||||
mut versions,
|
||||
repositories,
|
||||
..
|
||||
} = manifest;
|
||||
let version = versions.pop().context("Manifest has no Versions!")?;
|
||||
let version = versions
|
||||
.pop()
|
||||
.ok_or_else(|| miette!("Manifest has no Versions!"))?;
|
||||
|
||||
let repos = util::repo_map(repositories);
|
||||
|
||||
|
@ -68,7 +70,7 @@ pub async fn run(
|
|||
let rel_id = rel.id;
|
||||
let file = rel
|
||||
.file
|
||||
.with_context(|| format!("Relation `{}` has no file!", rel_id))?;
|
||||
.ok_or_else(|| miette!("Relation `{}` has no file!", rel_id))?;
|
||||
|
||||
if !matches!(file.installer(), Installer::Dir(d) if all || d == Path::new("mods")) {
|
||||
pb.println(
|
||||
|
@ -111,8 +113,11 @@ pub async fn run(
|
|||
|
||||
match link {
|
||||
Link::File(p) => {
|
||||
to.push(p.file_name().context("Local file has no file name!")?);
|
||||
tokio::fs::copy(&p, to).await?;
|
||||
to.push(
|
||||
p.file_name()
|
||||
.ok_or_else(|| miette!("Local file has no file name!"))?,
|
||||
);
|
||||
tokio::fs::copy(&p, to).await.into_diagnostic()?;
|
||||
println!(
|
||||
"{} {}",
|
||||
"Copied local file".green(),
|
||||
|
@ -122,9 +127,9 @@ pub async fn run(
|
|||
Link::Http(url) => {
|
||||
let file = url
|
||||
.path_segments()
|
||||
.context("File uses base URL without path!")?
|
||||
.ok_or_else(|| miette!("File uses base URL without path!"))?
|
||||
.last()
|
||||
.context("File uses empty URL!")?;
|
||||
.ok_or_else(|| miette!("File uses empty URL!"))?;
|
||||
let file = percent_decode(file.as_bytes()).decode_utf8_lossy();
|
||||
to.push(Borrow::<str>::borrow(&file));
|
||||
if !to.exists() {
|
||||
|
@ -188,7 +193,7 @@ async fn process_file(
|
|||
repos: &HashMap<String, Repository>,
|
||||
pb: &ProgressBar,
|
||||
http: Arc<Client>,
|
||||
) -> anyhow::Result<(Installer, Link)> {
|
||||
) -> miette::Result<(Installer, Link)> {
|
||||
Ok(match file {
|
||||
File::Link {
|
||||
link, installer, ..
|
||||
|
@ -198,8 +203,8 @@ async fn process_file(
|
|||
artifact,
|
||||
repository,
|
||||
} => {
|
||||
let repo = repos.get(&repository).with_context(|| {
|
||||
format!("File references non-existant repository `{}`", repository)
|
||||
let repo = repos.get(&repository).ok_or_else(|| {
|
||||
miette!("File references non-existant repository `{}`", repository)
|
||||
})?;
|
||||
|
||||
let url = match repo.repo_type {
|
||||
|
@ -220,8 +225,18 @@ async fn process_file(
|
|||
p_id, f_id
|
||||
);
|
||||
|
||||
let url = Url::parse(http.get(url).send().await?.text().await?.trim())
|
||||
.context("failed to parse curseforge URL")?;
|
||||
let url = Url::parse(
|
||||
http.get(url)
|
||||
.send()
|
||||
.await
|
||||
.into_diagnostic()?
|
||||
.text()
|
||||
.await
|
||||
.into_diagnostic()?
|
||||
.trim(),
|
||||
)
|
||||
.into_diagnostic()
|
||||
.wrap_err("failed to parse curseforge URL")?;
|
||||
|
||||
pb.println(format!(
|
||||
"{} {}",
|
||||
|
|
|
@ -15,10 +15,10 @@ use addonscript::{
|
|||
},
|
||||
util::default_file_opts,
|
||||
};
|
||||
use anyhow::{bail, Context};
|
||||
use crossterm::style::Stylize;
|
||||
use heck::KebabCase;
|
||||
use log::info;
|
||||
use miette::{bail, IntoDiagnostic, WrapErr};
|
||||
use twitch::manifest::Manifest as TwManifest;
|
||||
use url::Url;
|
||||
|
||||
|
@ -26,7 +26,7 @@ use std::path::PathBuf;
|
|||
|
||||
use crate::config::Config;
|
||||
|
||||
pub async fn run(config: Config, infile: PathBuf) -> anyhow::Result<()> {
|
||||
pub async fn run(config: Config, infile: PathBuf) -> miette::Result<()> {
|
||||
if config.locations.src.join("modpack.json").exists() ||
|
||||
config.locations.src.join("modpack.json5").exists()
|
||||
{
|
||||
|
@ -36,9 +36,11 @@ pub async fn run(config: Config, infile: PathBuf) -> anyhow::Result<()> {
|
|||
let mut data = serde_json::from_slice::<TwManifest>(
|
||||
&tokio::fs::read(infile)
|
||||
.await
|
||||
.context("Failed to read twitch manifest")?,
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to read twitch manifest")?,
|
||||
)
|
||||
.context("Failed to parse twitch manifest")?;
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to parse twitch manifest")?;
|
||||
|
||||
info!("converting twitch mods to AS relations");
|
||||
let mut relations = data
|
||||
|
@ -116,9 +118,13 @@ pub async fn run(config: Config, infile: PathBuf) -> anyhow::Result<()> {
|
|||
},
|
||||
};
|
||||
|
||||
let json = serde_json::to_vec_pretty(&manif)?;
|
||||
tokio::fs::create_dir_all(&config.locations.src).await?;
|
||||
tokio::fs::write(config.locations.src.join("modpack.json5"), json).await?;
|
||||
let json = serde_json::to_vec_pretty(&manif).into_diagnostic()?;
|
||||
tokio::fs::create_dir_all(&config.locations.src)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
tokio::fs::write(config.locations.src.join("modpack.json5"), json)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
println!("{}", "Imported manifest!".green());
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ use addonscript::{
|
|||
},
|
||||
util::default_file_opts,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use crossterm::{
|
||||
execute,
|
||||
style::{
|
||||
|
@ -32,6 +31,7 @@ use crossterm::{
|
|||
ExecutableCommand,
|
||||
};
|
||||
use heck::KebabCase;
|
||||
use miette::{IntoDiagnostic, WrapErr};
|
||||
use reqwest::Client;
|
||||
use std::path::Path;
|
||||
use url::Url;
|
||||
|
@ -48,7 +48,7 @@ pub async fn run(
|
|||
modpack_name: String,
|
||||
author_name: String,
|
||||
mcversion: String,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> miette::Result<()> {
|
||||
let mut stdout = std::io::stdout();
|
||||
|
||||
execute!(
|
||||
|
@ -66,44 +66,57 @@ pub async fn run(
|
|||
Print(&mcversion),
|
||||
ResetColor,
|
||||
Print('\n'),
|
||||
)?;
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
|
||||
let config_path = Path::new("modpacktoolsconfig.toml");
|
||||
if !config_path.exists() {
|
||||
tokio::fs::write(config_path, DEFAULT_CONFIG).await?;
|
||||
tokio::fs::write(config_path, DEFAULT_CONFIG)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
stdout.execute(PrintStyledContent(
|
||||
"Created config!\n"
|
||||
.with(Color::Green)
|
||||
.attribute(Attribute::Bold),
|
||||
))?;
|
||||
stdout
|
||||
.execute(PrintStyledContent(
|
||||
"Created config!\n"
|
||||
.with(Color::Green)
|
||||
.attribute(Attribute::Bold),
|
||||
))
|
||||
.into_diagnostic()?;
|
||||
} else {
|
||||
stdout.execute(PrintStyledContent(
|
||||
"Config already exists, skipping...\n"
|
||||
.with(Color::Red)
|
||||
.attribute(Attribute::Italic),
|
||||
))?;
|
||||
stdout
|
||||
.execute(PrintStyledContent(
|
||||
"Config already exists, skipping...\n"
|
||||
.with(Color::Red)
|
||||
.attribute(Attribute::Italic),
|
||||
))
|
||||
.into_diagnostic()?;
|
||||
}
|
||||
|
||||
let config = tokio::fs::read(config_path).await?;
|
||||
let config = tokio::fs::read(config_path).await.into_diagnostic()?;
|
||||
let Config {
|
||||
locations: Locations { src, .. },
|
||||
..
|
||||
} = toml::from_slice::<Config>(&config).context("failed to deserialize config")?;
|
||||
} = toml::from_slice::<Config>(&config)
|
||||
.into_diagnostic()
|
||||
.wrap_err("failed to deserialize config")?;
|
||||
|
||||
let path = Path::new(&src);
|
||||
|
||||
if path.join("modpack.json").exists() || path.join("modpack.json5").exists() {
|
||||
stdout.execute(PrintStyledContent(
|
||||
"Manifest already exists, skipping...\n"
|
||||
.with(Color::Red)
|
||||
.attribute(Attribute::Italic),
|
||||
))?;
|
||||
stdout
|
||||
.execute(PrintStyledContent(
|
||||
"Manifest already exists, skipping...\n"
|
||||
.with(Color::Red)
|
||||
.attribute(Attribute::Italic),
|
||||
))
|
||||
.into_diagnostic()?;
|
||||
} else {
|
||||
let mut relations = vec![];
|
||||
stdout.execute(PrintStyledContent(
|
||||
"Trying to find newest forge version...\n".with(Color::Magenta),
|
||||
))?;
|
||||
stdout
|
||||
.execute(PrintStyledContent(
|
||||
"Trying to find newest forge version...\n".with(Color::Magenta),
|
||||
))
|
||||
.into_diagnostic()?;
|
||||
|
||||
if let Some(ver) = forge::newest_forge_version(&Client::new(), &mcversion).await? {
|
||||
execute!(
|
||||
|
@ -115,7 +128,8 @@ pub async fn run(
|
|||
Print(&ver),
|
||||
ResetColor,
|
||||
Print('\n'),
|
||||
)?;
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
|
||||
relations.push(Relation {
|
||||
id: "forge".into(),
|
||||
|
@ -141,11 +155,14 @@ pub async fn run(
|
|||
SetForegroundColor(Color::Red),
|
||||
Print(" skipping forge...\n"),
|
||||
ResetColor,
|
||||
)?;
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
}
|
||||
|
||||
// also create overrides
|
||||
tokio::fs::create_dir_all(path.join("overrides")).await?;
|
||||
tokio::fs::create_dir_all(path.join("overrides"))
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
let data = serde_json::to_vec_pretty(&Manifest {
|
||||
id: modpack_name.to_kebab_case(),
|
||||
|
@ -176,31 +193,42 @@ pub async fn run(
|
|||
icon_url: None,
|
||||
website_url: None,
|
||||
},
|
||||
})?;
|
||||
})
|
||||
.into_diagnostic()?;
|
||||
|
||||
tokio::fs::write(path.join("modpack.json5"), data).await?;
|
||||
tokio::fs::write(path.join("modpack.json5"), data)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
stdout.execute(PrintStyledContent(
|
||||
"Created manifest!\n"
|
||||
.with(Color::Green)
|
||||
.attribute(Attribute::Bold),
|
||||
))?;
|
||||
stdout
|
||||
.execute(PrintStyledContent(
|
||||
"Created manifest!\n"
|
||||
.with(Color::Green)
|
||||
.attribute(Attribute::Bold),
|
||||
))
|
||||
.into_diagnostic()?;
|
||||
}
|
||||
|
||||
if Path::new(".gitignore").exists() {
|
||||
stdout.execute(PrintStyledContent(
|
||||
".gitignore exists, skipping...\n"
|
||||
.with(Color::Red)
|
||||
.attribute(Attribute::Italic),
|
||||
))?;
|
||||
stdout
|
||||
.execute(PrintStyledContent(
|
||||
".gitignore exists, skipping...\n"
|
||||
.with(Color::Red)
|
||||
.attribute(Attribute::Italic),
|
||||
))
|
||||
.into_diagnostic()?;
|
||||
} else {
|
||||
tokio::fs::write(".gitignore", DEFAULT_GITIGNORE).await?;
|
||||
tokio::fs::write(".gitignore", DEFAULT_GITIGNORE)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
stdout.execute(PrintStyledContent(
|
||||
"Created .gitignore!\n"
|
||||
.with(Color::Green)
|
||||
.attribute(Attribute::Bold),
|
||||
))?;
|
||||
stdout
|
||||
.execute(PrintStyledContent(
|
||||
"Created .gitignore!\n"
|
||||
.with(Color::Green)
|
||||
.attribute(Attribute::Bold),
|
||||
))
|
||||
.into_diagnostic()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -2,6 +2,7 @@ use async_trait::async_trait;
|
|||
use crossterm::style::Stylize;
|
||||
use futures::{stream, StreamExt};
|
||||
use log::info;
|
||||
use miette::Diagnostic;
|
||||
use reqwest::{Client, StatusCode};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use thiserror::Error;
|
||||
|
@ -119,7 +120,7 @@ impl DownloadInfo {
|
|||
}
|
||||
|
||||
/// An error that can occur while a file is being downloaded
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
pub enum DownloadError {
|
||||
#[error("HTTP Error: {0}")]
|
||||
HttpError(#[from] reqwest::Error),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use miette::IntoDiagnostic;
|
||||
use reqwest::Client;
|
||||
use serde::Deserialize;
|
||||
|
||||
|
@ -13,15 +14,19 @@ struct ForgeVersionResponse {
|
|||
pub async fn newest_forge_version(
|
||||
http: &Client,
|
||||
mcversion: &str,
|
||||
) -> anyhow::Result<Option<String>> {
|
||||
) -> miette::Result<Option<String>> {
|
||||
let resp = http
|
||||
.get("https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json")
|
||||
.send()
|
||||
.await?
|
||||
.await
|
||||
.into_diagnostic()?
|
||||
.bytes()
|
||||
.await?;
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
|
||||
let mut resp = json5::from_str::<ForgeVersionResponse>(std::str::from_utf8(&resp)?)?;
|
||||
let mut resp =
|
||||
json5::from_str::<ForgeVersionResponse>(std::str::from_utf8(&resp).into_diagnostic()?)
|
||||
.into_diagnostic()?;
|
||||
|
||||
Ok(resp.promos.remove(&format!("{}-latest", mcversion)))
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use anyhow::Context;
|
||||
use log::{info, LevelFilter};
|
||||
use miette::{IntoDiagnostic, WrapErr};
|
||||
use simplelog::{ColorChoice, TermLogger, TerminalMode};
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
@ -74,7 +74,7 @@ enum Command {
|
|||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
async fn main() -> miette::Result<()> {
|
||||
let Opt { cmd, verbose } = Opt::from_args();
|
||||
|
||||
let log_level = match verbose {
|
||||
|
@ -93,7 +93,8 @@ async fn main() -> anyhow::Result<()> {
|
|||
TerminalMode::Stderr,
|
||||
ColorChoice::Auto,
|
||||
)
|
||||
.context("Failed to init logger")?;
|
||||
.into_diagnostic()
|
||||
.wrap_err("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.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use addonscript::manifest::{link::Link, Manifest, Repository};
|
||||
use anyhow::Context;
|
||||
use crossterm::style::{Attribute, Attributes, Color, ContentStyle, Stylize};
|
||||
use indicatif::ProgressStyle;
|
||||
use log::info;
|
||||
use miette::{Diagnostic, IntoDiagnostic, WrapErr};
|
||||
use percent_encoding::percent_decode;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
|
@ -17,18 +17,21 @@ use walkdir::WalkDir;
|
|||
use crate::config::Config;
|
||||
|
||||
/// reads and parses the config from the current working directory
|
||||
pub async fn parse_config() -> anyhow::Result<Config> {
|
||||
pub async fn parse_config() -> miette::Result<Config> {
|
||||
info!("reading config");
|
||||
let conf = tokio::fs::read("modpacktoolsconfig.toml")
|
||||
.await
|
||||
.context("Failed to read config")?;
|
||||
Ok(toml::from_slice(&conf).context("Failed to parse config")?)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to read config")?;
|
||||
Ok(toml::from_slice(&conf)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to parse config")?)
|
||||
}
|
||||
|
||||
/// parses the config from the current working directory, reads the location of
|
||||
/// the manifest file from it, parses the manifest and returns both the conig
|
||||
/// and the manifest.
|
||||
pub async fn parse_config_and_manifest() -> anyhow::Result<(Config, Manifest)> {
|
||||
pub async fn parse_config_and_manifest() -> miette::Result<(Config, Manifest)> {
|
||||
let config = parse_config().await?;
|
||||
let src = Path::new(&config.locations.src);
|
||||
|
||||
|
@ -42,14 +45,19 @@ pub async fn parse_config_and_manifest() -> anyhow::Result<(Config, Manifest)> {
|
|||
|
||||
let data = tokio::fs::read(path)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
.context("Failed to read manifest")?;
|
||||
let data = std::str::from_utf8(&data).context("Manifest is invalid UTF-8")?;
|
||||
let manifest = json5::from_str::<Manifest>(data).context("Failed to parse manifest")?;
|
||||
let data = std::str::from_utf8(&data)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Manifest is invalid UTF-8")?;
|
||||
let manifest = json5::from_str::<Manifest>(data)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to parse manifest")?;
|
||||
|
||||
Ok((config, manifest))
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
#[derive(Debug, Error, Diagnostic, PartialEq, Eq)]
|
||||
pub enum MavenParseError {
|
||||
#[error("Maven Artifact specifier has invalid format!")]
|
||||
InvalidFormat,
|
||||
|
@ -112,11 +120,12 @@ pub fn progress_style() -> ProgressStyle {
|
|||
}
|
||||
|
||||
/// 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) -> miette::Result<()> {
|
||||
info!("creating temporary directory");
|
||||
tokio::fs::create_dir_all(&config.locations.temp_dir)
|
||||
.await
|
||||
.context("Failed to create temporary directory")?;
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to create temporary directory")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -141,7 +150,7 @@ pub fn parse_curseforge_artifact(artifact: &str) -> Result<(&str, &str), MavenPa
|
|||
Ok((p_id, f_id))
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq, Eq)]
|
||||
#[derive(Debug, Error, Diagnostic, PartialEq, Eq)]
|
||||
pub enum UrlFileNameError {
|
||||
#[error("URL is base URL without file name!")]
|
||||
BaseUrl,
|
||||
|
@ -165,7 +174,7 @@ pub fn url_file_name(url: &Url) -> Result<String, UrlFileNameError> {
|
|||
Ok(String::from_utf8(bytes)?)
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
pub enum LinkFileNameError {
|
||||
#[error("Empty path has no file name!")]
|
||||
EmptyPath,
|
||||
|
|
Loading…
Reference in a new issue