From 38d78ff2c50fe7077534e33b1694d08293819ebb Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Fri, 13 Dec 2024 19:18:35 -0800
Subject: [PATCH] cookie: refactor http errors to include more useful
 information

---
 rbx_asset/src/cookie.rs | 71 ++++++++++++++++++++++++++++++++---------
 1 file changed, 56 insertions(+), 15 deletions(-)

diff --git a/rbx_asset/src/cookie.rs b/rbx_asset/src/cookie.rs
index 4dbf9e7..f13bcb0 100644
--- a/rbx_asset/src/cookie.rs
+++ b/rbx_asset/src/cookie.rs
@@ -23,6 +23,7 @@ pub struct CreateRequest{
 pub enum CreateError{
 	ParseError(url::ParseError),
 	PostError(PostError),
+	Response(ResponseError),
 	Reqwest(reqwest::Error),
 }
 impl std::fmt::Display for CreateError{
@@ -46,6 +47,7 @@ pub enum UploadError{
 	ParseError(url::ParseError),
 	PostError(PostError),
 	Reqwest(reqwest::Error),
+	Response(ResponseError),
 	AssetIdIsZero,
 }
 impl std::fmt::Display for UploadError{
@@ -64,6 +66,7 @@ pub struct GetAssetRequest{
 #[derive(Debug)]
 pub enum GetError{
 	ParseError(url::ParseError),
+	Response(ResponseError),
 	Reqwest(reqwest::Error),
 	IO(std::io::Error)
 }
@@ -100,6 +103,7 @@ pub struct AssetVersionsPageResponse{
 #[derive(Debug)]
 pub enum PageError{
 	ParseError(url::ParseError),
+	Response(ResponseError),
 	Reqwest(reqwest::Error),
 }
 impl std::fmt::Display for PageError{
@@ -195,6 +199,40 @@ fn read_readable(mut readable:impl std::io::Read)->std::io::Result<Vec<u8>>{
 	readable.read_to_end(&mut contents)?;
 	Ok(contents)
 }
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct StatusCodeWithUrlAndBody{
+	pub status_code:reqwest::StatusCode,
+	pub url:url::Url,
+	pub body:String,
+}
+#[derive(Debug)]
+pub enum ResponseError{
+	Reqwest(reqwest::Error),
+	StatusCodeWithUrlAndBody(StatusCodeWithUrlAndBody),
+}
+impl std::fmt::Display for ResponseError{
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		write!(f,"{self:?}")
+	}
+}
+impl std::error::Error for ResponseError{}
+// lazy function to draw out meaningful info from http response on failure
+async fn response_ok(response:reqwest::Response)->Result<reqwest::Response,ResponseError>{
+	let status_code=response.status();
+	if status_code.is_success(){
+		Ok(response)
+	}else{
+		let url=response.url().to_owned();
+		let bytes=response.bytes().await.map_err(ResponseError::Reqwest)?;
+		let body=String::from_utf8_lossy(&bytes).to_string();
+		Err(ResponseError::StatusCodeWithUrlAndBody(StatusCodeWithUrlAndBody{
+			status_code,
+			url,
+			body,
+		}))
+	}
+}
 
 #[derive(Clone)]
 pub struct Cookie(String);
@@ -262,8 +300,9 @@ impl CookieContext{
 			}
 		}
 
-		self.post(url,body).await.map_err(CreateError::PostError)?
-		.error_for_status().map_err(CreateError::Reqwest)?
+		response_ok(
+			self.post(url,body).await.map_err(CreateError::PostError)?
+		).await.map_err(CreateError::Response)?
 		.json::<UploadResponse>().await.map_err(CreateError::Reqwest)
 	}
 	pub async fn upload(&self,config:UploadRequest,body:impl Into<reqwest::Body>+Clone)->Result<UploadResponse,UploadError>{
@@ -293,8 +332,9 @@ impl CookieContext{
 			}
 		}
 
-		self.post(url,body).await.map_err(UploadError::PostError)?
-		.error_for_status().map_err(UploadError::Reqwest)?
+		response_ok(
+			self.post(url,body).await.map_err(UploadError::PostError)?
+		).await.map_err(UploadError::Response)?
 		.json::<UploadResponse>().await.map_err(UploadError::Reqwest)
 	}
 	pub async fn get_asset(&self,config:GetAssetRequest)->Result<Vec<u8>,GetError>{
@@ -307,8 +347,9 @@ impl CookieContext{
 				query.append_pair("version",version.to_string().as_str());
 			}
 		}
-		let body=self.get(url).await.map_err(GetError::Reqwest)?
-		.error_for_status().map_err(GetError::Reqwest)?
+		let body=response_ok(
+			self.get(url).await.map_err(GetError::Reqwest)?
+		).await.map_err(GetError::Response)?
 		.bytes().await.map_err(GetError::Reqwest)?;
 
 		match maybe_gzip_decode(&mut std::io::Cursor::new(body)){
@@ -329,9 +370,9 @@ impl CookieContext{
 				query.append_pair("cursor",cursor);
 			}
 		}
-
-		self.get(url).await.map_err(PageError::Reqwest)?
-		.error_for_status().map_err(PageError::Reqwest)?
+		response_ok(
+			self.get(url).await.map_err(PageError::Reqwest)?
+		).await.map_err(PageError::Response)?
 		.json::<AssetVersionsPageResponse>().await.map_err(PageError::Reqwest)
 	}
 	pub async fn get_creations_page(&self,config:&CreationsPageRequest)->Result<CreationsPageResponse,PageError>{
@@ -344,9 +385,9 @@ impl CookieContext{
 				query.append_pair("cursor",cursor);
 			}
 		}
-
-		self.get(url).await.map_err(PageError::Reqwest)?
-		.error_for_status().map_err(PageError::Reqwest)?
+		response_ok(
+			self.get(url).await.map_err(PageError::Reqwest)?
+		).await.map_err(PageError::Response)?
 		.json::<CreationsPageResponse>().await.map_err(PageError::Reqwest)
 	}
 	pub async fn get_user_inventory_page(&self,config:&UserInventoryPageRequest)->Result<UserInventoryPageResponse,PageError>{
@@ -358,9 +399,9 @@ impl CookieContext{
 				query.append_pair("cursor",cursor);
 			}
 		}
-
-		self.get(url).await.map_err(PageError::Reqwest)?
-		.error_for_status().map_err(PageError::Reqwest)?
+		response_ok(
+			self.get(url).await.map_err(PageError::Reqwest)?
+		).await.map_err(PageError::Response)?
 		.json::<UserInventoryPageResponse>().await.map_err(PageError::Reqwest)
 	}
 }