use core::fmt;
use luau_vm::Thread as VmThread;
use luau_vm::types::LUA_VECTOR_SIZE;
use crate::error::Error;
use crate::thread::Thread;
use crate::value::{FromLua, IntoLua, LuaType, Value};
#[cfg(feature = "vector4")]
const _: () = assert!(
LUA_VECTOR_SIZE == 4,
"`luau/vector4` is enabled but `luau-vm/vector4` is not"
);
#[cfg(not(feature = "vector4"))]
const _: () = assert!(
LUA_VECTOR_SIZE == 3,
"`luau-vm/vector4` is enabled without `luau/vector4`; enable `luau/vector4` instead"
);
#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
pub struct Vector(pub(crate) [f32; Self::SIZE]);
impl Vector {
pub(crate) const SIZE: usize = LUA_VECTOR_SIZE;
#[cfg(not(feature = "vector4"))]
pub const fn new(x: f32, y: f32, z: f32) -> Self {
let mut components = [0.0; Self::SIZE];
components[0] = x;
components[1] = y;
components[2] = z;
Self(components)
}
#[cfg(feature = "vector4")]
pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
Self([x, y, z, w])
}
pub const fn zero() -> Self {
Self([0.0; Self::SIZE])
}
pub const fn x(&self) -> f32 {
self.0[0]
}
pub const fn y(&self) -> f32 {
self.0[1]
}
pub const fn z(&self) -> f32 {
self.0[2]
}
#[cfg(feature = "vector4")]
pub const fn w(&self) -> f32 {
self.0[3]
}
}
impl fmt::Display for Vector {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "vector4"))]
return write!(
formatter,
"vector({}, {}, {})",
self.x(),
self.y(),
self.z()
);
#[cfg(feature = "vector4")]
return write!(
formatter,
"vector({}, {}, {}, {})",
self.x(),
self.y(),
self.z(),
self.w()
);
}
}
impl PartialEq<[f32; Self::SIZE]> for Vector {
fn eq(&self, other: &[f32; Self::SIZE]) -> bool {
self.0 == *other
}
}
impl LuaType for Vector {
fn push_type_key(thread: impl AsRef<VmThread>) -> Result<(), Error> {
let thread = thread.as_ref();
unsafe {
thread
.push_vector(Self::zero().0)
.map_err(|exit| Error::from_thread_exit(thread, exit))
}
}
}
impl<'lua> IntoLua<'lua> for Vector {
fn into_lua(self, _: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
Ok(Value::Vector(self))
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe {
thread
.as_vm()
.push_vector(self.0)
.map_err(|exit| Error::from_thread_exit(thread, exit))?;
}
Ok(())
}
}
impl<'lua> FromLua<'lua> for Vector {
fn from_lua(value: Value<'lua>, _: crate::LuaRef<'lua>) -> Result<Self, Error> {
match value {
Value::Vector(value) => Ok(value),
value => Err(Error::from_lua_conversion(
value.type_name(),
"vector",
None,
)),
}
}
unsafe fn from_stack(thread: &Thread<'lua>, index: i32) -> Result<Self, Error> {
unsafe { thread.as_vm().to_vector(index) }
.map(Vector)
.ok_or_else(|| {
Error::from_lua_conversion(thread.stack_type_name(index).as_str(), "vector", None)
})
}
}