switch from anyhow to miette
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
LordMZTE 2021-10-04 21:30:13 +02:00
parent 5d3b2eb8b5
commit 4b2c701574
11 changed files with 228 additions and 133 deletions

View file

@ -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"] }

View file

@ -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!")?,
));
},
}

View file

@ -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.

View file

@ -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))
},

View file

@ -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!(
"{} {}",

View file

@ -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());

View file

@ -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(())

View file

@ -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),

View file

@ -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)))
}

View file

@ -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.

View file

@ -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,