Roblox datatypes refactor & organization

This commit is contained in:
Filip Tibell 2023-03-11 16:39:23 +01:00
parent 4afb51830c
commit 0f9caf5461
No known key found for this signature in database
11 changed files with 470 additions and 440 deletions

View file

@ -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<RbxVariantType>,
) -> DatatypeConversionResult<RbxVariant>;
}
pub(crate) trait FromRbxVariant: Sized {
fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult<Self>;
}
pub(crate) trait FromRbxVariantLua<'lua>: Sized {
fn from_rbx_variant_lua(variant: &RbxVariant, lua: &'lua Lua)
-> DatatypeConversionResult<Self>;
}
/*
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<Self> {
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::<str>::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<RbxVariantType>,
) -> DatatypeConversionResult<RbxVariant> {
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<Self> {
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<RbxVariantType>,
) -> DatatypeConversionResult<RbxVariant> {
use super::types::*;
if let Ok(v2) = self.borrow::<Vector2>() {
v2.to_rbx_variant(desired_type)
} else if let Ok(v2i) = self.borrow::<Vector2int16>() {
v2i.to_rbx_variant(desired_type)
} else if let Ok(v3) = self.borrow::<Vector3>() {
v3.to_rbx_variant(desired_type)
} else if let Ok(v3i) = self.borrow::<Vector3int16>() {
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,
})
}
}
}

View file

@ -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()
}
}

View file

@ -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<String>,
},
ToRbxVariant {
to: &'static str,
from: &'static str,
detail: Option<String>,
},
DesiredTypeMismatch {
can_convert_to: Option<&'static str>,
detail: Option<String>,
},
External {
message: String,
},
}
impl RbxConversionError {
pub fn external(e: impl std::error::Error) -> Self {
RbxConversionError::External {
message: e.to_string(),
}
}
}
pub(crate) type RbxConversionResult<T> = Result<T, RbxConversionError>;
pub(crate) trait ToRbxVariant {
fn to_rbx_variant(
&self,
desired_type: Option<RbxVariantType>,
) -> RbxConversionResult<RbxVariant>;
}
pub(crate) trait FromRbxVariant: Sized {
fn from_rbx_variant(variant: &RbxVariant) -> RbxConversionResult<Self>;
}
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<D>(_: &Lua, datatype: &D, _: ()) -> LuaResult<String>
where
D: LuaUserData + ToString + 'static,
{
Ok(datatype.to_string())
}
fn datatype_impl_eq<D>(_: &Lua, datatype: &D, value: LuaValue) -> LuaResult<bool>
where
D: LuaUserData + PartialEq + 'static,
{
if let LuaValue::UserData(ud) = value {
if let Ok(vec) = ud.borrow::<D>() {
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<Self>;
}
impl<'lua> RbxVariantToLua<'lua> for LuaValue<'lua> {
fn rbx_variant_to_lua(variant: &RbxVariant, lua: &'lua Lua) -> RbxConversionResult<Self> {
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::<str>::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<RbxVariantType>,
) -> RbxConversionResult<RbxVariant> {
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::<Vector2>() {
v2.to_rbx_variant(Some(d))
} else if let Ok(v2i) = u.borrow::<Vector2int16>() {
v2i.to_rbx_variant(Some(d))
} else if let Ok(v3) = u.borrow::<Vector3>() {
v3.to_rbx_variant(Some(d))
} else if let Ok(v3i) = u.borrow::<Vector3int16>() {
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::<Vector2>() {
v2.to_rbx_variant(None)
} else if let Ok(v2i) = u.borrow::<Vector2int16>() {
v2i.to_rbx_variant(None)
} else if let Ok(v3) = u.borrow::<Vector3>() {
v3.to_rbx_variant(None)
} else if let Ok(v3i) = u.borrow::<Vector3int16>() {
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::*;

View file

@ -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<String>,
},
ToRbxVariant {
to: &'static str,
from: &'static str,
detail: Option<String>,
},
}
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<LuaError> for DatatypeConversionError {
fn from(value: LuaError) -> Self {
Self::LuaError(value)
}
}
impl From<IoError> for DatatypeConversionError {
fn from(value: IoError) -> Self {
DatatypeConversionError::External {
message: value.to_string(),
}
}
}
impl From<base64::DecodeError> for DatatypeConversionError {
fn from(value: base64::DecodeError) -> Self {
DatatypeConversionError::External {
message: value.to_string(),
}
}
}
pub(crate) type DatatypeConversionResult<T> = Result<T, DatatypeConversionError>;

View file

@ -0,0 +1,23 @@
use mlua::prelude::*;
pub(super) fn userdata_impl_to_string<D>(_: &Lua, datatype: &D, _: ()) -> LuaResult<String>
where
D: LuaUserData + ToString + 'static,
{
Ok(datatype.to_string())
}
pub(super) fn userdata_impl_eq<D>(_: &Lua, datatype: &D, value: LuaValue) -> LuaResult<bool>
where
D: LuaUserData + PartialEq + 'static,
{
if let LuaValue::UserData(ud) = value {
if let Ok(value_as_datatype) = ud.borrow::<D>() {
Ok(*datatype == *value_as_datatype)
} else {
Ok(false)
}
} else {
Ok(false)
}
}

View file

@ -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;

View file

@ -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<f32>, Option<f32>)| {
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<f32>, Option<f32>)| {
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<Self> {
fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult<Self> {
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<RbxVariantType>,
) -> RbxConversionResult<RbxVariant> {
) -> DatatypeConversionResult<RbxVariant> {
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,
})
}

View file

@ -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<i16>, Option<i16>)| {
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<i16>, Option<i16>)| {
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<Self> {
fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult<Self> {
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<RbxVariantType>,
) -> RbxConversionResult<RbxVariant> {
) -> DatatypeConversionResult<RbxVariant> {
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,
})
}

View file

@ -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<f32>, Option<f32>, Option<f32>)| {
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<f32>, Option<f32>, Option<f32>)| {
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<Self> {
fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult<Self> {
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<RbxVariantType>,
) -> RbxConversionResult<RbxVariant> {
) -> DatatypeConversionResult<RbxVariant> {
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,
})
}

View file

@ -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<i16>, Option<i16>, Option<i16>)| {
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<i16>, Option<i16>, Option<i16>)| {
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<Self> {
fn from_rbx_variant(variant: &RbxVariant) -> DatatypeConversionResult<Self> {
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<RbxVariantType>,
) -> RbxConversionResult<RbxVariant> {
) -> DatatypeConversionResult<RbxVariant> {
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,
})
}

View file

@ -8,7 +8,6 @@ pub mod instance;
mod tests;
use datatypes::types::*;
use datatypes::DatatypeTable;
fn make_dt<F>(lua: &Lua, f: F) -> LuaResult<LuaTable>
where
@ -23,10 +22,10 @@ where
#[rustfmt::skip]
fn make_all_datatypes(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaTable)>> {
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)?),
])
}