Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d52ee30
refactor(app-lib): use anyhow errors in util
Sychic Oct 16, 2026
82d7a37
refactor(app-lib): match error wording with labrinth
Sychic Oct 16, 2026
c13e53e
refactor(app-lib): remove redundant running instance check
Sychic Oct 16, 2026
29070c6
refactor(app-lib): add error context to launcher
Sychic Oct 16, 2026
9be7476
refactor(app-lib): install error pass
Sychic Oct 16, 2026
f9ee46f
refactor(app-lib): content store error pass
Sychic Oct 16, 2026
27089be
refactor(app-lib): instances error pass
Sychic Oct 17, 2026
f176c3c
refactor(app-lib): shared instance error pass
Sychic Oct 17, 2026
9f62b18
feat(app-lib): better shared instances error context
Sychic Oct 17, 2026
eceae20
refactor(app-lib): synced options error pass
Sychic Oct 17, 2026
797cfa1
refactor(app-lib): synced servers error pass
Sychic Oct 17, 2026
0a2d97b
refactor(app-lib): instance api error pass
Sychic Oct 17, 2026
8794e34
refactor(app-lib): api error pass
Sychic Oct 17, 2026
4e1e039
refactor(app-lib): pack error pass
Sychic Oct 17, 2026
a0020e8
refactor(app): error pass
Sychic Oct 17, 2026
ef41818
refactor(app-lib): fetch error pass
Sychic Oct 17, 2026
4162b6b
refactor(app-lib): state error pass
Sychic Oct 17, 2026
dad0b92
refactor(app-lib): inline input error
Sychic Oct 17, 2026
f947582
refactor(app-lib): remove other error
Sychic Oct 17, 2026
c4133b0
refactor(app-lib): remove ErrorKind::as_error
Sychic Oct 17, 2026
bb28940
refactor(app-lib): bail on shared instance auth errors
Sychic Oct 17, 2026
ea85b21
refactor(app-lib): keep source errors in content store context
Sychic Oct 17, 2026
4b74397
refactor(app-lib): propagate game locale task and archive errors
Sychic Oct 17, 2026
4389f2b
refactor(app): add context to windows shortcut join error
Sychic Oct 17, 2026
2d001e3
fix(app-lib): include error chain in auth code reply socket error
Sychic Oct 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions apps/app/src/api/auth.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::api::Result;
use anyhow::Context;
use chrono::{Duration, Utc};
use tauri::plugin::TauriPlugin;
use tauri::{Manager, Runtime, UserAttentionType};
Expand Down Expand Up @@ -41,14 +42,11 @@ pub async fn login<R: Runtime>(
let window = tauri::WebviewWindowBuilder::new(
&app,
"signin",
tauri::WebviewUrl::External(flow.auth_request_uri.parse().map_err(
|_| {
theseus::ErrorKind::OtherError(
"Error parsing auth redirect URL".to_string(),
)
.as_error()
},
)?),
tauri::WebviewUrl::External(
flow.auth_request_uri
.parse()
.context("error parsing auth redirect URL")?,
),
)
.title("Sign into Modrinth")
.always_on_top(true)
Expand Down
50 changes: 17 additions & 33 deletions apps/app/src/api/files.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use crate::api::Result;
use anyhow::{Context, anyhow};
use async_zip::base::read::seek::ZipFileReader;
use serde::Serialize;
use std::io::Cursor;
use tauri::Runtime;
use tauri_plugin_dialog::DialogExt;
use theseus::ErrorKind;

pub fn init<R: Runtime>() -> tauri::plugin::TauriPlugin<R> {
tauri::plugin::Builder::new("files")
Expand Down Expand Up @@ -31,10 +33,7 @@ pub struct ExtractDryRunResult {
pub async fn file_read_dragged_file(path: String) -> Result<Vec<u8>> {
let metadata = tokio::fs::metadata(&path).await?;
if !metadata.is_file() {
return Err(theseus::Error::from(theseus::ErrorKind::OtherError(
"Dropped path is not a file".to_string(),
))
.into());
return Err(anyhow!("dropped path is not a file").into());
}

Ok(tokio::fs::read(path).await?)
Expand All @@ -57,25 +56,19 @@ pub async fn file_extract_zip(
theseus::instance::read_instance_file(instance_id, file_path).await?;
let zip_reader = ZipFileReader::with_tokio(Cursor::new(file_bytes))
.await
.map_err(|error| {
theseus::Error::from(theseus::ErrorKind::OtherError(format!(
"Failed to read zip file: {error}"
)))
})?;
.context("failed to read zip file")?;
let mut entries = Vec::new();
for (index, entry) in zip_reader.file().entries().iter().enumerate() {
let name = entry.filename().as_str().map_err(|error| {
theseus::Error::from(theseus::ErrorKind::InputError(
error.to_string(),
))
})?;
let name = entry.filename().as_str().context(ErrorKind::InputError(
"invalid archive entry name".to_string(),
))?;
if name.ends_with('/') {
continue;
}
if name.starts_with('/') || name.contains('\\') {
return Err(theseus::Error::from(theseus::ErrorKind::InputError(
"Invalid archive path".to_string(),
))
return Err(ErrorKind::InputError(
"invalid archive path".to_string(),
)
.into());
}
let target = if parent.is_empty() {
Expand Down Expand Up @@ -107,20 +100,14 @@ pub async fn file_extract_zip(
continue;
}
let mut bytes = Vec::new();
let mut reader =
zip_reader.reader_with_entry(index).await.map_err(|error| {
theseus::Error::from(theseus::ErrorKind::OtherError(
error.to_string(),
))
})?;
let mut reader = zip_reader
.reader_with_entry(index)
.await
.context(format!("failed to read archive entry {path}"))?;
reader
.read_to_end_checked(&mut bytes)
.await
.map_err(|error| {
theseus::Error::from(theseus::ErrorKind::OtherError(
error.to_string(),
))
})?;
.context(format!("failed to read archive entry {path}"))?;
theseus::instance::write_instance_file(
instance_id,
&path,
Expand Down Expand Up @@ -217,11 +204,8 @@ pub async fn file_save_as<R: Runtime>(
});

if let Some(dest) = rx.await.unwrap_or(None) {
let dest_path = std::path::PathBuf::try_from(dest).map_err(|e| {
theseus::Error::from(theseus::ErrorKind::OtherError(format!(
"Invalid save path: {e}"
)))
})?;
let dest_path =
std::path::PathBuf::try_from(dest).context("invalid save path")?;
theseus::instance::save_instance_file_as(
instance_id,
file_path,
Expand Down
33 changes: 12 additions & 21 deletions apps/app/src/api/instance.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::api::Result;
use anyhow::Context;
use dashmap::DashMap;
use path_util::SafeRelativeUtf8UnixPathBuf;
use serde::{Deserialize, Serialize};
Expand All @@ -7,7 +8,6 @@ use std::path::{Path, PathBuf};
use tauri::{AppHandle, Manager, Runtime};
use tauri_plugin_fs::FsExt;
use tauri_plugin_opener::OpenerExt;
use theseus::DownloadReason;
use theseus::data::{
AppliedContentSetPatch, ContentItem, Dependency,
EditInstance as CoreEditInstance, InstanceInstallCandidate,
Expand All @@ -21,6 +21,7 @@ use theseus::instance::InstallProjectWithDependenciesRequest;
use theseus::instance::QuickPlayType;
use theseus::prelude::*;
use theseus::server_address::ServerAddress;
use theseus::{DownloadReason, ErrorKind};

pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
tauri::plugin::Builder::new("instance")
Expand Down Expand Up @@ -442,32 +443,22 @@ impl InstanceLink {
instance_ids,
active_instance_id,
} => Ok(CoreInstanceLink::ModrinthHosting {
server_id: server_id.parse().map_err(|err| {
theseus::Error::from(theseus::ErrorKind::InputError(
format!("Invalid server id: {err}"),
))
})?,
server_id: server_id.parse().context(ErrorKind::InputError(
"invalid server id".to_string(),
))?,
instance_ids: instance_ids
.into_iter()
.map(|id| {
id.parse().map_err(|err| {
theseus::Error::from(
theseus::ErrorKind::InputError(format!(
"Invalid hosted instance id: {err}"
)),
)
})
id.parse().context(ErrorKind::InputError(
"invalid hosted instance id".to_string(),
))
})
.collect::<std::result::Result<Vec<_>, _>>()?,
.collect::<anyhow::Result<Vec<_>>>()?,
active_instance_id: active_instance_id
.map(|id| {
id.parse().map_err(|err| {
theseus::Error::from(
theseus::ErrorKind::InputError(format!(
"Invalid active instance id: {err}"
)),
)
})
id.parse().context(ErrorKind::InputError(
"invalid active instance id".to_string(),
))
})
.transpose()?,
}),
Expand Down
19 changes: 6 additions & 13 deletions apps/app/src/api/mr_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ use crate::api::Result;
use crate::api::oauth_utils;
use crate::api::oauth_utils::auth_code_reply::AuthReplySession;
use crate::api::oauth_utils::auth_seal::AuthSeal;
use anyhow::Context;
use tauri::Manager;
use tauri::Runtime;
use tauri::plugin::TauriPlugin;
use tauri_plugin_opener::OpenerExt;
use theseus::ErrorKind;
use theseus::prelude::*;
use url::Url;

Expand Down Expand Up @@ -44,15 +46,10 @@ pub async fn modrinth_login<R: Runtime>(

app.opener()
.open_url(auth_request_uri, None::<&str>)
.map_err(|e| {
theseus::ErrorKind::OtherError(format!(
"Failed to open auth request URI: {e}"
))
.as_error()
})?;
.context("failed to open auth request URI")?;

let Some(auth_code) = reply.wait().await? else {
return Err(theseus::ErrorKind::LoginCanceled.as_error().into());
return Err(ErrorKind::LoginCanceled.into());
};
let session_token = seal.open(&auth_code)?;
let credentials = mr_auth::authenticate_finish_flow(&session_token).await?;
Expand Down Expand Up @@ -101,12 +98,8 @@ fn auth_request_url(
nonce: &str,
add_account: bool,
) -> Result<String> {
let mut url = Url::parse(begin_url).map_err(|error| -> theseus::Error {
theseus::ErrorKind::OtherError(format!(
"Failed to build auth request URI: {error}"
))
.into()
})?;
let mut url =
Url::parse(begin_url).context("failed to build auth request URI")?;
{
let mut query = url.query_pairs_mut();
query.append_pair("launcher", "true");
Expand Down
33 changes: 14 additions & 19 deletions apps/app/src/api/oauth_utils/auth_code_reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@

use std::{net::SocketAddr, sync::Mutex, time::Duration};

use anyhow::{Context, anyhow, bail};
use hyper::body::Incoming;
use hyper_util::rt::{TokioIo, TokioTimer};
use theseus::ErrorKind;
use theseus::prelude::tcp_listen_any_loopback;
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
Expand Down Expand Up @@ -78,11 +78,7 @@ impl AuthReplySession {
pub async fn wait(&mut self) -> Result<Option<String>, theseus::Error> {
let result = tokio::select! {
localhost_result = &mut self.listener => {
localhost_result.map_err(|error| {
ErrorKind::OtherError(format!(
"Auth code listener stopped: {error}"
))
})?
localhost_result.context("auth code listener stopped")?
}
deeplink_result = &mut self.deeplink_rx => {
self.listener.abort();
Expand Down Expand Up @@ -182,7 +178,7 @@ fn clear_pending_auth(nonce: &str) {
}

fn auth_listener_stopped() -> theseus::Error {
ErrorKind::OtherError("Auth code listener stopped".into()).into()
anyhow!("auth code listener stopped")
}

pub fn normalize_deep_link_command(command: &str) -> String {
Expand All @@ -208,25 +204,24 @@ async fn listen(
let listener = match tcp_listen_any_loopback().await {
Ok(listener) => {
listen_socket_tx
.send(listener.local_addr().map_err(|e| {
ErrorKind::OtherError(format!(
"Failed to get auth code reply socket address: {e}"
))
.into()
}))
.send(
listener.local_addr().context(
"failed to get auth code reply socket address",
),
)
.ok();

listener
}
Err(e) => {
let error_msg =
format!("Failed to bind auth code reply socket: {e}");

Err(error) => {
let cause = error.to_string();
listen_socket_tx
.send(Err(ErrorKind::OtherError(error_msg.clone()).into()))
.send(
Err(error).context("failed to bind auth code reply socket"),
)
.ok();

return Err(ErrorKind::OtherError(error_msg).into());
bail!("failed to bind auth code reply socket: {cause}");
}
};

Expand Down
18 changes: 7 additions & 11 deletions apps/app/src/api/oauth_utils/auth_seal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

use aes_gcm::aead::Aead;
use aes_gcm::{Aes256Gcm, KeyInit, Nonce};
use anyhow::{Context, anyhow};
use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use hkdf::Hkdf;
Expand Down Expand Up @@ -52,15 +53,11 @@ impl AuthSeal {
}

pub fn public_key(&self) -> Result<String, theseus::Error> {
let der =
self.secret
.public_key()
.to_public_key_der()
.map_err(|error| {
theseus::ErrorKind::OtherError(format!(
"Failed to encode auth public key: {error}"
))
})?;
let der = self
.secret
.public_key()
.to_public_key_der()
.context("failed to encode auth public key")?;

Ok(URL_SAFE_NO_PAD.encode(der.as_bytes()))
}
Expand Down Expand Up @@ -144,6 +141,5 @@ fn is_session_token(token: &str) -> bool {
}

fn invalid_sealed_session() -> theseus::Error {
theseus::ErrorKind::OtherError("Invalid sealed app sign-in session".into())
.into()
anyhow!("invalid sealed app sign-in session")
}
7 changes: 2 additions & 5 deletions apps/app/src/api/shortcuts/windows.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::api::Result;
use anyhow::Context;
use std::{
os::windows::ffi::OsStrExt,
path::{Path, PathBuf},
Expand Down Expand Up @@ -40,11 +41,7 @@ pub(super) async fn create_shortcut(
)
})
.await
.map_err(|error| {
std::io::Error::other(format!(
"failed to join shortcut creation task: {error}"
))
})??;
.context("failed to join shortcut creation task")??;

Ok(())
}
Expand Down
9 changes: 4 additions & 5 deletions apps/app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
)]
#![recursion_limit = "256"]

use anyhow::Context;
use native_dialog::{DialogBuilder, MessageLevel};
use std::env;
use std::sync::atomic::Ordering;
Expand Down Expand Up @@ -88,11 +89,9 @@ pub use updater_impl_noop::*;
// Toggles decorations
#[tauri::command]
async fn toggle_decorations(b: bool, window: tauri::Window) -> api::Result<()> {
window.set_decorations(b).map_err(|e| {
theseus::Error::from(theseus::ErrorKind::OtherError(format!(
"Failed to toggle decorations: {e}"
)))
})?;
window
.set_decorations(b)
.context("failed to toggle decorations")?;
Ok(())
}

Expand Down
Loading
Loading