1use thiserror::Error;
2
3#[derive(Debug, Error)]
5pub enum SystemError {
6 #[error("Unsupported operating system")]
7 UnsupportedOS,
8
9 #[error("Unsupported architecture")]
10 UnsupportedArchitecture,
11
12 #[error("IO error: {0}")]
13 Io(#[from] std::io::Error),
14}
15
16#[derive(Debug, Error)]
18pub enum ExtractError {
19 #[error("Invalid ZIP entry at index {index}")]
20 ZipEntryNotFound { index: usize },
21
22 #[error("Path has no parent directory")]
23 InvalidPath,
24
25 #[error("Absolute paths not allowed: {path}")]
26 AbsolutePath { path: String },
27
28 #[error("Path traversal detected: {path} escapes base directory")]
29 PathTraversal { path: String },
30
31 #[error("File too large: {size} bytes (max: {max})")]
32 FileTooLarge { size: u64, max: u64 },
33
34 #[error("ZIP error: {0}")]
35 Zip(#[from] async_zip::error::ZipError),
36
37 #[error("TAR error: {0}")]
38 Tar(#[from] std::io::Error),
39}
40
41#[derive(Debug, Error)]
43pub enum DownloadError {
44 #[error("HTTP request failed: {0}")]
45 Http(#[from] reqwest::Error),
46
47 #[error("IO error: {0}")]
48 Io(#[from] std::io::Error),
49}
50
51pub type SystemResult<T> = Result<T, SystemError>;
53
54pub type ExtractResult<T> = Result<T, ExtractError>;
56
57#[derive(Debug, Error)]
59pub enum AppStateError {
60 #[error("AppState::init() has already been called")]
61 AlreadyInitialized,
62
63 #[error("AppState::init() has not been called yet — call it once at startup")]
64 NotInitialized,
65
66 #[error("Platform doesn't expose a standard {0} directory")]
67 MissingPlatformDir(&'static str),
68}
69
70pub type DownloadResult<T> = Result<T, DownloadError>;
72
73pub type AppStateResult<T> = Result<T, AppStateError>;
75
76#[derive(Error, Debug)]
78pub enum QueryError {
79 #[error("Network error: {0}")]
80 Network(#[from] reqwest::Error),
81
82 #[error("JSON parsing error: {0}")]
83 JsonParsing(#[from] serde_json::Error),
84
85 #[error("IO error: {0}")]
86 Io(#[from] std::io::Error),
87
88 #[error("Version '{version}' not found in manifest")]
89 VersionNotFound { version: String },
90
91 #[error("Missing field '{field}' in manifest data")]
92 MissingField { field: String },
93
94 #[error("Assets fetch error: failed to fetch assets from URL '{url}'")]
95 AssetsFetch { url: String },
96
97 #[error("Conversion error: {message}")]
98 Conversion { message: String },
99
100 #[error("Archive corrupted ({context}): {reason}")]
101 ArchiveCorrupted { context: String, reason: String },
102
103 #[error("Unsupported {what}: expected {expected}, found {found}")]
104 UnsupportedFormat {
105 what: String,
106 expected: String,
107 found: String,
108 },
109
110 #[error("Unsupported loader: {0}")]
111 UnsupportedLoader(String),
112
113 #[error("Invalid metadata format")]
114 InvalidMetadata,
115
116 #[error("Mod not found on {provider}: {id}")]
117 ModNotFound { provider: &'static str, id: String },
118
119 #[error("Mod {id} on {provider} has no version compatible with Minecraft {mc} + loader {loader}")]
120 ModIncompatible {
121 provider: &'static str,
122 id: String,
123 mc: String,
124 loader: String,
125 },
126
127 #[error("Mod {id} on CurseForge disallows third-party distribution; download manually from the project page")]
128 ModDistributionForbidden { id: String },
129}
130
131pub type QueryResult<T> = Result<T, QueryError>;