GitHub - sniper00/lrust: Lua extension library written in Rust

Moon Extensions

This library provides Lua extensions for Moon, implemented in Rust . By using Rust, we can use its ecosystem, including the tokio runtime.

To compile this extension, you need to install the Rust environment.

Add extension library: premake5 add --package=https://github.com/sniper00/lrust.git

Libraries

1. Excel Reader

local excel = require "rust.excel"

local res = excel.read("example.xlsx")

--[[
---format
{
    {
        "data":{
            {"A1","B1","C1"},
            {"A2","B2","C2"},
            {"A3","B3","C3"}
        },
        "sheet_name":"Sheet1"
    },
    {
        "data":{
            {"A1","B1","C1"},
            {"A2","B2","C2"},
            {"A3","B3","C3"}
        },
        "sheet_name":"Sheet2"
    }
}
]]

2. Https Client

    local httpc = require("ext.httpc")
    local moon = require("moon")
    moon.async(function()
        local response = httpc.get("https://bing.com")
        print(response.status_code==200)
    end)

3. Sqlx

IMPORTANT: When shutting down and you need to ensure all data is persisted to the database, you must wait until the counter for the specific database connection in M.stats() returns to 0 before closing that connection or exiting the process

local moon = require "moon"
local sqlx = require "ext.sqlx"

moon.loglevel("INFO")

moon.async(function()
    local info = {
        cc      = 300,
        gpsname = "gps1",
        track   = {
            segments = {
                [1] = {
                    HR        = 73,
                    location  = {
                        [1] = 47.763,
                        [2] = 13.4034,
                    },
                    starttime = "2018-10-14 10:05:14",
                },
                [2] = {
                    HR        = 130,
                    location  = {
                        [1] = 47.706,
                        [2] = 13.2635,
                    },
                    starttime = "2018-10-14 10:39:21",
                },
            },
        },
    }

    local sql = string.format([[
        drop table if exists userdata;
    ]])

    local sql2 = [[
        --create userdata table
        create table userdata (
            uid	bigint,
            key		text,
            value   text,
            CONSTRAINT pk_userdata PRIMARY KEY (uid, key)
           );
    ]]


    local db = sqlx.connect("postgres://postgres:123456@localhost/postgres", "test")
    print(db)
    if db.kind then
        print("connect failed", db.message)
        return
    end

    print_r(db:transaction({
        {sql},
        {sql2},
    }))

    local result = db:query(
        "INSERT INTO userdata (uid, key, value) values($1, $2, $3) on conflict (uid, key) do update set value = excluded.value;",
        235, "info2", info)
    print_r(result)

    local st = moon.clock()
    local trans = {}
    for i = 1, 10000 do
        trans[#trans+1] = {"INSERT INTO userdata (uid, key, value) values($1, $2, $3) on conflict (uid, key) do update set value = excluded.value;", 235, "info2", info}
    end
    print_r(db:transaction(trans))
    print("trans cost", moon.clock() - st)

    local st = moon.clock()
    for i = 10001, 20000 do
        local res = db:query(
            "INSERT INTO userdata (uid, key, value) values($1, $2, $3) on conflict (uid, key) do update set value = excluded.value;",
            235, "info2", info)

        if res.kind then
            print("error", res.message)
            break
        end
    end
    print("cost", moon.clock() - st)

    ---sqlite
    local sqlitedb = sqlx.connect("sqlite://memory:", "test2")

    print_r(sqlitedb:query("CREATE TABLE test (id INTEGER PRIMARY KEY, content TEXT);"))
    print_r(sqlitedb:query("INSERT INTO test (content) VALUES ('Hello, World!');"))
    print_r(sqlitedb:query("SELECT * FROM test;"))

    while true do
        local done = true
        local stats = sqlx.stats()
        print_r(stats) -- Query sqlx left task count
        for k,v in pairs(sqlx.stats()) do
            if v > 0 then
                done =false
                break
            end
        end
        if done then
            break
        end
        moon.sleep(100)
    end
    moon.quit()
end)

4. MongoDB

local moon = require "moon"

local mongodb = require "ext.mongodb"

moon.async(function()
    local db = mongodb.connect("mongodb://127.0.0.1:27017/?serverSelectionTimeoutMS=2000", "gamedb1")
    if db.kind then
        print("connect failed", db.message)
        return
    end

    local coll = db:collection("mydatabase", "mycollection")

    local res = coll:insert_one({
        cc      = 300,
        gpsname = "gps1",
        track   = {
            segments = {
                [1] = {
                    HR        = 73,
                    location  = {
                        [1] = 47.763,
                        [2] = 13.4034,
                    },
                    starttime = "2018-10-14 10:05:14",
                },
                [2] = {
                    HR        = 130,
                    location  = {
                        [1] = 47.706,
                        [2] = 13.2635,
                    },
                    starttime = "2018-10-14 10:39:21",
                },
            },
        },
    })

    print_r(res)

    res = coll:update_one({cc = 300}, {
        ["$set"] = {
            ["track.segments.1.HR"] = 100,
        }
    })

    print_r(res)

    res = coll:find({cc = 300}, {limit = 10})

    print_r(res)

    res = coll:find_one({cc = 300})
    print_r(res)

    print_r(coll:delete_one({cc = 300}))

    print_r(coll:delete_many({cc = 300}))

    res = coll:find_one({cc = 300})
    print_r(res)

    res = coll:insert_one({
        cc      = 300,
        gpsname = "gps1",
        track   = {
            segments = {
                [1] = {
                    HR        = 73,
                    location  = {
                        [1] = 47.763,
                        [2] = 13.4034,
                    },
                    starttime = "2018-10-14 10:05:14",
                },
                [2] = {
                    HR        = 130,
                    location  = {
                        [1] = 47.706,
                        [2] = 13.2635,
                    },
                    starttime = "2018-10-14 10:39:21",
                },
            },
        },
    })

    print_r(res)

    local bt = moon.clock()
    for i=1,10000 do
        coll:insert_one({
            cc      = 300,
            gpsname = "gps1",
            track   = {
                segments = {
                    [1] = {
                        HR        = 73,
                        location  = {
                            [1] = 47.763,
                            [2] = 13.4034,
                        },
                        starttime = "2018-10-14 10:05:14",
                    },
                    [2] = {
                        HR        = 130,
                        location  = {
                            [1] = 47.706,
                            [2] = 13.2635,
                        },
                        starttime = "2018-10-14 10:39:21",
                    },
                },
            },
        })
    end
    print("insert 10000 use time", moon.clock() - bt)

end)

5. Crypto

-- AES Encryption/Decryption Test Script
local crypto = require("rust.crypto")

print("=== AES Encryption/Decryption Test ===")

-- Test 1: Basic encryption/decryption
print("\n1. Basic encryption/decryption test:")

-- Generate random key
local key = crypto.generate_key() -- Default 32 bytes for AES-256
print("Generated key length:", #key, "bytes")

-- Data to encrypt
local data = "Hello, AES-GCM encryption!"
print("Original data:", data)

-- Encrypt data
local encrypted_data, nonce = crypto.aes_encrypt(data, key)
print("Encrypted data length:", #encrypted_data, "bytes")
print("Nonce length:", #nonce, "bytes")

-- Decrypt data
local decrypted_data = crypto.aes_decrypt(encrypted_data, key, nonce)
print("Decrypted data:", decrypted_data)

-- Verify result
if data == decrypted_data then
    print("✓ Encryption/decryption test passed")
else
    print("✗ Encryption/decryption test failed")
end

-- Test 2: Using custom nonce
print("\n2. Custom nonce test:")

local custom_nonce = crypto.generate_nonce()
print("Custom nonce length:", #custom_nonce, "bytes")

local data2 = "Testing with custom nonce"
local encrypted_data2, returned_nonce = crypto.aes_encrypt(data2, key, custom_nonce)
local decrypted_data2 = crypto.aes_decrypt(encrypted_data2, key, custom_nonce)

print("Original data:", data2)
print("Decrypted data:", decrypted_data2)

if data2 == decrypted_data2 then
    print("✓ Custom nonce test passed")
else
    print("✗ Custom nonce test failed")
end

-- Test 3: Generate keys of different lengths
print("\n3. Different key length test:")

local key16 = crypto.generate_key(16) -- 16 bytes for AES-128
local key24 = crypto.generate_key(24) -- 24 bytes for AES-192
local key32 = crypto.generate_key(32) -- 32 bytes for AES-256

print("16-byte key length:", #key16)
print("24-byte key length:", #key24)
print("32-byte key length:", #key32)

-- Test 4: AES-128 encryption/decryption
print("\n4. AES-128 encryption/decryption test:")

local aes128_key = crypto.generate_key(16) -- 16 bytes for AES-128
local aes128_data = "Testing AES-128 GCM encryption!"
print("AES-128 key length:", #aes128_key, "bytes")
print("Original data:", aes128_data)

-- Encrypt with AES-128
local aes128_encrypted, aes128_nonce = crypto.aes_encrypt(aes128_data, aes128_key)
print("AES-128 encrypted data length:", #aes128_encrypted, "bytes")
print("AES-128 nonce length:", #aes128_nonce, "bytes")

-- Decrypt with AES-128
local aes128_decrypted = crypto.aes_decrypt(aes128_encrypted, aes128_key, aes128_nonce)
print("AES-128 decrypted data:", aes128_decrypted)

-- Verify AES-128 result
if aes128_data == aes128_decrypted then
    print("✓ AES-128 encryption/decryption test passed")
else
    print("✗ AES-128 encryption/decryption test failed")
end

-- Test 5: Binary data encryption/decryption
print("\n5. Binary data test:")

local binary_data = string.rep("\x00\x01\x02\x03\x04\x05\x06\x07", 10)
print("Binary data length:", #binary_data, "bytes")

local encrypted_binary, nonce_binary = crypto.aes_encrypt(binary_data, key)
local decrypted_binary = crypto.aes_decrypt(encrypted_binary, key, nonce_binary)

if binary_data == decrypted_binary then
    print("✓ Binary data test passed")
else
    print("✗ Binary data test failed")
end

-- Test 6: AES-128 vs AES-256 comparison
print("\n6. AES-128 vs AES-256 comparison test:")

local test_data = "This is a test message for comparing AES-128 and AES-256 performance and functionality."
local aes128_key_comp = crypto.generate_key(16) -- AES-128 key
local aes256_key_comp = crypto.generate_key(32) -- AES-256 key

print("Test data:", test_data)
print("Test data length:", #test_data, "bytes")

-- AES-128 encryption
local aes128_enc, aes128_nonce_comp = crypto.aes_encrypt(test_data, aes128_key_comp)
local aes128_dec = crypto.aes_decrypt(aes128_enc, aes128_key_comp, aes128_nonce_comp)

-- AES-256 encryption  
local aes256_enc, aes256_nonce_comp = crypto.aes_encrypt(test_data, aes256_key_comp)
local aes256_dec = crypto.aes_decrypt(aes256_enc, aes256_key_comp, aes256_nonce_comp)

print("AES-128 encrypted length:", #aes128_enc, "bytes")
print("AES-256 encrypted length:", #aes256_enc, "bytes")

-- Verify both results
local aes128_ok = (test_data == aes128_dec)
local aes256_ok = (test_data == aes256_dec)

if aes128_ok and aes256_ok then
    print("✓ Both AES-128 and AES-256 comparison test passed")
elseif aes128_ok then
    print("✓ AES-128 passed, ✗ AES-256 failed")
elseif aes256_ok then
    print("✗ AES-128 failed, ✓ AES-256 passed")
else
    print("✗ Both AES-128 and AES-256 comparison test failed")
end

print("\n=== Test Complete ===")

6. SqlServer

Modify lib-lualib/Cargo.toml to enable tiberius feature。

[features]
default = ["excel", "sqlx", "mongodb", "websocket", "http", "json", "tiberius"]
local moon = require("moon")
local sqlserver = require("ext.sqlserver")

local info = {
    cc      = 300,
    gpsname = "gps1",
    track   = {
        segments = {
            [1] = {
                HR        = 73,
                location  = {
                    [1] = 47.763,
                    [2] = 13.4034,
                },
                starttime = "2018-10-14 10:05:14",
            },
            [2] = {
                HR        = 130,
                location  = {
                    [1] = 47.706,
                    [2] = 13.2635,
                },
                starttime = "2018-10-14 10:39:21",
            },
        },
    },
}

-- Build connection string
local config_string = sqlserver.build_connection_string({
    server = "localhost",
    port = 1433,
    database = "testdb",
    username = "sa",
    password = "A12345678b!",
    integrated_security = true,
    trust_server_certificate = true
})

print(config_string)

local sql = string.format([[
        IF OBJECT_ID('userdata', 'U') IS NOT NULL DROP TABLE userdata;
    ]])

local sql2 = [[
        --create userdata table
        CREATE TABLE userdata (
            uid BIGINT NOT NULL,
            [key] NVARCHAR(255) NOT NULL,
            value NVARCHAR(MAX),
            CONSTRAINT pk_userdata PRIMARY KEY (uid, [key])
        );
]]

moon.async(function()
    -- Connect to database
    local db = sqlserver.connect(config_string, "main_db", 30000)

    if db.kind then
        print("connect failed", db.message)
        return
    end

    print_r(db:transaction({
        { sql },
        { sql2 },
    }))

    local result = db:query(
        "MERGE userdata AS target USING (VALUES (@P1, @P2, @P3)) AS source (uid, [key], value) ON (target.uid = source.uid AND target.[key] = source.[key]) WHEN MATCHED THEN UPDATE SET value = source.value WHEN NOT MATCHED THEN INSERT (uid, [key], value) VALUES (source.uid, source.[key], source.value);",
        235, "info2", info)
    print_r(result)

    local st = moon.clock()
    local trans = {}
    for i = 1, 10000 do
        trans[#trans + 1] = {
            "MERGE userdata AS target USING (VALUES (@P1, @P2, @P3)) AS source (uid, [key], value) ON (target.uid = source.uid AND target.[key] = source.[key]) WHEN MATCHED THEN UPDATE SET value = source.value WHEN NOT MATCHED THEN INSERT (uid, [key], value) VALUES (source.uid, source.[key], source.value);", 235,
            "info2", info }
    end
    print_r(db:transaction(trans))
    print("trans cost", moon.clock() - st)

    local st = moon.clock()
    for i = 10001, 20000 do
        local res = db:query(
            "MERGE userdata AS target USING (VALUES (@P1, @P2, @P3)) AS source (uid, [key], value) ON (target.uid = source.uid AND target.[key] = source.[key]) WHEN MATCHED THEN UPDATE SET value = source.value WHEN NOT MATCHED THEN INSERT (uid, [key], value) VALUES (source.uid, source.[key], source.value);",
            235, "info2", info)
        --  print_r(res)
        if res.kind then
            print("error", res.message)
            break
        end
    end
    print("cost", moon.clock() - st)
end)