mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Initial implementation of datatypes module, first datatype impl
This commit is contained in:
parent
1129d32b7b
commit
501933e3f9
6 changed files with 317 additions and 7 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -534,6 +534,12 @@ dependencies = [
|
|||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c"
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.16"
|
||||
|
@ -855,6 +861,7 @@ dependencies = [
|
|||
name = "lune-roblox"
|
||||
version = "0.5.5"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"mlua",
|
||||
"rbx_binary",
|
||||
"rbx_dom_weak",
|
||||
|
|
|
@ -17,6 +17,7 @@ path = "src/lib.rs"
|
|||
[dependencies]
|
||||
mlua.workspace = true
|
||||
|
||||
glam = "0.23"
|
||||
thiserror = "1.0"
|
||||
|
||||
rbx_binary = { git = "https://github.com/rojo-rbx/rbx-dom", rev = "ce4c5bf7b18c813417ad14cc37e5abe281dfb51a" }
|
||||
|
@ -24,6 +25,3 @@ rbx_dom_weak = { git = "https://github.com/rojo-rbx/rbx-dom", rev = "ce4c5bf7b18
|
|||
rbx_reflection = { git = "https://github.com/rojo-rbx/rbx-dom", rev = "ce4c5bf7b18c813417ad14cc37e5abe281dfb51a" }
|
||||
rbx_reflection_database = { git = "https://github.com/rojo-rbx/rbx-dom", rev = "ce4c5bf7b18c813417ad14cc37e5abe281dfb51a" }
|
||||
rbx_xml = { git = "https://github.com/rojo-rbx/rbx-dom", rev = "ce4c5bf7b18c813417ad14cc37e5abe281dfb51a" }
|
||||
|
||||
# TODO: Split lune lib out into something like lune-core so
|
||||
# that we can use filesystem and async apis in this crate
|
||||
|
|
107
packages/lib-roblox/src/datatypes/mod.rs
Normal file
107
packages/lib-roblox/src/datatypes/mod.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
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 vector3;
|
||||
|
||||
pub mod types {
|
||||
pub use super::vector3::Vector3;
|
||||
}
|
||||
|
||||
// Trait definitions for conversion between rbx_dom_weak variant <-> datatype
|
||||
|
||||
#[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 {
|
||||
actual: &'static str,
|
||||
detail: Option<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<()>;
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// tests should also live next to other folders like fs, net, task, ..
|
182
packages/lib-roblox/src/datatypes/vector3.rs
Normal file
182
packages/lib-roblox/src/datatypes/vector3.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
use core::fmt;
|
||||
|
||||
use glam::Vec3A;
|
||||
use mlua::prelude::*;
|
||||
use rbx_dom_weak::types::Vector3 as RbxVector3;
|
||||
|
||||
use super::*;
|
||||
|
||||
/**
|
||||
An implementation of the [Vector3](https://create.roblox.com/docs/reference/engine/datatypes/Vector3)
|
||||
Roblox datatype, backed by [`glam::Vec3A`].
|
||||
|
||||
This implements all documented properties & methods of the Vector3
|
||||
class as of March 2023, as well as the `new(x, y, z)` constructor.
|
||||
|
||||
Note that this does not use native Luau vectors to simplify implementation
|
||||
and instead allow us to implement all abovementioned APIs accurately.
|
||||
*/
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Vector3(pub Vec3A);
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for Vector3 {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("Magnitude", |_, this| Ok(this.0.length()));
|
||||
fields.add_field_method_get("Unit", |_, this| Ok(Vector3(this.0.normalize())));
|
||||
fields.add_field_method_get("X", |_, this| Ok(this.0.x));
|
||||
fields.add_field_method_get("Y", |_, this| Ok(this.0.y));
|
||||
fields.add_field_method_get("Z", |_, this| Ok(this.0.z));
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
// Methods
|
||||
methods.add_method("Angle", |_, this, rhs: Vector3| {
|
||||
Ok(this.0.angle_between(rhs.0))
|
||||
});
|
||||
methods.add_method("Cross", |_, this, rhs: Vector3| {
|
||||
Ok(Vector3(this.0.cross(rhs.0)))
|
||||
});
|
||||
methods.add_method("Dot", |_, this, rhs: Vector3| Ok(this.0.dot(rhs.0)));
|
||||
methods.add_method("FuzzyEq", |_, this, (rhs, epsilon): (Vector3, f32)| {
|
||||
let eq_x = (rhs.0.x - this.0.x).abs() <= epsilon;
|
||||
let eq_y = (rhs.0.y - this.0.y).abs() <= epsilon;
|
||||
let eq_z = (rhs.0.z - this.0.z).abs() <= epsilon;
|
||||
Ok(eq_x && eq_y && eq_z)
|
||||
});
|
||||
methods.add_method("Lerp", |_, this, (rhs, alpha): (Vector3, f32)| {
|
||||
Ok(Vector3(this.0.lerp(rhs.0, alpha)))
|
||||
});
|
||||
methods.add_method("Max", |_, this, rhs: Vector3| {
|
||||
Ok(Vector3(this.0.max(rhs.0)))
|
||||
});
|
||||
methods.add_method("Min", |_, this, rhs: Vector3| {
|
||||
Ok(Vector3(this.0.min(rhs.0)))
|
||||
});
|
||||
// Metamethods - normal
|
||||
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(this.to_string()));
|
||||
methods.add_meta_method(LuaMetaMethod::Eq, |_, this, rhs: LuaValue| {
|
||||
if let LuaValue::UserData(ud) = rhs {
|
||||
if let Ok(vec) = ud.borrow::<Vector3>() {
|
||||
Ok(this.0 == vec.0)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
});
|
||||
// Metamethods - math
|
||||
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))
|
||||
});
|
||||
methods.add_meta_method(LuaMetaMethod::Sub, |_, this, rhs: Vector3| {
|
||||
Ok(Vector3(this.0 - rhs.0))
|
||||
});
|
||||
methods.add_meta_method(LuaMetaMethod::Mul, |_, this, rhs: LuaValue| {
|
||||
match &rhs {
|
||||
LuaValue::Number(n) => return Ok(Vector3(this.0 * Vec3A::splat(*n as f32))),
|
||||
LuaValue::UserData(ud) => {
|
||||
if let Ok(vec) = ud.borrow::<Vector3>() {
|
||||
return Ok(Vector3(this.0 * vec.0));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Err(LuaError::FromLuaConversionError {
|
||||
from: rhs.type_name(),
|
||||
to: "Vector3",
|
||||
message: Some(format!(
|
||||
"Expected Vector3 or number, got {}",
|
||||
rhs.type_name()
|
||||
)),
|
||||
})
|
||||
});
|
||||
methods.add_meta_method(LuaMetaMethod::Div, |_, this, rhs: LuaValue| {
|
||||
match &rhs {
|
||||
LuaValue::Number(n) => return Ok(Vector3(this.0 / Vec3A::splat(*n as f32))),
|
||||
LuaValue::UserData(ud) => {
|
||||
if let Ok(vec) = ud.borrow::<Vector3>() {
|
||||
return Ok(Vector3(this.0 / vec.0));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
Err(LuaError::FromLuaConversionError {
|
||||
from: rhs.type_name(),
|
||||
to: "Vector3",
|
||||
message: Some(format!(
|
||||
"Expected Vector3 or number, got {}",
|
||||
rhs.type_name()
|
||||
)),
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl DatatypeTable for Vector3 {
|
||||
fn make_dt_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
|
||||
// Constants
|
||||
datatype_table.set("xAxis", Vector3(Vec3A::X))?;
|
||||
datatype_table.set("yAxis", Vector3(Vec3A::Y))?;
|
||||
datatype_table.set("zAxis", Vector3(Vec3A::Z))?;
|
||||
datatype_table.set("zero", Vector3(Vec3A::ZERO))?;
|
||||
datatype_table.set("one", Vector3(Vec3A::ONE))?;
|
||||
// Constructors
|
||||
datatype_table.set(
|
||||
"new",
|
||||
lua.create_function(|_, (x, y, z): (Option<f32>, Option<f32>, Option<f32>)| {
|
||||
Ok(Vector3(Vec3A {
|
||||
x: x.unwrap_or_default(),
|
||||
y: y.unwrap_or_default(),
|
||||
z: z.unwrap_or_default(),
|
||||
}))
|
||||
})?,
|
||||
)
|
||||
// FUTURE: Implement FromNormalId and FromAxis constructors?
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRbxVariant for Vector3 {
|
||||
fn from_rbx_variant(variant: &RbxVariant) -> RbxConversionResult<Self> {
|
||||
if let RbxVariant::Vector3(v) = variant {
|
||||
Ok(Vector3(Vec3A {
|
||||
x: v.x,
|
||||
y: v.y,
|
||||
z: v.z,
|
||||
}))
|
||||
} else {
|
||||
Err(RbxConversionError::FromRbxVariant {
|
||||
from: variant.display_name(),
|
||||
to: "Vector3",
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToRbxVariant for Vector3 {
|
||||
fn to_rbx_variant(
|
||||
&self,
|
||||
desired_type: Option<RbxVariantType>,
|
||||
) -> RbxConversionResult<RbxVariant> {
|
||||
if matches!(desired_type, None | Some(RbxVariantType::Vector3)) {
|
||||
Ok(RbxVariant::Vector3(RbxVector3 {
|
||||
x: self.0.x,
|
||||
y: self.0.y,
|
||||
z: self.0.z,
|
||||
}))
|
||||
} else {
|
||||
Err(RbxConversionError::DesiredTypeMismatch {
|
||||
actual: RbxVariantType::Vector3.display_name(),
|
||||
detail: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ pub use kind::*;
|
|||
pub type DocumentResult<T> = Result<T, DocumentError>;
|
||||
|
||||
/**
|
||||
A wrapper for [`rbx_dom_weak::WeakDom`] that also takes care of
|
||||
A container for [`rbx_dom_weak::WeakDom`] that also takes care of
|
||||
reading and writing different kinds and formats of roblox files.
|
||||
|
||||
```rust ignore
|
||||
|
|
|
@ -1,11 +1,27 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
mod instance;
|
||||
|
||||
pub mod datatypes;
|
||||
pub mod document;
|
||||
pub mod instance;
|
||||
|
||||
use datatypes::types::*;
|
||||
use datatypes::DatatypeTable;
|
||||
|
||||
fn make_dt<F>(lua: &Lua, f: F) -> LuaResult<LuaTable>
|
||||
where
|
||||
F: Fn(&Lua, &LuaTable) -> LuaResult<()>,
|
||||
{
|
||||
let tab = lua.create_table()?;
|
||||
f(lua, &tab)?;
|
||||
tab.set_readonly(true);
|
||||
Ok(tab)
|
||||
}
|
||||
|
||||
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
|
||||
let datatypes = vec![("Vector3", make_dt(lua, Vector3::make_dt_table)?)];
|
||||
let exports = lua.create_table()?;
|
||||
|
||||
for (name, tab) in datatypes {
|
||||
exports.set(name, tab)?;
|
||||
}
|
||||
Ok(exports)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue