From 0f9caf5461c52bafa81555fcf395e7f8c4c5b572 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sat, 11 Mar 2023 16:39:23 +0100 Subject: [PATCH] Roblox datatypes refactor & organization --- .../lib-roblox/src/datatypes/conversion.rs | 190 ++++++++++ .../lib-roblox/src/datatypes/extension.rs | 52 +++ packages/lib-roblox/src/datatypes/mod.rs | 336 +----------------- packages/lib-roblox/src/datatypes/result.rs | 71 ++++ packages/lib-roblox/src/datatypes/shared.rs | 23 ++ .../lib-roblox/src/datatypes/types/mod.rs | 9 + .../src/datatypes/{ => types}/vector2.rs | 59 +-- .../src/datatypes/{ => types}/vector2int16.rs | 47 +-- .../src/datatypes/{ => types}/vector3.rs | 65 ++-- .../src/datatypes/{ => types}/vector3int16.rs | 49 +-- packages/lib-roblox/src/lib.rs | 9 +- 11 files changed, 470 insertions(+), 440 deletions(-) create mode 100644 packages/lib-roblox/src/datatypes/conversion.rs create mode 100644 packages/lib-roblox/src/datatypes/extension.rs create mode 100644 packages/lib-roblox/src/datatypes/result.rs create mode 100644 packages/lib-roblox/src/datatypes/shared.rs create mode 100644 packages/lib-roblox/src/datatypes/types/mod.rs rename packages/lib-roblox/src/datatypes/{ => types}/vector2.rs (88%) rename packages/lib-roblox/src/datatypes/{ => types}/vector2int16.rs (86%) rename packages/lib-roblox/src/datatypes/{ => types}/vector3.rs (90%) rename packages/lib-roblox/src/datatypes/{ => types}/vector3int16.rs (87%) diff --git a/packages/lib-roblox/src/datatypes/conversion.rs b/packages/lib-roblox/src/datatypes/conversion.rs new file mode 100644 index 0000000..d8946f7 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/conversion.rs @@ -0,0 +1,190 @@ +use mlua::prelude::*; + +use rbx_dom_weak::types::{Variant as RbxVariant, VariantType as RbxVariantType}; + +use crate::datatypes::extension::RbxVariantExt; + +use super::*; + +pub(crate) trait ToRbxVariant { + fn to_rbx_variant( + &self, + desired_type: Option, + ) -> DatatypeConversionResult; +} + +pub(crate) trait FromRbxVariant: Sized { + fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult; +} + +pub(crate) trait FromRbxVariantLua<'lua>: Sized { + fn from_rbx_variant_lua(variant: &RbxVariant, lua: &'lua Lua) + -> DatatypeConversionResult; +} + +/* + + Blanket trait implementations for converting between LuaValue and rbx_dom Variant values + + These should be considered stable and one, already containing all of the primitive types + + See bottom of module for implementations between our custom datatypes and lua userdata + +*/ + +impl<'lua> FromRbxVariantLua<'lua> for LuaValue<'lua> { + fn from_rbx_variant_lua( + variant: &RbxVariant, + lua: &'lua Lua, + ) -> DatatypeConversionResult { + use base64::engine::general_purpose::STANDARD_NO_PAD; + use base64::engine::Engine as _; + use RbxVariant as Rbx; + + match LuaAnyUserData::from_rbx_variant_lua(variant, lua) { + Ok(value) => Ok(LuaValue::UserData(value)), + Err(e) => match variant { + Rbx::Bool(b) => Ok(LuaValue::Boolean(*b)), + Rbx::Int64(i) => Ok(LuaValue::Number(*i as f64)), + Rbx::Int32(i) => Ok(LuaValue::Number(*i as f64)), + Rbx::Float64(n) => Ok(LuaValue::Number(*n)), + Rbx::Float32(n) => Ok(LuaValue::Number(*n as f64)), + Rbx::String(s) => Ok(LuaValue::String(lua.create_string(s)?)), + Rbx::Content(s) => Ok(LuaValue::String( + lua.create_string(AsRef::::as_ref(s))?, + )), + Rbx::BinaryString(s) => { + let encoded = STANDARD_NO_PAD.encode(AsRef::<[u8]>::as_ref(s)); + Ok(LuaValue::String(lua.create_string(&encoded)?)) + } + _ => Err(e), + }, + } + } +} + +impl<'lua> ToRbxVariant for LuaValue<'lua> { + fn to_rbx_variant( + &self, + desired_type: Option, + ) -> DatatypeConversionResult { + use base64::engine::general_purpose::STANDARD_NO_PAD; + use base64::engine::Engine as _; + use RbxVariantType as Rbx; + + if let Some(desired_type) = desired_type { + match (self, desired_type) { + (LuaValue::Boolean(b), Rbx::Bool) => Ok(RbxVariant::Bool(*b)), + (LuaValue::Integer(i), Rbx::Int64) => Ok(RbxVariant::Int64(*i as i64)), + (LuaValue::Integer(i), Rbx::Int32) => Ok(RbxVariant::Int32(*i)), + (LuaValue::Integer(i), Rbx::Float64) => Ok(RbxVariant::Float64(*i as f64)), + (LuaValue::Integer(i), Rbx::Float32) => Ok(RbxVariant::Float32(*i as f32)), + (LuaValue::Number(n), Rbx::Int64) => Ok(RbxVariant::Int64(*n as i64)), + (LuaValue::Number(n), Rbx::Int32) => Ok(RbxVariant::Int32(*n as i32)), + (LuaValue::Number(n), Rbx::Float64) => Ok(RbxVariant::Float64(*n)), + (LuaValue::Number(n), Rbx::Float32) => Ok(RbxVariant::Float32(*n as f32)), + (LuaValue::String(s), Rbx::String) => { + Ok(RbxVariant::String(s.to_str()?.to_string())) + } + (LuaValue::String(s), Rbx::Content) => { + Ok(RbxVariant::Content(s.to_str()?.to_string().into())) + } + (LuaValue::String(s), Rbx::BinaryString) => { + Ok(RbxVariant::BinaryString(STANDARD_NO_PAD.decode(s)?.into())) + } + (LuaValue::UserData(u), d) => u.to_rbx_variant(Some(d)), + (v, d) => Err(DatatypeConversionError::ToRbxVariant { + to: d.variant_name(), + from: v.type_name(), + detail: None, + }), + } + } else { + match self { + // Primitives + LuaValue::Boolean(b) => Ok(RbxVariant::Bool(*b)), + LuaValue::Integer(i) => Ok(RbxVariant::Int32(*i)), + LuaValue::Number(n) => Ok(RbxVariant::Float64(*n)), + LuaValue::String(s) => Ok(RbxVariant::String(s.to_str()?.to_string())), + LuaValue::UserData(u) => u.to_rbx_variant(None), + v => Err(DatatypeConversionError::ToRbxVariant { + to: "Variant", + from: v.type_name(), + detail: None, + }), + } + } + } +} + +/* + + Trait implementations for converting between all of + our custom datatypes and generic Lua userdata values + + NOTE: When adding a new datatype, make sure to add it below to _both_ + of the traits and not just one to allow for bidirectional conversion + +*/ + +impl<'lua> FromRbxVariantLua<'lua> for LuaAnyUserData<'lua> { + #[rustfmt::skip] + fn from_rbx_variant_lua(variant: &RbxVariant, lua: &'lua Lua) -> DatatypeConversionResult { + use RbxVariant as Rbx; + use super::types::*; + match variant { + Rbx::Vector2(_) => Ok(lua.create_userdata(Vector2::from_rbx_variant(variant)?)?), + Rbx::Vector2int16(_) => Ok(lua.create_userdata(Vector2int16::from_rbx_variant(variant)?)?), + Rbx::Vector3(_) => Ok(lua.create_userdata(Vector3::from_rbx_variant(variant)?)?), + Rbx::Vector3int16(_) => Ok(lua.create_userdata(Vector3int16::from_rbx_variant(variant)?)?), + // Not yet implemented datatypes + // Rbx::Axes(_) => todo!(), + // Rbx::BrickColor(_) => todo!(), + // Rbx::CFrame(_) => todo!(), + // Rbx::Color3(_) => todo!(), + // Rbx::Color3uint8(_) => todo!(), + // Rbx::ColorSequence(_) => todo!(), + // Rbx::Enum(_) => todo!(), + // Rbx::Faces(_) => todo!(), + // Rbx::NumberRange(_) => todo!(), + // Rbx::NumberSequence(_) => todo!(), + // Rbx::OptionalCFrame(_) => todo!(), + // Rbx::PhysicalProperties(_) => todo!(), + // Rbx::Ray(_) => todo!(), + // Rbx::Rect(_) => todo!(), + // Rbx::Region3(_) => todo!(), + // Rbx::Region3int16(_) => todo!(), + // Rbx::UDim(_) => todo!(), + // Rbx::UDim2(_) => todo!(), + v => Err(DatatypeConversionError::FromRbxVariant { + from: v.variant_name(), + to: "LuaValue", + detail: Some("Type not supported".to_string()), + }), + } + } +} + +impl<'lua> ToRbxVariant for LuaAnyUserData<'lua> { + fn to_rbx_variant( + &self, + desired_type: Option, + ) -> DatatypeConversionResult { + use super::types::*; + if let Ok(v2) = self.borrow::() { + v2.to_rbx_variant(desired_type) + } else if let Ok(v2i) = self.borrow::() { + v2i.to_rbx_variant(desired_type) + } else if let Ok(v3) = self.borrow::() { + v3.to_rbx_variant(desired_type) + } else if let Ok(v3i) = self.borrow::() { + v3i.to_rbx_variant(desired_type) + } else { + Err(DatatypeConversionError::ToRbxVariant { + to: desired_type.map(|d| d.variant_name()).unwrap_or("Variant"), + from: "userdata", + detail: None, + }) + } + } +} diff --git a/packages/lib-roblox/src/datatypes/extension.rs b/packages/lib-roblox/src/datatypes/extension.rs new file mode 100644 index 0000000..c2f16e0 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/extension.rs @@ -0,0 +1,52 @@ +use super::*; + +pub(super) trait RbxVariantExt { + fn variant_name(&self) -> &'static str; +} + +impl RbxVariantExt for RbxVariantType { + fn variant_name(&self) -> &'static str { + use RbxVariantType::*; + match self { + Axes => "Axes", + BinaryString => "BinaryString", + Bool => "Bool", + BrickColor => "BrickColor", + CFrame => "CFrame", + Color3 => "Color3", + Color3uint8 => "Color3uint8", + ColorSequence => "ColorSequence", + Content => "Content", + Enum => "Enum", + Faces => "Faces", + Float32 => "Float32", + Float64 => "Float64", + Int32 => "Int32", + Int64 => "Int64", + NumberRange => "NumberRange", + NumberSequence => "NumberSequence", + PhysicalProperties => "PhysicalProperties", + Ray => "Ray", + Rect => "Rect", + Ref => "Ref", + Region3 => "Region3", + Region3int16 => "Region3int16", + SharedString => "SharedString", + String => "String", + UDim => "UDim", + UDim2 => "UDim2", + Vector2 => "Vector2", + Vector2int16 => "Vector2int16", + Vector3 => "Vector3", + Vector3int16 => "Vector3int16", + OptionalCFrame => "OptionalCFrame", + _ => "?", + } + } +} + +impl RbxVariantExt for RbxVariant { + fn variant_name(&self) -> &'static str { + self.ty().variant_name() + } +} diff --git a/packages/lib-roblox/src/datatypes/mod.rs b/packages/lib-roblox/src/datatypes/mod.rs index 657c936..6e64efe 100644 --- a/packages/lib-roblox/src/datatypes/mod.rs +++ b/packages/lib-roblox/src/datatypes/mod.rs @@ -1,331 +1,13 @@ -use mlua::prelude::*; - pub(crate) use rbx_dom_weak::types::{Variant as RbxVariant, VariantType as RbxVariantType}; -// NOTE: We create a new inner module scope here to make imports of datatypes more ergonomic +mod conversion; +mod extension; +mod result; +mod shared; -mod vector2; -mod vector2int16; -mod vector3; -mod vector3int16; +pub mod types; -pub mod types { - pub use super::vector2::Vector2; - pub use super::vector2int16::Vector2int16; - pub use super::vector3::Vector3; - pub use super::vector3int16::Vector3int16; -} - -// Trait definitions for conversion between rbx_dom_weak variant <-> our custom datatypes - -#[allow(dead_code)] -pub(crate) enum RbxConversionError { - FromRbxVariant { - from: &'static str, - to: &'static str, - detail: Option, - }, - ToRbxVariant { - to: &'static str, - from: &'static str, - detail: Option, - }, - DesiredTypeMismatch { - can_convert_to: Option<&'static str>, - detail: Option, - }, - External { - message: String, - }, -} - -impl RbxConversionError { - pub fn external(e: impl std::error::Error) -> Self { - RbxConversionError::External { - message: e.to_string(), - } - } -} - -pub(crate) type RbxConversionResult = Result; - -pub(crate) trait ToRbxVariant { - fn to_rbx_variant( - &self, - desired_type: Option, - ) -> RbxConversionResult; -} - -pub(crate) trait FromRbxVariant: Sized { - fn from_rbx_variant(variant: &RbxVariant) -> RbxConversionResult; -} - -pub(crate) trait DatatypeTable { - fn make_dt_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()>; -} - -// Shared impls for datatype metamethods belonging to this module - -fn datatype_impl_to_string(_: &Lua, datatype: &D, _: ()) -> LuaResult -where - D: LuaUserData + ToString + 'static, -{ - Ok(datatype.to_string()) -} - -fn datatype_impl_eq(_: &Lua, datatype: &D, value: LuaValue) -> LuaResult -where - D: LuaUserData + PartialEq + 'static, -{ - if let LuaValue::UserData(ud) = value { - if let Ok(vec) = ud.borrow::() { - Ok(*datatype == *vec) - } else { - Ok(false) - } - } else { - Ok(false) - } -} - -// NOTE: This implementation is .. not great, but it's the best we can -// do since we can't implement a trait like Display on a foreign type, -// and we are really only using it to make better error messages anyway - -trait RbxVariantDisplayName { - fn display_name(&self) -> &'static str; -} - -impl RbxVariantDisplayName for RbxVariantType { - fn display_name(&self) -> &'static str { - use RbxVariantType::*; - match self { - Axes => "Axes", - BinaryString => "BinaryString", - Bool => "Bool", - BrickColor => "BrickColor", - CFrame => "CFrame", - Color3 => "Color3", - Color3uint8 => "Color3uint8", - ColorSequence => "ColorSequence", - Content => "Content", - Enum => "Enum", - Faces => "Faces", - Float32 => "Float32", - Float64 => "Float64", - Int32 => "Int32", - Int64 => "Int64", - NumberRange => "NumberRange", - NumberSequence => "NumberSequence", - PhysicalProperties => "PhysicalProperties", - Ray => "Ray", - Rect => "Rect", - Ref => "Ref", - Region3 => "Region3", - Region3int16 => "Region3int16", - SharedString => "SharedString", - String => "String", - UDim => "UDim", - UDim2 => "UDim2", - Vector2 => "Vector2", - Vector2int16 => "Vector2int16", - Vector3 => "Vector3", - Vector3int16 => "Vector3int16", - OptionalCFrame => "OptionalCFrame", - _ => "?", - } - } -} - -impl RbxVariantDisplayName for RbxVariant { - fn display_name(&self) -> &'static str { - self.ty().display_name() - } -} - -// Generic impls for converting from lua values <-> rbx_dom_weak variants -// We use a separate trait here since creating lua stuff needs the lua context - -pub(crate) trait RbxVariantToLua<'lua>: Sized { - fn rbx_variant_to_lua(variant: &RbxVariant, lua: &'lua Lua) -> RbxConversionResult; -} - -impl<'lua> RbxVariantToLua<'lua> for LuaValue<'lua> { - fn rbx_variant_to_lua(variant: &RbxVariant, lua: &'lua Lua) -> RbxConversionResult { - use self::types::*; - use base64::engine::general_purpose::STANDARD_NO_PAD; - use base64::engine::Engine as _; - use RbxVariant as Rbx; - - match variant { - // Primitives - Rbx::Bool(b) => Ok(LuaValue::Boolean(*b)), - Rbx::Int64(i) => Ok(LuaValue::Number(*i as f64)), - Rbx::Int32(i) => Ok(LuaValue::Number(*i as f64)), - Rbx::Float64(n) => Ok(LuaValue::Number(*n)), - Rbx::Float32(n) => Ok(LuaValue::Number(*n as f64)), - Rbx::String(s) => Ok(LuaValue::String( - lua.create_string(s).map_err(RbxConversionError::external)?, - )), - Rbx::Content(s) => Ok(LuaValue::String( - lua.create_string(AsRef::::as_ref(s)) - .map_err(RbxConversionError::external)?, - )), - Rbx::BinaryString(s) => { - let encoded = STANDARD_NO_PAD.encode(AsRef::<[u8]>::as_ref(s)); - Ok(LuaValue::String( - lua.create_string(&encoded) - .map_err(RbxConversionError::external)?, - )) - } - // Custom datatypes - // NOTE: When adding a new datatype, also add it in the FromRbxVariantLua impl below - Rbx::Vector2(_) => Vector2::from_rbx_variant(variant)? - .to_lua(lua) - .map_err(RbxConversionError::external), - Rbx::Vector2int16(_) => Vector2int16::from_rbx_variant(variant)? - .to_lua(lua) - .map_err(RbxConversionError::external), - Rbx::Vector3(_) => Vector3::from_rbx_variant(variant)? - .to_lua(lua) - .map_err(RbxConversionError::external), - Rbx::Vector3int16(_) => Vector3int16::from_rbx_variant(variant)? - .to_lua(lua) - .map_err(RbxConversionError::external), - // Not yet implemented datatypes - Rbx::Axes(_) => todo!(), - Rbx::BrickColor(_) => todo!(), - Rbx::CFrame(_) => todo!(), - Rbx::Color3(_) => todo!(), - Rbx::Color3uint8(_) => todo!(), - Rbx::ColorSequence(_) => todo!(), - Rbx::Enum(_) => todo!(), - Rbx::Faces(_) => todo!(), - Rbx::NumberRange(_) => todo!(), - Rbx::NumberSequence(_) => todo!(), - Rbx::OptionalCFrame(_) => todo!(), - Rbx::PhysicalProperties(_) => todo!(), - Rbx::Ray(_) => todo!(), - Rbx::Rect(_) => todo!(), - Rbx::Region3(_) => todo!(), - Rbx::Region3int16(_) => todo!(), - Rbx::UDim(_) => todo!(), - Rbx::UDim2(_) => todo!(), - v => Err(RbxConversionError::FromRbxVariant { - from: v.display_name(), - to: "LuaValue", - detail: Some("Type not supported".to_string()), - }), - } - } -} - -impl<'lua> ToRbxVariant for LuaValue<'lua> { - fn to_rbx_variant( - &self, - desired_type: Option, - ) -> RbxConversionResult { - use self::types::*; - use base64::engine::general_purpose::STANDARD_NO_PAD; - use base64::engine::Engine as _; - use RbxVariantType as Rbx; - - if let Some(desired_type) = desired_type { - match (self, desired_type) { - // Primitives - (LuaValue::Boolean(b), Rbx::Bool) => Ok(RbxVariant::Bool(*b)), - (LuaValue::Integer(i), Rbx::Int64) => Ok(RbxVariant::Int64(*i as i64)), - (LuaValue::Integer(i), Rbx::Int32) => Ok(RbxVariant::Int32(*i)), - (LuaValue::Integer(i), Rbx::Float64) => Ok(RbxVariant::Float64(*i as f64)), - (LuaValue::Integer(i), Rbx::Float32) => Ok(RbxVariant::Float32(*i as f32)), - (LuaValue::Number(n), Rbx::Int64) => Ok(RbxVariant::Int64(*n as i64)), - (LuaValue::Number(n), Rbx::Int32) => Ok(RbxVariant::Int32(*n as i32)), - (LuaValue::Number(n), Rbx::Float64) => Ok(RbxVariant::Float64(*n)), - (LuaValue::Number(n), Rbx::Float32) => Ok(RbxVariant::Float32(*n as f32)), - (LuaValue::String(s), Rbx::String) => Ok(RbxVariant::String( - s.to_str() - .map_err(RbxConversionError::external)? - .to_string(), - )), - (LuaValue::String(s), Rbx::Content) => Ok(RbxVariant::Content( - s.to_str() - .map_err(RbxConversionError::external)? - .to_string() - .into(), - )), - (LuaValue::String(s), Rbx::BinaryString) => Ok(RbxVariant::BinaryString( - STANDARD_NO_PAD - .decode(s) - .map_err(RbxConversionError::external)? - .into(), - )), - // Custom datatypes - // NOTE: When adding a new datatype, also add it below + in the FromRbxVariantLua impl above - (LuaValue::UserData(u), d) => { - if let Ok(v2) = u.borrow::() { - v2.to_rbx_variant(Some(d)) - } else if let Ok(v2i) = u.borrow::() { - v2i.to_rbx_variant(Some(d)) - } else if let Ok(v3) = u.borrow::() { - v3.to_rbx_variant(Some(d)) - } else if let Ok(v3i) = u.borrow::() { - v3i.to_rbx_variant(Some(d)) - } else { - Err(RbxConversionError::ToRbxVariant { - to: d.display_name(), - from: "userdata", - detail: None, - }) - } - } - // Not yet implemented rbx types - (v, d) => Err(RbxConversionError::ToRbxVariant { - to: d.display_name(), - from: v.type_name(), - detail: None, - }), - } - } else { - match self { - // Primitives - LuaValue::Boolean(b) => Ok(RbxVariant::Bool(*b)), - LuaValue::Integer(i) => Ok(RbxVariant::Int32(*i)), - LuaValue::Number(n) => Ok(RbxVariant::Float64(*n)), - LuaValue::String(s) => Ok(RbxVariant::String( - s.to_str() - .map_err(RbxConversionError::external)? - .to_string(), - )), - // Custom datatypes - // NOTE: When adding a new datatype, also add it above - LuaValue::UserData(u) => { - if let Ok(v2) = u.borrow::() { - v2.to_rbx_variant(None) - } else if let Ok(v2i) = u.borrow::() { - v2i.to_rbx_variant(None) - } else if let Ok(v3) = u.borrow::() { - v3.to_rbx_variant(None) - } else if let Ok(v3i) = u.borrow::() { - v3i.to_rbx_variant(None) - } else { - Err(RbxConversionError::ToRbxVariant { - to: "Variant", - from: "userdata", - detail: None, - }) - } - } - // Not yet implemented rbx types - v => Err(RbxConversionError::ToRbxVariant { - to: "Variant", - from: v.type_name(), - detail: None, - }), - } - } - } -} - -// TODO: Implement tests for all datatypes in lua and run them here -// using the same mechanic we use to run tests in the main lib, these -// tests should also live next to other folders like fs, net, task, .. +use conversion::*; +use extension::*; +use result::*; +use shared::*; diff --git a/packages/lib-roblox/src/datatypes/result.rs b/packages/lib-roblox/src/datatypes/result.rs new file mode 100644 index 0000000..380ac4b --- /dev/null +++ b/packages/lib-roblox/src/datatypes/result.rs @@ -0,0 +1,71 @@ +use core::fmt; + +use std::error::Error; +use std::io::Error as IoError; + +use mlua::Error as LuaError; + +#[allow(dead_code)] +#[derive(Clone, Debug)] +pub(crate) enum DatatypeConversionError { + LuaError(LuaError), + External { + message: String, + }, + FromRbxVariant { + from: &'static str, + to: &'static str, + detail: Option, + }, + ToRbxVariant { + to: &'static str, + from: &'static str, + detail: Option, + }, +} + +impl fmt::Display for DatatypeConversionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::LuaError(error) => error.to_string(), + Self::External { message } => message.to_string(), + Self::FromRbxVariant { from, to, detail } + | Self::ToRbxVariant { from, to, detail } => { + match detail { + Some(d) => format!("Failed to convert from '{from}' into '{to}' - {d}"), + None => format!("Failed to convert from '{from}' into '{to}'",), + } + } + } + ) + } +} + +impl Error for DatatypeConversionError {} + +impl From for DatatypeConversionError { + fn from(value: LuaError) -> Self { + Self::LuaError(value) + } +} + +impl From for DatatypeConversionError { + fn from(value: IoError) -> Self { + DatatypeConversionError::External { + message: value.to_string(), + } + } +} + +impl From for DatatypeConversionError { + fn from(value: base64::DecodeError) -> Self { + DatatypeConversionError::External { + message: value.to_string(), + } + } +} + +pub(crate) type DatatypeConversionResult = Result; diff --git a/packages/lib-roblox/src/datatypes/shared.rs b/packages/lib-roblox/src/datatypes/shared.rs new file mode 100644 index 0000000..ae95b09 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/shared.rs @@ -0,0 +1,23 @@ +use mlua::prelude::*; + +pub(super) fn userdata_impl_to_string(_: &Lua, datatype: &D, _: ()) -> LuaResult +where + D: LuaUserData + ToString + 'static, +{ + Ok(datatype.to_string()) +} + +pub(super) fn userdata_impl_eq(_: &Lua, datatype: &D, value: LuaValue) -> LuaResult +where + D: LuaUserData + PartialEq + 'static, +{ + if let LuaValue::UserData(ud) = value { + if let Ok(value_as_datatype) = ud.borrow::() { + Ok(*datatype == *value_as_datatype) + } else { + Ok(false) + } + } else { + Ok(false) + } +} diff --git a/packages/lib-roblox/src/datatypes/types/mod.rs b/packages/lib-roblox/src/datatypes/types/mod.rs new file mode 100644 index 0000000..0baf644 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/types/mod.rs @@ -0,0 +1,9 @@ +mod vector2; +mod vector2int16; +mod vector3; +mod vector3int16; + +pub use vector2::Vector2; +pub use vector2int16::Vector2int16; +pub use vector3::Vector3; +pub use vector3int16::Vector3int16; diff --git a/packages/lib-roblox/src/datatypes/vector2.rs b/packages/lib-roblox/src/datatypes/types/vector2.rs similarity index 88% rename from packages/lib-roblox/src/datatypes/vector2.rs rename to packages/lib-roblox/src/datatypes/types/vector2.rs index 4ccb794..34047f6 100644 --- a/packages/lib-roblox/src/datatypes/vector2.rs +++ b/packages/lib-roblox/src/datatypes/types/vector2.rs @@ -4,7 +4,7 @@ use glam::{Vec2, Vec3}; use mlua::prelude::*; use rbx_dom_weak::types::Vector2 as RbxVector2; -use super::*; +use super::super::*; /** An implementation of the [Vector2](https://create.roblox.com/docs/reference/engine/datatypes/Vector2) @@ -16,6 +16,26 @@ use super::*; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Vector2(pub Vec2); +impl Vector2 { + pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { + // Constants + datatype_table.set("xAxis", Vector2(Vec2::X))?; + datatype_table.set("yAxis", Vector2(Vec2::Y))?; + datatype_table.set("zero", Vector2(Vec2::ZERO))?; + datatype_table.set("one", Vector2(Vec2::ONE))?; + // Constructors + datatype_table.set( + "new", + lua.create_function(|_, (x, y): (Option, Option)| { + Ok(Vector2(Vec2 { + x: x.unwrap_or_default(), + y: y.unwrap_or_default(), + })) + })?, + ) + } +} + impl fmt::Display for Vector2 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}, {}", self.0.x, self.0.y) @@ -48,8 +68,8 @@ impl LuaUserData for Vector2 { Ok(Vector2(this.0.min(rhs.0))) }); // Metamethods - methods.add_meta_method(LuaMetaMethod::Eq, datatype_impl_eq); - methods.add_meta_method(LuaMetaMethod::ToString, datatype_impl_to_string); + methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); + methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); methods.add_meta_method(LuaMetaMethod::Unm, |_, this, ()| Ok(Vector2(-this.0))); methods.add_meta_method(LuaMetaMethod::Add, |_, this, rhs: Vector2| { Ok(Vector2(this.0 + rhs.0)) @@ -100,26 +120,6 @@ impl LuaUserData for Vector2 { } } -impl DatatypeTable for Vector2 { - fn make_dt_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - // Constants - datatype_table.set("xAxis", Vector2(Vec2::X))?; - datatype_table.set("yAxis", Vector2(Vec2::Y))?; - datatype_table.set("zero", Vector2(Vec2::ZERO))?; - datatype_table.set("one", Vector2(Vec2::ONE))?; - // Constructors - datatype_table.set( - "new", - lua.create_function(|_, (x, y): (Option, Option)| { - Ok(Vector2(Vec2 { - x: x.unwrap_or_default(), - y: y.unwrap_or_default(), - })) - })?, - ) - } -} - impl From<&RbxVector2> for Vector2 { fn from(v: &RbxVector2) -> Self { Vector2(Vec2 { x: v.x, y: v.y }) @@ -133,12 +133,12 @@ impl From<&Vector2> for RbxVector2 { } impl FromRbxVariant for Vector2 { - fn from_rbx_variant(variant: &RbxVariant) -> RbxConversionResult { + fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult { if let RbxVariant::Vector2(v) = variant { Ok(v.into()) } else { - Err(RbxConversionError::FromRbxVariant { - from: variant.display_name(), + Err(DatatypeConversionError::FromRbxVariant { + from: variant.variant_name(), to: "Vector2", detail: None, }) @@ -150,12 +150,13 @@ impl ToRbxVariant for Vector2 { fn to_rbx_variant( &self, desired_type: Option, - ) -> RbxConversionResult { + ) -> DatatypeConversionResult { if matches!(desired_type, None | Some(RbxVariantType::Vector2)) { Ok(RbxVariant::Vector2(self.into())) } else { - Err(RbxConversionError::DesiredTypeMismatch { - can_convert_to: Some(RbxVariantType::Vector2.display_name()), + Err(DatatypeConversionError::ToRbxVariant { + to: desired_type.map(|d| d.variant_name()).unwrap_or("?"), + from: "Vector2", detail: None, }) } diff --git a/packages/lib-roblox/src/datatypes/vector2int16.rs b/packages/lib-roblox/src/datatypes/types/vector2int16.rs similarity index 86% rename from packages/lib-roblox/src/datatypes/vector2int16.rs rename to packages/lib-roblox/src/datatypes/types/vector2int16.rs index 1de284b..9c5e26c 100644 --- a/packages/lib-roblox/src/datatypes/vector2int16.rs +++ b/packages/lib-roblox/src/datatypes/types/vector2int16.rs @@ -4,7 +4,7 @@ use glam::IVec2; use mlua::prelude::*; use rbx_dom_weak::types::Vector2int16 as RbxVector2int16; -use super::*; +use super::super::*; /** An implementation of the [Vector2int16](https://create.roblox.com/docs/reference/engine/datatypes/Vector2int16) @@ -16,6 +16,20 @@ use super::*; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Vector2int16(pub IVec2); +impl Vector2int16 { + pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { + datatype_table.set( + "new", + lua.create_function(|_, (x, y): (Option, Option)| { + Ok(Vector2int16(IVec2 { + x: x.unwrap_or_default() as i32, + y: y.unwrap_or_default() as i32, + })) + })?, + ) + } +} + impl fmt::Display for Vector2int16 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}, {}", self.0.x, self.0.y) @@ -29,8 +43,8 @@ impl LuaUserData for Vector2int16 { } fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_meta_method(LuaMetaMethod::Eq, datatype_impl_eq); - methods.add_meta_method(LuaMetaMethod::ToString, datatype_impl_to_string); + methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); + methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); methods.add_meta_method(LuaMetaMethod::Unm, |_, this, ()| Ok(Vector2int16(-this.0))); methods.add_meta_method(LuaMetaMethod::Add, |_, this, rhs: Vector2int16| { Ok(Vector2int16(this.0 + rhs.0)) @@ -81,20 +95,6 @@ impl LuaUserData for Vector2int16 { } } -impl DatatypeTable for Vector2int16 { - fn make_dt_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, (x, y): (Option, Option)| { - Ok(Vector2int16(IVec2 { - x: x.unwrap_or_default() as i32, - y: y.unwrap_or_default() as i32, - })) - })?, - ) - } -} - impl From<&RbxVector2int16> for Vector2int16 { fn from(v: &RbxVector2int16) -> Self { Vector2int16(IVec2 { @@ -114,12 +114,12 @@ impl From<&Vector2int16> for RbxVector2int16 { } impl FromRbxVariant for Vector2int16 { - fn from_rbx_variant(variant: &RbxVariant) -> RbxConversionResult { + fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult { if let RbxVariant::Vector2int16(v) = variant { Ok(v.into()) } else { - Err(RbxConversionError::FromRbxVariant { - from: variant.display_name(), + Err(DatatypeConversionError::FromRbxVariant { + from: variant.variant_name(), to: "Vector2int16", detail: None, }) @@ -131,12 +131,13 @@ impl ToRbxVariant for Vector2int16 { fn to_rbx_variant( &self, desired_type: Option, - ) -> RbxConversionResult { + ) -> DatatypeConversionResult { if matches!(desired_type, None | Some(RbxVariantType::Vector2int16)) { Ok(RbxVariant::Vector2int16(self.into())) } else { - Err(RbxConversionError::DesiredTypeMismatch { - can_convert_to: Some(RbxVariantType::Vector2int16.display_name()), + Err(DatatypeConversionError::ToRbxVariant { + to: desired_type.map(|d| d.variant_name()).unwrap_or("?"), + from: "Vector2", detail: None, }) } diff --git a/packages/lib-roblox/src/datatypes/vector3.rs b/packages/lib-roblox/src/datatypes/types/vector3.rs similarity index 90% rename from packages/lib-roblox/src/datatypes/vector3.rs rename to packages/lib-roblox/src/datatypes/types/vector3.rs index bcf0377..1803e88 100644 --- a/packages/lib-roblox/src/datatypes/vector3.rs +++ b/packages/lib-roblox/src/datatypes/types/vector3.rs @@ -4,7 +4,7 @@ use glam::Vec3; use mlua::prelude::*; use rbx_dom_weak::types::Vector3 as RbxVector3; -use super::*; +use super::super::*; /** An implementation of the [Vector3](https://create.roblox.com/docs/reference/engine/datatypes/Vector3) @@ -19,6 +19,29 @@ use super::*; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Vector3(pub Vec3); +impl Vector3 { + pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { + // Constants + datatype_table.set("xAxis", Vector3(Vec3::X))?; + datatype_table.set("yAxis", Vector3(Vec3::Y))?; + datatype_table.set("zAxis", Vector3(Vec3::Z))?; + datatype_table.set("zero", Vector3(Vec3::ZERO))?; + datatype_table.set("one", Vector3(Vec3::ONE))?; + // Constructors + datatype_table.set( + "new", + lua.create_function(|_, (x, y, z): (Option, Option, Option)| { + Ok(Vector3(Vec3 { + x: x.unwrap_or_default(), + y: y.unwrap_or_default(), + z: z.unwrap_or_default(), + })) + })?, + ) + // FUTURE: Implement FromNormalId and FromAxis constructors? + } +} + impl fmt::Display for Vector3 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}, {}, {}", self.0.x, self.0.y, self.0.z) @@ -59,8 +82,8 @@ impl LuaUserData for Vector3 { Ok(Vector3(this.0.min(rhs.0))) }); // Metamethods - methods.add_meta_method(LuaMetaMethod::Eq, datatype_impl_eq); - methods.add_meta_method(LuaMetaMethod::ToString, datatype_impl_to_string); + methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); + methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); methods.add_meta_method(LuaMetaMethod::Unm, |_, this, ()| Ok(Vector3(-this.0))); methods.add_meta_method(LuaMetaMethod::Add, |_, this, rhs: Vector3| { Ok(Vector3(this.0 + rhs.0)) @@ -111,29 +134,6 @@ impl LuaUserData for Vector3 { } } -impl DatatypeTable for Vector3 { - fn make_dt_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - // Constants - datatype_table.set("xAxis", Vector3(Vec3::X))?; - datatype_table.set("yAxis", Vector3(Vec3::Y))?; - datatype_table.set("zAxis", Vector3(Vec3::Z))?; - datatype_table.set("zero", Vector3(Vec3::ZERO))?; - datatype_table.set("one", Vector3(Vec3::ONE))?; - // Constructors - datatype_table.set( - "new", - lua.create_function(|_, (x, y, z): (Option, Option, Option)| { - Ok(Vector3(Vec3 { - x: x.unwrap_or_default(), - y: y.unwrap_or_default(), - z: z.unwrap_or_default(), - })) - })?, - ) - // FUTURE: Implement FromNormalId and FromAxis constructors? - } -} - impl From<&RbxVector3> for Vector3 { fn from(v: &RbxVector3) -> Self { Vector3(Vec3 { @@ -155,12 +155,12 @@ impl From<&Vector3> for RbxVector3 { } impl FromRbxVariant for Vector3 { - fn from_rbx_variant(variant: &RbxVariant) -> RbxConversionResult { + fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult { if let RbxVariant::Vector3(v) = variant { Ok(v.into()) } else { - Err(RbxConversionError::FromRbxVariant { - from: variant.display_name(), + Err(DatatypeConversionError::FromRbxVariant { + from: variant.variant_name(), to: "Vector3", detail: None, }) @@ -172,12 +172,13 @@ impl ToRbxVariant for Vector3 { fn to_rbx_variant( &self, desired_type: Option, - ) -> RbxConversionResult { + ) -> DatatypeConversionResult { if matches!(desired_type, None | Some(RbxVariantType::Vector3)) { Ok(RbxVariant::Vector3(self.into())) } else { - Err(RbxConversionError::DesiredTypeMismatch { - can_convert_to: Some(RbxVariantType::Vector3.display_name()), + Err(DatatypeConversionError::ToRbxVariant { + to: desired_type.map(|d| d.variant_name()).unwrap_or("?"), + from: "Vector2", detail: None, }) } diff --git a/packages/lib-roblox/src/datatypes/vector3int16.rs b/packages/lib-roblox/src/datatypes/types/vector3int16.rs similarity index 87% rename from packages/lib-roblox/src/datatypes/vector3int16.rs rename to packages/lib-roblox/src/datatypes/types/vector3int16.rs index 488fcca..f934e4e 100644 --- a/packages/lib-roblox/src/datatypes/vector3int16.rs +++ b/packages/lib-roblox/src/datatypes/types/vector3int16.rs @@ -4,7 +4,7 @@ use glam::IVec3; use mlua::prelude::*; use rbx_dom_weak::types::Vector3int16 as RbxVector3int16; -use super::*; +use super::super::*; /** An implementation of the [Vector3int16](https://create.roblox.com/docs/reference/engine/datatypes/Vector3int16) @@ -16,6 +16,21 @@ use super::*; #[derive(Debug, Clone, Copy, PartialEq)] pub struct Vector3int16(pub IVec3); +impl Vector3int16 { + pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { + datatype_table.set( + "new", + lua.create_function(|_, (x, y, z): (Option, Option, Option)| { + Ok(Vector3int16(IVec3 { + x: x.unwrap_or_default() as i32, + y: y.unwrap_or_default() as i32, + z: z.unwrap_or_default() as i32, + })) + })?, + ) + } +} + impl fmt::Display for Vector3int16 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}, {}", self.0.x, self.0.y) @@ -29,8 +44,8 @@ impl LuaUserData for Vector3int16 { } fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_meta_method(LuaMetaMethod::Eq, datatype_impl_eq); - methods.add_meta_method(LuaMetaMethod::ToString, datatype_impl_to_string); + methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); + methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); methods.add_meta_method(LuaMetaMethod::Unm, |_, this, ()| Ok(Vector3int16(-this.0))); methods.add_meta_method(LuaMetaMethod::Add, |_, this, rhs: Vector3int16| { Ok(Vector3int16(this.0 + rhs.0)) @@ -81,21 +96,6 @@ impl LuaUserData for Vector3int16 { } } -impl DatatypeTable for Vector3int16 { - fn make_dt_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { - datatype_table.set( - "new", - lua.create_function(|_, (x, y, z): (Option, Option, Option)| { - Ok(Vector3int16(IVec3 { - x: x.unwrap_or_default() as i32, - y: y.unwrap_or_default() as i32, - z: z.unwrap_or_default() as i32, - })) - })?, - ) - } -} - impl From<&RbxVector3int16> for Vector3int16 { fn from(v: &RbxVector3int16) -> Self { Vector3int16(IVec3 { @@ -117,12 +117,12 @@ impl From<&Vector3int16> for RbxVector3int16 { } impl FromRbxVariant for Vector3int16 { - fn from_rbx_variant(variant: &RbxVariant) -> RbxConversionResult { + fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult { if let RbxVariant::Vector3int16(v) = variant { Ok(v.into()) } else { - Err(RbxConversionError::FromRbxVariant { - from: variant.display_name(), + Err(DatatypeConversionError::FromRbxVariant { + from: variant.variant_name(), to: "Vector3int16", detail: None, }) @@ -134,12 +134,13 @@ impl ToRbxVariant for Vector3int16 { fn to_rbx_variant( &self, desired_type: Option, - ) -> RbxConversionResult { + ) -> DatatypeConversionResult { if matches!(desired_type, None | Some(RbxVariantType::Vector3int16)) { Ok(RbxVariant::Vector3int16(self.into())) } else { - Err(RbxConversionError::DesiredTypeMismatch { - can_convert_to: Some(RbxVariantType::Vector3int16.display_name()), + Err(DatatypeConversionError::ToRbxVariant { + to: desired_type.map(|d| d.variant_name()).unwrap_or("?"), + from: "Vector2", detail: None, }) } diff --git a/packages/lib-roblox/src/lib.rs b/packages/lib-roblox/src/lib.rs index 8fad922..5240a01 100644 --- a/packages/lib-roblox/src/lib.rs +++ b/packages/lib-roblox/src/lib.rs @@ -8,7 +8,6 @@ pub mod instance; mod tests; use datatypes::types::*; -use datatypes::DatatypeTable; fn make_dt(lua: &Lua, f: F) -> LuaResult where @@ -23,10 +22,10 @@ where #[rustfmt::skip] fn make_all_datatypes(lua: &Lua) -> LuaResult> { Ok(vec![ - ("Vector2", make_dt(lua, Vector2::make_dt_table)?), - ("Vector2int16", make_dt(lua, Vector2int16::make_dt_table)?), - ("Vector3", make_dt(lua, Vector3::make_dt_table)?), - ("Vector3int16", make_dt(lua, Vector3int16::make_dt_table)?), + ("Vector2", make_dt(lua, Vector2::make_table)?), + ("Vector2int16", make_dt(lua, Vector2int16::make_table)?), + ("Vector3", make_dt(lua, Vector3::make_table)?), + ("Vector3int16", make_dt(lua, Vector3int16::make_table)?), ]) }