From acac3114a85fc3d0b23c299e6b6bbf17f26e4df7 Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Tue, 23 Jan 2024 18:43:23 -0800
Subject: [PATCH] support Script.module.lua properly

---
 src/main.rs | 155 +++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 117 insertions(+), 38 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 47b6c15..4222c7b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1120,10 +1120,10 @@ async fn get_file_async(mut path:std::path::PathBuf,file_name:impl AsRef<std::pa
 		},
 	}
 }
-type QueryResult=Result<tokio::fs::File,QueryResolveError>;
-type QueryResultHandle=tokio::task::JoinHandle<QueryResult>;
+type QueryHintResult=Result<FileHint,QueryResolveError>;
+type QueryResultHandle=tokio::task::JoinHandle<Result<tokio::fs::File,QueryResolveError>>;
 trait Query{
-	async fn resolve(self)->Result<tokio::fs::File,QueryResolveError>;
+	async fn resolve(self)->QueryHintResult;
 }
 struct QuerySingle(QueryResultHandle);
 impl QuerySingle{
@@ -1132,20 +1132,20 @@ impl QuerySingle{
 	}
 }
 impl Query for QuerySingle{
-	async fn resolve(self)->Result<tokio::fs::File,QueryResolveError>{
+	async fn resolve(self)->QueryHintResult{
 		match self.0.await{
-			Ok(Ok(file))=>Ok(file),
+			Ok(Ok(file))=>Ok(FileHint{file,hint:ScriptHint::Undetermined}),
 			Ok(Err(e))=>Err(e),
 			Err(e)=>Err(QueryResolveError::JoinError(e)),
 		}
 	}
 }
-struct QueryTriplet{
+struct QueryTriple{
 	module:QuerySingle,
 	server:QuerySingle,
 	client:QuerySingle,
 }
-impl QueryTriplet{
+impl QueryTriple{
 	fn rox_rojo(search_path:&std::path::PathBuf,search_name:&str,search_module:bool)->Self{
 		//this should be implemented as constructors of Triplet and Quadruplet to fully support Trey's suggestion
 		let module_name=if search_module{
@@ -1159,15 +1159,28 @@ impl QueryTriplet{
 			client:QuerySingle(tokio::spawn(get_file_async(search_path.clone(),format!("{}.client.lua",search_name)))),
 		}
 	}
-	fn rojo(search_path:&std::path::PathBuf,search_name:&str,search_module:bool,is_subfolder:bool)->Self{
-		if is_subfolder{
-			QueryTriplet::rox_rojo(search_path,"init",search_module)
-		}else{
-			QueryTriplet::rox_rojo(search_path,search_name,search_module)
-		}
+	fn rojo(search_path:&std::path::PathBuf,search_name:&str)->Self{
+		QueryTriple::rox_rojo(search_path,"init",false)
 	}
 }
-fn mega_triple_join(query_triplet:(QueryResult,QueryResult,QueryResult))->QueryResult{
+//these functions can be achieved with macros, but I have not learned that yet
+fn mega_double_join(query_pair:(QueryHintResult,QueryHintResult))->QueryHintResult{
+	match query_pair{
+		//unambiguously locate file
+		(Ok(f),Err(QueryResolveError::NotFound))
+		|(Err(QueryResolveError::NotFound),Ok(f))=>Ok(f),
+		//multiple files located
+		(Ok(_),Err(QueryResolveError::NotFound))
+		|(Err(QueryResolveError::NotFound),Ok(_))
+		|(Ok(_),Ok(_))=>Err(QueryResolveError::Ambiguous),
+		//no files located
+		(Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound))=>Err(QueryResolveError::NotFound),
+		//other error
+		(Err(e),_)
+		|(_,Err(e))=>Err(e),
+	}
+}
+fn mega_triple_join(query_triplet:(QueryHintResult,QueryHintResult,QueryHintResult))->QueryHintResult{
 	match query_triplet{
 		//unambiguously locate file
 		(Ok(f),Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound))
@@ -1183,16 +1196,73 @@ fn mega_triple_join(query_triplet:(QueryResult,QueryResult,QueryResult))->QueryR
 		//other error
 		(Err(e),_,_)
 		|(_,Err(e),_)
-		|(_,_,Err(e))=>Err(e)
+		|(_,_,Err(e))=>Err(e),
 	}
 }
-impl Query for QueryTriplet{
-	async fn resolve(self)->Result<tokio::fs::File,QueryResolveError>{
+//LETS GOOOOOOOOOOOOOOOO
+fn mega_quadruple_join(query_quad:(QueryHintResult,QueryHintResult,QueryHintResult,QueryHintResult))->QueryHintResult{
+	match query_quad{
+		//unambiguously locate file
+		(Ok(f),Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound))
+		|(Err(QueryResolveError::NotFound),Ok(f),Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound))
+		|(Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound),Ok(f),Err(QueryResolveError::NotFound))
+		|(Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound),Ok(f))=>Ok(f),
+		//multiple files located
+		(Ok(_),Ok(_),Ok(_),Err(QueryResolveError::NotFound))
+		|(Ok(_),Ok(_),Err(QueryResolveError::NotFound),Ok(_))
+		|(Ok(_),Ok(_),Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound))
+		|(Ok(_),Err(QueryResolveError::NotFound),Ok(_),Ok(_))
+		|(Ok(_),Err(QueryResolveError::NotFound),Ok(_),Err(QueryResolveError::NotFound))
+		|(Ok(_),Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound),Ok(_))
+		|(Err(QueryResolveError::NotFound),Ok(_),Ok(_),Ok(_))
+		|(Err(QueryResolveError::NotFound),Ok(_),Ok(_),Err(QueryResolveError::NotFound))
+		|(Err(QueryResolveError::NotFound),Ok(_),Err(QueryResolveError::NotFound),Ok(_))
+		|(Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound),Ok(_),Ok(_))
+		|(Ok(_),Ok(_),Ok(_),Ok(_))=>Err(QueryResolveError::Ambiguous),
+		//no files located
+		(Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound),Err(QueryResolveError::NotFound))=>Err(QueryResolveError::NotFound),
+		//other error
+		(Err(e),_,_,_)
+		|(_,Err(e),_,_)
+		|(_,_,Err(e),_)
+		|(_,_,_,Err(e))=>Err(e),
+	}
+}
+impl Query for QueryTriple{
+	async fn resolve(self)->QueryHintResult{
 		let (module,server,client)=tokio::join!(self.module.0,self.server.0,self.client.0);
 		mega_triple_join((
-			module.map_err(|e|QueryResolveError::JoinError(e))?,
-			server.map_err(|e|QueryResolveError::JoinError(e))?,
-			client.map_err(|e|QueryResolveError::JoinError(e))?,
+			module.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::ModuleScript}),
+			server.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::Script}),
+			client.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::LocalScript}),
+		))
+	}
+}
+struct QueryQuad{
+	module_implicit:QuerySingle,
+	module_explicit:QuerySingle,
+	server:QuerySingle,
+	client:QuerySingle,
+}
+impl QueryQuad{
+	fn rox_rojo(search_path:&std::path::PathBuf,search_name:&str)->Self{
+		let fill=QueryTriple::rox_rojo(search_path,search_name,true);
+		Self{
+			module_implicit:QuerySingle::rox(search_path,search_name),//Script.lua
+			module_explicit:fill.module,//Script.module.lua
+			server:fill.server,
+			client:fill.client,
+		}
+	}
+}
+impl Query for QueryQuad{
+	async fn resolve(self)->QueryHintResult{
+		let (module_implicit,module_explicit,server,client)=tokio::join!(self.module_implicit.0,self.module_explicit.0,self.server.0,self.client.0);
+		mega_quadruple_join((
+			module_implicit.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::ModuleScript}),
+			module_explicit.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::ModuleScript}),
+			server.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::Script}),
+			client.map_err(|e|QueryResolveError::JoinError(e))?.map(|file|FileHint{file,hint:ScriptHint::LocalScript}),
 		))
 	}
 }
@@ -1203,25 +1273,23 @@ async fn discern_node(search_path:&std::path::PathBuf,search_name:&str,style:Opt
 	//folder
 	Ok(if let Ok(dir)=tokio::fs::read_dir(contents_folder.as_path()).await{
 		//scan inside the folder for an object to define the class of the folder
-		let (script_file,model_file)=tokio::join!(
-			async {match style{
-				Some(DecompileStyle::Rox)=>QuerySingle::rox(&contents_folder,search_name).resolve().await,
-				Some(DecompileStyle::RoxRojo)=>QueryTriplet::rox_rojo(&contents_folder,search_name,false).resolve().await,
-				Some(DecompileStyle::Rojo)=>QueryTriplet::rojo(&contents_folder,search_name,false,true).resolve().await,
-				//try all three and complain if there is ambiguity
-				None=>mega_triple_join(tokio::join!(
-					QuerySingle::rox(&contents_folder,search_name).resolve(),
-					//true=search for module here to avoid ambiguity with QuerySingle::rox results
-					QueryTriplet::rox_rojo(&contents_folder,search_name,true).resolve(),
-					QueryTriplet::rojo(&contents_folder,search_name,true,true).resolve(),
-				))
-			}},
-			//model files are rox & rox-rojo only, so it's a lot less work...
-			get_file_async(contents_folder.clone(),format!("{}.rbxmx",search_name))
-		);
+		let script_query=async {match style{
+			Some(DecompileStyle::Rox)=>QuerySingle::rox(&contents_folder,search_name).resolve().await,
+			Some(DecompileStyle::RoxRojo)=>QueryQuad::rox_rojo(&contents_folder,search_name).resolve().await,
+			Some(DecompileStyle::Rojo)=>QueryTriple::rojo(&contents_folder,search_name).resolve().await,
+			//try all three and complain if there is ambiguity
+			None=>mega_triple_join(tokio::join!(
+				QuerySingle::rox(&contents_folder,search_name).resolve(),
+				//true=search for module here to avoid ambiguity with QuerySingle::rox results
+				QueryTriple::rox_rojo(&contents_folder,search_name,true).resolve(),
+				QueryTriple::rojo(&contents_folder,search_name).resolve(),
+			))
+		}};
+		//model files are rox & rox-rojo only, so it's a lot less work...
+		let model_query=get_file_async(contents_folder.clone(),format!("{}.rbxmx",search_name));
 		//model? script? both?
-		Some(match (script_file,model_file){
-			(Ok(mut file),Err(QueryResolveError::NotFound))=>{
+		Some(match tokio::join!(script_query,model_query){
+			(Ok(FileHint{mut file,hint}),Err(QueryResolveError::NotFound))=>{
 				//read entire file
 				let mut buf=String::new();
 				file.read_to_string(&mut buf).await?;
@@ -1259,6 +1327,17 @@ async fn discern_node(search_path:&std::path::PathBuf,search_name:&str,style:Opt
 	})
 }
 
+enum ScriptHint{
+	Script,
+	LocalScript,
+	ModuleScript,
+	Undetermined,
+}
+struct FileHint{
+	file:tokio::fs::File,
+	hint:ScriptHint,
+}
+
 enum CompileClass{
 	Folder,
 	Script(String),