improved error handling #1
This commit is contained in:
+22
-13
@@ -4,8 +4,6 @@ mod set_hat;
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{BufRead, BufReader},
|
||||
net::TcpStream,
|
||||
sync::{mpsc, Arc},
|
||||
};
|
||||
|
||||
@@ -39,7 +37,7 @@ impl Session {
|
||||
|
||||
std::thread::spawn(move || match stream.read() {
|
||||
Some(session_token) => token_send.send(Ok(session_token)),
|
||||
_ => token_send.send(Err(crate::response::Error::InvalidHandshake)),
|
||||
_ => token_send.send(Err(crate::response::Error::InvalidHandshake("Failed to read session token".to_string()))),
|
||||
});
|
||||
|
||||
match token_recv.recv_timeout(std::time::Duration::from_secs(20)) {
|
||||
@@ -48,17 +46,17 @@ impl Session {
|
||||
let response = minreq::get("https://api.minecraftservices.com/minecraft/profile")
|
||||
.with_header("Authorization", &format!("Bearer {session_token}"))
|
||||
.send()
|
||||
.map_err(|_| crate::response::Error::InvalidSession)?;
|
||||
.map_err(|_| crate::response::Error::InvalidSession("Failed to validate session".to_string()))?;
|
||||
|
||||
// If the session is invalid, return an error
|
||||
if response.status_code != 200 {
|
||||
return Err(crate::response::Error::InvalidSession);
|
||||
return Err(crate::response::Error::InvalidSession(format!("Invalid session status code: {}", response.status_code)));
|
||||
}
|
||||
|
||||
// Parse the player data
|
||||
let local_player: LocalPlayer = response
|
||||
.json()
|
||||
.map_err(|_| crate::response::Error::InvalidSession)?;
|
||||
.map_err(|_| crate::response::Error::InvalidSession("Failed to parse player data".to_string()))?;
|
||||
|
||||
println!("[MOJANG] {} successfully logged on", &local_player.name);
|
||||
|
||||
@@ -74,8 +72,8 @@ impl Session {
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
Err(mpsc::RecvTimeoutError::Timeout) => Err(crate::response::Error::Timeout),
|
||||
_ => Err(crate::response::Error::InvalidHandshake),
|
||||
Err(mpsc::RecvTimeoutError::Timeout) => Err(crate::response::Error::Timeout("Session handshake timed out".to_string())),
|
||||
_ => Err(crate::response::Error::InvalidHandshake("Failed to receive handshake response".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,18 +89,29 @@ impl Session {
|
||||
|
||||
"players" => {
|
||||
let mut players: Vec<PlayerResponse> = Vec::new();
|
||||
for uuid in params.parse_param::<String>("uuids")?.split("$") {
|
||||
let uuids = params.parse_param::<String>("uuids")?;
|
||||
if uuids.is_empty() {
|
||||
return Err(crate::response::Error::InvalidParameter {
|
||||
param: "uuids".to_string(),
|
||||
reason: "UUIDs list cannot be empty".to_string(),
|
||||
});
|
||||
}
|
||||
for uuid in uuids.split("$") {
|
||||
if uuid.is_empty() {
|
||||
return Err(crate::response::Error::InvalidParameter {
|
||||
param: "uuids".to_string(),
|
||||
reason: "UUID cannot be empty".to_string(),
|
||||
});
|
||||
}
|
||||
match player::player(self, uuid.to_string())? {
|
||||
Response::Player(p) => {
|
||||
players.push(p);
|
||||
}
|
||||
Response::Player(p) => players.push(p),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(Response::Players(players))
|
||||
}
|
||||
|
||||
_ => Err(crate::response::Error::InvalidRequest),
|
||||
_ => Err(crate::response::Error::InvalidMethod(method.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-19
@@ -7,26 +7,32 @@ use super::{player, Session};
|
||||
pub fn set_cloak(session: &Session, cloak: String) -> Result {
|
||||
match player::player(session, session.local_player.id.clone())? {
|
||||
Response::Player(crate::response::PlayerResponse::Player { cloaks, .. }) => {
|
||||
if cloaks.contains(&cloak) || cloak.is_empty() {
|
||||
session
|
||||
.database
|
||||
.players
|
||||
.update_one(
|
||||
doc! {
|
||||
"uuid": session.local_player.id.clone(),
|
||||
},
|
||||
doc! {
|
||||
"$set": {
|
||||
"cloak": cloak,
|
||||
},
|
||||
},
|
||||
)
|
||||
.run()?;
|
||||
Ok(Response::Success)
|
||||
} else {
|
||||
Err(Error::SomethingWentWrong)
|
||||
if !cloaks.contains(&cloak) && !cloak.is_empty() {
|
||||
return Err(Error::ValidationError(
|
||||
format!("Player does not own cloak: {}", cloak)
|
||||
));
|
||||
}
|
||||
|
||||
session
|
||||
.database
|
||||
.players
|
||||
.update_one(
|
||||
doc! {
|
||||
"uuid": session.local_player.id.clone(),
|
||||
},
|
||||
doc! {
|
||||
"$set": {
|
||||
"cloak": cloak.clone(),
|
||||
},
|
||||
},
|
||||
)
|
||||
.run()
|
||||
.map_err(|e| Error::DatabaseError(
|
||||
format!("Failed to update cloak: {}", e)
|
||||
))?;
|
||||
|
||||
Ok(Response::Success)
|
||||
}
|
||||
_ => Err(Error::SomethingWentWrong),
|
||||
_ => Err(Error::ValidationError("Invalid player data received".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
+25
-20
@@ -7,27 +7,32 @@ use super::{player, Session};
|
||||
pub fn set_hat(session: &Session, hat: String) -> Result {
|
||||
match player::player(session, session.local_player.id.clone())? {
|
||||
Response::Player(crate::response::PlayerResponse::Player { hats, .. }) => {
|
||||
if hats.contains(&hat) || hat.is_empty() {
|
||||
session
|
||||
.database
|
||||
.players
|
||||
.update_one(
|
||||
doc! {
|
||||
"uuid": session.local_player.id.clone(),
|
||||
},
|
||||
doc! {
|
||||
"$set": {
|
||||
"hat": hat,
|
||||
},
|
||||
},
|
||||
)
|
||||
.run()?;
|
||||
|
||||
Ok(Response::Success)
|
||||
} else {
|
||||
Err(Error::SomethingWentWrong)
|
||||
if !hats.contains(&hat) && !hat.is_empty() {
|
||||
return Err(Error::ValidationError(
|
||||
format!("Player does not own hat: {}", hat)
|
||||
));
|
||||
}
|
||||
|
||||
session
|
||||
.database
|
||||
.players
|
||||
.update_one(
|
||||
doc! {
|
||||
"uuid": session.local_player.id.clone(),
|
||||
},
|
||||
doc! {
|
||||
"$set": {
|
||||
"hat": hat.clone(),
|
||||
},
|
||||
},
|
||||
)
|
||||
.run()
|
||||
.map_err(|e| Error::DatabaseError(
|
||||
format!("Failed to update hat: {}", e)
|
||||
))?;
|
||||
|
||||
Ok(Response::Success)
|
||||
}
|
||||
_ => Err(Error::SomethingWentWrong),
|
||||
_ => Err(Error::ValidationError("Invalid player data received".to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
+30
-10
@@ -7,23 +7,43 @@ pub trait ParamMap {
|
||||
impl ParamMap for HashMap<String, String> {
|
||||
fn parse_param<T: FromStr>(&self, param: &str) -> Result<T, crate::response::Error> {
|
||||
match self.get(param) {
|
||||
Some(value) => T::from_str(value).map_err(|_| crate::response::Error::InvalidParameter),
|
||||
None => Err(crate::response::Error::InvalidParameter),
|
||||
Some(value) => T::from_str(value).map_err(|_| crate::response::Error::InvalidParameter {
|
||||
param: param.to_string(),
|
||||
reason: "Failed to parse parameter value".to_string(),
|
||||
}),
|
||||
None => Err(crate::response::Error::ParameterNotFound(param.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(s: &str) -> std::io::Result<(String, HashMap<String, String>)> {
|
||||
pub fn parse(s: &str) -> Result<(String, HashMap<String, String>), crate::response::Error> {
|
||||
let mut parts = s.split("@");
|
||||
|
||||
let method = parts.next().unwrap().trim().to_lowercase();
|
||||
let method = parts.next()
|
||||
.ok_or_else(|| crate::response::Error::InvalidRequest("Empty request".to_string()))?
|
||||
.trim()
|
||||
.to_lowercase();
|
||||
|
||||
let params = parts
|
||||
.map(|p| {
|
||||
let parts = p.split("=").collect::<Vec<&str>>();
|
||||
(parts[0].trim().to_string(), parts[1].trim().to_string())
|
||||
})
|
||||
.collect::<HashMap<String, String>>();
|
||||
if method.is_empty() {
|
||||
return Err(crate::response::Error::InvalidRequest("Method name cannot be empty".to_string()));
|
||||
}
|
||||
|
||||
let mut params = HashMap::new();
|
||||
for part in parts {
|
||||
let key_value: Vec<&str> = part.split("=").collect();
|
||||
match key_value.as_slice() {
|
||||
[key, value] => {
|
||||
let key = key.trim();
|
||||
if key.is_empty() {
|
||||
return Err(crate::response::Error::InvalidRequest("Parameter key cannot be empty".to_string()));
|
||||
}
|
||||
params.insert(key.to_string(), value.trim().to_string());
|
||||
},
|
||||
_ => return Err(crate::response::Error::InvalidRequest(
|
||||
format!("Invalid parameter format in part: {}", part)
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
Ok((method, params))
|
||||
}
|
||||
|
||||
+23
-19
@@ -62,29 +62,33 @@ impl std::fmt::Display for Response {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Error {
|
||||
InvalidRequest,
|
||||
InvalidMethod,
|
||||
InvalidParameter,
|
||||
ParameterNotFound,
|
||||
InvalidSession,
|
||||
InvalidHandshake,
|
||||
DatabaseError,
|
||||
SomethingWentWrong,
|
||||
Timeout,
|
||||
InvalidRequest(String),
|
||||
InvalidMethod(String),
|
||||
InvalidParameter { param: String, reason: String },
|
||||
ParameterNotFound(String),
|
||||
InvalidSession(String),
|
||||
InvalidHandshake(String),
|
||||
DatabaseError(String),
|
||||
NetworkError(String),
|
||||
Timeout(String),
|
||||
AuthenticationError(String),
|
||||
ValidationError(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::InvalidRequest => write!(f, "Invalid request"),
|
||||
Error::InvalidMethod => write!(f, "Invalid method"),
|
||||
Error::InvalidParameter => write!(f, "Invalid parameter"),
|
||||
Error::ParameterNotFound => write!(f, "Parameter not found"),
|
||||
Error::InvalidSession => write!(f, "Invalid session"),
|
||||
Error::InvalidHandshake => write!(f, "Invalid handshake"),
|
||||
Error::DatabaseError => write!(f, "Database error"),
|
||||
Error::SomethingWentWrong => write!(f, "Something went wrong"),
|
||||
Error::Timeout => write!(f, "Timeout"),
|
||||
Error::InvalidRequest(msg) => write!(f, "Invalid request: {}", msg),
|
||||
Error::InvalidMethod(method) => write!(f, "Invalid method: {}", method),
|
||||
Error::InvalidParameter { param, reason } => write!(f, "Invalid parameter '{}': {}", param, reason),
|
||||
Error::ParameterNotFound(param) => write!(f, "Required parameter not found: {}", param),
|
||||
Error::InvalidSession(details) => write!(f, "Invalid session: {}", details),
|
||||
Error::InvalidHandshake(details) => write!(f, "Handshake failed: {}", details),
|
||||
Error::DatabaseError(details) => write!(f, "Database error: {}", details),
|
||||
Error::NetworkError(details) => write!(f, "Network error: {}", details),
|
||||
Error::Timeout(operation) => write!(f, "Operation timed out: {}", operation),
|
||||
Error::AuthenticationError(details) => write!(f, "Authentication failed: {}", details),
|
||||
Error::ValidationError(details) => write!(f, "Validation failed: {}", details),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,7 +96,7 @@ impl std::fmt::Display for Error {
|
||||
impl From<mongodb::error::Error> for Error {
|
||||
fn from(e: mongodb::error::Error) -> Self {
|
||||
println!("{e}");
|
||||
Error::DatabaseError
|
||||
Error::DatabaseError(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user