luau 0.732.0

Safe lifetime-bound Rust embedding API for the Luau runtime
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"
);

/// A Luau vector value.
#[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"))]
    /// Creates a three-component vector.
    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")]
    /// Creates a four-component vector.
    pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
        Self([x, y, z, w])
    }

    /// Creates a vector with every component set to zero.
    pub const fn zero() -> Self {
        Self([0.0; Self::SIZE])
    }

    /// Returns the x component.
    pub const fn x(&self) -> f32 {
        self.0[0]
    }

    /// Returns the y component.
    pub const fn y(&self) -> f32 {
        self.0[1]
    }

    /// Returns the z component.
    pub const fn z(&self) -> f32 {
        self.0[2]
    }

    #[cfg(feature = "vector4")]
    /// Returns the w component.
    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)
            })
    }
}