From 59f666a8ae932be049343eb3b79583d500514931 Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Wed, 2 Apr 2025 17:35:00 -0700
Subject: [PATCH] deduplicate create

---
 validation/src/create.rs            | 77 +++++++++++++++++++++++++++++
 validation/src/create_mapfix.rs     | 72 ++++++++-------------------
 validation/src/create_submission.rs | 70 ++++++++------------------
 validation/src/main.rs              |  1 +
 validation/src/message_handler.rs   |  2 +
 5 files changed, 121 insertions(+), 101 deletions(-)
 create mode 100644 validation/src/create.rs

diff --git a/validation/src/create.rs b/validation/src/create.rs
new file mode 100644
index 0000000..1513904
--- /dev/null
+++ b/validation/src/create.rs
@@ -0,0 +1,77 @@
+use crate::rbx_util::{get_mapinfo,read_dom,MapInfo,ReadDomError,GetMapInfoError,ParseGameIDError};
+
+#[allow(dead_code)]
+#[derive(Debug)]
+pub enum Error{
+	ModelVersionsPage(rbx_asset::cookie::PageError),
+	EmptyVersionsPage,
+	WrongCreatorType,
+	ModelFileDownload(rbx_asset::cookie::GetError),
+	ModelFileDecode(ReadDomError),
+	GetMapInfo(GetMapInfoError),
+	ParseGameID(ParseGameIDError),
+}
+impl std::fmt::Display for Error{
+	fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
+		write!(f,"{self:?}")
+	}
+}
+impl std::error::Error for Error{}
+
+#[allow(nonstandard_style)]
+pub struct CreateRequest{
+	pub ModelID:u64,
+}
+#[allow(nonstandard_style)]
+pub struct CreateResult{
+	pub AssetOwner:i64,
+	pub DisplayName:String,
+	pub Creator:String,
+	pub GameID:i32,
+	pub AssetVersion:u64,
+}
+impl crate::message_handler::MessageHandler{
+	pub async fn create_inner(&self,create_info:CreateRequest)->Result<CreateResult,Error>{
+		// discover the latest asset version
+		let asset_versions_page=self.cookie_context.get_asset_versions_page(rbx_asset::cookie::AssetVersionsPageRequest{
+			asset_id:create_info.ModelID,
+			cursor:None
+		}).await.map_err(Error::ModelVersionsPage)?;
+
+		// grab version info
+		let first_version=asset_versions_page.data.first().ok_or(Error::EmptyVersionsPage)?;
+
+		if first_version.creatorType!="User"{
+			return Err(Error::WrongCreatorType);
+		}
+
+		let asset_creator_id=first_version.creatorTargetId;
+		let asset_version=first_version.assetVersionNumber;
+
+		// download the map model version
+		let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
+			asset_id:create_info.ModelID,
+			version:Some(asset_version),
+		}).await.map_err(Error::ModelFileDownload)?;
+
+		// decode dom (slow!)
+		let dom=read_dom(&mut std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
+
+		// parse create fields out of asset
+		let MapInfo{
+			display_name,
+			creator,
+			game_id,
+		}=get_mapinfo(&dom).map_err(Error::GetMapInfo)?;
+
+		let game_id=game_id.map_err(Error::ParseGameID)?;
+
+		Ok(CreateResult{
+			AssetOwner:asset_creator_id as i64,
+			DisplayName:display_name.unwrap_or_default().to_owned(),
+			Creator:creator.unwrap_or_default().to_owned(),
+			GameID:game_id as i32,
+			AssetVersion:asset_version,
+		})
+	}
+}
diff --git a/validation/src/create_mapfix.rs b/validation/src/create_mapfix.rs
index 7c163bc..84c33b1 100644
--- a/validation/src/create_mapfix.rs
+++ b/validation/src/create_mapfix.rs
@@ -1,16 +1,9 @@
 use crate::nats_types::CreateMapfixRequest;
-use crate::rbx_util::{MapInfo,get_mapinfo,read_dom,ReadDomError,GetMapInfoError,ParseGameIDError};
+use crate::create::CreateRequest;
 
 #[allow(dead_code)]
 #[derive(Debug)]
 pub enum Error{
-	ModelVersionsPage(rbx_asset::cookie::PageError),
-	EmptyVersionsPage,
-	WrongCreatorType,
-	ModelFileDownload(rbx_asset::cookie::GetError),
-	ModelFileDecode(ReadDomError),
-	GetMapInfo(GetMapInfoError),
-	ParseGameID(ParseGameIDError),
 	ApiActionMapfixCreate(submissions_api::Error),
 }
 impl std::fmt::Display for Error{
@@ -22,52 +15,29 @@ impl std::error::Error for Error{}
 
 impl crate::message_handler::MessageHandler{
 	pub async fn create_mapfix(&self,create_info:CreateMapfixRequest)->Result<(),Error>{
-		// discover the latest asset version
-		let asset_versions_page=self.cookie_context.get_asset_versions_page(rbx_asset::cookie::AssetVersionsPageRequest{
-			asset_id:create_info.ModelID,
-			cursor:None
-		}).await.map_err(Error::ModelVersionsPage)?;
+		let create_result=self.create_inner(CreateRequest{
+			ModelID:create_info.ModelID,
+		}).await;
 
-		// grab version info
-		let first_version=asset_versions_page.data.first().ok_or(Error::EmptyVersionsPage)?;
-
-		if first_version.creatorType!="User"{
-			return Err(Error::WrongCreatorType);
+		match create_result{
+			Ok(create_request)=>{
+				// call create on api
+				self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{
+					OperationID:create_info.OperationID,
+					AssetOwner:create_request.AssetOwner,
+					DisplayName:create_request.DisplayName.as_str(),
+					Creator:create_request.Creator.as_str(),
+					GameID:create_request.GameID,
+					AssetID:create_info.ModelID,
+					AssetVersion:create_request.AssetVersion,
+					TargetAssetID:create_info.TargetAssetID,
+				}).await.map_err(Error::ApiActionMapfixCreate)?;
+			},
+			Err(e)=>{
+				println!("oh no! {e}");
+			},
 		}
 
-		let asset_creator_id=first_version.creatorTargetId;
-		let asset_version=first_version.assetVersionNumber;
-
-		// download the map model version
-		let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
-			asset_id:create_info.ModelID,
-			version:Some(asset_version),
-		}).await.map_err(Error::ModelFileDownload)?;
-
-		// decode dom (slow!)
-		let dom=read_dom(&mut std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
-
-		// parse create fields out of asset
-		let MapInfo{
-			display_name,
-			creator,
-			game_id,
-		}=get_mapinfo(&dom).map_err(Error::GetMapInfo)?;
-
-		let game_id=game_id.map_err(Error::ParseGameID)?;
-
-		// call create on api
-		self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{
-			OperationID:create_info.OperationID,
-			AssetOwner:asset_creator_id as i64,
-			DisplayName:display_name.unwrap_or_default(),
-			Creator:creator.unwrap_or_default(),
-			GameID:game_id as i32,
-			AssetID:create_info.ModelID,
-			AssetVersion:asset_version,
-			TargetAssetID:create_info.TargetAssetID,
-		}).await.map_err(Error::ApiActionMapfixCreate)?;
-
 		Ok(())
 	}
 }
diff --git a/validation/src/create_submission.rs b/validation/src/create_submission.rs
index 141338d..a5f57cf 100644
--- a/validation/src/create_submission.rs
+++ b/validation/src/create_submission.rs
@@ -1,16 +1,9 @@
 use crate::nats_types::CreateSubmissionRequest;
-use crate::rbx_util::{MapInfo,get_mapinfo,read_dom,ReadDomError,GetMapInfoError,ParseGameIDError};
+use crate::create::CreateRequest;
 
 #[allow(dead_code)]
 #[derive(Debug)]
 pub enum Error{
-	ModelVersionsPage(rbx_asset::cookie::PageError),
-	EmptyVersionsPage,
-	WrongCreatorType,
-	ModelFileDownload(rbx_asset::cookie::GetError),
-	ModelFileDecode(ReadDomError),
-	GetMapInfo(GetMapInfoError),
-	ParseGameID(ParseGameIDError),
 	ApiActionSubmissionCreate(submissions_api::Error),
 }
 impl std::fmt::Display for Error{
@@ -22,51 +15,28 @@ impl std::error::Error for Error{}
 
 impl crate::message_handler::MessageHandler{
 	pub async fn create_submission(&self,create_info:CreateSubmissionRequest)->Result<(),Error>{
-		// discover the latest asset version
-		let asset_versions_page=self.cookie_context.get_asset_versions_page(rbx_asset::cookie::AssetVersionsPageRequest{
-			asset_id:create_info.ModelID,
-			cursor:None
-		}).await.map_err(Error::ModelVersionsPage)?;
+		let create_result=self.create_inner(CreateRequest{
+			ModelID:create_info.ModelID,
+		}).await;
 
-		// grab version info
-		let first_version=asset_versions_page.data.first().ok_or(Error::EmptyVersionsPage)?;
-
-		if first_version.creatorType!="User"{
-			return Err(Error::WrongCreatorType);
+		match create_result{
+			Ok(create_request)=>{
+				// call create on api
+				self.api.create_submission(submissions_api::types::CreateSubmissionRequest{
+					OperationID:create_info.OperationID,
+					AssetOwner:create_request.AssetOwner,
+					DisplayName:create_request.DisplayName.as_str(),
+					Creator:create_request.Creator.as_str(),
+					GameID:create_request.GameID,
+					AssetID:create_info.ModelID,
+					AssetVersion:create_request.AssetVersion,
+				}).await.map_err(Error::ApiActionSubmissionCreate)?;
+			},
+			Err(e)=>{
+				println!("oh no! {e}");
+			},
 		}
 
-		let asset_creator_id=first_version.creatorTargetId;
-		let asset_version=first_version.assetVersionNumber;
-
-		// download the map model version
-		let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
-			asset_id:create_info.ModelID,
-			version:Some(asset_version),
-		}).await.map_err(Error::ModelFileDownload)?;
-
-		// decode dom (slow!)
-		let dom=read_dom(&mut std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?;
-
-		// parse create fields out of asset
-		let MapInfo{
-			display_name,
-			creator,
-			game_id,
-		}=get_mapinfo(&dom).map_err(Error::GetMapInfo)?;
-
-		let game_id=game_id.map_err(Error::ParseGameID)?;
-
-		// call create on api
-		self.api.create_submission(submissions_api::types::CreateSubmissionRequest{
-			OperationID:create_info.OperationID,
-			AssetOwner:asset_creator_id as i64,
-			DisplayName:display_name.unwrap_or_default(),
-			Creator:creator.unwrap_or_default(),
-			GameID:game_id as i32,
-			AssetID:create_info.ModelID,
-			AssetVersion:asset_version,
-		}).await.map_err(Error::ApiActionSubmissionCreate)?;
-
 		Ok(())
 	}
 }
diff --git a/validation/src/main.rs b/validation/src/main.rs
index e6fdc41..30b443d 100644
--- a/validation/src/main.rs
+++ b/validation/src/main.rs
@@ -4,6 +4,7 @@ mod rbx_util;
 mod message_handler;
 mod nats_types;
 mod types;
+mod create;
 mod create_mapfix;
 mod create_submission;
 mod upload_mapfix;
diff --git a/validation/src/message_handler.rs b/validation/src/message_handler.rs
index bff0f10..b106f75 100644
--- a/validation/src/message_handler.rs
+++ b/validation/src/message_handler.rs
@@ -19,6 +19,8 @@ impl std::fmt::Display for HandleMessageError{
 }
 impl std::error::Error for HandleMessageError{}
 
+// TODO: update operation on failure
+
 pub type MessageResult=Result<async_nats::jetstream::Message,async_nats::jetstream::consumer::pull::MessagesError>;
 
 fn from_slice<'a,T:serde::de::Deserialize<'a>>(slice:&'a [u8])->Result<T,HandleMessageError>{