luau 0.732.0

Safe lifetime-bound Rust embedding API for the Luau runtime
use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};

use luau_vm::Thread as VmThread;
use luau_vm::native::NativeCallResult;
use luau_vm::thread::StackGuard;

use super::Lua;
use crate::error::Error;

type LibraryOpen = unsafe fn(&VmThread) -> NativeCallResult;

const LIBRARIES: &[(StdLib, LibraryOpen)] = &[
    (StdLib::BASE, VmThread::open_base),
    (StdLib::COROUTINE, VmThread::open_coroutine),
    (StdLib::TABLE, VmThread::open_table),
    (StdLib::OS, VmThread::open_os),
    (StdLib::STRING, VmThread::open_string),
    (StdLib::MATH, VmThread::open_math),
    (StdLib::DEBUG, VmThread::open_debug),
    (StdLib::UTF8, VmThread::open_utf8),
    (StdLib::BIT32, VmThread::open_bit32),
    (StdLib::BUFFER, VmThread::open_buffer),
    (StdLib::VECTOR, VmThread::open_vector),
    (StdLib::INTEGER, VmThread::open_integer),
    (StdLib::CLASS, VmThread::open_class),
];

/// Flags describing the Luau standard libraries to load.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct StdLib(u32);

impl StdLib {
    /// The base library.
    pub const BASE: Self = Self(1);
    /// The coroutine library.
    pub const COROUTINE: Self = Self(1 << 1);
    /// The table library.
    pub const TABLE: Self = Self(1 << 2);
    /// The OS library.
    pub const OS: Self = Self(1 << 3);
    /// The string library.
    pub const STRING: Self = Self(1 << 4);
    /// The math library.
    pub const MATH: Self = Self(1 << 5);
    /// The debug library.
    pub const DEBUG: Self = Self(1 << 6);
    /// The UTF-8 library.
    pub const UTF8: Self = Self(1 << 7);
    /// The 32-bit bitwise library.
    pub const BIT32: Self = Self(1 << 8);
    /// The buffer library.
    pub const BUFFER: Self = Self(1 << 9);
    /// The vector library.
    pub const VECTOR: Self = Self(1 << 10);
    /// The integer library.
    pub const INTEGER: Self = Self(1 << 11);
    /// The class library.
    pub const CLASS: Self = Self(1 << 12);

    /// No standard libraries.
    pub const NONE: Self = Self(0);
    /// All standard libraries.
    pub const ALL: Self = Self(u32::MAX);

    /// Returns whether this set contains any library in `lib`.
    pub const fn contains(self, lib: Self) -> bool {
        (self.0 & lib.0) != 0
    }

    pub(crate) fn open(self, thread: &VmThread) -> Result<(), Error> {
        if self == Self::NONE {
            return Ok(());
        }

        if self == Self::ALL {
            return unsafe { open_all(thread) };
        }

        for (lib, open) in LIBRARIES {
            if self.contains(*lib) {
                unsafe { open_library(thread, *open)? };
            }
        }

        Ok(())
    }
}

impl Lua {
    /// Loads the selected standard libraries into this state.
    pub fn load_std_libs(&self, libs: StdLib) -> Result<(), Error> {
        if libs != StdLib::NONE {
            self.runtime.invalidate_managed_safe_env();
        }
        libs.open(self.state.main_thread())
    }
}

impl BitAnd for StdLib {
    type Output = Self;

    fn bitand(self, rhs: Self) -> Self::Output {
        Self(self.0 & rhs.0)
    }
}

impl BitAndAssign for StdLib {
    fn bitand_assign(&mut self, rhs: Self) {
        self.0 &= rhs.0;
    }
}

impl BitOr for StdLib {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self::Output {
        Self(self.0 | rhs.0)
    }
}

impl BitOrAssign for StdLib {
    fn bitor_assign(&mut self, rhs: Self) {
        self.0 |= rhs.0;
    }
}

impl BitXor for StdLib {
    type Output = Self;

    fn bitxor(self, rhs: Self) -> Self::Output {
        Self(self.0 ^ rhs.0)
    }
}

impl BitXorAssign for StdLib {
    fn bitxor_assign(&mut self, rhs: Self) {
        self.0 ^= rhs.0;
    }
}

impl Not for StdLib {
    type Output = Self;

    fn not(self) -> Self::Output {
        Self(!self.0)
    }
}

unsafe fn open_all(thread: &VmThread) -> Result<(), Error> {
    let _stack = unsafe { StackGuard::new(thread) };
    match unsafe { thread.open_libs() } {
        Ok(_) => Ok(()),
        Err(exit) => Err(Error::from_thread_exit(thread, exit)),
    }
}

unsafe fn open_library(thread: &VmThread, open: LibraryOpen) -> Result<(), Error> {
    let _stack = unsafe { StackGuard::new(thread) };
    match unsafe { open(thread) } {
        Ok(_) => Ok(()),
        Err(exit) => Err(Error::from_thread_exit(thread, exit)),
    }
}