Implement translation layer between lua values and rbx_dom_weak variants

This commit is contained in:
Filip Tibell 2023-03-10 13:35:49 +01:00
parent a31553e130
commit 078c08eabf
No known key found for this signature in database
3 changed files with 187 additions and 3 deletions

1
Cargo.lock generated
View file

@ -861,6 +861,7 @@ dependencies = [
name = "lune-roblox" name = "lune-roblox"
version = "0.5.5" version = "0.5.5"
dependencies = [ dependencies = [
"base64 0.21.0",
"glam", "glam",
"mlua", "mlua",
"rbx_binary", "rbx_binary",

View file

@ -17,6 +17,7 @@ path = "src/lib.rs"
[dependencies] [dependencies]
mlua.workspace = true mlua.workspace = true
base64 = "0.21"
glam = "0.23" glam = "0.23"
thiserror = "1.0" thiserror = "1.0"

View file

@ -12,7 +12,7 @@ pub mod types {
pub use super::vector3::Vector3; pub use super::vector3::Vector3;
} }
// Trait definitions for conversion between rbx_dom_weak variant <-> datatype // Trait definitions for conversion between rbx_dom_weak variant <-> our custom datatypes
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) enum RbxConversionError { pub(crate) enum RbxConversionError {
@ -30,6 +30,17 @@ pub(crate) enum RbxConversionError {
can_convert_to: Option<&'static str>, can_convert_to: Option<&'static str>,
detail: Option<String>, 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) type RbxConversionResult<T> = Result<T, RbxConversionError>;
@ -49,7 +60,7 @@ pub(crate) trait DatatypeTable {
fn make_dt_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()>; fn make_dt_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()>;
} }
// Shared impls for datatype metamethods // Shared impls for datatype metamethods belonging to this module
fn datatype_impl_to_string<D>(_: &Lua, datatype: &D, _: ()) -> LuaResult<String> fn datatype_impl_to_string<D>(_: &Lua, datatype: &D, _: ()) -> LuaResult<String>
where where
@ -128,6 +139,177 @@ impl RbxVariantDisplayName for RbxVariant {
} }
} }
// 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 FromRbxVariantLua<'lua>: Sized {
fn from_rbx_variant_lua(variant: &RbxVariant, lua: &'lua Lua) -> RbxConversionResult<Self>;
}
impl<'lua> FromRbxVariantLua<'lua> for LuaValue<'lua> {
fn from_rbx_variant_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::Integer(*i as i32)),
Rbx::Int32(i) => Ok(LuaValue::Integer(*i)),
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::Vector3(_) => Vector3::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!(),
Rbx::Vector2int16(_) => todo!(),
Rbx::Vector3int16(_) => 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(v3) = u.borrow::<Vector3>() {
v3.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(v3) = u.borrow::<Vector3>() {
v3.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 // TODO: Implement tests for all datatypes in lua and run them here
// using the same mechanic we have to run tests in the main lib, these // 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, .. // tests should also live next to other folders like fs, net, task, ..