From 19ac6347dff8e3af1d4a3156a5343d9edd3785c1 Mon Sep 17 00:00:00 2001 From: web96lol Date: Sat, 18 Oct 2025 02:45:48 -0700 Subject: [PATCH] Improve REST client error handling --- src/error.rs | 43 ++++++++++++++++++++++++++++++++++++++++ src/rest.rs | 55 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 80 insertions(+), 18 deletions(-) diff --git a/src/error.rs b/src/error.rs index 17e55b3..41af171 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,7 @@ use std::{error::Error, fmt, fmt::Display}; +use reqwest::StatusCode; + /// Errors that can occur when trying to get the Riot process information #[derive(Debug, Clone)] pub enum ProcessInfoError { @@ -81,6 +83,47 @@ impl Display for IngameClientError { } } +/// Errors for requests to the LCU REST API +#[derive(Debug, Clone)] +pub enum RestClientError { + /// The API returned a non-success status code + HttpStatus(StatusCode), + /// There was an error deserializing the received data + DeserializationError(String), + /// All other errors are categorized as connection errors + ConnectionError(String), +} + +impl From for RestClientError { + fn from(error: reqwest::Error) -> Self { + if let Some(status) = error.status() { + return RestClientError::HttpStatus(status); + } + if error.is_decode() { + return RestClientError::DeserializationError(error.to_string()); + } + RestClientError::ConnectionError(error.to_string()) + } +} + +impl Error for RestClientError {} + +impl Display for RestClientError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RestClientError::HttpStatus(status) => { + write!(f, "LCU REST API responded with status {}", status) + } + RestClientError::DeserializationError(err) => { + write!(f, "Failed to deserialize LCU REST response: {}", err) + } + RestClientError::ConnectionError(err) => { + write!(f, "LCU REST connection error: {}", err) + } + } + } +} + /// Errors for the Websocket connection to the LCU API #[derive(Debug, Clone)] pub enum LcuWebsocketError { diff --git a/src/rest.rs b/src/rest.rs index 580995d..b68312e 100644 --- a/src/rest.rs +++ b/src/rest.rs @@ -1,7 +1,7 @@ use reqwest::StatusCode; use serde::{Deserialize, Serialize}; -use crate::utils::{process_info, request::build_reqwest_client}; +use crate::{error::RestClientError, utils::request::build_reqwest_client}; #[derive(Clone)] /// A client for the League-Client(LCU) REST API @@ -46,15 +46,22 @@ impl RESTClient { } /// Make a get request to the specified endpoint - pub async fn get(&self, endpoint: String) -> Result { + pub async fn get(&self, endpoint: String) -> Result { let port = self.get_port(); - let req: serde_json::Value = self + let response = self .client .get(format!("https://127.0.0.1:{}{}", port, endpoint)) .send() - .await? - .json() - .await?; + .await + .map_err(RestClientError::from)?; + + if response.status() == StatusCode::NO_CONTENT { + return Ok(serde_json::json!({ "status": 204 })); + } + + let response = response.error_for_status().map_err(RestClientError::from)?; + + let req: serde_json::Value = response.json().await.map_err(RestClientError::from)?; Ok(req) } @@ -64,39 +71,45 @@ impl RESTClient { &self, endpoint: String, body: T, - ) -> Result { + ) -> Result { let port = self.get_port(); let response = self .client .post(format!("https://127.0.0.1:{}{}", port, endpoint)) .json(&body) .send() - .await?; + .await + .map_err(RestClientError::from)?; if response.status() == StatusCode::NO_CONTENT { return Ok(serde_json::json!({ "status": 204 })); } - let req: serde_json::Value = response.json().await?; + let response = response.error_for_status().map_err(RestClientError::from)?; + + let req: serde_json::Value = response.json().await.map_err(RestClientError::from)?; Ok(req) } pub async fn post_no_body( &self, endpoint: String, - ) -> Result { + ) -> Result { let port = self.get_port(); let response = self .client .post(format!("https://127.0.0.1:{}{}", port, endpoint)) .send() - .await?; + .await + .map_err(RestClientError::from)?; if response.status() == StatusCode::NO_CONTENT { return Ok(serde_json::json!({ "status": 204 })); } - let req: serde_json::Value = response.json().await?; + let response = response.error_for_status().map_err(RestClientError::from)?; + + let req: serde_json::Value = response.json().await.map_err(RestClientError::from)?; Ok(req) } @@ -105,37 +118,43 @@ impl RESTClient { &self, endpoint: String, body: T, - ) -> Result { + ) -> Result { let port = self.get_port(); let response = self .client .put(format!("https://127.0.0.1:{}{}", port, endpoint)) .json(&body) .send() - .await?; + .await + .map_err(RestClientError::from)?; if response.status() == StatusCode::NO_CONTENT { return Ok(serde_json::json!({ "status": 204 })); } - let req: serde_json::Value = response.json().await?; + let response = response.error_for_status().map_err(RestClientError::from)?; + + let req: serde_json::Value = response.json().await.map_err(RestClientError::from)?; Ok(req) } /// Make a delete request to the specified endpoint - pub async fn delete(&self, endpoint: String) -> Result { + pub async fn delete(&self, endpoint: String) -> Result { let port = self.get_port(); let response = self .client .delete(format!("https://127.0.0.1:{}{}", port, endpoint)) .send() - .await?; + .await + .map_err(RestClientError::from)?; if response.status() == StatusCode::NO_CONTENT { return Ok(serde_json::json!({ "status": 204 })); } - let req: serde_json::Value = response.json().await?; + let response = response.error_for_status().map_err(RestClientError::from)?; + + let req: serde_json::Value = response.json().await.map_err(RestClientError::from)?; Ok(req) } }