luau 0.732.0

Safe lifetime-bound Rust embedding API for the Luau runtime
use core::{fmt, marker::PhantomData};

use luau_vm::Thread as VmThread;
use luau_vm::VmErrorResult;
use luau_vm::thread::StackGuard;

use crate::error::Error;
use crate::lua::Lua;
use crate::lua::runtime::RuntimeData;
use crate::thread::Thread;

pub(crate) struct ValueRef<'lua> {
    thread: &'lua VmThread,
    runtime: &'lua RuntimeData,
    reference: i32,
    pointer: *const (),
    _marker: PhantomData<&'lua Lua>,
}

impl<'lua> ValueRef<'lua> {
    pub(crate) const fn new(
        thread: &'lua VmThread,
        runtime: &'lua RuntimeData,
        reference: i32,
        pointer: *const (),
    ) -> Self {
        Self {
            thread,
            runtime,
            reference,
            pointer,
            _marker: PhantomData,
        }
    }

    pub(crate) fn from_stack(thread: &Thread<'lua>, index: i32) -> VmErrorResult<Self> {
        let pointer = unsafe { thread.as_vm().to_pointer(index) };
        let reference = unsafe { thread.as_vm().ref_value(index)? };
        Ok(Self::new(
            thread.reference_thread(),
            thread.runtime(),
            reference,
            pointer,
        ))
    }

    pub(crate) const fn runtime(&self) -> &'lua RuntimeData {
        self.runtime
    }

    pub(crate) const fn reference_thread(&self) -> &'lua VmThread {
        self.thread
    }

    pub(crate) fn thread(&self) -> Thread<'lua> {
        Thread::new(self.thread, self.runtime)
    }

    pub(crate) const fn pointer(&self) -> *const () {
        self.pointer
    }

    pub(crate) fn same_value(&self, other: &Self) -> bool {
        (unsafe { self.thread.same_vm(other.thread) }) && self.pointer() == other.pointer()
    }

    pub(crate) fn try_clone(&self) -> Result<Self, Error> {
        unsafe {
            let thread = self.thread();
            let vm_thread = thread.as_vm();
            let _stack = StackGuard::new(vm_thread);
            self.push()
                .map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
            let reference = vm_thread
                .ref_value(-1)
                .map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
            Ok(Self::new(
                self.thread,
                self.runtime,
                reference,
                self.pointer,
            ))
        }
    }

    pub(crate) fn push_to(&self, target: impl AsRef<VmThread>) -> Result<(), Error> {
        unsafe {
            let vm_thread = self.thread;
            let target = target.as_ref();
            if !target.same_vm(vm_thread) {
                return Err(Error::foreign_lua_handle());
            }
            if *vm_thread == *target {
                self.push()
                    .map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
            } else {
                let _stack = StackGuard::new(vm_thread);
                self.push()
                    .map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
                vm_thread
                    .x_move(target, 1)
                    .map_err(|exit| Error::from_thread_exit(vm_thread, exit))?;
            }
        }
        Ok(())
    }

    pub(crate) fn push(&self) -> VmErrorResult {
        unsafe { self.thread.get_ref(self.reference).map(drop) }
    }
}

impl Drop for ValueRef<'_> {
    fn drop(&mut self) {
        let thread = self.thread();
        unsafe {
            thread.as_vm().unref_value(self.reference);
        }
    }
}

impl PartialEq for ValueRef<'_> {
    fn eq(&self, other: &Self) -> bool {
        self.same_value(other)
    }
}

impl Eq for ValueRef<'_> {}

impl fmt::Debug for ValueRef<'_> {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(formatter, "Ref({:p})", self.pointer())
    }
}