From 2dddda685a34775ebafc34c50b8fdaf1055afabb Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Wed, 18 Sep 2024 18:47:38 -0700
Subject: [PATCH] must own (rework api)

---
 src/context.rs         | 11 +++---
 src/lib.rs             |  2 +-
 src/runner/instance.rs | 79 ++++++++++++++++++++++++++++++++++++++++++
 src/runner/mod.rs      |  1 +
 src/runner/runner.rs   | 48 +++++++++++++++++--------
 src/script.rs          | 34 +++++-------------
 6 files changed, 130 insertions(+), 45 deletions(-)
 create mode 100644 src/runner/instance.rs

diff --git a/src/context.rs b/src/context.rs
index 47957bc..cc5f4e0 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -10,14 +10,17 @@ pub fn class_is_a(class:&str,superclass:&str)->bool{
 	)
 }
 
-pub struct Context<'a>{
-	pub(crate)dom:&'a mut WeakDom,
+pub struct Context{
+	pub(crate)dom:WeakDom,
 }
 
-impl Context<'_>{
-	pub fn new(dom:&mut WeakDom)->Context<'_>{
+impl Context{
+	pub fn new(dom:WeakDom)->Context{
 		Context{dom}
 	}
+	pub fn into_inner(self)->WeakDom{
+		self.dom
+	}
 	/// Creates an iterator over all items of a particular class.
 	pub fn superclass_iter<'a>(&'a self,superclass:&'a str)->impl Iterator<Item=Ref>+'a{
 		self.dom.descendants().filter(|&instance|
diff --git a/src/lib.rs b/src/lib.rs
index fb0dd5f..8c34547 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,7 +1,7 @@
 pub mod script;
 pub mod context;
 
-mod runner;
+pub mod runner;
 
 pub type Result<T>=core::result::Result<T,script::Error>;
 
diff --git a/src/runner/instance.rs b/src/runner/instance.rs
new file mode 100644
index 0000000..f8103a4
--- /dev/null
+++ b/src/runner/instance.rs
@@ -0,0 +1,79 @@
+use rbx_dom_weak::WeakDom;
+
+use super::vector3::Vector3;
+
+pub struct Instance{
+	referent:rbx_types::Ref,
+}
+
+impl From<crate::script::Script> for Instance{
+	fn from(value:crate::script::Script)->Self{
+		Self{referent:value.script}
+	}
+}
+
+impl Instance{
+	pub const fn new(referent:rbx_types::Ref)->Self{
+		Self{referent}
+	}
+	pub fn get<'a>(&'a self,dom:&'a WeakDom)->mlua::Result<&'a rbx_dom_weak::Instance>{
+		dom.get_by_ref(self.referent).ok_or(mlua::Error::runtime("Instance missing"))
+	}
+	pub fn get_mut<'a>(&'a self,dom:&'a mut WeakDom)->mlua::Result<&'a mut rbx_dom_weak::Instance>{
+		dom.get_by_ref_mut(self.referent).ok_or(mlua::Error::runtime("Instance missing"))
+	}
+}
+
+// LMAO look at this function!
+fn dom<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result<T>)->mlua::Result<T>{
+	f(&mut *lua.app_data_mut::<WeakDom>().ok_or(mlua::Error::runtime("DataModel missing"))?)
+}
+
+impl mlua::UserData for Instance{
+	fn add_fields<'lua,F:mlua::UserDataFields<'lua,Self>>(fields:&mut F){
+		fields.add_field_method_get("Parent",|lua,this|{
+			dom(lua,|dom|{
+				let instance=this.get(dom)?;
+				Ok(Instance::new(instance.parent()))
+			})
+		});
+		fields.add_field_method_set("Parent",|lua,this,val:Self|{
+			dom(lua,|dom|{
+				dom.transfer_within(this.referent,val.referent);
+				Ok(())
+			})
+		});
+	}
+
+	fn add_methods<'lua,M:mlua::UserDataMethods<'lua,Self>>(methods:&mut M){
+		methods.add_meta_function(mlua::MetaMethod::NewIndex,|lua,(this,index,value):(Self,mlua::String,mlua::Value)|
+			dom(lua,|dom|{
+				let instance=this.get_mut(dom)?;
+				let index_str=index.to_str()?;
+				let class=rbx_reflection_database::get().classes.get(instance.class.as_str()).ok_or(mlua::Error::runtime("Class missing"))?;
+				let property=class.default_properties.get(index_str).ok_or(mlua::Error::runtime(format!("Property '{index_str}' missing on class '{}'",class.name)))?;
+				match property{
+					rbx_types::Variant::Vector3(_)=>{
+						let typed_value:Vector3=value.as_userdata().ok_or(mlua::Error::runtime("Expected Userdata"))?.take()?;
+						instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Vector3(typed_value.into()));
+					},
+					rbx_types::Variant::Float64(_)=>{
+						let typed_value:f64=value.as_f64().ok_or(mlua::Error::runtime("Expected f64"))?;
+						instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Float64(typed_value));
+					},
+					_=>unimplemented!(),
+				}
+				Ok(())
+			})
+		);
+	}
+}
+
+impl<'lua> mlua::FromLua<'lua> for Instance{
+	fn from_lua(value:mlua::prelude::LuaValue<'lua>,_lua:&'lua mlua::prelude::Lua)->mlua::prelude::LuaResult<Self>{
+		match value{
+			mlua::Value::UserData(ud)=>ud.take(),
+			other=>Err(mlua::Error::runtime(format!("Expected Instance got {:?}",other))),
+		}
+	}
+}
diff --git a/src/runner/mod.rs b/src/runner/mod.rs
index 905b778..a00b1cf 100644
--- a/src/runner/mod.rs
+++ b/src/runner/mod.rs
@@ -2,5 +2,6 @@ mod runner;
 
 mod cframe;
 mod vector3;
+mod instance;
 
 pub use runner::Runner;
diff --git a/src/runner/runner.rs b/src/runner/runner.rs
index 4297b71..0d68d5e 100644
--- a/src/runner/runner.rs
+++ b/src/runner/runner.rs
@@ -1,9 +1,28 @@
+use crate::context::Context;
+
 use super::vector3::Vector3;
 use super::cframe::CFrame;
 
 pub struct Runner{
 	lua:mlua::Lua,
 }
+#[derive(Debug)]
+pub enum Error{
+	Lua(mlua::Error),
+	Script(crate::script::Error),
+	/// If the lua.remove_app_data function fails
+	RemoveAppData,
+}
+impl Error{
+	pub fn print(self){
+		match self{
+			Self::Lua(mlua::Error::RuntimeError(s))=>{
+				println!("lua error: {s}");
+			},
+			other=>println!("{:?}",other),
+		}
+	}
+}
 
 fn init(lua:&mlua::Lua)->mlua::Result<()>{
 	lua.sandbox(true)?;
@@ -57,20 +76,19 @@ impl Runner{
 		init(&runner.lua)?;
 		Ok(runner)
 	}
-	pub fn set_script(&self,script:rbx_dom_weak::types::Ref,name:String)->mlua::Result<()>{
-		//TODO: Instance type
-		let script_table=self.lua.create_table()?;
-		//script.Name
-		script_table.raw_set("Name",name)?;
-		self.lua.globals().set("script",script_table)?;
-		Ok(())
-	}
-	pub fn run(&self,source:String,context:&mut crate::context::Context)->mlua::Result<()>{
-		//Set up dom access here?
-		let script:mlua::Table<'_>=self.lua.globals().raw_get("script")?;
-		let name:String=script.raw_get("Name")?;
-		self.lua.load(source)
-		.set_name(name)
-		.exec()
+	pub fn run_script(&self,script:crate::script::Script,context:Context)->(Context,Result<(),Error>){
+		let yoink=script.name_source(&context);
+		self.lua.set_app_data(context.dom);
+		let r=(||{
+			let (name,source)=yoink.map_err(Error::Script)?;
+				self.lua.globals().set("script",super::instance::Instance::from(script)).map_err(Error::Lua)?;
+			self.lua.load(source)
+			.set_name(name)
+			.exec().map_err(Error::Lua)?;
+			Ok(())
+		})();
+		let dom=self.lua.remove_app_data()
+			.expect("Fatal: Lua lost the dom");
+		(Context::new(dom),r)
 	}
 }
diff --git a/src/script.rs b/src/script.rs
index f2cb1a7..8a81941 100644
--- a/src/script.rs
+++ b/src/script.rs
@@ -1,21 +1,12 @@
 use rbx_dom_weak::types::Ref;
 
+use crate::context::Context;
+
 #[derive(Debug)]
 pub enum Error{
-	Runner(mlua::Error),
 	NoScript,
 	NoSource,
 }
-impl Error{
-	pub fn print(self){
-		match self{
-			Self::Runner(mlua::Error::RuntimeError(s))=>{
-				println!("lua error: {s}");
-			},
-			other=>println!("{:?}",other),
-		}
-	}
-}
 
 fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{
 	let mut full_name=instance.name.clone();
@@ -29,25 +20,18 @@ fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->S
 }
 
 pub struct Script{
-	script:Ref,
+	pub(crate)script:Ref,
 }
 impl Script{
 	pub const fn new(script:Ref)->Self{
 		Self{script}
 	}
-	pub fn run(&self,context:&mut crate::context::Context)->Result<(),Error>{
-		// grab source
-		let (source,full_name)={
-			let instance=context.dom.get_by_ref(self.script).ok_or(Error::NoScript)?;
-			let source=match instance.properties.get("Source").ok_or(Error::NoSource)?{
-				rbx_dom_weak::types::Variant::String(s)=>s.clone(),
-				_=>Err(Error::NoSource)?,
-			};
-			(source,get_full_name(&context.dom,instance))
+	pub fn name_source(&self,context:&Context)->Result<(String,String),Error>{
+		let instance=context.dom.get_by_ref(self.script).ok_or(Error::NoScript)?;
+		let source=match instance.properties.get("Source").ok_or(Error::NoSource)?{
+			rbx_dom_weak::types::Variant::String(s)=>s.clone(),
+			_=>Err(Error::NoSource)?,
 		};
-		// run it lole
-		let runner=crate::runner::Runner::new().map_err(Error::Runner)?;
-		runner.set_script(self.script,full_name).map_err(Error::Runner)?;
-		runner.run(source,context).map_err(Error::Runner)
+		Ok((get_full_name(&context.dom,instance),source))
 	}
 }