rust_mc_status/
error.rs

1//! Error types for the Minecraft server status library.
2//!
3//! This module provides comprehensive error handling using [`thiserror`] for
4//! all operations that can fail during server status queries.
5//!
6//! # Example
7//!
8//! ```no_run
9//! use rust_mc_status::{McClient, ServerEdition, McError};
10//!
11//! #[tokio::main]
12//! async fn main() -> Result<(), McError> {
13//!     let client = McClient::new();
14//!
15//!     match client.ping("invalid-server", ServerEdition::Java).await {
16//!         Ok(status) => println!("Server is online!"),
17//!         Err(McError::Timeout) => println!("Request timed out"),
18//!         Err(McError::DnsError(msg)) => println!("DNS error: {}", msg),
19//!         Err(e) => println!("Other error: {}", e),
20//!     }
21//!
22//!     Ok(())
23//! }
24//! ```
25
26use thiserror::Error;
27
28/// Errors that can occur during Minecraft server status queries.
29///
30/// All errors implement `std::error::Error` and can be easily converted to
31/// user-friendly error messages using the `Display` trait.
32///
33/// # Error Types
34///
35/// - **DnsError**: DNS resolution failed (e.g., hostname not found)
36/// - **ConnectionError**: Failed to establish connection to server
37/// - **Timeout**: Request timed out
38/// - **InvalidResponse**: Server returned invalid or malformed data
39/// - **IoError**: I/O error occurred (wrapped `std::io::Error`)
40/// - **JsonError**: Failed to parse JSON response (wrapped `serde_json::Error`)
41/// - **Utf8Error**: Invalid UTF-8 in server response
42/// - **Base64Error**: Failed to decode base64 data (e.g., favicon)
43/// - **InvalidEdition**: Invalid server edition specified
44/// - **InvalidPort**: Invalid port number in address
45/// - **InvalidAddress**: Invalid address format
46#[derive(Error, Debug)]
47pub enum McError {
48    /// DNS resolution failed.
49    ///
50    /// This error occurs when the hostname cannot be resolved to an IP address,
51    /// or when an SRV record lookup fails.
52    ///
53    /// # Example
54    ///
55    /// ```no_run
56    /// # use rust_mc_status::McError;
57    /// # fn example() -> Result<(), McError> {
58    /// Err(McError::DnsError("Hostname not found".to_string()))
59    /// # }
60    /// ```
61    #[error("DNS resolution failed: {0}")]
62    DnsError(String),
63
64    /// Connection to server failed.
65    ///
66    /// This error occurs when a TCP/UDP connection cannot be established,
67    /// typically due to network issues or the server being unreachable.
68    ///
69    /// # Example
70    ///
71    /// ```no_run
72    /// # use rust_mc_status::McError;
73    /// # fn example() -> Result<(), McError> {
74    /// Err(McError::ConnectionError("Connection refused".to_string()))
75    /// # }
76    /// ```
77    #[error("Connection failed: {0}")]
78    ConnectionError(String),
79
80    /// Request timed out.
81    ///
82    /// This error occurs when the server does not respond within the configured
83    /// timeout duration. You can adjust the timeout using `McClient::with_timeout`.
84    ///
85    /// # Example
86    ///
87    /// ```no_run
88    /// # use rust_mc_status::McClient;
89    /// # use std::time::Duration;
90    /// # #[tokio::main]
91    /// # async fn main() {
92    /// let client = McClient::new().with_timeout(Duration::from_secs(2));
93    /// // If server doesn't respond within 2 seconds, Timeout error is returned
94    /// # }
95    /// ```
96    #[error("Timeout occurred")]
97    Timeout,
98
99    /// Server returned invalid or malformed response.
100    ///
101    /// This error occurs when the server response does not match the expected
102    /// protocol format, or contains invalid data.
103    ///
104    /// # Example
105    ///
106    /// ```no_run
107    /// # use rust_mc_status::McError;
108    /// # fn example() -> Result<(), McError> {
109    /// Err(McError::InvalidResponse("Response too short".to_string()))
110    /// # }
111    /// ```
112    #[error("Invalid server response: {0}")]
113    InvalidResponse(String),
114
115    /// I/O error occurred.
116    ///
117    /// This is a wrapper around `std::io::Error` for operations like reading
118    /// from or writing to network streams.
119    ///
120    /// # Example
121    ///
122    /// ```no_run
123    /// # use rust_mc_status::McError;
124    /// # use std::io;
125    /// # fn example() -> Result<(), McError> {
126    /// let io_error = io::Error::new(io::ErrorKind::NotFound, "File not found");
127    /// Err(McError::IoError(io_error))
128    /// # }
129    /// ```
130    #[error("I/O error: {0}")]
131    IoError(#[from] std::io::Error),
132
133    /// JSON parsing error.
134    ///
135    /// This error occurs when the server response contains invalid JSON.
136    /// It's a wrapper around `serde_json::Error`.
137    ///
138    /// # Example
139    ///
140    /// ```no_run
141    /// # use rust_mc_status::McError;
142    /// # fn example() -> Result<(), McError> {
143    /// // Usually occurs when parsing Java server status JSON
144    /// # Ok(())
145    /// # }
146    /// ```
147    #[error("JSON parsing error: {0}")]
148    JsonError(#[from] serde_json::Error),
149
150    /// UTF-8 conversion error.
151    ///
152    /// This error occurs when the server response contains invalid UTF-8 sequences.
153    ///
154    /// # Example
155    ///
156    /// ```no_run
157    /// # use rust_mc_status::McError;
158    /// # fn example() -> Result<(), McError> {
159    /// // Usually occurs when reading server response
160    /// # Ok(())
161    /// # }
162    /// ```
163    #[error("UTF-8 conversion error: {0}")]
164    Utf8Error(#[from] std::string::FromUtf8Error),
165
166    /// Base64 decoding error.
167    ///
168    /// This error occurs when decoding base64-encoded data (e.g., server favicon)
169    /// fails.
170    ///
171    /// # Example
172    ///
173    /// ```no_run
174    /// # use rust_mc_status::{McClient, ServerEdition};
175    /// # #[tokio::main]
176    /// # async fn main() {
177    /// # let client = McClient::new();
178    /// # if let Ok(status) = client.ping("server.com", ServerEdition::Java).await {
179    /// # if let rust_mc_status::ServerData::Java(java) = status.data {
180    /// // Can occur when saving favicon
181    /// java.save_favicon("icon.png").unwrap();
182    /// # }
183    /// # }
184    /// # }
185    /// ```
186    #[error("Base64 decoding error: {0}")]
187    Base64Error(#[from] base64::DecodeError),
188
189    /// Invalid server edition specified.
190    ///
191    /// This error occurs when an invalid server edition is provided.
192    /// Valid editions are `Java` and `Bedrock`.
193    ///
194    /// # Example
195    ///
196    /// ```no_run
197    /// # use rust_mc_status::McError;
198    /// # use std::str::FromStr;
199    /// # use rust_mc_status::ServerEdition;
200    /// # fn example() -> Result<(), McError> {
201    /// ServerEdition::from_str("invalid")?;
202    /// # Ok(())
203    /// # }
204    /// ```
205    #[error("Invalid edition: {0}")]
206    InvalidEdition(String),
207
208    /// Invalid port number in address.
209    ///
210    /// This error occurs when the port in the address string cannot be parsed
211    /// as a valid `u16` value.
212    ///
213    /// # Example
214    ///
215    /// ```no_run
216    /// # use rust_mc_status::McClient;
217    /// # use rust_mc_status::ServerEdition;
218    /// # #[tokio::main]
219    /// # async fn main() {
220    /// let client = McClient::new();
221    /// // This will return InvalidPort error
222    /// let _ = client.ping("server.com:99999", ServerEdition::Java).await;
223    /// # }
224    /// ```
225    #[error("Invalid port: {0}")]
226    InvalidPort(String),
227
228    /// Invalid address format.
229    ///
230    /// This error occurs when the server address has an invalid format.
231    ///
232    /// # Example
233    ///
234    /// ```no_run
235    /// # use rust_mc_status::McError;
236    /// # fn example() -> Result<(), McError> {
237    /// Err(McError::InvalidAddress("Invalid format".to_string()))
238    /// # }
239    /// ```
240    #[error("Invalid address format: {0}")]
241    InvalidAddress(String),
242}