1use thiserror::Error;
4
5#[derive(Debug, Error)]
7pub enum SteamUserError {
8 #[error("Not logged in")]
10 NotLoggedIn,
11
12 #[error("Session expired")]
14 SessionExpired,
15
16 #[error("Family View restricted")]
18 FamilyViewRestricted,
19
20 #[error("Account is limited: {0}")]
22 LimitedAccount(String),
23
24 #[error("HTTP error: {0}")]
26 HttpError(#[from] reqwest::Error),
27
28 #[error("Malformed response: {0}")]
30 MalformedResponse(String),
31
32 #[error("Steam error: {0}")]
34 SteamError(String),
35
36 #[error("EResult {code}: {message}")]
38 EResult {
39 code: i32,
41 message: String,
43 },
44
45 #[error("Invalid confirmation key")]
47 InvalidConfirmationKey,
48
49 #[error("Confirmation not found for object {0}")]
51 ConfirmationNotFound(u64),
52
53 #[error("2FA error: {0}")]
55 TwoFactorError(String),
56
57 #[error("Invalid image format: {0}")]
59 InvalidImageFormat(String),
60
61 #[error("Rate limited")]
63 RateLimited,
64
65 #[error("Missing credential: {field}")]
67 MissingCredential {
68 field: &'static str,
71 },
72
73 #[error("HTTP {status} from {url}")]
75 HttpStatus {
76 status: u16,
78 url: String,
80 },
81
82 #[error("HTTP client build failed: {0}")]
84 ClientBuild(String),
85
86 #[error("Redirect error: {0}")]
88 RedirectError(String),
89
90 #[error("Invalid input: {0}")]
92 InvalidInput(String),
93
94 #[error("Protobuf encode error: {0}")]
96 ProtobufEncode(#[from] prost::EncodeError),
97
98 #[error("Protobuf decode error: {0}")]
100 ProtobufDecode(#[from] prost::DecodeError),
101
102 #[error("URL error: {0}")]
104 UrlError(#[from] url::ParseError),
105
106 #[error("JSON error: {0}")]
108 JsonError(#[from] serde_json::Error),
109
110 #[error("Base64 error: {0}")]
112 Base64Error(#[from] base64::DecodeError),
113
114 #[error("I/O error: {0}")]
116 Io(#[from] std::io::Error),
117
118 #[error("System time error: {0}")]
120 SystemTime(#[from] std::time::SystemTimeError),
121
122 #[error("{0}")]
124 Other(String),
125
126 #[cfg(feature = "remote")]
128 #[error(transparent)]
129 RemoteFailed(#[from] Box<crate::remote::RemoteSteamUserError>),
130
131 #[cfg(feature = "gas")]
133 #[error(transparent)]
134 GasFailed(#[from] Box<crate::gas::GasError>),
135
136 #[error("TOTP error: {0}")]
138 Totp(#[from] steam_totp::TotpError),
139
140 #[error("Middleware error: {0:#}")]
145 Middleware(anyhow::Error),
146
147 #[error("Failed to execute {action:?}: {source}")]
149 ActionFailed {
150 action: crate::action::ApiAction,
152 #[source]
154 source: Box<SteamUserError>,
155 },
156}
157
158impl From<reqwest_middleware::Error> for SteamUserError {
159 fn from(err: reqwest_middleware::Error) -> Self {
160 match err {
161 reqwest_middleware::Error::Reqwest(e) => Self::HttpError(e),
162 reqwest_middleware::Error::Middleware(e) => Self::Middleware(e),
163 }
164 }
165}
166
167impl SteamUserError {
168 pub fn api_action(&self) -> Option<crate::action::ApiAction> {
170 match self {
171 Self::ActionFailed { action, .. } => Some(*action),
172 _ => None,
173 }
174 }
175
176 pub fn from_eresult(code: i32) -> Self {
178 let message = steam_enums::eresult::EResult::from_i32(code).map(|e| format!("{e:?}")).unwrap_or_else(|| format!("Unknown({code})"));
179 Self::EResult { code, message }
180 }
181
182 pub fn check_eresult(code: i32) -> Result<(), Self> {
184 if code == 1 {
185 Ok(())
186 } else {
187 Err(Self::from_eresult(code))
188 }
189 }
190
191 pub fn is_retryable(&self) -> bool {
193 match self {
194 Self::ActionFailed { action, source } => action.is_read_only() && source.is_retryable(),
195 Self::RateLimited => true,
196 Self::HttpStatus { status, .. } => *status == 429 || *status >= 500,
197 Self::HttpError(e) => e.is_connect() || e.is_timeout(),
198 Self::Middleware(_) => true,
199 #[cfg(feature = "remote")]
200 Self::RemoteFailed(e) => matches!(
201 e.as_ref(),
202 crate::remote::RemoteSteamUserError::Http(_)
203 | crate::remote::RemoteSteamUserError::AllRetriesFailed
204 ),
205 #[cfg(feature = "gas")]
206 Self::GasFailed(e) => matches!(
207 e.as_ref(),
208 crate::gas::GasError::Http(_) | crate::gas::GasError::AllRetriesFailed
209 ),
210 _ => false,
211 }
212 }
213}