Move to eyre errors

This commit is contained in:
IQuant 2024-08-23 00:08:51 +03:00
parent b45d4985cf
commit 2cddde4de7
5 changed files with 109 additions and 74 deletions

17
noita-proxy/Cargo.lock generated
View file

@ -1056,6 +1056,16 @@ version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b"
[[package]]
name = "eyre"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
]
[[package]] [[package]]
name = "fastrand" name = "fastrand"
version = "1.9.0" version = "1.9.0"
@ -1651,6 +1661,12 @@ version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.4.0" version = "2.4.0"
@ -1991,6 +2007,7 @@ dependencies = [
"egui-file-dialog", "egui-file-dialog",
"egui_extras", "egui_extras",
"egui_plot", "egui_plot",
"eyre",
"fluent-bundle", "fluent-bundle",
"fluent-templates", "fluent-templates",
"image", "image",

View file

@ -46,6 +46,7 @@ argh = "0.1.12"
shlex = "1.3.0" shlex = "1.3.0"
quick-xml = { version = "0.36.0", features = ["serialize"] } quick-xml = { version = "0.36.0", features = ["serialize"] }
dashmap = "6.0.1" dashmap = "6.0.1"
eyre = "0.6.12"
[build-dependencies] [build-dependencies]
winresource = "0.1.17" winresource = "0.1.17"

View file

@ -3,6 +3,7 @@ use std::{
error::Error, error::Error,
fs::{self, File}, fs::{self, File},
io::{self, BufReader}, io::{self, BufReader},
mem,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -248,41 +249,57 @@ impl Modmanager {
match promise.ready() { match promise.ready() {
Some(Ok(downloader)) => { Some(Ok(downloader)) => {
downloader.show_progress(ui); downloader.show_progress(ui);
match downloader.ready() {
Some(Ok(_)) => {
let path = downloader.path().to_path_buf();
let directory = settings.mod_path();
let promise: Promise<Result<(), ReleasesError>> =
Promise::spawn_thread("unpack", move || {
extract_and_remove_zip(path, directory)
});
self.state = State::UnpackMod(promise);
}
Some(Err(err)) => self.state = State::ReleasesError(err.clone()),
None => {}
}
} }
Some(Err(err)) => self.state = State::ReleasesError(err.clone()), Some(Err(_)) => {}
None => { None => {
ui.label(tr("modman_receiving_rel_info")); ui.label(tr("modman_receiving_rel_info"));
ui.spinner(); ui.spinner();
} }
} }
} if promise.ready().is_some() {
State::UnpackMod(promise) => match promise.ready() { let State::DownloadMod(promise) = mem::take(&mut self.state) else {
Some(Ok(_)) => { unreachable!();
ui.label(tr("modman_installed"));
if ui.button(tr("button_continue")).clicked() {
self.state = State::Done;
}; };
match promise.block_and_take() {
Ok(downloader) => {
let path = downloader.path().to_path_buf();
let directory = settings.mod_path();
let promise: Promise<Result<(), ReleasesError>> =
Promise::spawn_thread("unpack", move || {
extract_and_remove_zip(path, directory)
});
self.state = State::UnpackMod(promise);
}
Err(err) => self.state = State::ReleasesError(err),
}
} }
Some(Err(err)) => { }
self.state = State::ReleasesError(err.clone()); State::UnpackMod(promise) => {
match promise.ready() {
Some(Ok(_)) => {
ui.label(tr("modman_installed"));
if ui.button(tr("button_continue")).clicked() {
self.state = State::Done;
return;
};
}
Some(Err(_)) => {}
None => {
ui.label(tr("modman_unpacking"));
}
} }
None => { if promise.ready().is_some() {
ui.label(tr("modman_unpacking")); let State::UnpackMod(promise) = mem::take(&mut self.state) else {
unreachable!();
};
match promise.block_and_take() {
Ok(_) => {}
Err(err) => {
self.state = State::ReleasesError(err);
}
}
} }
}, }
State::Error(err) => { State::Error(err) => {
ui.label(format!("Encountered an error: {}", err)); ui.label(format!("Encountered an error: {}", err));
if ui.button(tr("button_retry")).clicked() { if ui.button(tr("button_retry")).clicked() {
@ -290,7 +307,7 @@ impl Modmanager {
} }
} }
State::ReleasesError(err) => { State::ReleasesError(err) => {
ui.label(format!("Encountered an error: {}", err)); ui.label(format!("Encountered an error: \n {:?}", err));
if ui.button(tr("button_retry")).clicked() { if ui.button(tr("button_retry")).clicked() {
self.state = State::JustStarted; self.state = State::JustStarted;
} }

View file

@ -1,48 +1,19 @@
use std::{ use std::{
fmt::Display, fmt::Display,
fs::File, fs::File,
io::{self, Read, Write}, io::{Read, Write},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{atomic::AtomicU64, Arc}, sync::{atomic::AtomicU64, Arc},
time::Duration, time::Duration,
}; };
use eframe::egui::{self, Ui}; use eframe::egui::{self, Ui};
use eyre::{eyre, Context};
use poll_promise::Promise; use poll_promise::Promise;
use reqwest::blocking::Client; use reqwest::blocking::Client;
use serde::Deserialize; use serde::Deserialize;
use thiserror::Error;
use zip::result::ZipError;
#[derive(Debug, Error, Clone)] pub type ReleasesError = eyre::Report;
pub enum ReleasesError {
#[error("Could not complete request: {0}")]
Request(Arc<reqwest::Error>),
#[error("Asset not found")]
AssetNotFound,
#[error("Io error: {0}")]
Io(Arc<io::Error>),
#[error("Zip error: {0}")]
Zip(Arc<ZipError>),
}
impl From<reqwest::Error> for ReleasesError {
fn from(value: reqwest::Error) -> Self {
Self::Request(value.into())
}
}
impl From<io::Error> for ReleasesError {
fn from(value: io::Error) -> Self {
Self::Io(value.into())
}
}
impl From<ZipError> for ReleasesError {
fn from(value: ZipError) -> Self {
Self::Zip(value.into())
}
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Release { pub struct Release {
@ -92,18 +63,25 @@ fn download_thread(
.header("Accept", "application/octet-stream") .header("Accept", "application/octet-stream")
.header("X-GitHub-Api-Version", "2022-11-28") .header("X-GitHub-Api-Version", "2022-11-28")
.header("User-agent", "noita proxy") .header("User-agent", "noita proxy")
.send()?; .send()
.wrap_err_with(|| format!("Failed to download from {}", url))?;
let mut buf = [0; 4096]; let mut buf = [0; 4096];
loop { loop {
let len = response.read(&mut buf)?; let len = response.read(&mut buf).wrap_err_with(|| {
format!(
"Failed to download from {}: couldn't read from response",
url
)
})?;
shared shared
.progress .progress
.fetch_add(len as u64, std::sync::atomic::Ordering::Relaxed); .fetch_add(len as u64, std::sync::atomic::Ordering::Relaxed);
if len == 0 { if len == 0 {
break; break;
} }
file.write_all(&buf[..len])?; file.write_all(&buf[..len])
.wrap_err_with(|| format!("Failed to download from {}: couldn't write to file", url))?;
} }
Ok(()) Ok(())
@ -153,7 +131,7 @@ impl AssetList {
self.0 self.0
.iter() .iter()
.find(|asset| asset.name == name) .find(|asset| asset.name == name)
.ok_or(ReleasesError::AssetNotFound) .ok_or_else(|| eyre!("Asset not found: {}", name))
} }
} }
@ -213,9 +191,15 @@ impl Release {
.header("Accept", "application/vnd.github+json") .header("Accept", "application/vnd.github+json")
.header("X-GitHub-Api-Version", "2022-11-28") .header("X-GitHub-Api-Version", "2022-11-28")
.header("User-agent", "noita proxy") .header("User-agent", "noita proxy")
.send()?; .send()
.wrap_err_with(|| format!("Failed to request asset list from {}", self.assets_url))?;
Ok(response.json()?) Ok(response.json().wrap_err_with(|| {
format!(
"Failed to request asset list from {}: couldn't parse json",
self.assets_url
)
})?)
} }
} }
@ -225,21 +209,24 @@ pub fn get_latest_release(client: &Client) -> Result<Release, ReleasesError> {
.header("Accept", "application/vnd.github+json") .header("Accept", "application/vnd.github+json")
.header("X-GitHub-Api-Version", "2022-11-28") .header("X-GitHub-Api-Version", "2022-11-28")
.header("User-agent", "noita proxy") .header("User-agent", "noita proxy")
.send()?; .send()
.wrap_err("Failed to request latest release")?;
Ok(response.json()?) Ok(response.json()?)
} }
pub fn get_release_by_tag(client: &Client, tag: Tag) -> Result<Release, ReleasesError> { pub fn get_release_by_tag(client: &Client, tag: Tag) -> Result<Release, ReleasesError> {
let url = format!(
"https://api.github.com/repos/IntQuant/noita_entangled_worlds/releases/tags/{}",
tag.0
);
let response = client let response = client
.get(format!( .get(&url)
"https://api.github.com/repos/IntQuant/noita_entangled_worlds/releases/tags/{}",
tag.0
))
.header("Accept", "application/vnd.github+json") .header("Accept", "application/vnd.github+json")
.header("X-GitHub-Api-Version", "2022-11-28") .header("X-GitHub-Api-Version", "2022-11-28")
.header("User-agent", "noita proxy") .header("User-agent", "noita proxy")
.send()?; .send()
.wrap_err_with(|| format!("Failed to get release by tag from {}", url))?;
Ok(response.json()?) Ok(response.json()?)
} }

View file

@ -1,11 +1,12 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
fs::{self, File}, fs::{self, File},
io, io, mem,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use eframe::egui::{Align, Layout, Ui}; use eframe::egui::{Align, Layout, Ui};
use eyre::Context;
use poll_promise::Promise; use poll_promise::Promise;
use reqwest::blocking::Client; use reqwest::blocking::Client;
use tracing::info; use tracing::info;
@ -20,10 +21,13 @@ struct VersionCheckResult {
ord: Ordering, ord: Ordering,
} }
#[derive(Default)]
enum State { enum State {
#[default]
Initial, Initial,
Download(Promise<Result<Downloader, ReleasesError>>), Download(Promise<Result<Downloader, ReleasesError>>),
ReleasesError(ReleasesError), ReleasesError(ReleasesError),
ReleasesError2(String),
Unpack(Promise<Result<(), ReleasesError>>), Unpack(Promise<Result<(), ReleasesError>>),
} }
@ -113,11 +117,17 @@ impl SelfUpdateManager {
}); });
self.state = State::Unpack(promise); self.state = State::Unpack(promise);
} }
Some(Err(err)) => self.state = State::ReleasesError(err.clone()), Some(Err(_)) => {
let State::Download(promise) = mem::take(&mut self.state) else {
unreachable!();
};
self.state =
State::ReleasesError(promise.block_and_take().err().unwrap())
}
None => {} None => {}
} }
} }
Some(Err(err)) => self.state = State::ReleasesError(err.clone()), Some(Err(err)) => self.state = State::ReleasesError2(format!("{:?}", err)),
None => { None => {
ui.label(tr("selfupdate_receiving_rel_info")); ui.label(tr("selfupdate_receiving_rel_info"));
ui.spinner(); ui.spinner();
@ -137,7 +147,10 @@ impl SelfUpdateManager {
} }
}, },
State::ReleasesError(err) => { State::ReleasesError(err) => {
ui.label(format!("Encountered an error: {}", err)); ui.label(format!("Encountered an error: {:?}", err));
}
State::ReleasesError2(err) => {
ui.label(format!("Encountered an error:\n{}", err));
} }
} }
} }