diff --git a/Cargo.lock b/Cargo.lock index 6432cc7..de30793 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,6 +346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", + "regex-automata 0.4.6", "serde", ] @@ -1367,6 +1368,7 @@ dependencies = [ "async-trait", "async_zip", "blocking", + "bstr", "chrono", "chrono_lc", "clap", diff --git a/Cargo.toml b/Cargo.toml index 8552e1e..2eace8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ path-clean = "1.0" pathdiff = "0.2" pin-project = "1.0" urlencoding = "2.1" +bstr = "1.9.1" ### RUNTIME @@ -87,7 +88,7 @@ tokio = { version = "1.24", features = ["full", "tracing"] } os_str_bytes = { version = "7.0", features = ["conversions"] } mlua-luau-scheduler = { version = "0.0.2" } -mlua = { version = "0.9.6", features = [ +mlua = { version = "0.9.7", features = [ "luau", "luau-jit", "async", diff --git a/src/lune/builtins/fs/mod.rs b/src/lune/builtins/fs/mod.rs index 2c7b9fb..0db1f7f 100644 --- a/src/lune/builtins/fs/mod.rs +++ b/src/lune/builtins/fs/mod.rs @@ -1,6 +1,7 @@ use std::io::ErrorKind as IoErrorKind; use std::path::{PathBuf, MAIN_SEPARATOR}; +use bstr::{BString, ByteSlice}; use mlua::prelude::*; use tokio::fs; @@ -32,6 +33,7 @@ pub fn create(lua: &Lua) -> LuaResult { async fn fs_read_file(lua: &Lua, path: String) -> LuaResult { let bytes = fs::read(&path).await.into_lua_err()?; + lua.create_string(bytes) } @@ -64,8 +66,8 @@ async fn fs_read_dir(_: &Lua, path: String) -> LuaResult> { Ok(dir_strings_no_prefix) } -async fn fs_write_file(_: &Lua, (path, contents): (String, LuaString<'_>)) -> LuaResult<()> { - fs::write(&path, &contents.as_bytes()).await.into_lua_err() +async fn fs_write_file(_: &Lua, (path, contents): (String, BString)) -> LuaResult<()> { + fs::write(&path, contents.as_bytes()).await.into_lua_err() } async fn fs_write_dir(_: &Lua, path: String) -> LuaResult<()> { diff --git a/src/lune/builtins/net/config.rs b/src/lune/builtins/net/config.rs index d801c47..1abd121 100644 --- a/src/lune/builtins/net/config.rs +++ b/src/lune/builtins/net/config.rs @@ -3,6 +3,7 @@ use std::{ net::{IpAddr, Ipv4Addr}, }; +use bstr::{BString, ByteSlice}; use mlua::prelude::*; use reqwest::Method; @@ -107,10 +108,11 @@ impl FromLua<'_> for RequestConfig { Err(_) => HashMap::new(), }; // Extract body - let body = match tab.get::<_, LuaString>("body") { + let body = match tab.get::<_, BString>("body") { Ok(config_body) => Some(config_body.as_bytes().to_owned()), Err(_) => None, }; + // Convert method string into proper enum let method = method.trim().to_ascii_uppercase(); let method = match method.as_ref() { diff --git a/src/lune/builtins/net/mod.rs b/src/lune/builtins/net/mod.rs index 6253dec..d41b047 100644 --- a/src/lune/builtins/net/mod.rs +++ b/src/lune/builtins/net/mod.rs @@ -1,5 +1,6 @@ #![allow(unused_variables)] +use bstr::BString; use mlua::prelude::*; use mlua_luau_scheduler::LuaSpawnExt; @@ -45,7 +46,7 @@ fn net_json_encode<'lua>( .serialize_to_string(lua, val) } -fn net_json_decode<'lua>(lua: &'lua Lua, json: LuaString<'lua>) -> LuaResult> { +fn net_json_decode<'lua>(lua: &'lua Lua, json: BString) -> LuaResult> { EncodeDecodeConfig::from(EncodeDecodeFormat::Json).deserialize_from_string(lua, json) } diff --git a/src/lune/builtins/net/server/response.rs b/src/lune/builtins/net/server/response.rs index f575990..240a7cd 100644 --- a/src/lune/builtins/net/server/response.rs +++ b/src/lune/builtins/net/server/response.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use bstr::{BString, ByteSlice}; use http_body_util::Full; use hyper::{ body::Bytes, @@ -56,7 +57,7 @@ impl FromLua<'_> for LuaResponse { LuaValue::Table(t) => { let status: Option = t.get("status")?; let headers: Option = t.get("headers")?; - let body: Option = t.get("body")?; + let body: Option = t.get("body")?; let mut headers_map = HeaderMap::new(); if let Some(headers) = headers { diff --git a/src/lune/builtins/net/websocket.rs b/src/lune/builtins/net/websocket.rs index fb7233c..5dba4ec 100644 --- a/src/lune/builtins/net/websocket.rs +++ b/src/lune/builtins/net/websocket.rs @@ -3,6 +3,7 @@ use std::sync::{ Arc, }; +use bstr::{BString, ByteSlice}; use mlua::prelude::*; use futures_util::{ @@ -160,7 +161,7 @@ where methods.add_async_method( "send", - |_, this, (string, as_binary): (LuaString, Option)| async move { + |_, this, (string, as_binary): (BString, Option)| async move { this.send(if as_binary.unwrap_or_default() { WsMessage::Binary(string.as_bytes().to_vec()) } else { diff --git a/src/lune/builtins/serde/encode_decode.rs b/src/lune/builtins/serde/encode_decode.rs index 1f83ce4..d8bae5c 100644 --- a/src/lune/builtins/serde/encode_decode.rs +++ b/src/lune/builtins/serde/encode_decode.rs @@ -1,3 +1,4 @@ +use bstr::{BString, ByteSlice}; use mlua::prelude::*; use serde_json::Value as JsonValue; @@ -89,7 +90,7 @@ impl EncodeDecodeConfig { pub fn deserialize_from_string<'lua>( self, lua: &'lua Lua, - string: LuaString<'lua>, + string: BString, ) -> LuaResult> { let bytes = string.as_bytes(); match self.format { diff --git a/src/lune/builtins/serde/mod.rs b/src/lune/builtins/serde/mod.rs index c149467..910cb59 100644 --- a/src/lune/builtins/serde/mod.rs +++ b/src/lune/builtins/serde/mod.rs @@ -1,3 +1,4 @@ +use bstr::BString; use mlua::prelude::*; pub(super) mod compress_decompress; @@ -27,7 +28,7 @@ fn serde_encode<'lua>( fn serde_decode<'lua>( lua: &'lua Lua, - (format, str): (EncodeDecodeFormat, LuaString<'lua>), + (format, str): (EncodeDecodeFormat, BString), ) -> LuaResult> { let config = EncodeDecodeConfig::from(format); config.deserialize_from_string(lua, str) @@ -35,7 +36,7 @@ fn serde_decode<'lua>( async fn serde_compress<'lua>( lua: &'lua Lua, - (format, str): (CompressDecompressFormat, LuaString<'lua>), + (format, str): (CompressDecompressFormat, BString), ) -> LuaResult> { let bytes = compress(format, str).await?; lua.create_string(bytes) @@ -43,7 +44,7 @@ async fn serde_compress<'lua>( async fn serde_decompress<'lua>( lua: &'lua Lua, - (format, str): (CompressDecompressFormat, LuaString<'lua>), + (format, str): (CompressDecompressFormat, BString), ) -> LuaResult> { let bytes = decompress(format, str).await?; lua.create_string(bytes) diff --git a/tests/fs/copy.luau b/tests/fs/copy.luau index c7ed6dd..1b52320 100644 --- a/tests/fs/copy.luau +++ b/tests/fs/copy.luau @@ -50,15 +50,15 @@ assert(fs.isFile(TEMP_ROOT_PATH_2 .. "/foo/buzz"), "Missing copied file - root/f -- Make sure the copied files are correct assert( - fs.readFile(TEMP_ROOT_PATH_2 .. "/foo/bar/baz") == utils.binaryBlob, + fs.readFile(TEMP_ROOT_PATH_2 .. "/foo/bar/baz") == buffer.tostring(utils.binaryBlob), "Invalid copied file - root/foo/bar/baz" ) assert( - fs.readFile(TEMP_ROOT_PATH_2 .. "/foo/fizz") == utils.binaryBlob, + fs.readFile(TEMP_ROOT_PATH_2 .. "/foo/fizz") == buffer.tostring(utils.binaryBlob), "Invalid copied file - root/foo/fizz" ) assert( - fs.readFile(TEMP_ROOT_PATH_2 .. "/foo/buzz") == utils.binaryBlob, + fs.readFile(TEMP_ROOT_PATH_2 .. "/foo/buzz") == buffer.tostring(utils.binaryBlob), "Invalid copied file - root/foo/buzz" ) diff --git a/tests/fs/files.luau b/tests/fs/files.luau index a49412e..38b7485 100644 --- a/tests/fs/files.luau +++ b/tests/fs/files.luau @@ -11,6 +11,8 @@ fs.writeDir(TEMP_ROOT_PATH) -- Write both of our files +-- binaryBlob is of type buffer to make sure fs.writeFile +-- works with both strings and buffers fs.writeFile(TEMP_ROOT_PATH .. "/test_binary", utils.binaryBlob) fs.writeFile(TEMP_ROOT_PATH .. "/test_json.json", utils.jsonBlob) @@ -18,7 +20,7 @@ fs.writeFile(TEMP_ROOT_PATH .. "/test_json.json", utils.jsonBlob) -- wrote gets us back the original strings assert( - fs.readFile(TEMP_ROOT_PATH .. "/test_binary") == utils.binaryBlob, + fs.readFile(TEMP_ROOT_PATH .. "/test_binary") == buffer.tostring(utils.binaryBlob), "Binary file round-trip resulted in different strings" ) diff --git a/tests/fs/metadata.luau b/tests/fs/metadata.luau index e926a2c..8a5a795 100644 --- a/tests/fs/metadata.luau +++ b/tests/fs/metadata.luau @@ -45,7 +45,7 @@ assert(metaFile.kind == "file", "File metadata kind was invalid") local metaBefore = fs.metadata(TEMP_FILE_PATH) task.wait(1) -fs.writeFile(TEMP_FILE_PATH, utils.binaryBlob .. "\n") +fs.writeFile(TEMP_FILE_PATH, buffer.tostring(utils.binaryBlob) .. "\n") local metaAfter = fs.metadata(TEMP_FILE_PATH) assert( diff --git a/tests/fs/move.luau b/tests/fs/move.luau index 868a095..749c8cb 100644 --- a/tests/fs/move.luau +++ b/tests/fs/move.luau @@ -20,7 +20,7 @@ fs.move("bin/move_test_json.json", "bin/moved_test_json.json") -- wrote gets us back the original strings assert( - fs.readFile("bin/moved_test_binary") == utils.binaryBlob, + fs.readFile("bin/moved_test_binary") == buffer.tostring(utils.binaryBlob), "Binary file round-trip resulted in different strings" ) diff --git a/tests/fs/utils.luau b/tests/fs/utils.luau index a5a947e..89a6964 100644 --- a/tests/fs/utils.luau +++ b/tests/fs/utils.luau @@ -16,6 +16,6 @@ local jsonBlob = serde.encode("json", { -- Return testing data and utils return { - binaryBlob = binaryBlob, + binaryBlob = buffer.fromstring(binaryBlob), jsonBlob = jsonBlob, } diff --git a/tests/net/socket/wss_rw.luau b/tests/net/socket/wss_rw.luau index 8ae6066..fefb244 100644 --- a/tests/net/socket/wss_rw.luau +++ b/tests/net/socket/wss_rw.luau @@ -22,7 +22,9 @@ end) task.wait(1) -socket.send('{"op":1,"d":null}') +local payload = '{"op":1,"d":null}' +socket.send(payload) +socket.send(buffer.fromstring(payload)) socket.close(1000) task.cancel(delayedThread) diff --git a/tests/roblox/rbx-test-files b/tests/roblox/rbx-test-files index 52f2c1a..655b5cc 160000 --- a/tests/roblox/rbx-test-files +++ b/tests/roblox/rbx-test-files @@ -1 +1 @@ -Subproject commit 52f2c1a686e7b67d996005eeddf63b97b170a741 +Subproject commit 655b5cc6a64024709d3662cc45ec4319c87de5a2 diff --git a/tests/serde/compression/files.luau b/tests/serde/compression/files.luau index 8d41492..6f32ed0 100644 --- a/tests/serde/compression/files.luau +++ b/tests/serde/compression/files.luau @@ -33,69 +33,60 @@ local TESTS: { Test } = { } local failed = false -for _, test in TESTS do - local source = fs.readFile(test.Source) - local target = fs.readFile(test.Target) - - local success, compressed = pcall(serde.compress, test.Format, source) +local function testOperation( + operationName: "Compress" | "Decompress", + operation: ( + format: serde.CompressDecompressFormat, + s: buffer | string + ) -> string, + format: serde.CompressDecompressFormat, + source: string | buffer, + target: string +) + local success, res = pcall(operation, format, source) if not success then stdio.ewrite( string.format( - "Compressing source using '%s' format threw an error!\n%s", - tostring(test.Format), - tostring(compressed) + "%sing source using '%s' format threw an error!\n%s", + operationName, + tostring(format), + tostring(res) ) ) failed = true - continue - elseif compressed ~= target then + elseif res ~= target then stdio.ewrite( string.format( - "Compressing source using '%s' format did not produce target!\n", - tostring(test.Format) + "%sing source using '%s' format did not produce target!\n", + operationName, + tostring(format) ) ) stdio.ewrite( string.format( - "Compressed (%d chars long):\n%s\nTarget (%d chars long):\n%s\n\n", - #compressed, - tostring(compressed), + "%sed (%d chars long):\n%s\nTarget (%d chars long):\n%s\n\n", + operationName, + #res, + tostring(res), #target, tostring(target) ) ) failed = true - continue end +end - local success2, decompressed = pcall(serde.decompress, test.Format, target) - if not success2 then - stdio.ewrite( - string.format( - "Decompressing source using '%s' format threw an error!\n%s", - tostring(test.Format), - tostring(decompressed) - ) - ) - failed = true - continue - elseif decompressed ~= source then - stdio.ewrite( - string.format( - "Decompressing target using '%s' format did not produce source!\n", - tostring(test.Format) - ) - ) - stdio.ewrite( - string.format( - "Decompressed (%d chars long):\n%s\n\n", - #decompressed, - tostring(decompressed) - ) - ) - failed = true - continue - end +for _, test in TESTS do + local source = fs.readFile(test.Source) + local target = fs.readFile(test.Target) + + -- Compression + testOperation("Compress", serde.compress, test.Format, source, target) + testOperation("Compress", serde.compress, test.Format, buffer.fromstring(source), target) + + -- Decompression + testOperation("Decompress", serde.decompress, test.Format, target, source) + testOperation("Decompress", serde.decompress, test.Format, buffer.fromstring(target), source) end if failed then diff --git a/types/fs.luau b/types/fs.luau index 1d80604..823f6f7 100644 --- a/types/fs.luau +++ b/types/fs.luau @@ -144,7 +144,7 @@ end @param path The path of the file @param contents The contents of the file ]=] -function fs.writeFile(path: string, contents: string) end +function fs.writeFile(path: string, contents: buffer | string) end --[=[ @within FS diff --git a/types/net.luau b/types/net.luau index 47c6249..e9b793e 100644 --- a/types/net.luau +++ b/types/net.luau @@ -36,7 +36,7 @@ export type FetchParamsOptions = { export type FetchParams = { url: string, method: HttpMethod?, - body: string?, + body: (string | buffer)?, query: HttpQueryMap?, headers: HttpHeaderMap?, options: FetchParamsOptions?, @@ -101,7 +101,7 @@ export type ServeRequest = { export type ServeResponse = { status: number?, headers: { [string]: string }?, - body: string?, + body: (string | buffer)?, } type ServeHttpHandler = (request: ServeRequest) -> string | ServeResponse @@ -174,7 +174,7 @@ export type ServeHandle = { export type WebSocket = { closeCode: number?, close: (code: number?) -> (), - send: (message: string, asBinaryMessage: boolean?) -> (), + send: (message: (string | buffer)?, asBinaryMessage: boolean?) -> (), next: () -> string?, } diff --git a/types/serde.luau b/types/serde.luau index 6bd63db..c4a21d8 100644 --- a/types/serde.luau +++ b/types/serde.luau @@ -70,7 +70,7 @@ end @param encoded The string to decode @return The decoded lua value ]=] -function serde.decode(format: EncodeDecodeFormat, encoded: string): any +function serde.decode(format: EncodeDecodeFormat, encoded: buffer | string): any return nil :: any end @@ -93,7 +93,7 @@ end @param s The string to compress @return The compressed string ]=] -function serde.compress(format: CompressDecompressFormat, s: string): string +function serde.compress(format: CompressDecompressFormat, s: buffer | string): string return nil :: any end @@ -116,7 +116,7 @@ end @param s The string to decompress @return The decompressed string ]=] -function serde.decompress(format: CompressDecompressFormat, s: string): string +function serde.decompress(format: CompressDecompressFormat, s: buffer | string): string return nil :: any end