diff --git a/src/lune/builtins/luau.rs b/src/lune/builtins/luau.rs index 60ae267..c80a630 100644 --- a/src/lune/builtins/luau.rs +++ b/src/lune/builtins/luau.rs @@ -1,102 +1,12 @@ use mlua::prelude::*; -use mlua::Compiler as LuaCompiler; -use crate::lune::lua::table::TableBuilder; +use crate::lune::lua::{ + luau::{LuauCompileOptions, LuauLoadOptions}, + table::TableBuilder, +}; -const DEFAULT_DEBUG_NAME: &str = "luau.load(...)"; const BYTECODE_ERROR_BYTE: u8 = 0; -struct CompileOptions { - pub optimization_level: u8, - pub coverage_level: u8, - pub debug_level: u8, -} - -impl Default for CompileOptions { - fn default() -> Self { - Self { - optimization_level: 1, - coverage_level: 0, - debug_level: 1, - } - } -} - -impl<'lua> FromLua<'lua> for CompileOptions { - fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult { - Ok(match value { - LuaValue::Nil => Self { - optimization_level: 1, - coverage_level: 0, - debug_level: 1, - }, - LuaValue::Table(t) => { - let optimization_level: Option = t.get("optimizationLevel")?; - let coverage_level: Option = t.get("coverageLevel")?; - let debug_level: Option = t.get("debugLevel")?; - - Self { - optimization_level: optimization_level.unwrap_or(1), - coverage_level: coverage_level.unwrap_or(0), - debug_level: debug_level.unwrap_or(1), - } - } - _ => { - return Err(LuaError::FromLuaConversionError { - from: value.type_name(), - to: "CompileOptions", - message: Some(format!( - "Invalid compile options - expected table, got {}", - value.type_name() - )), - }) - } - }) - } -} - -struct LoadOptions { - pub debug_name: String, -} - -impl Default for LoadOptions { - fn default() -> Self { - Self { - debug_name: DEFAULT_DEBUG_NAME.to_string(), - } - } -} - -impl<'lua> FromLua<'lua> for LoadOptions { - fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult { - Ok(match value { - LuaValue::Nil => Self { - debug_name: DEFAULT_DEBUG_NAME.to_string(), - }, - LuaValue::Table(t) => { - let debug_name: Option = t.get("debugName")?; - - Self { - debug_name: debug_name.unwrap_or(DEFAULT_DEBUG_NAME.to_string()), - } - } - LuaValue::String(s) => Self { - debug_name: s.to_str()?.to_string(), - }, - _ => { - return Err(LuaError::FromLuaConversionError { - from: value.type_name(), - to: "LoadOptions", - message: Some(format!( - "Invalid load options - expected string or table, got {}", - value.type_name() - )), - }) - } - }) - } -} - pub fn create(lua: &'static Lua) -> LuaResult { TableBuilder::new(lua)? .with_function("compile", compile_source)? @@ -106,29 +16,24 @@ pub fn create(lua: &'static Lua) -> LuaResult { fn compile_source<'lua>( lua: &'lua Lua, - (source, options): (LuaString<'lua>, CompileOptions), + (source, options): (LuaString<'lua>, LuauCompileOptions), ) -> LuaResult> { - let source_bytecode_bytes = LuaCompiler::default() - .set_optimization_level(options.optimization_level) - .set_coverage_level(options.coverage_level) - .set_debug_level(options.debug_level) - .compile(source); + let bytecode = options.into_compiler().compile(source); - let first_byte = source_bytecode_bytes.first().unwrap(); - - match *first_byte { - BYTECODE_ERROR_BYTE => Err(LuaError::RuntimeError( - String::from_utf8(source_bytecode_bytes).unwrap(), + match bytecode.first() { + Some(&BYTECODE_ERROR_BYTE) => Err(LuaError::RuntimeError( + String::from_utf8_lossy(&bytecode).into_owned(), )), - _ => lua.create_string(source_bytecode_bytes), + Some(_) => lua.create_string(bytecode), + None => panic!("Compiling resulted in empty bytecode"), } } fn load_source<'a>( lua: &'static Lua, - (source, options): (LuaString<'a>, LoadOptions), + (source, options): (LuaString<'a>, LuauLoadOptions), ) -> LuaResult> { - lua.load(source.to_str()?.trim_start()) + lua.load(source.as_bytes()) .set_name(options.debug_name) .into_function() } diff --git a/src/lune/lua/luau/mod.rs b/src/lune/lua/luau/mod.rs new file mode 100644 index 0000000..bf42f1f --- /dev/null +++ b/src/lune/lua/luau/mod.rs @@ -0,0 +1,3 @@ +mod options; + +pub use options::{LuauCompileOptions, LuauLoadOptions}; diff --git a/src/lune/lua/luau/options.rs b/src/lune/lua/luau/options.rs new file mode 100644 index 0000000..a3c0cc0 --- /dev/null +++ b/src/lune/lua/luau/options.rs @@ -0,0 +1,106 @@ +use mlua::prelude::*; +use mlua::Compiler as LuaCompiler; + +const DEFAULT_DEBUG_NAME: &str = "luau.load(...)"; + +pub struct LuauCompileOptions { + pub(crate) optimization_level: u8, + pub(crate) coverage_level: u8, + pub(crate) debug_level: u8, +} + +impl LuauCompileOptions { + pub fn into_compiler(self) -> LuaCompiler { + LuaCompiler::default() + .set_optimization_level(self.optimization_level) + .set_coverage_level(self.coverage_level) + .set_debug_level(self.debug_level) + } +} + +impl Default for LuauCompileOptions { + fn default() -> Self { + // NOTE: This is the same as LuaCompiler::default() values, but they are + // not accessible from outside of mlua so we need to recreate them here. + Self { + optimization_level: 1, + coverage_level: 0, + debug_level: 1, + } + } +} + +impl<'lua> FromLua<'lua> for LuauCompileOptions { + fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult { + Ok(match value { + LuaValue::Nil => Self::default(), + LuaValue::Table(t) => { + let mut options = Self::default(); + + if let Some(optimization_level) = t.get("optimizationLevel")? { + options.optimization_level = optimization_level; + } + if let Some(coverage_level) = t.get("coverageLevel")? { + options.coverage_level = coverage_level; + } + if let Some(debug_level) = t.get("debugLevel")? { + options.debug_level = debug_level; + } + + options + } + _ => { + return Err(LuaError::FromLuaConversionError { + from: value.type_name(), + to: "CompileOptions", + message: Some(format!( + "Invalid compile options - expected table, got {}", + value.type_name() + )), + }) + } + }) + } +} + +pub struct LuauLoadOptions { + pub(crate) debug_name: String, +} + +impl Default for LuauLoadOptions { + fn default() -> Self { + Self { + debug_name: DEFAULT_DEBUG_NAME.to_string(), + } + } +} + +impl<'lua> FromLua<'lua> for LuauLoadOptions { + fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult { + Ok(match value { + LuaValue::Nil => Self::default(), + LuaValue::Table(t) => { + let mut options = Self::default(); + + if let Some(debug_name) = t.get("debugName")? { + options.debug_name = debug_name; + } + + options + } + LuaValue::String(s) => Self { + debug_name: s.to_string_lossy().to_string(), + }, + _ => { + return Err(LuaError::FromLuaConversionError { + from: value.type_name(), + to: "LoadOptions", + message: Some(format!( + "Invalid load options - expected string or table, got {}", + value.type_name() + )), + }) + } + }) + } +} diff --git a/src/lune/lua/mod.rs b/src/lune/lua/mod.rs index 12c39cd..944ea44 100644 --- a/src/lune/lua/mod.rs +++ b/src/lune/lua/mod.rs @@ -2,6 +2,7 @@ mod create; pub mod async_ext; pub mod fs; +pub mod luau; pub mod net; pub mod process; pub mod serde;