1use std::{
2 fmt, io,
3 path::{Path, PathBuf},
4};
5
6pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Debug)]
9#[non_exhaustive]
10pub enum Error {
11 FailedLocate(LocateError),
12 InvalidSteamDir(ValidationError),
13 Io {
14 inner: io::Error,
15 path: PathBuf,
16 },
17 Parse {
18 kind: ParseErrorKind,
19 error: ParseError,
20 path: PathBuf,
21 },
22 MissingExpectedApp {
23 app_id: u32,
24 },
25}
26
27impl fmt::Display for Error {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 Self::FailedLocate(error) => {
31 write!(f, "Failed locating the steam dir. Error: {error}")
32 }
33 Self::InvalidSteamDir(error) => {
34 write!(f, "Failed validating steam dir. Error: {error}")
35 }
36 Self::Io { inner: err, path } => {
37 write!(f, "Encountered an I/O error: {} at {}", err, path.display())
38 }
39 Self::Parse { kind, error, path } => write!(
40 f,
41 "Failed parsing VDF file. File kind: {:?}, Error: {} at {}",
42 kind,
43 error,
44 path.display(),
45 ),
46 Self::MissingExpectedApp { app_id } => {
47 write!(f, "Missing expected app with id: {app_id}")
48 }
49 }
50 }
51}
52
53impl std::error::Error for Error {}
54
55impl Error {
56 pub(crate) fn locate(locate: LocateError) -> Self {
57 Self::FailedLocate(locate)
58 }
59
60 pub(crate) fn validation(validation: ValidationError) -> Self {
61 Self::InvalidSteamDir(validation)
62 }
63
64 pub(crate) fn io(io: io::Error, path: &Path) -> Self {
65 Self::Io {
66 inner: io,
67 path: path.to_owned(),
68 }
69 }
70
71 pub(crate) fn parse(kind: ParseErrorKind, error: ParseError, path: &Path) -> Self {
72 Self::Parse {
73 kind,
74 error,
75 path: path.to_owned(),
76 }
77 }
78}
79
80#[derive(Clone, Debug)]
81pub enum LocateError {
82 Backend(BackendError),
83 Unsupported,
84}
85
86impl LocateError {
87 #[cfg(target_os = "windows")]
88 pub(crate) fn winreg(io: io::Error) -> Self {
89 Self::Backend(BackendError {
90 inner: BackendErrorInner(std::sync::Arc::new(io)),
91 })
92 }
93
94 #[cfg(any(target_os = "macos", target_os = "linux"))]
95 pub(crate) fn no_home() -> Self {
96 Self::Backend(BackendError {
97 inner: BackendErrorInner::NoHome,
98 })
99 }
100}
101
102impl fmt::Display for LocateError {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 match self {
105 Self::Backend(error) => error.fmt(f),
106 Self::Unsupported => f.write_str("Unsupported platform"),
107 }
108 }
109}
110
111#[derive(Clone, Debug)]
112pub struct BackendError {
113 #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))]
114 #[allow(dead_code)] inner: BackendErrorInner,
116}
117
118impl fmt::Display for BackendError {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 #[cfg(target_os = "windows")]
121 {
122 write!(f, "{}", self.inner.0)
123 }
124 #[cfg(any(target_os = "macos", target_os = "linux"))]
125 {
126 match self.inner {
127 BackendErrorInner::NoHome => f.write_str("Unable to locate the user's $HOME"),
128 }
129 }
130 #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
131 {
132 let _ = f;
134 unreachable!("This should never be constructed!");
135 }
136 }
137}
138
139#[derive(Clone, Debug)]
142#[cfg(target_os = "windows")]
143struct BackendErrorInner(std::sync::Arc<io::Error>);
144#[derive(Clone, Debug)]
145#[cfg(any(target_os = "macos", target_os = "linux"))]
146enum BackendErrorInner {
147 NoHome,
148}
149
150#[derive(Clone, Debug)]
151pub struct ValidationError {
152 #[allow(dead_code)] inner: ValidationErrorInner,
154}
155
156impl ValidationError {
157 pub(crate) fn missing_dir() -> Self {
158 Self {
159 inner: ValidationErrorInner::MissingDirectory,
160 }
161 }
162}
163
164impl fmt::Display for ValidationError {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 match self.inner {
167 ValidationErrorInner::MissingDirectory => f.write_str(
168 "The Steam installation directory either isn't a directory or doesn't exist",
169 ),
170 }
171 }
172}
173
174#[derive(Clone, Debug)]
175enum ValidationErrorInner {
176 MissingDirectory,
177}
178
179#[derive(Copy, Clone, Debug)]
180#[non_exhaustive]
181pub enum ParseErrorKind {
182 Config,
183 LibraryFolders,
184 App,
185 Shortcut,
186}
187
188#[derive(Debug)]
189pub struct ParseError {
190 #[allow(dead_code)] inner: Box<ParseErrorInner>,
194}
195
196impl fmt::Display for ParseError {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 write!(f, "{}", self.inner)
199 }
200}
201
202#[derive(Debug)]
203pub(crate) enum ParseErrorInner {
204 Parse(keyvalues_parser::error::Error),
205 Serde(keyvalues_serde::error::Error),
206 UnexpectedStructure,
207 Missing,
208}
209
210impl fmt::Display for ParseErrorInner {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 match self {
213 Self::Parse(err) => write!(f, "{err}"),
214 Self::Serde(err) => write!(f, "{err}"),
215 Self::UnexpectedStructure => f.write_str("File did not match expected structure"),
216 Self::Missing => f.write_str("Expected file was missing"),
217 }
218 }
219}
220
221impl ParseError {
222 pub(crate) fn new(inner: ParseErrorInner) -> Self {
223 Self {
224 inner: Box::new(inner),
225 }
226 }
227
228 pub(crate) fn from_parser(err: keyvalues_parser::error::Error) -> Self {
229 Self::new(ParseErrorInner::Parse(err))
230 }
231
232 pub(crate) fn from_serde(err: keyvalues_serde::error::Error) -> Self {
233 Self::new(ParseErrorInner::Serde(err))
234 }
235
236 pub(crate) fn unexpected_structure() -> Self {
237 Self::new(ParseErrorInner::UnexpectedStructure)
238 }
239
240 pub(crate) fn missing() -> Self {
241 Self::new(ParseErrorInner::Missing)
242 }
243}