lu 0.5.1

A library for embedding Luau
Documentation
use std::{
    cell::RefCell,
    ffi::{CStr, CString},
    marker::PhantomData,
    ops::Deref,
    ptr::NonNull,
};

use crate::{Config, Stack, ThreadRef, Type, Userdata};

#[repr(transparent)]
pub struct FnReturn(i32);

#[repr(transparent)]
pub struct Context<C: Config>(NonNull<sys::lua_State>, PhantomData<C>);

impl<C: Config> Deref for Context<C> {
    type Target = Stack<C>;

    fn deref(&self) -> &Self::Target {
        unsafe { std::mem::transmute(self) }
    }
}

impl<C: Config> Context<C> {
    pub fn arg_error(&self, narg: u32, reason: &CStr) -> ! {
        unsafe { sys::luaL_argerror(self.as_ptr(), narg as _, reason.as_ptr()) }
    }

    pub fn arg_type_error(&self, narg: u32, type_name: &CStr) -> ! {
        unsafe { sys::luaL_typeerror(self.as_ptr(), narg as _, type_name.as_ptr()) }
    }

    pub fn arg_boolean(&self, narg: u32) -> bool {
        self.to_boolean(narg as _)
            .unwrap_or_else(|| self.arg_type_error(narg, c"boolean"))
    }

    pub fn arg_boolean_opt(&self, narg: u32) -> Option<bool> {
        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::Boolean => Some(self.to_boolean_unchecked(narg as _)),
            _ => self.arg_type_error(narg, c"boolean or nil"),
        }
    }

    pub fn arg_number(&self, narg: u32) -> f64 {
        self.to_number(narg as _)
            .unwrap_or_else(|| self.arg_type_error(narg, c"number"))
    }

    pub fn arg_number_opt(&self, narg: u32) -> Option<f64> {
        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::Number => Some(self.to_number_unchecked(narg as _)),
            _ => self.arg_type_error(narg, c"number or nil"),
        }
    }

    pub fn arg_vector(&self, narg: u32) -> (f32, f32, f32) {
        self.to_vector(narg as _)
            .unwrap_or_else(|| self.arg_type_error(narg, c"vector"))
    }

    pub fn arg_vector_opt(&self, narg: u32) -> Option<(f32, f32, f32)> {
        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::Vector => Some(unsafe { self.to_vector_unchecked(narg as _) }),
            _ => self.arg_type_error(narg, c"vector or nil"),
        }
    }

    pub fn arg_string_slice(&self, narg: u32) -> &[u8] {
        self.to_string_slice(narg as _)
            .unwrap_or_else(|| self.arg_type_error(narg, c"string"))
    }

    pub fn arg_string_slice_opt(&self, narg: u32) -> Option<&[u8]> {
        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::String => Some(unsafe { self.to_string_slice_unchecked(narg as _) }),
            _ => self.arg_type_error(narg, c"string or nil"),
        }
    }

    pub fn arg_string_str(&self, narg: u32) -> &str {
        self.to_string_str(narg as _)
            .unwrap_or_else(|| self.arg_type_error(narg, c"utf-8 string"))
    }

    pub fn arg_string_str_opt(&self, narg: u32) -> Option<&str> {
        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::String => {
                if let Some(s) = unsafe { self.to_string_str_unchecked(narg as _) } {
                    Some(s)
                } else {
                    self.arg_type_error(narg, c"utf-8 string")
                }
            }
            _ => self.arg_type_error(narg, c"utf-8 string or nil"),
        }
    }

    pub fn arg_table(&self, narg: u32) {
        if !self.is_table(narg as _) {
            self.arg_type_error(narg, c"table");
        }
    }

    pub fn arg_table_opt(&self, narg: u32) -> Option<()> {
        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::Table => Some(()),
            _ => self.arg_type_error(narg, c"table or nil"),
        }
    }

    pub fn arg_function(&self, narg: u32) {
        if !self.is_function(narg as _) {
            self.arg_type_error(narg, c"function");
        }
    }

    pub fn arg_function_opt(&self, narg: u32) -> Option<()> {
        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::Function => Some(()),
            _ => self.arg_type_error(narg, c"function or nil"),
        }
    }

    pub fn arg_userdata<T: Userdata>(&self, narg: u32) -> &RefCell<T> {
        self.to_userdata(narg as _).unwrap_or_else(|| {
            let type_name = CString::new(T::name()).expect("userdata name contains null byte");

            self.arg_type_error(narg, type_name.as_c_str())
        })
    }

    pub fn arg_userdata_opt<T: Userdata>(&self, narg: u32) -> Option<&RefCell<T>> {
        fn error<C: Config, T: Userdata>(ctx: &Context<C>, narg: u32) -> ! {
            let type_name = CString::new(format!("{} or nil", T::name()))
                .expect("userdata name contains null byte");

            ctx.arg_type_error(narg, type_name.as_c_str())
        }

        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::Userdata => Some(
                self.to_userdata(narg as _)
                    .unwrap_or_else(|| error::<_, T>(self, narg)),
            ),
            _ => error::<_, T>(self, narg),
        }
    }

    pub fn arg_thread(&self, narg: u32) -> ThreadRef<C> {
        self.to_thread(narg as _)
            .unwrap_or_else(|| self.arg_type_error(narg, c"thread"))
    }

    pub fn arg_thread_opt(&self, narg: u32) -> Option<ThreadRef<C>> {
        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::Thread => Some(unsafe { self.to_thread_unchecked(narg as _) }),
            _ => self.arg_type_error(narg, c"thread or nil"),
        }
    }

    pub fn arg_buffer(&self, narg: u32) -> (*mut u8, usize) {
        self.to_buffer(narg as _)
            .unwrap_or_else(|| self.arg_type_error(narg, c"buffer"))
    }

    pub fn arg_buffer_opt(&self, narg: u32) -> Option<(*mut u8, usize)> {
        match self.type_of(narg as _) {
            Type::Nil | Type::None => None,
            Type::Buffer => Some(unsafe { self.to_buffer_unchecked(narg as _) }),
            _ => self.arg_type_error(narg, c"buffer or nil"),
        }
    }

    pub fn push_upvalue(&self, nup: u32) {
        self.push_copy(sys::lua_upvalueindex(nup as _));
    }

    pub fn set_upvalue(&self, nup: u32) {
        self.replace(sys::lua_upvalueindex(nup as _));
    }

    pub fn ret(self) -> FnReturn {
        FnReturn(0)
    }

    pub fn ret_with(self, n: u32) -> FnReturn {
        FnReturn(n as _)
    }

    pub fn yld(self) -> FnReturn {
        unsafe { FnReturn(sys::lua_yield(self.as_ptr(), 0)) }
    }

    pub fn yld_with(self, n: u32) -> FnReturn {
        unsafe { FnReturn(sys::lua_yield(self.as_ptr(), n as _)) }
    }
}