├── .github └── workflows │ └── prebuild.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── lualib └── ext │ ├── httpc.lua │ ├── mongodb.lua │ ├── opendal.lua │ └── sqlx.lua ├── premake5.lua └── rust ├── Cargo.lock ├── Cargo.toml └── crates └── libs ├── lib-core ├── Cargo.toml └── src │ ├── buffer.rs │ ├── context.rs │ ├── error.rs │ └── lib.rs ├── lib-lua-sys ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── src │ ├── laux.rs │ ├── lib.rs │ ├── lua54 │ │ ├── lauxlib.rs │ │ ├── lua.rs │ │ ├── lualib.rs │ │ └── mod.rs │ └── macros.rs └── wrapper_lua.h └── lib-lualib ├── Cargo.toml ├── build.rs └── src ├── lib.rs ├── lua_excel.rs ├── lua_http.rs ├── lua_json.rs ├── lua_mongodb.rs ├── lua_opendal.rs ├── lua_runtime.rs └── lua_sqlx.rs /.github/workflows/prebuild.yml: -------------------------------------------------------------------------------- 1 | name: prebuild 2 | permissions: 3 | contents: write 4 | # Run this workflow every time a new commit pushed to your repository 5 | on: [push, pull_request] 6 | 7 | jobs: 8 | init: 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - uses: actions/github-script@v7 12 | with: 13 | script: | 14 | const releases = await github.rest.repos.listReleases({ 15 | owner: context.repo.owner, 16 | repo: context.repo.repo, 17 | }); 18 | for (const release of releases.data) { 19 | if (release.tag_name === 'prebuilt') { 20 | await github.rest.repos.deleteRelease({ 21 | owner: context.repo.owner, 22 | repo: context.repo.repo, 23 | release_id: release.id, 24 | }); 25 | } 26 | } 27 | try { 28 | await github.rest.git.deleteRef({ 29 | owner: context.repo.owner, 30 | repo: context.repo.repo, 31 | ref: `tags/prebuilt`, 32 | }); 33 | } catch (error) {} 34 | prebuilt: 35 | needs: init 36 | strategy: 37 | fail-fast: false 38 | matrix: 39 | include: 40 | - os: windows-latest 41 | name: windows-x86_64 42 | - os: ubuntu-22.04 43 | name: linux-x86_64 44 | - os: macos-14 45 | name: macos-arm64 46 | runs-on: ${{ matrix.os }} 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@v3 50 | with: 51 | submodules: recursive 52 | - name: Installing premake5 53 | if: runner.os != 'macOS' 54 | uses: Jarod42/install-premake5@v3 55 | - name: Installing premake5 on macOS 56 | if: runner.os == 'macOS' 57 | run: | 58 | brew install premake 59 | - name: Install rust 60 | uses: dtolnay/rust-toolchain@1.86 61 | - name: Build 62 | if: runner.os != 'Windows' 63 | run: | 64 | premake5 build 65 | premake5 publish 66 | zip_name=$(ls *.zip) 67 | echo "zip_name=$zip_name" >> $GITHUB_ENV 68 | - name: Build on Windows 69 | if: runner.os == 'Windows' 70 | run: | 71 | premake5.exe build 72 | premake5.exe publish 73 | pwsh -command '$env:zip_name = (Get-ChildItem -Filter *.zip).Name; echo "zip_name=$env:zip_name" >> $env:GITHUB_ENV' 74 | - uses: softprops/action-gh-release@v2 75 | with: 76 | name: prebuilt 77 | tag_name: prebuilt 78 | fail_on_unmatched_files: true 79 | files: ${{ env.zip_name }} 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 82 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | *.db 5 | *.opendb 6 | *.dll 7 | *.exp 8 | *.ilk 9 | *.lib 10 | *.pdb 11 | *.exe 12 | *.obj 13 | *.log 14 | *.tlog 15 | *.idb 16 | *.so 17 | *.o 18 | *.d 19 | .vs/ 20 | .vscode/ 21 | *.sln 22 | build/ 23 | rust/target/ 24 | Makefile 25 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rd/math3d"] 2 | path = 3rd/math3d 3 | url = https://github.com/cloudwu/math3d.git 4 | [submodule "moon"] 5 | path = moon 6 | url = https://github.com/sniper00/moon.git 7 | [submodule "3rd/sproto"] 8 | path = 3rd/sproto 9 | url = https://github.com/cloudwu/sproto.git 10 | [submodule "3rd/lpeg"] 11 | path = 3rd/lpeg 12 | url = https://github.com/roberto-ieru/LPeg.git 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Bruce 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Moon Extensions 2 | 3 | This library provides Lua extensions for [Moon](https://github.com/sniper00/moon), implemented in Rust and C/C++. By using Rust, we can use its ecosystem, including the `tokio` runtime. 4 | 5 | # Usage 6 | 7 | ## Option 1: Use Precompiled Releases 8 | 9 | You can directly use the precompiled releases. [Download](https://github.com/sniper00/moon-extensions/releases/tag/prebuilt). 10 | 11 | ## Option 2: Manual Compilation 12 | 13 | To compile the project, follow these steps: 14 | 15 | - `git clone --recursive https://github.com/sniper00/moon-extensions.git` 16 | - [Install Premake5](https://premake.github.io/download). 17 | - Run `premake5 build` `premake5 publish`. 18 | 19 | After compiling, the `clib` and `lualib` directories will be automatically copied to the moon directory. 20 | 21 | # Libraries 22 | 23 | ## Rust 24 | 25 | ### 1. Excel Reader 26 | 27 | ```lua 28 | local excel = require "rust.excel" 29 | 30 | local res = excel.read("example.xlsx") 31 | 32 | --[[ 33 | ---format 34 | { 35 | { 36 | "data":{ 37 | {"A1","B1","C1"}, 38 | {"A2","B2","C2"}, 39 | {"A3","B3","C3"} 40 | }, 41 | "sheet_name":"Sheet1" 42 | }, 43 | { 44 | "data":{ 45 | {"A1","B1","C1"}, 46 | {"A2","B2","C2"}, 47 | {"A3","B3","C3"} 48 | }, 49 | "sheet_name":"Sheet2" 50 | } 51 | } 52 | ]] 53 | 54 | ``` 55 | 56 | ### 2. Https Client 57 | 58 | ```lua 59 | local httpc = require("ext.httpc") 60 | local moon = require("moon") 61 | moon.async(function() 62 | local response = httpc.get("https://bing.com") 63 | print(response.status_code==200) 64 | end) 65 | ``` 66 | 67 | ### 3. Sqlx 68 | 69 | ```lua 70 | local moon = require "moon" 71 | local sqlx = require "ext.sqlx" 72 | 73 | moon.loglevel("INFO") 74 | 75 | moon.async(function() 76 | local info = { 77 | cc = 300, 78 | gpsname = "gps1", 79 | track = { 80 | segments = { 81 | [1] = { 82 | HR = 73, 83 | location = { 84 | [1] = 47.763, 85 | [2] = 13.4034, 86 | }, 87 | starttime = "2018-10-14 10:05:14", 88 | }, 89 | [2] = { 90 | HR = 130, 91 | location = { 92 | [1] = 47.706, 93 | [2] = 13.2635, 94 | }, 95 | starttime = "2018-10-14 10:39:21", 96 | }, 97 | }, 98 | }, 99 | } 100 | 101 | local sql = string.format([[ 102 | drop table if exists userdata; 103 | ]]) 104 | 105 | local sql2 = [[ 106 | --create userdata table 107 | create table userdata ( 108 | uid bigint, 109 | key text, 110 | value text, 111 | CONSTRAINT pk_userdata PRIMARY KEY (uid, key) 112 | ); 113 | ]] 114 | 115 | 116 | local db = sqlx.connect("postgres://bruce:123456@localhost/postgres", "test") 117 | print(db) 118 | if db.kind then 119 | print("connect failed", db.message) 120 | return 121 | end 122 | 123 | print_r(db:transaction({ 124 | {sql}, 125 | {sql2}, 126 | })) 127 | 128 | local result = db:query( 129 | "INSERT INTO userdata (uid, key, value) values($1, $2, $3) on conflict (uid, key) do update set value = excluded.value;", 130 | 235, "info2", info) 131 | print_r(result) 132 | 133 | local st = moon.clock() 134 | local trans = {} 135 | for i = 1, 10000 do 136 | 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} 137 | end 138 | print_r(db:transaction(trans)) 139 | print("trans cost", moon.clock() - st) 140 | 141 | local st = moon.clock() 142 | for i = 10001, 20000 do 143 | local res = db:query( 144 | "INSERT INTO userdata (uid, key, value) values($1, $2, $3) on conflict (uid, key) do update set value = excluded.value;", 145 | 235, "info2", info) 146 | 147 | if res.kind then 148 | print("error", res.message) 149 | break 150 | end 151 | end 152 | print("cost", moon.clock() - st) 153 | 154 | ---sqlite 155 | local sqlitedb = sqlx.connect("sqlite://memory:", "test2") 156 | 157 | print_r(sqlitedb:query("CREATE TABLE test (id INTEGER PRIMARY KEY, content TEXT);")) 158 | print_r(sqlitedb:query("INSERT INTO test (content) VALUES ('Hello, World!');")) 159 | print_r(sqlitedb:query("SELECT * FROM test;")) 160 | 161 | print_r(sqlx.stats()) -- Query sqlx left task count 162 | end) 163 | 164 | 165 | ``` 166 | 167 | ### 4. MongoDB 168 | 169 | ```lua 170 | local moon = require "moon" 171 | 172 | local mongodb = require "ext.mongodb" 173 | 174 | moon.async(function() 175 | local db = mongodb.connect("mongodb://127.0.0.1:27017/?serverSelectionTimeoutMS=2000", "gamedb1") 176 | if db.kind then 177 | print("connect failed", db.message) 178 | return 179 | end 180 | 181 | local coll = db:collection("mydatabase", "mycollection") 182 | 183 | local res = coll:insert_one({ 184 | cc = 300, 185 | gpsname = "gps1", 186 | track = { 187 | segments = { 188 | [1] = { 189 | HR = 73, 190 | location = { 191 | [1] = 47.763, 192 | [2] = 13.4034, 193 | }, 194 | starttime = "2018-10-14 10:05:14", 195 | }, 196 | [2] = { 197 | HR = 130, 198 | location = { 199 | [1] = 47.706, 200 | [2] = 13.2635, 201 | }, 202 | starttime = "2018-10-14 10:39:21", 203 | }, 204 | }, 205 | }, 206 | }) 207 | 208 | print_r(res) 209 | 210 | res = coll:update_one({cc = 300}, { 211 | ["$set"] = { 212 | ["track.segments.1.HR"] = 100, 213 | } 214 | }) 215 | 216 | print_r(res) 217 | 218 | res = coll:find({cc = 300}, {limit = 10}) 219 | 220 | print_r(res) 221 | 222 | res = coll:find_one({cc = 300}) 223 | print_r(res) 224 | 225 | print_r(coll:delete_one({cc = 300})) 226 | 227 | print_r(coll:delete_many({cc = 300})) 228 | 229 | res = coll:find_one({cc = 300}) 230 | print_r(res) 231 | 232 | res = coll:insert_one({ 233 | cc = 300, 234 | gpsname = "gps1", 235 | track = { 236 | segments = { 237 | [1] = { 238 | HR = 73, 239 | location = { 240 | [1] = 47.763, 241 | [2] = 13.4034, 242 | }, 243 | starttime = "2018-10-14 10:05:14", 244 | }, 245 | [2] = { 246 | HR = 130, 247 | location = { 248 | [1] = 47.706, 249 | [2] = 13.2635, 250 | }, 251 | starttime = "2018-10-14 10:39:21", 252 | }, 253 | }, 254 | }, 255 | }) 256 | 257 | print_r(res) 258 | 259 | local bt = moon.clock() 260 | for i=1,10000 do 261 | coll:insert_one({ 262 | cc = 300, 263 | gpsname = "gps1", 264 | track = { 265 | segments = { 266 | [1] = { 267 | HR = 73, 268 | location = { 269 | [1] = 47.763, 270 | [2] = 13.4034, 271 | }, 272 | starttime = "2018-10-14 10:05:14", 273 | }, 274 | [2] = { 275 | HR = 130, 276 | location = { 277 | [1] = 47.706, 278 | [2] = 13.2635, 279 | }, 280 | starttime = "2018-10-14 10:39:21", 281 | }, 282 | }, 283 | }, 284 | }) 285 | end 286 | print("insert 10000 use time", moon.clock() - bt) 287 | 288 | end) 289 | ``` 290 | 291 | ## C/Cpp 292 | 293 | ### 1. [lpeg](https://github.com/roberto-ieru/LPeg) 294 | 295 | ### 2. [math3d](https://github.com/cloudwu/math3d) 296 | 297 | ### 3. [sproto](https://github.com/cloudwu/sproto) 298 | -------------------------------------------------------------------------------- /lualib/ext/httpc.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: inject-field 2 | local moon = require "moon" 3 | local json = require "json" 4 | local c = require "rust.httpc" 5 | 6 | local protocol_type = 21 7 | 8 | moon.register_protocol { 9 | name = "http", 10 | PTYPE = protocol_type, 11 | pack = function(...) return ... end, 12 | unpack = function (val) 13 | return c.decode(val) 14 | end 15 | } 16 | 17 | ---@return table 18 | local function tojson(response) 19 | if response.status_code ~= 200 then return {} end 20 | return json.decode(response.body) 21 | end 22 | 23 | ---@class HttpRequestOptions 24 | ---@field headers? table 25 | ---@field timeout? integer Request timeout in seconds. default 5s 26 | ---@field proxy? string 27 | 28 | local client = {} 29 | 30 | ---@param url string 31 | ---@param opts? HttpRequestOptions 32 | ---@return HttpResponse 33 | function client.get(url, opts) 34 | opts = opts or {} 35 | opts.owner = moon.id 36 | opts.session = moon.next_sequence() 37 | opts.url = url 38 | opts.method = "GET" 39 | return moon.wait(c.request(opts, protocol_type)) 40 | end 41 | 42 | local json_content_type = { ["Content-Type"] = "application/json" } 43 | 44 | ---@param url string 45 | ---@param data table 46 | ---@param opts? HttpRequestOptions 47 | ---@return HttpResponse 48 | function client.post_json(url, data, opts) 49 | opts = opts or {} 50 | opts.owner = moon.id 51 | opts.session = moon.next_sequence() 52 | if not opts.headers then 53 | opts.headers = json_content_type 54 | else 55 | if not opts.headers['Content-Type'] then 56 | opts.headers['Content-Type'] = "application/json" 57 | end 58 | end 59 | 60 | opts.url = url 61 | opts.method = "POST" 62 | opts.body = json.encode(data) 63 | 64 | local res = moon.wait(c.request(opts, protocol_type)) 65 | 66 | if res.status_code == 200 then 67 | res.body = tojson(res) 68 | end 69 | return res 70 | end 71 | 72 | ---@param url string 73 | ---@param data string 74 | ---@param opts? HttpRequestOptions 75 | ---@return HttpResponse 76 | function client.post(url, data, opts) 77 | opts = opts or {} 78 | opts.owner = moon.id 79 | opts.session = moon.next_sequence() 80 | opts.url = url 81 | opts.body = data 82 | opts.method = "POST" 83 | return moon.wait(c.request(opts, protocol_type)) 84 | end 85 | 86 | local form_headers = { ["Content-Type"] = "application/x-www-form-urlencoded" } 87 | 88 | ---@param url string 89 | ---@param data table 90 | ---@param opts? HttpRequestOptions 91 | ---@return HttpResponse 92 | function client.post_form(url, data, opts) 93 | opts = opts or {} 94 | opts.owner = moon.id 95 | opts.session = moon.next_sequence() 96 | if not opts.headers then 97 | opts.headers = form_headers 98 | else 99 | if not opts.headers['Content-Type'] then 100 | opts.headers['Content-Type'] = "application/x-www-form-urlencoded" 101 | end 102 | end 103 | 104 | opts.body = {} 105 | for k, v in pairs(data) do 106 | opts.body[k] = tostring(v) 107 | end 108 | 109 | opts.url = url 110 | opts.method = "POST" 111 | opts.body = c.form_urlencode(opts.body) 112 | 113 | return moon.wait(c.request(opts, protocol_type)) 114 | end 115 | 116 | return client -------------------------------------------------------------------------------- /lualib/ext/mongodb.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: inject-field 2 | local moon = require "moon" 3 | local c = require "rust.mongodb" 4 | 5 | local protocol_type = 24 6 | 7 | moon.register_protocol { 8 | name = "mongodb", 9 | PTYPE = protocol_type, 10 | pack = function(...) return ... end, 11 | unpack = function(val) 12 | return c.decode(val) 13 | end 14 | } 15 | 16 | local function operators(self, ...) 17 | local res = self.obj:operators(moon.id, moon.next_sequence(), self.op_name, self.db_name, self.col_name, ...) 18 | if type(res) == "table" then 19 | return res 20 | end 21 | return moon.wait(res) 22 | end 23 | 24 | ---@class Collection 25 | ---@field find_one fun(query:table, opts?:table):table 26 | ---@field find fun(query:table, opts?:table):table 27 | ---@field insert_one fun(doc:table):table 28 | ---@field insert_many fun(docs:table):table 29 | ---@field update_one fun(filter:table, update?:table):table 30 | ---@field update_many fun(filter:table, update?:table):table 31 | ---@field delete_one fun(filter:table):table 32 | ---@field delete_many fun(filter:table):table 33 | ---@field count fun(filter:table):table 34 | 35 | ---@class MongoDB 36 | local M = {} 37 | 38 | ---@async 39 | ---@nodiscard 40 | ---@param database_url string Database url e. "mongodb://127.0.0.1:27017" 41 | ---@param name string Connection name for find by other services 42 | ---@return MongoDB 43 | function M.connect(database_url, name) 44 | local res = moon.wait(c.connect(protocol_type, moon.id, moon.next_sequence(), database_url, name)) 45 | if res.kind then 46 | error(string.format("connect database failed: %s", res.message)) 47 | end 48 | return M.find_connection(name) 49 | end 50 | 51 | ---@nodiscard 52 | ---@param name string Connection name 53 | ---@return MongoDB 54 | function M.find_connection(name) 55 | local o = { 56 | obj = assert(c.find_connection(name), "connection not found") 57 | } 58 | return setmetatable(o, { __index = M }) 59 | end 60 | 61 | function M.stats() 62 | return c.stats() 63 | end 64 | 65 | function M:close() 66 | self.obj:close() 67 | end 68 | 69 | ---@async 70 | ---@nodiscard 71 | ---@param db_name string 72 | ---@param col_name string 73 | ---@vararg any 74 | ---@return Collection 75 | function M:collection(db_name, col_name) 76 | return setmetatable({ 77 | db_name = db_name, 78 | col_name = col_name, 79 | obj = self.obj 80 | }, { 81 | __index = function(t, op_name) 82 | t.op_name = op_name 83 | return t 84 | end, 85 | __call = function(t, ...) 86 | return operators(...) 87 | end 88 | }) 89 | end 90 | 91 | return M 92 | -------------------------------------------------------------------------------- /lualib/ext/opendal.lua: -------------------------------------------------------------------------------- 1 | local c = require "rust.opendal" 2 | local moon = require "moon" 3 | local protocol_type = 22 4 | local callback = _G['send_message'] 5 | 6 | moon.register_protocol { 7 | name = "opendal", 8 | PTYPE = protocol_type, 9 | pack = function(...) return ... end, 10 | unpack = moon.tostring, 11 | } 12 | 13 | local M = {} 14 | 15 | function M.new(schema, options) 16 | local o = { 17 | obj = assert(c.new(schema, options)) 18 | } 19 | return setmetatable(o, { __index = M }) 20 | end 21 | 22 | local function operators(self, ...) 23 | return moon.wait(self.obj:operators(protocol_type, callback, moon.next_sequence(), moon.id, ...)) 24 | end 25 | 26 | function M:read(path) 27 | return operators(self, "read", path) 28 | end 29 | 30 | function M:write(path, data) 31 | return operators(self, "write", path, data) 32 | end 33 | 34 | function M:delete(path) 35 | return operators(self, "delete", path) 36 | end 37 | 38 | function M:exists(path) 39 | return operators(self, "exists", path) 40 | end 41 | 42 | function M:create_dir(path) 43 | return operators(self, "create_dir", path) 44 | end 45 | 46 | function M:rename(path, to_name) 47 | return operators(self, "rename", path, to_name) 48 | end 49 | 50 | function M:stat(path) 51 | return operators(self, "stat", path) 52 | end 53 | 54 | return M 55 | -------------------------------------------------------------------------------- /lualib/ext/sqlx.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: inject-field 2 | local moon = require "moon" 3 | local c = require "rust.sqlx" 4 | 5 | local protocol_type = 23 6 | 7 | moon.register_protocol { 8 | name = "database", 9 | PTYPE = protocol_type, 10 | pack = function(...) return ... end, 11 | unpack = function(val) 12 | return c.decode(val) 13 | end 14 | } 15 | 16 | ---@class SqlX 17 | local M = {} 18 | 19 | ---@nodiscard 20 | ---@param database_url string Database url e. "postgres://postgres:123456@localhost/postgres" 21 | ---@param name string Connection name for find by other services 22 | ---@param timeout? integer Connect timeout. Default 5000ms 23 | ---@return SqlX 24 | function M.connect(database_url, name, timeout) 25 | local res = moon.wait(c.connect(protocol_type, moon.id, moon.next_sequence(), database_url, name, timeout)) 26 | if res.kind then 27 | error(string.format("connect database failed: %s", res.message)) 28 | end 29 | return M.find_connection(name) 30 | end 31 | 32 | ---@nodiscard 33 | ---@param name string Connection name 34 | ---@return SqlX 35 | function M.find_connection(name) 36 | local o = { 37 | obj = c.find_connection(name) 38 | } 39 | return setmetatable(o, { __index = M }) 40 | end 41 | 42 | function M.stats() 43 | return c.stats() 44 | end 45 | 46 | function M:close() 47 | self.obj:close() 48 | end 49 | 50 | ---@param sql string 51 | ---@vararg any 52 | function M:execute(sql, ...) 53 | local res = self.obj:query(moon.id, 0, sql, ...) 54 | if type(res) == "table" then 55 | moon.error(print_r(res, true)) 56 | end 57 | end 58 | 59 | ---@nodiscard 60 | ---@param sql string 61 | ---@vararg any 62 | ---@return table 63 | function M:query(sql, ...) 64 | local session = self.obj:query(moon.id, moon.next_sequence(), sql, ...) 65 | if type(session) == "table" then 66 | return session 67 | end 68 | return moon.wait(session) 69 | end 70 | 71 | ---@async 72 | ---@nodiscard 73 | ---@param querys table 74 | ---@return table 75 | function M:transaction(querys) 76 | local trans = c.make_transaction() 77 | for _, v in ipairs(querys) do 78 | trans:push(table.unpack(v)) 79 | end 80 | local session = self.obj:transaction(moon.id, moon.next_sequence(), trans) 81 | if type(session) == "table" then 82 | return session 83 | end 84 | return moon.wait(session) 85 | end 86 | 87 | ---@param querys table 88 | function M:execute_transaction(querys) 89 | local trans = c.make_transaction() 90 | for _, v in ipairs(querys) do 91 | trans:push(table.unpack(v)) 92 | end 93 | local res = self.obj:transaction(moon.id, 0, trans) 94 | if type(res) == "table" then 95 | moon.error(print_r(res, true)) 96 | end 97 | end 98 | 99 | return M 100 | -------------------------------------------------------------------------------- /premake5.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: undefined-global 2 | 3 | workspace "extensions" 4 | configurations { "Debug", "Release" } 5 | flags{"NoPCH","RelativeLinks"} 6 | cppdialect "C++20" 7 | location "./" 8 | architecture "x64" 9 | -- staticruntime "on" 10 | 11 | filter "configurations:Debug" 12 | defines { "DEBUG" } 13 | symbols "On" 14 | 15 | filter "configurations:Release" 16 | defines { "NDEBUG" } 17 | optimize "On" 18 | symbols "On" 19 | 20 | filter {"system:windows"} 21 | characterset "MBCS" 22 | systemversion "latest" 23 | warnings "Extra" 24 | cdialect "C11" 25 | buildoptions{"/experimental:c11atomics"} 26 | 27 | filter { "system:linux" } 28 | warnings "High" 29 | 30 | filter { "system:macosx" } 31 | warnings "High" 32 | 33 | --- Adds a Lua module to the build configuration. 34 | --- 35 | --- This function configures a Lua module located in the specified directory 36 | --- and assigns it the given name. Additional platform-specific options can 37 | --- be provided to customize the build process for different operating systems. 38 | --- 39 | --- @param dir string The path to the module's source files, relative to the current directory. 40 | --- @param name string The name of the Lua module. 41 | --- @param options? table Additional options for the module configuration. 42 | --- The options table can contain the following keys: 43 | --- - all: function() end - A function to be executed for all platforms. 44 | --- - windows: function() end - A function to be executed only on Windows. 45 | --- - linux: function() end - A function to be executed only on Linux. 46 | --- - macosx: function() end - A function to be executed only on macOS. 47 | local function add_lua_module(dir, name, options ) 48 | project(name) 49 | location("build/projects/%{prj.name}") 50 | objdir "build/obj/%{prj.name}/%{cfg.buildcfg}" 51 | targetdir "build/bin/%{cfg.buildcfg}" 52 | 53 | language "C" 54 | kind "SharedLib" 55 | includedirs {"./cpp", "moon/third/lua/"} 56 | libdirs {"moon/build/bin/Release"} 57 | files { dir.."/*.h",dir.."/*.hpp", dir.."/*.c",dir.."/*.cpp"} 58 | 59 | defines{"SOL_ALL_SAFETIES_ON"} 60 | 61 | if not options then 62 | options = {} 63 | end 64 | 65 | if type(options.all)=="function" then 66 | options.all() 67 | end 68 | 69 | filter { "system:windows" } 70 | defines{"LUA_BUILD_AS_DLL"} 71 | links{"moon"} 72 | if type(options.windows)=="function" then 73 | options.windows() 74 | end 75 | filter {"system:linux"} 76 | targetprefix "" 77 | if type(options.linux)=="function" then 78 | options.linux() 79 | end 80 | filter {"system:macosx"} 81 | targetprefix "" 82 | linkoptions { 83 | "-undefined dynamic_lookup" 84 | } 85 | if type(options.macosx)=="function" then 86 | options.macosx() 87 | end 88 | end 89 | 90 | ----------------------Lua C/C++ Modules------------------------ 91 | 92 | add_lua_module("3rd/sproto", "sproto") 93 | add_lua_module("3rd/lpeg", "lpeg") 94 | add_lua_module( 95 | "3rd/math3d", 96 | "math3d", 97 | { 98 | all = function () 99 | includedirs("3rd/math3d/glm") 100 | language "C++" 101 | defines{"GLM_ENABLE_EXPERIMENTAL","GLM_FORCE_QUAT_DATA_XYZW","_USE_MATH_DEFINES"} 102 | end} 103 | ) 104 | 105 | local function string_trim(input, chars) 106 | chars = chars or " \t\n\r" 107 | local pattern = "^[" .. chars .. "]+" 108 | input = string.gsub(input, pattern, "") 109 | pattern = "[" .. chars .. "]+$" 110 | return string.gsub(input, pattern, "") 111 | end 112 | 113 | newaction { 114 | trigger = "build", 115 | description = "Build", 116 | execute = function () 117 | os.execute("git pull") 118 | os.execute("git submodule init") 119 | os.execute("git submodule update --recursive") 120 | --- Build moon 121 | os.execute([[cd moon && premake5 build --release]]) 122 | 123 | -- Build Cpp library 124 | local host = os.host() 125 | local switch = { 126 | windows = function () 127 | os.execute("premake5.exe vs2022") 128 | local command = os.getenv("ProgramFiles(x86)")..[[\Microsoft Visual Studio\Installer\vswhere.exe]] 129 | command = string.format('"%s" %s', string_trim(command), " -latest -products * -requires Microsoft.Component.MSBuild -property installationPath") 130 | local handle = assert(io.popen(command)) 131 | command = handle:read("*a") 132 | handle:close() 133 | os.execute(string.format('"%s%s" -maxcpucount:4 extensions.sln /t:build /p:Configuration=Release ', string_trim(command), [[\MSBuild\Current\Bin\MSBuild.exe]])) 134 | end, 135 | linux = function () 136 | os.execute("premake5 gmake") 137 | os.execute( "make -j4 config=release") 138 | end, 139 | macosx = function () 140 | os.execute("premake5 gmake --cc=clang") 141 | os.execute("make -j4 config=release") 142 | end, 143 | } 144 | 145 | switch[host]() 146 | 147 | -- Build Rust library 148 | os.execute([[cd rust && cargo build --release]]) 149 | end 150 | } 151 | 152 | newaction { 153 | trigger = "publish", 154 | description = "Publish", 155 | execute = function () 156 | os.mkdir("moon/clib") 157 | 158 | local host = os.host() 159 | local switch = { 160 | windows = function () 161 | os.execute([[xcopy .\build\bin\Release\*.dll .\moon\clib\ /Y /F]]) 162 | os.execute([[xcopy .\rust\target\release\*.dll .\moon\clib\ /Y /F]]) 163 | os.execute([[xcopy .\lualib moon\lualib\ /E /Y /F]]) 164 | end, 165 | linux = function () 166 | os.execute([[cp -rf lualib moon]]) 167 | os.execute([[cp -f ./build/bin/Release/*.so moon/clib/]]) 168 | os.execute([[cp -f ./rust/target/release/librust.so moon/clib/rust.so]]) 169 | end, 170 | macosx = function () 171 | os.execute([[cp -rf lualib moon]]) 172 | os.execute([[cp -f ./build/bin/Release/*.dylib moon/clib/]]) 173 | os.execute([[cp -f ./rust/target/release/librust.dylib moon/clib/rust.dylib]]) 174 | end, 175 | } 176 | 177 | switch[host]() 178 | 179 | os.execute([[cd moon && premake5 publish]]) 180 | 181 | if os.host() == "windows" then 182 | os.execute('move moon\\*.zip .\\') 183 | else 184 | os.execute([[mv moon/*.zip ./]]) 185 | end 186 | 187 | end 188 | } 189 | 190 | -- Helper function to remove build artifacts 191 | local function cleanup() 192 | os.remove("moon") 193 | os.rmdir("build") 194 | end 195 | 196 | -- Custom action: clean 197 | -- Removes build outputs and intermediate files. 198 | newaction { 199 | trigger = "clean", 200 | description = "Remove build artifacts", 201 | execute = function () 202 | cleanup() 203 | end 204 | } -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace.lints.rust] 2 | # unsafe_code = "forbid" 3 | # unused = "allow" # For experimental dev. 4 | 5 | [workspace] 6 | resolver = "2" 7 | members = [ 8 | # -- Application Libraries 9 | "crates/libs/lib-lua-sys", 10 | "crates/libs/lib-core", 11 | "crates/libs/lib-lualib", 12 | ] 13 | 14 | [profile.release] 15 | # strip = true 16 | # codegen-units = 1 17 | # lto = true 18 | # panic = "abort" -------------------------------------------------------------------------------- /rust/crates/libs/lib-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lib-core" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [lib] 8 | doctest = false 9 | 10 | [dependencies] 11 | tokio = { version = "1", features = ["full"] } 12 | dashmap = "6.1.0" 13 | lazy_static = "1.5.0" 14 | reqwest = { version = "0.12", features = ["rustls-tls","trust-dns"],default-features = false} -------------------------------------------------------------------------------- /rust/crates/libs/lib-core/src/buffer.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug)] 5 | pub struct Buffer { 6 | rpos: usize, 7 | data: Vec, 8 | } 9 | 10 | pub const DEFAULT_RESERVE: usize = 128; 11 | 12 | #[allow(dead_code)] 13 | impl Buffer { 14 | pub fn new() -> Buffer { 15 | Buffer { 16 | data: Vec::::with_capacity(DEFAULT_RESERVE), 17 | rpos: 0, 18 | } 19 | } 20 | 21 | pub fn with_capacity(capacity: usize) -> Buffer { 22 | Buffer { 23 | data: Vec::::with_capacity(capacity), 24 | rpos: 0, 25 | } 26 | } 27 | 28 | pub fn from_slice(data: &[u8]) -> Buffer { 29 | let mut raw = Vec::::with_capacity(data.len()); 30 | raw.extend_from_slice(data); 31 | Buffer { data: raw, rpos: 0 } 32 | } 33 | 34 | pub fn write_slice(&mut self, data: &[u8]) { 35 | self.prepare(data.len()); 36 | self.data.extend_from_slice(data); 37 | } 38 | 39 | pub fn write(&mut self, c: u8) { 40 | self.data.push(c); 41 | } 42 | 43 | pub fn unsafe_write(&mut self, c: u8) { 44 | unsafe { 45 | let len = self.data.len() + 1; 46 | self.data.set_len(len); 47 | *self.data.get_unchecked_mut(len - 1) = c; 48 | } 49 | } 50 | 51 | pub fn write_front(&mut self, data: &[u8]) -> bool { 52 | let len: usize = data.len(); 53 | if len > self.rpos { 54 | return false; 55 | } 56 | self.rpos -= len; 57 | unsafe { 58 | std::ptr::copy_nonoverlapping( 59 | data.as_ptr(), 60 | self.data.as_mut_ptr().add(self.rpos), 61 | len, 62 | ); 63 | } 64 | true 65 | } 66 | 67 | pub fn write_front_byte(&mut self, c: u8) -> bool { 68 | if self.rpos == 0 { 69 | return false; 70 | } 71 | self.rpos -= 1; 72 | unsafe { 73 | *self.data.get_unchecked_mut(self.rpos) = c; 74 | } 75 | true 76 | } 77 | 78 | pub fn write_chars(&mut self, data: T) 79 | where 80 | T: ToString, 81 | { 82 | let s = data.to_string(); 83 | self.write_slice(s.as_bytes()); 84 | } 85 | 86 | pub fn write_str(&mut self, data: &str) { 87 | self.write_slice(data.as_bytes()); 88 | } 89 | 90 | pub fn read(&mut self, count: usize) -> Option> { 91 | if self.data.len() < self.rpos + count { 92 | return None; 93 | } 94 | let mut v = Vec::with_capacity(count); 95 | unsafe { 96 | std::ptr::copy_nonoverlapping(self.data.as_ptr().add(self.rpos), v.as_mut_ptr(), count); 97 | v.set_len(count); 98 | } 99 | self.rpos += count; 100 | Some(v) 101 | } 102 | 103 | pub fn consume(&mut self, count: usize) { 104 | if self.data.len() < self.rpos + count { 105 | return; 106 | } 107 | self.rpos += count; 108 | } 109 | 110 | pub fn seek(&mut self, pos: isize) -> bool { 111 | if pos < 0 { 112 | if self.rpos < pos.unsigned_abs() { 113 | return false; 114 | } 115 | self.rpos -= pos.unsigned_abs(); 116 | return true; 117 | } 118 | 119 | if self.data.len() < self.rpos + pos as usize { 120 | return false; 121 | } 122 | self.rpos += pos as usize; 123 | true 124 | } 125 | 126 | pub fn clear(&mut self) { 127 | self.data.clear(); 128 | self.rpos = 0; 129 | } 130 | 131 | pub fn len(&self) -> usize { 132 | self.data.len() - self.rpos 133 | } 134 | 135 | pub fn is_empty(&self) -> bool { 136 | self.data.len() == self.rpos 137 | } 138 | 139 | pub fn prepare(&mut self, size: usize) -> &mut [u8] { 140 | let tail_free_space = self.data.capacity() - self.data.len(); 141 | if tail_free_space < size { 142 | let count = self.data.len() - self.rpos; 143 | if tail_free_space + self.rpos >= size { 144 | unsafe { 145 | if count != 0 { 146 | let ptr = self.data.as_mut_ptr(); 147 | std::ptr::copy(ptr.add(self.rpos), ptr, count); 148 | } 149 | self.rpos = 0; 150 | self.data.set_len(self.rpos + count); 151 | } 152 | } else { 153 | let required_size = self.data.len() + size; 154 | let mut new_vec = Vec::::with_capacity(required_size); 155 | unsafe { 156 | std::ptr::copy_nonoverlapping( 157 | self.data.as_ptr(), 158 | new_vec.as_mut_ptr(), 159 | self.data.len(), 160 | ); 161 | new_vec.set_len(self.data.len()); 162 | self.data = new_vec; 163 | } 164 | } 165 | } 166 | 167 | unsafe { std::slice::from_raw_parts_mut(self.data.as_mut_ptr().add(self.data.len()), size) } 168 | } 169 | 170 | pub fn commit(&mut self, size: usize) -> bool { 171 | let len = self.data.len() + size; 172 | if len > self.data.capacity() { 173 | return false; 174 | } 175 | 176 | unsafe { 177 | self.data.set_len(len); 178 | } 179 | 180 | true 181 | } 182 | 183 | pub fn revert(&mut self, size: usize) { 184 | assert!( 185 | self.data.len() >= self.rpos + size, 186 | "revert size is larger than readable size", 187 | ); 188 | 189 | unsafe { 190 | self.data.set_len(self.data.len() - size); 191 | } 192 | } 193 | 194 | pub fn data(&self) -> &[u8] { 195 | &self.data[self.rpos..] 196 | } 197 | 198 | pub fn as_ptr(&self) -> *const u8 { 199 | unsafe { self.data.as_ptr().add(self.rpos) } 200 | } 201 | 202 | pub fn read_u8(&self, pos: usize) -> u8 { 203 | self.data[self.rpos + pos] 204 | } 205 | 206 | pub fn read_i16(&self, pos: usize, le: bool) -> i16 { 207 | let mut v = 0i16; 208 | unsafe { 209 | std::ptr::copy_nonoverlapping( 210 | self.data.as_ptr().add(self.rpos + pos), 211 | &mut v as *mut i16 as *mut u8, 212 | 2, 213 | ); 214 | } 215 | if le { 216 | v.to_le() 217 | } else { 218 | v.to_be() 219 | } 220 | } 221 | 222 | pub fn read_u16(&self, pos: usize, le: bool) -> u16 { 223 | let mut v = 0u16; 224 | unsafe { 225 | std::ptr::copy_nonoverlapping( 226 | self.data.as_ptr().add(self.rpos + pos), 227 | &mut v as *mut u16 as *mut u8, 228 | 2, 229 | ); 230 | } 231 | if le { 232 | v.to_le() 233 | } else { 234 | v.to_be() 235 | } 236 | } 237 | 238 | pub fn read_i32(&self, pos: usize, le: bool) -> i32 { 239 | let mut v = 0i32; 240 | unsafe { 241 | std::ptr::copy_nonoverlapping( 242 | self.data.as_ptr().add(self.rpos + pos), 243 | &mut v as *mut i32 as *mut u8, 244 | 4, 245 | ); 246 | } 247 | if le { 248 | v.to_le() 249 | } else { 250 | v.to_be() 251 | } 252 | } 253 | 254 | pub fn read_u32(&self, pos: usize, le: bool) -> u32 { 255 | let mut v = 0u32; 256 | unsafe { 257 | std::ptr::copy_nonoverlapping( 258 | self.data.as_ptr().add(self.rpos + pos), 259 | &mut v as *mut u32 as *mut u8, 260 | 4, 261 | ); 262 | } 263 | if le { 264 | v.to_le() 265 | } else { 266 | v.to_be() 267 | } 268 | } 269 | 270 | pub fn as_slice(&self) -> &[u8] { 271 | unsafe { std::slice::from_raw_parts(self.as_ptr(), self.len()) } 272 | } 273 | 274 | pub fn as_mut_slice(&mut self) -> &mut [u8] { 275 | unsafe { std::slice::from_raw_parts_mut(self.as_ptr() as *mut u8, self.len()) } 276 | } 277 | 278 | pub fn as_mut_vec(&mut self) -> &mut Vec { 279 | &mut self.data 280 | } 281 | 282 | pub fn as_vec(&mut self) -> &Vec { 283 | &self.data 284 | } 285 | 286 | pub fn as_pointer(&mut self) -> *mut Buffer { 287 | self as *mut Buffer 288 | } 289 | 290 | pub fn as_str(&self) -> &str { 291 | unsafe { std::str::from_utf8_unchecked(self.as_slice()) } 292 | } 293 | } 294 | 295 | impl From> for Buffer { 296 | fn from(v: Vec) -> Self { 297 | Buffer { data: v, rpos: 0 } 298 | } 299 | } 300 | 301 | impl From<&[u8]> for Buffer { 302 | fn from(v: &[u8]) -> Self { 303 | Buffer { 304 | data: v.to_vec(), 305 | rpos: 0, 306 | } 307 | } 308 | } 309 | 310 | impl From<&str> for Buffer { 311 | fn from(v: &str) -> Self { 312 | Buffer { 313 | data: v.as_bytes().to_vec(), 314 | rpos: 0, 315 | } 316 | } 317 | } 318 | 319 | impl From for Buffer { 320 | fn from(v: String) -> Self { 321 | Buffer { 322 | data: v.into_bytes(), 323 | rpos: 0, 324 | } 325 | } 326 | } 327 | 328 | impl Default for Buffer { 329 | fn default() -> Self { 330 | Buffer::new() 331 | } 332 | } 333 | 334 | impl fmt::Display for Buffer { 335 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 336 | write!(f, "{}", self.as_str()) 337 | } 338 | } 339 | 340 | #[cfg(test)] 341 | mod tests { 342 | use super::*; 343 | 344 | #[test] 345 | fn test_buffer_1() { 346 | let mut buf = Buffer::with_capacity(12); 347 | buf.commit(4); 348 | buf.write_str("1234567"); 349 | buf.seek(4); 350 | assert_eq!(buf.write_front("bbbb".as_bytes()), true); 351 | 352 | let r = buf.read(8); 353 | println!("{}", String::from_utf8_lossy(r.unwrap().as_ref())); 354 | 355 | buf.write_str("abcd"); 356 | assert!(buf.read(4).unwrap() == "567a".as_bytes()); 357 | assert!(buf.len() == 3); 358 | } 359 | #[test] 360 | fn test_buffer_2() { 361 | let mut buf = Buffer::with_capacity(128); 362 | assert!(buf.len() == 0); 363 | let n: i32 = 0; 364 | buf.write_slice(n.to_le_bytes().as_ref()); 365 | assert_eq!(buf.len(), 4); 366 | assert!(buf.read(4).unwrap() == n.to_le_bytes().as_ref()); 367 | assert!(buf.len() == 0); 368 | assert!(buf.read(4).is_none()); 369 | } 370 | 371 | #[test] 372 | fn test_buffer_3() { 373 | let mut buf = Buffer::with_capacity(32); 374 | assert!(buf.len() == 0); 375 | 376 | for _ in 0..100 { 377 | buf.write_slice(vec![0; 17].as_ref()); 378 | assert!(buf.read(17).is_some()); 379 | } 380 | } 381 | 382 | #[test] 383 | fn test_buffer_new() { 384 | let buffer = Buffer::new(); 385 | assert_eq!(buffer.data.capacity(), DEFAULT_RESERVE); 386 | assert_eq!(buffer.rpos, 0); 387 | } 388 | 389 | #[test] 390 | fn test_buffer_with_capacity() { 391 | let capacity = 256; 392 | let buffer = Buffer::with_capacity(capacity); 393 | assert_eq!(buffer.data.capacity(), capacity); 394 | assert_eq!(buffer.rpos, 0); 395 | } 396 | 397 | #[test] 398 | fn test_buffer_from_slice() { 399 | let data = [1, 2, 3, 4]; 400 | let buffer = Buffer::from_slice(&data); 401 | assert_eq!(buffer.data, data); 402 | assert_eq!(buffer.rpos, 0); 403 | } 404 | 405 | #[test] 406 | fn test_write_slice() { 407 | let mut buffer = Buffer::new(); 408 | let data = [1, 2, 3, 4]; 409 | buffer.write_slice(&data); 410 | assert_eq!(buffer.data, data); 411 | } 412 | 413 | #[test] 414 | fn test_write() { 415 | let mut buffer = Buffer::new(); 416 | buffer.write(1); 417 | assert_eq!(buffer.data, vec![1]); 418 | } 419 | 420 | #[test] 421 | fn test_unsafe_write() { 422 | let mut buffer = Buffer::new(); 423 | buffer.unsafe_write(1); 424 | assert_eq!(buffer.data, vec![1]); 425 | } 426 | 427 | #[test] 428 | fn test_write_front() { 429 | let mut buffer = Buffer::new(); 430 | buffer.commit(1); 431 | buffer.write_slice(&[1, 2, 3, 4]); 432 | buffer.seek(1); 433 | assert!(buffer.write_front(&[0])); 434 | assert_eq!(buffer.data, vec![0, 1, 2, 3, 4]); 435 | } 436 | 437 | #[test] 438 | fn test_write_front_byte() { 439 | let mut buffer = Buffer::new(); 440 | buffer.commit(1); 441 | buffer.write_slice(&[1, 2, 3, 4]); 442 | buffer.seek(1); 443 | assert!(buffer.write_front_byte(0)); 444 | assert_eq!(buffer.data, vec![0, 1, 2, 3, 4]); 445 | } 446 | 447 | #[test] 448 | fn test_write_chars() { 449 | let mut buffer = Buffer::new(); 450 | buffer.write_chars("hello"); 451 | assert_eq!(buffer.data, b"hello".to_vec()); 452 | } 453 | 454 | #[test] 455 | fn test_write_str() { 456 | let mut buffer = Buffer::new(); 457 | buffer.write_str("hello"); 458 | assert_eq!(buffer.data, b"hello".to_vec()); 459 | } 460 | 461 | #[test] 462 | fn test_read() { 463 | let mut buffer = Buffer::from_slice(&[1, 2, 3, 4]); 464 | let data = buffer.read(2); 465 | assert_eq!(data, Some(vec![1, 2])); 466 | assert_eq!(buffer.rpos, 2); 467 | } 468 | 469 | #[test] 470 | fn test_consume() { 471 | let mut buffer = Buffer::from_slice(&[1, 2, 3, 4]); 472 | buffer.consume(2); 473 | assert_eq!(buffer.rpos, 2); 474 | } 475 | 476 | #[test] 477 | fn test_seek() { 478 | let mut buffer = Buffer::from_slice(&[1, 2, 3, 4]); 479 | assert!(buffer.seek(2)); 480 | assert_eq!(buffer.rpos, 2); 481 | assert!(buffer.seek(-1)); 482 | assert_eq!(buffer.rpos, 1); 483 | } 484 | 485 | #[test] 486 | fn test_clear() { 487 | let mut buffer = Buffer::from_slice(&[1, 2, 3, 4]); 488 | buffer.clear(); 489 | assert!(buffer.data.is_empty()); 490 | assert_eq!(buffer.rpos, 0); 491 | } 492 | 493 | #[test] 494 | fn test_len() { 495 | let buffer = Buffer::from_slice(&[1, 2, 3, 4]); 496 | assert_eq!(buffer.len(), 4); 497 | } 498 | 499 | #[test] 500 | fn test_is_empty() { 501 | let buffer = Buffer::new(); 502 | assert!(buffer.is_empty()); 503 | } 504 | 505 | #[test] 506 | fn test_prepare() { 507 | let mut buffer = Buffer::new(); 508 | let _ = buffer.prepare(10); 509 | assert!(buffer.data.capacity() >= 10); 510 | } 511 | 512 | #[test] 513 | fn test_commit() { 514 | let mut buffer = Buffer::new(); 515 | let _ = buffer.prepare(10); 516 | assert!(buffer.commit(10)); 517 | assert_eq!(buffer.data.len(), 10); 518 | } 519 | 520 | #[test] 521 | fn test_revert() { 522 | let mut buffer = Buffer::from_slice(&[1, 2, 3, 4]); 523 | buffer.revert(2); 524 | assert_eq!(buffer.data.len(), 2); 525 | } 526 | 527 | #[test] 528 | fn test_data() { 529 | let buffer = Buffer::from_slice(&[1, 2, 3, 4]); 530 | assert_eq!(buffer.data(), &[1, 2, 3, 4]); 531 | } 532 | 533 | #[test] 534 | fn test_as_ptr() { 535 | let buffer = Buffer::from_slice(&[1, 2, 3, 4]); 536 | unsafe { 537 | assert_eq!(*buffer.as_ptr(), 1); 538 | } 539 | } 540 | 541 | #[test] 542 | fn test_read_u8() { 543 | let buffer = Buffer::from_slice(&[1, 2, 3, 4]); 544 | assert_eq!(buffer.read_u8(1), 2); 545 | } 546 | 547 | #[test] 548 | fn test_read_i16() { 549 | let buffer = Buffer::from_slice(&[0, 1, 0, 2]); 550 | assert_eq!(buffer.read_i16(0, true), 256); 551 | assert_eq!(buffer.read_i16(2, true), 512); 552 | } 553 | 554 | #[test] 555 | fn test_read_u16() { 556 | let buffer = Buffer::from_slice(&[0, 1, 0, 2]); 557 | assert_eq!(buffer.read_u16(0, true), 256); 558 | assert_eq!(buffer.read_u16(2, true), 512); 559 | } 560 | 561 | #[test] 562 | fn test_read_i32() { 563 | let buffer = Buffer::from_slice(&[1, 0, 0, 0]); 564 | assert_eq!(buffer.read_i32(0, true), 1); 565 | } 566 | 567 | #[test] 568 | fn test_read_u32() { 569 | let buffer = Buffer::from_slice(&[1, 0, 0, 0]); 570 | assert_eq!(buffer.read_u32(0, true), 1); 571 | } 572 | 573 | #[test] 574 | fn test_as_slice() { 575 | let buffer = Buffer::from_slice(&[1, 2, 3, 4]); 576 | assert_eq!(buffer.as_slice(), &[1, 2, 3, 4]); 577 | } 578 | 579 | #[test] 580 | fn test_as_mut_slice() { 581 | let mut buffer = Buffer::from_slice(&[1, 2, 3, 4]); 582 | buffer.as_mut_slice()[0] = 0; 583 | assert_eq!(buffer.data, vec![0, 2, 3, 4]); 584 | } 585 | 586 | #[test] 587 | fn test_as_mut_vec() { 588 | let mut buffer = Buffer::from_slice(&[1, 2, 3, 4]); 589 | buffer.as_mut_vec().push(5); 590 | assert_eq!(buffer.data, vec![1, 2, 3, 4, 5]); 591 | } 592 | 593 | #[test] 594 | fn test_as_vec() { 595 | let mut buffer = Buffer::from_slice(&[1, 2, 3, 4]); 596 | assert_eq!(buffer.as_vec(), &vec![1, 2, 3, 4]); 597 | } 598 | 599 | #[test] 600 | fn test_as_pointer() { 601 | let mut buffer = Buffer::new(); 602 | let ptr = buffer.as_pointer(); 603 | assert!(!ptr.is_null()); 604 | } 605 | 606 | #[test] 607 | fn test_as_str() { 608 | let buffer = Buffer::from("hello"); 609 | assert_eq!(buffer.as_str(), "hello"); 610 | } 611 | 612 | #[test] 613 | fn test_from_vec() { 614 | let v = vec![1, 2, 3, 4]; 615 | let buffer: Buffer = v.into(); 616 | assert_eq!(buffer.data, vec![1, 2, 3, 4]); 617 | } 618 | 619 | #[test] 620 | fn test_from_slice() { 621 | let s = &[1, 2, 3, 4]; 622 | let buffer = Buffer::from_slice(s); 623 | assert_eq!(buffer.data, vec![1, 2, 3, 4]); 624 | } 625 | 626 | #[test] 627 | fn test_from_str() { 628 | let s = "hello"; 629 | let buffer: Buffer = s.into(); 630 | assert_eq!(buffer.data, b"hello".to_vec()); 631 | } 632 | 633 | #[test] 634 | fn test_from_string() { 635 | let s = "hello".to_string(); 636 | let buffer: Buffer = s.into(); 637 | assert_eq!(buffer.data, b"hello".to_vec()); 638 | } 639 | 640 | #[test] 641 | fn test_default() { 642 | let buffer: Buffer = Default::default(); 643 | assert_eq!(buffer.data.capacity(), DEFAULT_RESERVE); 644 | assert_eq!(buffer.rpos, 0); 645 | } 646 | 647 | #[test] 648 | fn test_display() { 649 | let buffer = Buffer::from("hello"); 650 | assert_eq!(format!("{}", buffer), "hello"); 651 | } 652 | } 653 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-core/src/context.rs: -------------------------------------------------------------------------------- 1 | use dashmap::DashMap; 2 | use lazy_static::lazy_static; 3 | use reqwest::ClientBuilder; 4 | use std::time::Duration; 5 | use tokio::runtime::Builder; 6 | 7 | lazy_static! { 8 | pub static ref CONTEXT: Context = { 9 | let tokio_runtime = Builder::new_multi_thread() 10 | .worker_threads(4) 11 | .enable_time() 12 | .enable_io() 13 | .build(); 14 | 15 | Context { 16 | http_clients: DashMap::new(), 17 | tokio_runtime: tokio_runtime.expect("Init tokio runtime failed") 18 | } 19 | }; 20 | } 21 | 22 | pub struct Context { 23 | http_clients: DashMap, 24 | pub tokio_runtime: tokio::runtime::Runtime, 25 | } 26 | 27 | impl Context { 28 | pub fn get_http_client(&self, timeout: u64, proxy: &String) -> reqwest::Client { 29 | let name = format!("{}_{}", timeout, proxy); 30 | if let Some(client) = self.http_clients.get(&name) { 31 | return client.clone(); 32 | } 33 | 34 | let builder = ClientBuilder::new() 35 | .timeout(Duration::from_secs(timeout)) 36 | .use_rustls_tls() 37 | .tcp_nodelay(true); 38 | 39 | let client = if proxy.is_empty() { 40 | builder.build().unwrap_or_default() 41 | } else { 42 | match reqwest::Proxy::all(proxy) { 43 | Ok(proxy) => builder.proxy(proxy).build().unwrap_or_default(), 44 | Err(_) => builder.build().unwrap_or_default(), 45 | } 46 | }; 47 | 48 | self.http_clients.insert(name.to_string(), client.clone()); 49 | client 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-core/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | #[derive(Debug)] 4 | pub struct Error(String); 5 | 6 | impl fmt::Display for Error { 7 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 8 | write!(f, "There is an error: {}", self.0) 9 | } 10 | } 11 | 12 | impl std::error::Error for Error {} 13 | 14 | impl Error { 15 | pub fn from_string(error: String) -> Result<(), Box> { 16 | Err(Box::new(Error(error))) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod context; 2 | pub mod buffer; 3 | pub mod error; 4 | 5 | 6 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/.gitignore: -------------------------------------------------------------------------------- 1 | /target -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "cc" 7 | version = "1.0.83" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 10 | dependencies = [ 11 | "libc", 12 | ] 13 | 14 | [[package]] 15 | name = "libc" 16 | version = "0.2.151" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" 19 | 20 | [[package]] 21 | name = "lua-sys" 22 | version = "0.1.0" 23 | dependencies = [ 24 | "cc", 25 | "libc", 26 | ] 27 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lib-lua-sys" 3 | version = "0.1.0" 4 | edition = "2021" 5 | categories = ["external-ffi-bindings"] 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | doctest = false 10 | 11 | [dependencies] 12 | libc = "0.2" 13 | 14 | [features] 15 | lua54 = [] 16 | luau = [] 17 | raw_dylib = [] 18 | 19 | [build-dependencies] 20 | cc = "1.0" -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/src/laux.rs: -------------------------------------------------------------------------------- 1 | use crate::ffi; 2 | use std::{ 3 | ffi::{c_char, c_int}, 4 | fmt::{Display, Formatter}, 5 | marker::PhantomData, 6 | }; 7 | 8 | pub type LuaStateRef = *mut ffi::lua_State; 9 | 10 | pub struct LuaNil; 11 | 12 | pub trait LuaStack { 13 | fn from_checked(state: LuaStateRef, index: i32) -> Self; 14 | 15 | fn from_unchecked(state: LuaStateRef, index: i32) -> Self; 16 | 17 | fn from_opt(state: LuaStateRef, index: i32) -> Option 18 | where 19 | Self: Sized; 20 | 21 | fn push(state: LuaStateRef, v: Self); 22 | } 23 | 24 | macro_rules! impl_lua_stack_integer { 25 | ($($t:ty),*) => { 26 | $( 27 | impl LuaStack for $t { 28 | #[inline] 29 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 30 | fn from_checked(state: LuaStateRef, index: i32) -> $t { 31 | unsafe { ffi::luaL_checkinteger(state, index) as $t } 32 | } 33 | 34 | #[inline] 35 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 36 | fn from_unchecked(state: LuaStateRef, index: i32) -> $t { 37 | unsafe { ffi::lua_tointeger(state, index) as $t } 38 | } 39 | 40 | #[inline] 41 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 42 | fn from_opt(state: LuaStateRef, index: i32) -> Option<$t> { 43 | unsafe { 44 | if ffi::lua_isinteger(state, index) == 0 { 45 | None 46 | } else { 47 | Some(ffi::lua_tointeger(state, index) as $t) 48 | } 49 | } 50 | } 51 | 52 | #[inline] 53 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 54 | fn push(state: LuaStateRef, v: $t) { 55 | unsafe { 56 | ffi::lua_pushinteger(state, v as ffi::lua_Integer); 57 | } 58 | } 59 | } 60 | )* 61 | }; 62 | } 63 | 64 | impl_lua_stack_integer!(i8, u8, i16, u16, i32, u32, usize, isize, i64, u64); 65 | 66 | impl LuaStack for f64 { 67 | #[inline] 68 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 69 | fn from_checked(state: LuaStateRef, index: i32) -> f64 { 70 | unsafe { ffi::luaL_checknumber(state, index) as f64 } 71 | } 72 | 73 | #[inline] 74 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 75 | fn from_unchecked(state: LuaStateRef, index: i32) -> f64 { 76 | unsafe { ffi::lua_tonumber(state, index) as f64 } 77 | } 78 | 79 | #[inline] 80 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 81 | fn from_opt(state: LuaStateRef, index: i32) -> Option { 82 | unsafe { 83 | if ffi::lua_isnumber(state, index) == 0 { 84 | None 85 | } else { 86 | Some(ffi::lua_tonumber(state, index) as f64) 87 | } 88 | } 89 | } 90 | 91 | #[inline] 92 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 93 | fn push(state: LuaStateRef, v: f64) { 94 | unsafe { 95 | ffi::lua_pushnumber(state, v as ffi::lua_Number); 96 | } 97 | } 98 | } 99 | 100 | impl LuaStack for bool { 101 | #[inline] 102 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 103 | fn from_checked(state: LuaStateRef, index: i32) -> bool { 104 | unsafe { 105 | ffi::luaL_checktype(state, index, ffi::LUA_TBOOLEAN); 106 | ffi::lua_toboolean(state, index) != 0 107 | } 108 | } 109 | 110 | #[inline] 111 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 112 | fn from_unchecked(state: LuaStateRef, index: i32) -> bool { 113 | unsafe { ffi::lua_toboolean(state, index) != 0 } 114 | } 115 | 116 | #[inline] 117 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 118 | fn from_opt(state: LuaStateRef, index: i32) -> Option { 119 | unsafe { 120 | if ffi::lua_isnoneornil(state, index) != 0 { 121 | None 122 | } else { 123 | Some(ffi::lua_toboolean(state, index) != 0) 124 | } 125 | } 126 | } 127 | 128 | #[inline] 129 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 130 | fn push(state: LuaStateRef, v: bool) { 131 | unsafe { 132 | ffi::lua_pushboolean(state, v as c_int); 133 | } 134 | } 135 | } 136 | 137 | impl LuaStack for &[u8] { 138 | #[inline] 139 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 140 | fn from_checked(state: LuaStateRef, index: i32) -> &'static [u8] { 141 | unsafe { 142 | let mut len = 0; 143 | let ptr = ffi::luaL_checklstring(state, index, &mut len); 144 | std::slice::from_raw_parts(ptr as *const u8, len) 145 | } 146 | } 147 | 148 | #[inline] 149 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 150 | fn from_unchecked(state: LuaStateRef, index: i32) -> &'static [u8] { 151 | unsafe { 152 | let mut len = 0; 153 | let ptr = ffi::lua_tolstring(state, index, &mut len); 154 | std::slice::from_raw_parts(ptr as *const u8, len) 155 | } 156 | } 157 | 158 | #[inline] 159 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 160 | fn from_opt(state: LuaStateRef, index: i32) -> Option<&'static [u8]> { 161 | unsafe { 162 | if ffi::lua_type(state, index) != ffi::LUA_TSTRING { 163 | None 164 | } else { 165 | let mut len = 0; 166 | let ptr = ffi::lua_tolstring(state, index, &mut len); 167 | if ptr.is_null() { 168 | None 169 | } else { 170 | Some(std::slice::from_raw_parts(ptr as *const u8, len)) 171 | } 172 | } 173 | } 174 | } 175 | 176 | #[inline] 177 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 178 | fn push(state: LuaStateRef, v: &[u8]) { 179 | unsafe { 180 | ffi::lua_pushlstring(state, v.as_ptr() as *const c_char, v.len()); 181 | } 182 | } 183 | } 184 | 185 | impl LuaStack for &str { 186 | #[inline] 187 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 188 | fn from_checked(state: LuaStateRef, index: i32) -> &'static str { 189 | unsafe { std::str::from_utf8_unchecked(LuaStack::from_checked(state, index)) } 190 | } 191 | 192 | #[inline] 193 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 194 | fn from_unchecked(state: LuaStateRef, index: i32) -> &'static str { 195 | unsafe { std::str::from_utf8_unchecked(LuaStack::from_unchecked(state, index)) } 196 | } 197 | 198 | #[inline] 199 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 200 | fn from_opt(state: LuaStateRef, index: i32) -> Option<&'static str> { 201 | LuaStack::from_opt(state, index).map(|s| unsafe { std::str::from_utf8_unchecked(s) }) 202 | } 203 | 204 | #[inline] 205 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 206 | fn push(state: LuaStateRef, v: &str) { 207 | unsafe { 208 | ffi::lua_pushlstring(state, v.as_ptr() as *const c_char, v.len()); 209 | } 210 | } 211 | } 212 | 213 | impl LuaStack for String { 214 | #[inline] 215 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 216 | fn from_checked(state: LuaStateRef, index: i32) -> String { 217 | String::from_utf8_lossy(LuaStack::from_checked(state, index)).into_owned() 218 | } 219 | 220 | #[inline] 221 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 222 | fn from_unchecked(state: LuaStateRef, index: i32) -> String { 223 | String::from_utf8_lossy(LuaStack::from_unchecked(state, index)).into_owned() 224 | } 225 | 226 | #[inline] 227 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 228 | fn from_opt(state: LuaStateRef, index: i32) -> Option { 229 | LuaStack::from_opt(state, index).map(|s| String::from_utf8_lossy(s).into_owned()) 230 | } 231 | 232 | #[inline] 233 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 234 | fn push(state: LuaStateRef, v: String) { 235 | unsafe { 236 | ffi::lua_pushlstring(state, v.as_ptr() as *const c_char, v.len()); 237 | } 238 | } 239 | } 240 | 241 | impl LuaStack for LuaNil { 242 | #[inline] 243 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 244 | fn from_checked(state: LuaStateRef, index: i32) -> LuaNil { 245 | unsafe {ffi::luaL_checktype(state, index, ffi::LUA_TNIL);} 246 | LuaNil { } 247 | } 248 | 249 | #[inline] 250 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 251 | fn from_unchecked(_state: LuaStateRef, _index: i32) -> LuaNil { 252 | LuaNil { } 253 | } 254 | 255 | #[inline] 256 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 257 | fn from_opt(state: LuaStateRef, index: i32) -> Option { 258 | if unsafe { 259 | ffi::lua_isnil(state, index) == 1 260 | }{ 261 | Some(LuaNil{}) 262 | } 263 | else{ 264 | None 265 | } 266 | } 267 | 268 | #[inline] 269 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 270 | fn push(state: LuaStateRef, _v: LuaNil) { 271 | unsafe { 272 | ffi::lua_pushnil(state); 273 | } 274 | } 275 | } 276 | 277 | #[derive(PartialEq)] 278 | pub struct LuaThread(pub LuaStateRef); 279 | 280 | unsafe impl Send for LuaThread {} 281 | 282 | impl LuaThread { 283 | pub fn new(l: LuaStateRef) -> Self { 284 | LuaThread(l) 285 | } 286 | } 287 | 288 | #[derive(PartialEq)] 289 | pub struct LuaState(pub LuaStateRef); 290 | 291 | unsafe impl Send for LuaState {} 292 | 293 | impl LuaState { 294 | pub fn new(l: LuaStateRef) -> Self { 295 | LuaState(l) 296 | } 297 | } 298 | 299 | impl Drop for LuaState { 300 | fn drop(&mut self) { 301 | unsafe { 302 | if !self.0.is_null() { 303 | ffi::lua_close(self.0); 304 | } 305 | } 306 | } 307 | } 308 | 309 | pub extern "C-unwind" fn lua_null_function(_: LuaStateRef) -> c_int { 310 | 0 311 | } 312 | 313 | pub struct LuaScopePop { 314 | state: LuaStateRef, 315 | } 316 | 317 | impl LuaScopePop { 318 | pub fn new(state: LuaStateRef) -> Self { 319 | LuaScopePop { state } 320 | } 321 | } 322 | 323 | impl Drop for LuaScopePop { 324 | fn drop(&mut self) { 325 | unsafe { 326 | ffi::lua_pop(self.state, 1); 327 | } 328 | } 329 | } 330 | 331 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 332 | pub extern "C-unwind" fn lua_traceback(state: LuaStateRef) -> c_int { 333 | unsafe { 334 | let msg = ffi::lua_tostring(state, 1); 335 | if !msg.is_null() { 336 | ffi::luaL_traceback(state, state, msg, 1); 337 | } else { 338 | ffi::lua_pushliteral(state, "(no error message)"); 339 | } 340 | 1 341 | } 342 | } 343 | 344 | #[inline] 345 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 346 | pub fn opt_field(state: LuaStateRef, mut index: i32, field: &str) -> Option 347 | where 348 | T: LuaStack, 349 | { 350 | if index < 0 { 351 | unsafe { 352 | index = ffi::lua_gettop(state) + index + 1; 353 | } 354 | } 355 | 356 | let _scope = LuaScopePop::new(state); 357 | unsafe { 358 | ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len()); 359 | if ffi::lua_rawget(state, index) <= ffi::LUA_TNIL { 360 | return None; 361 | } 362 | } 363 | 364 | LuaStack::from_opt(state, -1) 365 | } 366 | 367 | #[inline] 368 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 369 | pub fn lua_get(state: LuaStateRef, index: i32) -> T 370 | where 371 | T: LuaStack, 372 | { 373 | LuaStack::from_checked(state, index) 374 | } 375 | 376 | #[inline] 377 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 378 | pub fn lua_to(state: LuaStateRef, index: i32) -> T 379 | where 380 | T: LuaStack, 381 | { 382 | LuaStack::from_unchecked(state, index) 383 | } 384 | 385 | #[inline] 386 | pub fn lua_opt(state: LuaStateRef, index: i32) -> Option 387 | where 388 | T: LuaStack, 389 | { 390 | LuaStack::from_opt(state, index) 391 | } 392 | 393 | #[inline] 394 | pub fn lua_push(state: LuaStateRef, v: T) 395 | where 396 | T: LuaStack, 397 | { 398 | LuaStack::push(state, v); 399 | } 400 | 401 | #[derive(PartialEq, Eq)] 402 | pub enum LuaType { 403 | None, 404 | Nil, 405 | Boolean, 406 | LightUserData, 407 | Number, 408 | String, 409 | Table, 410 | Function, 411 | UserData, 412 | Thread, 413 | Integer, 414 | } 415 | 416 | impl From for i32 { 417 | fn from(lua_type: LuaType) -> Self { 418 | match lua_type { 419 | LuaType::None => ffi::LUA_TNONE, 420 | LuaType::Nil => ffi::LUA_TNIL, 421 | LuaType::Boolean => ffi::LUA_TBOOLEAN, 422 | LuaType::LightUserData => ffi::LUA_TLIGHTUSERDATA, 423 | LuaType::Number => ffi::LUA_TNUMBER, 424 | LuaType::Integer => ffi::LUA_TNUMBER, 425 | LuaType::String => ffi::LUA_TSTRING, 426 | LuaType::Table => ffi::LUA_TTABLE, 427 | LuaType::Function => ffi::LUA_TFUNCTION, 428 | LuaType::UserData => ffi::LUA_TUSERDATA, 429 | LuaType::Thread => ffi::LUA_TTHREAD, 430 | } 431 | } 432 | } 433 | 434 | #[inline] 435 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 436 | pub fn lua_type(state: LuaStateRef, index: i32) -> LuaType { 437 | let ltype = unsafe { ffi::lua_type(state, index) }; 438 | match ltype { 439 | ffi::LUA_TNONE => LuaType::None, 440 | ffi::LUA_TNIL => LuaType::Nil, 441 | ffi::LUA_TBOOLEAN => LuaType::Boolean, 442 | ffi::LUA_TLIGHTUSERDATA => LuaType::LightUserData, 443 | ffi::LUA_TNUMBER => { 444 | if unsafe { ffi::lua_isinteger(state, index) != 0 } { 445 | LuaType::Integer 446 | } else { 447 | LuaType::Number 448 | } 449 | } 450 | ffi::LUA_TSTRING => LuaType::String, 451 | ffi::LUA_TTABLE => LuaType::Table, 452 | ffi::LUA_TFUNCTION => LuaType::Function, 453 | ffi::LUA_TUSERDATA => LuaType::UserData, 454 | ffi::LUA_TTHREAD => LuaType::Thread, 455 | _ => unreachable!(), 456 | } 457 | } 458 | 459 | #[inline] 460 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 461 | pub fn lua_error(state: LuaStateRef, message: &str) -> ! { 462 | unsafe { 463 | ffi::lua_pushlstring(state, message.as_ptr() as *const c_char, message.len()); 464 | ffi::lua_error(state) 465 | } 466 | } 467 | 468 | #[inline] 469 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 470 | pub fn throw_error(state: LuaStateRef) -> ! { 471 | unsafe { ffi::lua_error(state) } 472 | } 473 | 474 | #[inline] 475 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 476 | pub fn type_name(state: LuaStateRef, idx: i32) -> &'static str { 477 | unsafe { 478 | std::ffi::CStr::from_ptr(ffi::lua_typename(state, idx)) 479 | .to_str() 480 | .unwrap_or_default() 481 | } 482 | } 483 | 484 | #[inline] 485 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 486 | pub fn lua_pushnil(state: LuaStateRef) { 487 | unsafe { 488 | ffi::lua_pushnil(state); 489 | } 490 | } 491 | 492 | #[inline] 493 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 494 | pub fn is_integer(state: LuaStateRef, index: i32) -> bool { 495 | unsafe { ffi::lua_isinteger(state, index) != 0 } 496 | } 497 | 498 | #[inline] 499 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 500 | pub fn lua_top(state: LuaStateRef) -> i32 { 501 | unsafe { ffi::lua_gettop(state) } 502 | } 503 | 504 | #[inline] 505 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 506 | pub fn lua_settop(state: LuaStateRef, idx: i32) { 507 | unsafe { 508 | ffi::lua_settop(state, idx); 509 | } 510 | } 511 | 512 | #[inline] 513 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 514 | pub fn lua_pop(state: LuaStateRef, n: i32) { 515 | unsafe { 516 | ffi::lua_pop(state, n); 517 | } 518 | } 519 | 520 | #[inline] 521 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 522 | pub fn lua_checktype(state: LuaStateRef, index: i32, ltype: i32) { 523 | unsafe { 524 | ffi::luaL_checktype(state, index, ltype); 525 | } 526 | } 527 | 528 | #[inline] 529 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 530 | pub fn luaL_checkstack(state: LuaStateRef, sz: i32, msg: *const c_char) { 531 | unsafe { 532 | ffi::luaL_checkstack(state, sz, msg); 533 | } 534 | } 535 | 536 | ///stack +1 537 | #[inline] 538 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 539 | pub fn lua_as_slice(state: LuaStateRef, index: i32) -> &'static [u8] { 540 | unsafe { 541 | let mut len = 0; 542 | let ptr = ffi::luaL_tolstring(state, index, &mut len); 543 | std::slice::from_raw_parts(ptr as *const u8, len) 544 | } 545 | } 546 | 547 | #[inline] 548 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 549 | pub fn lua_pushlightuserdata(state: LuaStateRef, p: *mut std::ffi::c_void) { 550 | unsafe { 551 | ffi::lua_pushlightuserdata(state, p); 552 | } 553 | } 554 | 555 | #[inline] 556 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 557 | pub fn lua_newuserdata( 558 | state: *mut ffi::lua_State, 559 | val: T, 560 | metaname: *const c_char, 561 | lib: &[ffi::luaL_Reg], 562 | ) -> Option<&T> { 563 | extern "C-unwind" fn lua_dropuserdata(state: *mut ffi::lua_State) -> c_int { 564 | unsafe { 565 | let p = ffi::lua_touserdata(state, 1); 566 | if p.is_null() { 567 | return 0; 568 | } 569 | let p = p as *mut T; 570 | std::ptr::drop_in_place(p); 571 | } 572 | 0 573 | } 574 | 575 | unsafe { 576 | let ptr = ffi::lua_newuserdatauv(state, std::mem::size_of::(), 0) as *mut T; 577 | let ptr = std::ptr::NonNull::new(ptr)?; 578 | 579 | ptr.as_ptr().write(val); 580 | 581 | if ffi::luaL_newmetatable(state, metaname) != 0 { 582 | ffi::lua_createtable(state, 0, lib.len() as c_int); 583 | ffi::luaL_setfuncs(state, lib.as_ptr(), 0); 584 | ffi::lua_setfield(state, -2, cstr!("__index")); 585 | ffi::lua_pushcfunction(state, lua_dropuserdata::); 586 | ffi::lua_setfield(state, -2, cstr!("__gc")); 587 | } 588 | 589 | ffi::lua_setmetatable(state, -2); 590 | Some(&*ptr.as_ptr()) 591 | } 592 | } 593 | 594 | #[inline] 595 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 596 | pub fn lua_touserdata(state: *mut ffi::lua_State, index: i32) -> Option<&'static mut T> { 597 | unsafe { 598 | let ptr = ffi::lua_touserdata(state, index); 599 | let ptr = std::ptr::NonNull::new(ptr)?; 600 | let ptr = ptr.as_ptr() as *mut T; 601 | Some(&mut *ptr) 602 | } 603 | } 604 | 605 | /// Converts an `isize` value from Lua state at the given index into a Rust `T` object. 606 | /// 607 | /// # Arguments 608 | /// 609 | /// * `state` - The Lua state. 610 | /// * `index` - The index in the Lua stack. 611 | /// 612 | /// # Safety 613 | /// 614 | /// This function is unsafe because it dereferences a raw pointer. 615 | /// 616 | /// # Returns 617 | /// 618 | /// A `Box` containing the Rust object. 619 | pub fn lua_into_userdata(state: LuaStateRef, index: i32) -> Box { 620 | let p_as_isize: isize = lua_get(state, index); 621 | unsafe { Box::from_raw(p_as_isize as *mut T) } 622 | } 623 | 624 | pub struct LuaTable { 625 | state: LuaStateRef, 626 | index: i32, 627 | } 628 | 629 | impl LuaTable { 630 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 631 | pub fn new(state: LuaStateRef, narr: usize, nrec: usize) -> Self { 632 | unsafe { 633 | ffi::lua_createtable(state, narr as i32, nrec as i32); 634 | LuaTable { 635 | state, 636 | index: ffi::lua_gettop(state), 637 | } 638 | } 639 | } 640 | 641 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 642 | pub fn from_stack(state: LuaStateRef, mut index: i32) -> Self { 643 | if index < 0 { 644 | index = unsafe { ffi::lua_gettop(state) + index + 1 }; 645 | } 646 | LuaTable { state, index } 647 | } 648 | 649 | pub fn len(&self) -> usize { 650 | unsafe { ffi::lua_rawlen(self.state, self.index) } 651 | } 652 | 653 | pub fn array_len(&self) -> usize { 654 | lua_array_size(self.state, self.index) 655 | } 656 | 657 | pub fn lua_state(&self) -> LuaStateRef { 658 | self.state 659 | } 660 | 661 | pub fn index(&self) -> i32 { 662 | self.index 663 | } 664 | 665 | pub fn is_empty(&self) -> bool { 666 | self.len() == 0 667 | } 668 | 669 | pub fn seti(&self, n: usize) { 670 | unsafe { 671 | ffi::lua_rawseti(self.state, self.index, n as ffi::lua_Integer); 672 | } 673 | } 674 | 675 | pub fn rawset(&self, key: K, val: V) -> &Self 676 | where 677 | K: LuaStack, 678 | V: LuaStack, 679 | { 680 | unsafe { 681 | K::push(self.state, key); 682 | V::push(self.state, val); 683 | ffi::lua_rawset(self.state, self.index); 684 | } 685 | self 686 | } 687 | 688 | pub fn rawset_x(&self, key: K, f: F) -> &Self 689 | where 690 | K: LuaStack, 691 | F: FnOnce(), 692 | { 693 | unsafe { 694 | K::push(self.state, key); 695 | f(); 696 | ffi::lua_rawset(self.state, self.index); 697 | } 698 | self 699 | } 700 | 701 | pub fn rawget(&self, key: K) -> LuaScopeValue 702 | where 703 | K: LuaStack, 704 | { 705 | unsafe { 706 | K::push(self.state, key); 707 | ffi::lua_rawget(self.state, self.index); 708 | LuaScopeValue { 709 | state: self.state, 710 | value: LuaValue::from_stack(self.state, -1), 711 | _marker: PhantomData, 712 | } 713 | } 714 | } 715 | 716 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 717 | pub fn getmetafield(&self, e: *const c_char) -> Option { 718 | unsafe { 719 | if ffi::luaL_getmetafield(self.state, self.index, e) == ffi::LUA_TNIL { 720 | None 721 | } else { 722 | Some(LuaScopeValue { 723 | state: self.state, 724 | value: LuaValue::from_stack(self.state, -1), 725 | _marker: PhantomData, 726 | }) 727 | } 728 | } 729 | } 730 | 731 | pub fn iter(&self) -> LuaTableIterator { 732 | unsafe { 733 | ffi::lua_pushnil(self.state); 734 | } 735 | LuaTableIterator { 736 | table: self, 737 | has_value: false, 738 | _marker: PhantomData, 739 | } 740 | } 741 | 742 | pub fn array_iter(&self, len: usize) -> LuaArrayIterator { 743 | LuaArrayIterator { 744 | table: self, 745 | pos: 0, 746 | len, 747 | has_value: false, 748 | _marker: PhantomData, 749 | } 750 | } 751 | } 752 | 753 | pub struct LuaTableIterator<'a> { 754 | table: &'a LuaTable, 755 | has_value: bool, 756 | _marker: PhantomData<&'a mut LuaTable>, 757 | } 758 | 759 | impl<'a> Iterator for LuaTableIterator<'a> { 760 | type Item = (LuaValue<'a>, LuaValue<'a>); 761 | 762 | fn next(&mut self) -> Option { 763 | unsafe { 764 | if self.has_value { 765 | ffi::lua_pop(self.table.state, 1); 766 | self.has_value = false; 767 | } 768 | 769 | if ffi::lua_next(self.table.state, self.table.index) == 0 { 770 | return None; 771 | } 772 | 773 | self.has_value = true; 774 | 775 | Some(( 776 | LuaValue::from_stack(self.table.state, -2), 777 | LuaValue::from_stack(self.table.state, -1), 778 | )) 779 | } 780 | } 781 | } 782 | 783 | impl Drop for LuaTableIterator<'_> { 784 | fn drop(&mut self) { 785 | unsafe { 786 | if self.has_value { 787 | ffi::lua_pop(self.table.state, 1); 788 | } 789 | } 790 | } 791 | } 792 | 793 | pub struct LuaArrayIterator<'a> { 794 | table: &'a LuaTable, 795 | pos: usize, 796 | len: usize, 797 | has_value: bool, 798 | _marker: PhantomData<&'a mut LuaTable>, 799 | } 800 | 801 | impl<'a> Iterator for LuaArrayIterator<'a> { 802 | type Item = LuaValue<'a>; 803 | 804 | fn next(&mut self) -> Option { 805 | unsafe { 806 | if self.has_value { 807 | self.has_value = false; 808 | ffi::lua_pop(self.table.state, 1); 809 | } 810 | 811 | if self.pos >= self.len { 812 | return None; 813 | } 814 | 815 | self.pos += 1; 816 | ffi::lua_rawgeti( 817 | self.table.state, 818 | self.table.index, 819 | self.pos as ffi::lua_Integer, 820 | ); 821 | self.has_value = true; 822 | Some(LuaValue::from_stack(self.table.state, -1)) 823 | } 824 | } 825 | } 826 | 827 | impl Drop for LuaArrayIterator<'_> { 828 | fn drop(&mut self) { 829 | unsafe { 830 | // Clean up any remaining items on stack 831 | if self.has_value { 832 | ffi::lua_pop(self.table.state, 1); 833 | } 834 | } 835 | } 836 | } 837 | 838 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 839 | pub fn lua_array_size(state: *mut ffi::lua_State, idx: i32) -> usize { 840 | unsafe { 841 | ffi::lua_pushnil(state); 842 | if ffi::lua_next(state, idx) == 0 { 843 | return 0; 844 | } 845 | 846 | let first_key = if ffi::lua_isinteger(state, -2) != 0 { 847 | ffi::lua_tointeger(state, -2) 848 | } else { 849 | 0 850 | }; 851 | 852 | ffi::lua_pop(state, 2); 853 | 854 | if first_key <= 0 { 855 | return 0; 856 | } else if first_key == 1 { 857 | /* 858 | * https://www.lua.org/manual/5.4/manual.html#3.4.7 859 | * The length operator applied on a table returns a border in that table. 860 | * A border in a table t is any natural number that satisfies the following condition : 861 | * (border == 0 or t[border] ~= nil) and t[border + 1] == nil 862 | */ 863 | let len = ffi::lua_rawlen(state, idx) as ffi::lua_Integer; 864 | ffi::lua_pushinteger(state, len); 865 | if ffi::lua_next(state, idx) != 0 { 866 | ffi::lua_pop(state, 2); 867 | return 0; 868 | } 869 | return len as usize; 870 | } 871 | 872 | let len = ffi::lua_rawlen(state, idx) as ffi::lua_Integer; 873 | if first_key > len { 874 | return 0; 875 | } 876 | 877 | ffi::lua_pushnil(state); 878 | while ffi::lua_next(state, idx) != 0 { 879 | if ffi::lua_isinteger(state, -2) != 0 { 880 | let x = ffi::lua_tointeger(state, -2); 881 | if x > 0 && x <= len { 882 | ffi::lua_pop(state, 1); 883 | continue; 884 | } 885 | } 886 | ffi::lua_pop(state, 2); 887 | return 0; 888 | } 889 | 890 | len as usize 891 | } 892 | } 893 | 894 | pub enum LuaValue<'a> { 895 | None, 896 | Nil, 897 | Boolean(bool), 898 | LightUserData(*mut std::ffi::c_void), 899 | Number(f64), 900 | Integer(i64), 901 | String(&'a [u8]), 902 | Table(LuaTable), 903 | Function(*const std::ffi::c_void), 904 | UserData(*mut std::ffi::c_void), 905 | Thread(LuaStateRef), 906 | } 907 | 908 | impl LuaValue<'_> { 909 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 910 | pub fn from_stack(state: LuaStateRef, index: i32) -> Self { 911 | match lua_type(state, index) { 912 | LuaType::None => LuaValue::None, 913 | LuaType::Nil => LuaValue::Nil, 914 | LuaType::Boolean => LuaValue::Boolean(lua_to(state, index)), 915 | LuaType::LightUserData => { 916 | LuaValue::LightUserData(unsafe { ffi::lua_touserdata(state, index) }) 917 | } 918 | LuaType::Number => LuaValue::Number(lua_to(state, index)), 919 | LuaType::Integer => LuaValue::Integer(lua_to(state, index)), 920 | LuaType::String => LuaValue::String(lua_to(state, index)), 921 | LuaType::Table => LuaValue::Table(LuaTable::from_stack(state, index)), 922 | LuaType::Function => LuaValue::Function(unsafe { ffi::lua_topointer(state, index) }), 923 | LuaType::UserData => LuaValue::UserData(unsafe { ffi::lua_touserdata(state, index) }), 924 | LuaType::Thread => LuaValue::Thread(unsafe { ffi::lua_tothread(state, index) }), 925 | } 926 | } 927 | 928 | pub fn name(&self) -> String { 929 | match self { 930 | LuaValue::None => "none".to_string(), 931 | LuaValue::Nil => "nil".to_string(), 932 | LuaValue::Boolean(_) => "boolean".to_string(), 933 | LuaValue::LightUserData(_) => "lightuserdata".to_string(), 934 | LuaValue::Number(_) => "number".to_string(), 935 | LuaValue::Integer(_) => "number".to_string(), 936 | LuaValue::String(_) => "string".to_string(), 937 | LuaValue::Table(_) => "table".to_string(), 938 | LuaValue::Function(_) => "function".to_string(), 939 | LuaValue::UserData(_) => "userdata".to_string(), 940 | LuaValue::Thread(_) => "thread".to_string(), 941 | } 942 | } 943 | 944 | pub fn to_vec(&self) -> Vec { 945 | match self { 946 | LuaValue::String(s) => s.to_vec(), 947 | LuaValue::Integer(n) => n.to_string().into_bytes(), 948 | LuaValue::Number(n) => n.to_string().into_bytes(), 949 | _ => Vec::new(), 950 | } 951 | } 952 | } 953 | 954 | impl Display for LuaValue<'_> { 955 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 956 | match self { 957 | LuaValue::None => write!(f, "none"), 958 | LuaValue::Nil => write!(f, "nil"), 959 | LuaValue::Boolean(b) => write!(f, "{}", b), 960 | LuaValue::LightUserData(p) => write!(f, "{:p}", p), 961 | LuaValue::Number(n) => write!(f, "{}", n), 962 | LuaValue::Integer(n) => write!(f, "{}", n), 963 | LuaValue::String(s) => write!(f, "{}", String::from_utf8_lossy(s)), 964 | LuaValue::Table(_) => write!(f, "table"), 965 | LuaValue::Function(p) => write!(f, "{:p}", p), 966 | LuaValue::UserData(p) => write!(f, "{:p}", p), 967 | LuaValue::Thread(p) => write!(f, "{:p}", p), 968 | } 969 | } 970 | } 971 | 972 | pub struct LuaScopeValue<'a> { 973 | state: LuaStateRef, 974 | pub value: LuaValue<'a>, 975 | _marker: PhantomData<&'a mut LuaTable>, 976 | } 977 | 978 | impl Drop for LuaScopeValue<'_> { 979 | fn drop(&mut self) { 980 | unsafe { 981 | ffi::lua_pop(self.state, 1); 982 | } 983 | } 984 | } 985 | 986 | pub struct LuaArgs { 987 | current: i32, 988 | } 989 | 990 | impl LuaArgs { 991 | pub fn new(start: i32) -> Self { 992 | LuaArgs { current: start } 993 | } 994 | 995 | pub fn iter_arg(&mut self) -> i32 { 996 | let result = self.current; 997 | self.current += 1; 998 | result 999 | } 1000 | } 1001 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Low level bindings to Lua 5.4/5.3/5.2/5.1 (including LuaJIT) and Roblox Luau. 2 | 3 | #![allow(non_camel_case_types, non_snake_case, dead_code)] 4 | #![allow(clippy::missing_safety_doc)] 5 | #![doc(test(attr(deny(warnings))))] 6 | #![cfg_attr(docsrs, feature(doc_cfg))] 7 | 8 | use std::os::raw::c_int; 9 | 10 | #[cfg(any(feature = "lua54", doc))] 11 | pub use lua54::*; 12 | 13 | #[cfg(any(feature = "luau", doc))] 14 | pub use luau::*; 15 | 16 | #[cfg(feature = "lua54")] 17 | #[doc(hidden)] 18 | pub const LUA_MAX_UPVALUES: c_int = 255; 19 | 20 | #[cfg(feature = "luau")] 21 | #[doc(hidden)] 22 | pub const LUA_MAX_UPVALUES: c_int = 200; 23 | 24 | // I believe `luaL_traceback` < 5.4 requires this much free stack to not error. 25 | // 5.4 uses `luaL_Buffer` 26 | #[doc(hidden)] 27 | pub const LUA_TRACEBACK_STACK: c_int = 11; 28 | 29 | // The minimum alignment guaranteed by the architecture. This value is used to 30 | // add fast paths for low alignment values. 31 | // Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs 32 | #[cfg(any( 33 | target_arch = "x86", 34 | target_arch = "arm", 35 | target_arch = "mips", 36 | target_arch = "powerpc", 37 | target_arch = "powerpc64", 38 | target_arch = "sparc", 39 | target_arch = "wasm32", 40 | target_arch = "hexagon", 41 | all(target_arch = "riscv32", not(target_os = "espidf")), 42 | all(target_arch = "xtensa", not(target_os = "espidf")), 43 | ))] 44 | #[doc(hidden)] 45 | pub const SYS_MIN_ALIGN: usize = 8; 46 | #[cfg(any( 47 | target_arch = "x86_64", 48 | target_arch = "aarch64", 49 | target_arch = "mips64", 50 | target_arch = "s390x", 51 | target_arch = "sparc64", 52 | target_arch = "riscv64", 53 | target_arch = "wasm64", 54 | ))] 55 | #[doc(hidden)] 56 | pub const SYS_MIN_ALIGN: usize = 16; 57 | // The allocator on the esp-idf platform guarentees 4 byte alignment. 58 | #[cfg(any( 59 | all(target_arch = "riscv32", target_os = "espidf"), 60 | all(target_arch = "xtensa", target_os = "espidf"), 61 | ))] 62 | #[doc(hidden)] 63 | pub const SYS_MIN_ALIGN: usize = 4; 64 | 65 | #[macro_use] 66 | mod macros; 67 | 68 | #[cfg(any(feature = "lua54", doc))] 69 | #[cfg_attr(docsrs, doc(cfg(feature = "lua54")))] 70 | pub mod lua54; 71 | 72 | pub use lua54 as ffi; 73 | 74 | #[cfg(any(feature = "luau", doc))] 75 | #[cfg_attr(docsrs, doc(cfg(feature = "luau")))] 76 | pub mod luau; 77 | 78 | pub mod laux; 79 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/src/lua54/lauxlib.rs: -------------------------------------------------------------------------------- 1 | //! Contains definitions from `lauxlib.h`. 2 | 3 | use std::os::raw::{c_char, c_int, c_void}; 4 | use std::ptr; 5 | 6 | use super::lua::{self, lua_CFunction, lua_Integer, lua_Number, lua_State}; 7 | 8 | // Extra error code for 'luaL_loadfilex' 9 | pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1; 10 | 11 | // Key, in the registry, for table of loaded modules 12 | pub const LUA_LOADED_TABLE: &str = "_LOADED"; 13 | 14 | // Key, in the registry, for table of preloaded loaders 15 | pub const LUA_PRELOAD_TABLE: &str = "_PRELOAD"; 16 | 17 | #[repr(C)] 18 | pub struct luaL_Reg { 19 | pub name: *const c_char, 20 | pub func: lua_CFunction, 21 | } 22 | 23 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 24 | extern "C-unwind" { 25 | pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: usize); 26 | 27 | pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; 28 | pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; 29 | pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; 30 | pub fn luaL_argerror(L: *mut lua_State, arg: c_int, extramsg: *const c_char) -> c_int; 31 | pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut usize) -> *const c_char; 32 | pub fn luaL_optlstring( 33 | L: *mut lua_State, 34 | arg: c_int, 35 | def: *const c_char, 36 | l: *mut usize, 37 | ) -> *const c_char; 38 | pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number; 39 | pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number; 40 | pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer; 41 | pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer; 42 | 43 | pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char); 44 | pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int); 45 | pub fn luaL_checkany(L: *mut lua_State, arg: c_int); 46 | 47 | pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int; 48 | pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char); 49 | pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void; 50 | pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void; 51 | 52 | pub fn luaL_where(L: *mut lua_State, lvl: c_int); 53 | pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !; 54 | 55 | pub fn luaL_checkoption( 56 | L: *mut lua_State, 57 | arg: c_int, 58 | def: *const c_char, 59 | lst: *const *const c_char, 60 | ) -> c_int; 61 | 62 | pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int; 63 | pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int; 64 | } 65 | 66 | // Pre-defined references 67 | pub const LUA_NOREF: c_int = -2; 68 | pub const LUA_REFNIL: c_int = -1; 69 | 70 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 71 | extern "C-unwind" { 72 | pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int; 73 | pub fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int); 74 | 75 | pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) 76 | -> c_int; 77 | } 78 | 79 | #[inline(always)] 80 | pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int { 81 | luaL_loadfilex(L, f, ptr::null()) 82 | } 83 | 84 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 85 | extern "C-unwind" { 86 | pub fn luaL_loadbufferx( 87 | L: *mut lua_State, 88 | buff: *const c_char, 89 | sz: usize, 90 | name: *const c_char, 91 | mode: *const c_char, 92 | ) -> c_int; 93 | pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int; 94 | 95 | pub fn luaL_newstate() -> *mut lua_State; 96 | 97 | pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer; 98 | 99 | // TODO: luaL_addgsub 100 | 101 | pub fn luaL_gsub( 102 | L: *mut lua_State, 103 | s: *const c_char, 104 | p: *const c_char, 105 | r: *const c_char, 106 | ) -> *const c_char; 107 | 108 | pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int); 109 | 110 | pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int; 111 | 112 | pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int); 113 | 114 | pub fn luaL_requiref( 115 | L: *mut lua_State, 116 | modname: *const c_char, 117 | openf: lua_CFunction, 118 | glb: c_int, 119 | ); 120 | 121 | pub fn luaL_initcodecache(); 122 | } 123 | 124 | // 125 | // Some useful macros (implemented as Rust functions) 126 | // 127 | 128 | // TODO: luaL_newlibtable, luaL_newlib 129 | 130 | #[inline(always)] 131 | pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) { 132 | if cond == 0 { 133 | luaL_argerror(L, arg, extramsg); 134 | } 135 | } 136 | 137 | #[inline(always)] 138 | pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char { 139 | luaL_checklstring(L, n, ptr::null_mut()) 140 | } 141 | 142 | #[inline(always)] 143 | pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char { 144 | luaL_optlstring(L, n, d, ptr::null_mut()) 145 | } 146 | 147 | #[inline(always)] 148 | pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char { 149 | lua::lua_typename(L, lua::lua_type(L, i)) 150 | } 151 | 152 | #[inline(always)] 153 | pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int { 154 | let status = luaL_loadfile(L, filename); 155 | if status == 0 { 156 | lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0) 157 | } else { 158 | status 159 | } 160 | } 161 | 162 | #[inline(always)] 163 | pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int { 164 | let status = luaL_loadstring(L, s); 165 | if status == 0 { 166 | lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0) 167 | } else { 168 | status 169 | } 170 | } 171 | 172 | #[inline(always)] 173 | pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) { 174 | lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n); 175 | } 176 | 177 | // luaL_opt would be implemented here but it is undocumented, so it's omitted 178 | 179 | #[inline(always)] 180 | pub unsafe fn luaL_loadbuffer( 181 | L: *mut lua_State, 182 | s: *const c_char, 183 | sz: usize, 184 | n: *const c_char, 185 | ) -> c_int { 186 | luaL_loadbufferx(L, s, sz, n, ptr::null()) 187 | } 188 | 189 | // 190 | // TODO: Generic Buffer Manipulation 191 | // 192 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/src/lua54/lua.rs: -------------------------------------------------------------------------------- 1 | //! Contains definitions from `lua.h`. 2 | 3 | use std::marker::{PhantomData, PhantomPinned}; 4 | use std::mem; 5 | use std::os::raw::{c_char, c_double, c_int, c_uchar, c_ushort, c_void}; 6 | use std::ptr; 7 | 8 | // Mark for precompiled code (`Lua`) 9 | pub const LUA_SIGNATURE: &[u8] = b"\x1bLua"; 10 | 11 | // Option for multiple returns in 'lua_pcall' and 'lua_call' 12 | pub const LUA_MULTRET: c_int = -1; 13 | 14 | // Size of the Lua stack 15 | pub const LUAI_MAXSTACK: c_int = 1000000; 16 | 17 | // Size of a raw memory area associated with a Lua state with very fast access. 18 | pub const LUA_EXTRASPACE: usize = mem::size_of::<*const ()>(); 19 | 20 | // 21 | // Pseudo-indices 22 | // 23 | pub const LUA_REGISTRYINDEX: c_int = -LUAI_MAXSTACK - 1000; 24 | 25 | pub const fn lua_upvalueindex(i: c_int) -> c_int { 26 | LUA_REGISTRYINDEX - i 27 | } 28 | 29 | // 30 | // Thread status 31 | // 32 | pub const LUA_OK: c_int = 0; 33 | pub const LUA_YIELD: c_int = 1; 34 | pub const LUA_ERRRUN: c_int = 2; 35 | pub const LUA_ERRSYNTAX: c_int = 3; 36 | pub const LUA_ERRMEM: c_int = 4; 37 | pub const LUA_ERRERR: c_int = 5; 38 | 39 | /// A raw Lua state associated with a thread. 40 | #[repr(C)] 41 | pub struct lua_State { 42 | _data: [u8; 0], 43 | _marker: PhantomData<(*mut u8, PhantomPinned)>, 44 | } 45 | 46 | // 47 | // Basic types 48 | // 49 | pub const LUA_TNONE: c_int = -1; 50 | 51 | pub const LUA_TNIL: c_int = 0; 52 | pub const LUA_TBOOLEAN: c_int = 1; 53 | pub const LUA_TLIGHTUSERDATA: c_int = 2; 54 | pub const LUA_TNUMBER: c_int = 3; 55 | pub const LUA_TSTRING: c_int = 4; 56 | pub const LUA_TTABLE: c_int = 5; 57 | pub const LUA_TFUNCTION: c_int = 6; 58 | pub const LUA_TUSERDATA: c_int = 7; 59 | pub const LUA_TTHREAD: c_int = 8; 60 | 61 | pub const LUA_NUMTYPES: c_int = 9; 62 | 63 | /// Minimum Lua stack available to a C function 64 | pub const LUA_MINSTACK: c_int = 20; 65 | 66 | // Predefined values in the registry 67 | pub const LUA_RIDX_MAINTHREAD: lua_Integer = 1; 68 | pub const LUA_RIDX_GLOBALS: lua_Integer = 2; 69 | pub const LUA_RIDX_LAST: lua_Integer = LUA_RIDX_GLOBALS; 70 | 71 | /// A Lua number, usually equivalent to `f64` 72 | pub type lua_Number = c_double; 73 | 74 | /// A Lua integer, usually equivalent to `i64` 75 | pub type lua_Integer = i64; 76 | 77 | /// A Lua unsigned integer, usually equivalent to `u64` 78 | pub type lua_Unsigned = u64; 79 | 80 | /// Type for continuation-function contexts 81 | pub type lua_KContext = isize; 82 | 83 | /// Type for native C functions that can be passed to Lua 84 | pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int; 85 | 86 | /// Type for continuation functions 87 | pub type lua_KFunction = 88 | unsafe extern "C-unwind" fn(L: *mut lua_State, status: c_int, ctx: lua_KContext) -> c_int; 89 | 90 | // Type for functions that read/write blocks when loading/dumping Lua chunks 91 | #[rustfmt::skip] 92 | pub type lua_Reader = 93 | unsafe extern "C-unwind" fn(L: *mut lua_State, ud: *mut c_void, sz: *mut usize) -> *const c_char; 94 | #[rustfmt::skip] 95 | pub type lua_Writer = 96 | unsafe extern "C-unwind" fn(L: *mut lua_State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int; 97 | 98 | /// Type for memory-allocation functions 99 | #[rustfmt::skip] 100 | pub type lua_Alloc = 101 | unsafe extern "C-unwind" fn(ud: *mut c_void, ptr: *mut c_void, osize: usize, nsize: usize) -> *mut c_void; 102 | 103 | /// Type for warning functions 104 | pub type lua_WarnFunction = 105 | unsafe extern "C-unwind" fn(ud: *mut c_void, msg: *const c_char, tocont: c_int); 106 | 107 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 108 | extern "C-unwind" { 109 | // 110 | // State manipulation 111 | // 112 | pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State; 113 | pub fn lua_close(L: *mut lua_State); 114 | pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State; 115 | // Deprecated in Lua 5.4.6 116 | pub fn lua_resetthread(L: *mut lua_State) -> c_int; 117 | 118 | pub fn lua_closethread(L: *mut lua_State, from: *mut lua_State) -> c_int; 119 | 120 | pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction; 121 | 122 | pub fn lua_version(L: *mut lua_State) -> lua_Number; 123 | 124 | // 125 | // Basic stack manipulation 126 | // 127 | pub fn lua_absindex(L: *mut lua_State, idx: c_int) -> c_int; 128 | pub fn lua_gettop(L: *mut lua_State) -> c_int; 129 | pub fn lua_settop(L: *mut lua_State, idx: c_int); 130 | pub fn lua_pushvalue(L: *mut lua_State, idx: c_int); 131 | pub fn lua_rotate(L: *mut lua_State, idx: c_int, n: c_int); 132 | pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int); 133 | pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int; 134 | 135 | pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int); 136 | 137 | // 138 | // Access functions (stack -> C) 139 | // 140 | pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int; 141 | pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int; 142 | pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int; 143 | pub fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int; 144 | pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int; 145 | pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int; 146 | pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char; 147 | 148 | pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number; 149 | pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer; 150 | pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int; 151 | pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; 152 | pub fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize; 153 | pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option; 154 | pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void; 155 | pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State; 156 | pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void; 157 | } 158 | 159 | // 160 | // Comparison and arithmetic functions 161 | // 162 | pub const LUA_OPADD: c_int = 0; 163 | pub const LUA_OPSUB: c_int = 1; 164 | pub const LUA_OPMUL: c_int = 2; 165 | pub const LUA_OPMOD: c_int = 3; 166 | pub const LUA_OPPOW: c_int = 4; 167 | pub const LUA_OPDIV: c_int = 5; 168 | pub const LUA_OPIDIV: c_int = 6; 169 | pub const LUA_OPBAND: c_int = 7; 170 | pub const LUA_OPBOR: c_int = 8; 171 | pub const LUA_OPBXOR: c_int = 9; 172 | pub const LUA_OPSHL: c_int = 10; 173 | pub const LUA_OPSHR: c_int = 11; 174 | pub const LUA_OPUNM: c_int = 12; 175 | pub const LUA_OPBNOT: c_int = 13; 176 | 177 | pub const LUA_OPEQ: c_int = 0; 178 | pub const LUA_OPLT: c_int = 1; 179 | pub const LUA_OPLE: c_int = 2; 180 | 181 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 182 | extern "C-unwind" { 183 | pub fn lua_arith(L: *mut lua_State, op: c_int); 184 | pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int; 185 | pub fn lua_compare(L: *mut lua_State, idx1: c_int, idx2: c_int, op: c_int) -> c_int; 186 | } 187 | 188 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 189 | extern "C-unwind" { 190 | // 191 | // Push functions (C -> stack) 192 | // 193 | pub fn lua_pushnil(L: *mut lua_State); 194 | pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number); 195 | pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer); 196 | pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, len: usize) -> *const c_char; 197 | pub fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char; 198 | // lua_pushvfstring 199 | pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char; 200 | pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int); 201 | pub fn lua_pushboolean(L: *mut lua_State, b: c_int); 202 | pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void); 203 | pub fn lua_pushthread(L: *mut lua_State) -> c_int; 204 | 205 | // 206 | // Get functions (Lua -> stack) 207 | // 208 | pub fn lua_getglobal(L: *mut lua_State, name: *const c_char) -> c_int; 209 | pub fn lua_gettable(L: *mut lua_State, idx: c_int) -> c_int; 210 | pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char) -> c_int; 211 | pub fn lua_geti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int; 212 | pub fn lua_rawget(L: *mut lua_State, idx: c_int) -> c_int; 213 | pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_int; 214 | pub fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int; 215 | 216 | pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int); 217 | pub fn lua_newuserdatauv(L: *mut lua_State, sz: usize, nuvalue: c_int) -> *mut c_void; 218 | pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int; 219 | pub fn lua_getiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int; 220 | 221 | // 222 | // Set functions (stack -> Lua) 223 | // 224 | pub fn lua_setglobal(L: *mut lua_State, name: *const c_char); 225 | pub fn lua_settable(L: *mut lua_State, idx: c_int); 226 | pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char); 227 | pub fn lua_seti(L: *mut lua_State, idx: c_int, n: lua_Integer); 228 | pub fn lua_rawset(L: *mut lua_State, idx: c_int); 229 | pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer); 230 | pub fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void); 231 | pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int; 232 | pub fn lua_setiuservalue(L: *mut lua_State, idx: c_int, n: c_int) -> c_int; 233 | 234 | // 235 | // 'load' and 'call' functions (load and run Lua code) 236 | // 237 | pub fn lua_callk( 238 | L: *mut lua_State, 239 | nargs: c_int, 240 | nresults: c_int, 241 | ctx: lua_KContext, 242 | k: Option, 243 | ); 244 | pub fn lua_pcallk( 245 | L: *mut lua_State, 246 | nargs: c_int, 247 | nresults: c_int, 248 | errfunc: c_int, 249 | ctx: lua_KContext, 250 | k: Option, 251 | ) -> c_int; 252 | 253 | pub fn lua_load( 254 | L: *mut lua_State, 255 | reader: lua_Reader, 256 | data: *mut c_void, 257 | chunkname: *const c_char, 258 | mode: *const c_char, 259 | ) -> c_int; 260 | 261 | pub fn lua_dump( 262 | L: *mut lua_State, 263 | writer: lua_Writer, 264 | data: *mut c_void, 265 | strip: c_int, 266 | ) -> c_int; 267 | } 268 | 269 | #[inline(always)] 270 | pub unsafe fn lua_call(L: *mut lua_State, n: c_int, r: c_int) { 271 | lua_callk(L, n, r, 0, None) 272 | } 273 | 274 | #[inline(always)] 275 | pub unsafe fn lua_pcall(L: *mut lua_State, n: c_int, r: c_int, f: c_int) -> c_int { 276 | lua_pcallk(L, n, r, f, 0, None) 277 | } 278 | 279 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 280 | extern "C-unwind" { 281 | // 282 | // Coroutine functions 283 | // 284 | pub fn lua_yieldk( 285 | L: *mut lua_State, 286 | nresults: c_int, 287 | ctx: lua_KContext, 288 | k: Option, 289 | ) -> c_int; 290 | pub fn lua_resume( 291 | L: *mut lua_State, 292 | from: *mut lua_State, 293 | narg: c_int, 294 | nres: *mut c_int, 295 | ) -> c_int; 296 | pub fn lua_status(L: *mut lua_State) -> c_int; 297 | pub fn lua_isyieldable(L: *mut lua_State) -> c_int; 298 | } 299 | 300 | #[inline(always)] 301 | pub unsafe fn lua_yield(L: *mut lua_State, n: c_int) -> c_int { 302 | lua_yieldk(L, n, 0, None) 303 | } 304 | 305 | // 306 | // Warning-related functions 307 | // 308 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 309 | extern "C-unwind" { 310 | pub fn lua_setwarnf(L: *mut lua_State, f: Option, ud: *mut c_void); 311 | pub fn lua_warning(L: *mut lua_State, msg: *const c_char, tocont: c_int); 312 | } 313 | 314 | // 315 | // Garbage-collection function and options 316 | // 317 | pub const LUA_GCSTOP: c_int = 0; 318 | pub const LUA_GCRESTART: c_int = 1; 319 | pub const LUA_GCCOLLECT: c_int = 2; 320 | pub const LUA_GCCOUNT: c_int = 3; 321 | pub const LUA_GCCOUNTB: c_int = 4; 322 | pub const LUA_GCSTEP: c_int = 5; 323 | pub const LUA_GCSETPAUSE: c_int = 6; 324 | pub const LUA_GCSETSTEPMUL: c_int = 7; 325 | pub const LUA_GCISRUNNING: c_int = 9; 326 | pub const LUA_GCGEN: c_int = 10; 327 | pub const LUA_GCINC: c_int = 11; 328 | 329 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 330 | extern "C-unwind" { 331 | pub fn lua_gc(L: *mut lua_State, what: c_int, ...) -> c_int; 332 | } 333 | 334 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 335 | extern "C-unwind" { 336 | // 337 | // Miscellaneous functions 338 | // 339 | pub fn lua_error(L: *mut lua_State) -> !; 340 | pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int; 341 | pub fn lua_concat(L: *mut lua_State, n: c_int); 342 | pub fn lua_len(L: *mut lua_State, idx: c_int); 343 | pub fn lua_stringtonumber(L: *mut lua_State, s: *const c_char) -> usize; 344 | pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc; 345 | pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void); 346 | 347 | pub fn lua_toclose(L: *mut lua_State, idx: c_int); 348 | pub fn lua_closeslot(L: *mut lua_State, idx: c_int); 349 | } 350 | 351 | // 352 | // Some useful macros (implemented as Rust functions) 353 | // 354 | #[inline(always)] 355 | pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void { 356 | (L as *mut c_char).sub(LUA_EXTRASPACE) as *mut c_void 357 | } 358 | 359 | #[inline(always)] 360 | pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number { 361 | lua_tonumberx(L, i, ptr::null_mut()) 362 | } 363 | 364 | #[inline(always)] 365 | pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer { 366 | lua_tointegerx(L, i, ptr::null_mut()) 367 | } 368 | 369 | #[inline(always)] 370 | pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) { 371 | lua_settop(L, -n - 1) 372 | } 373 | 374 | #[inline(always)] 375 | pub unsafe fn lua_newtable(L: *mut lua_State) { 376 | lua_createtable(L, 0, 0) 377 | } 378 | 379 | #[inline(always)] 380 | pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) { 381 | lua_pushcfunction(L, f); 382 | lua_setglobal(L, n) 383 | } 384 | 385 | #[inline(always)] 386 | pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) { 387 | lua_pushcclosure(L, f, 0) 388 | } 389 | 390 | #[inline(always)] 391 | pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int { 392 | (lua_type(L, n) == LUA_TFUNCTION) as c_int 393 | } 394 | 395 | #[inline(always)] 396 | pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int { 397 | (lua_type(L, n) == LUA_TTABLE) as c_int 398 | } 399 | 400 | #[inline(always)] 401 | pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int { 402 | (lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int 403 | } 404 | 405 | #[inline(always)] 406 | pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int { 407 | (lua_type(L, n) == LUA_TNIL) as c_int 408 | } 409 | 410 | #[inline(always)] 411 | pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int { 412 | (lua_type(L, n) == LUA_TBOOLEAN) as c_int 413 | } 414 | 415 | #[inline(always)] 416 | pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int { 417 | (lua_type(L, n) == LUA_TTHREAD) as c_int 418 | } 419 | 420 | #[inline(always)] 421 | pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int { 422 | (lua_type(L, n) == LUA_TNONE) as c_int 423 | } 424 | 425 | #[inline(always)] 426 | pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int { 427 | (lua_type(L, n) <= 0) as c_int 428 | } 429 | 430 | #[inline(always)] 431 | pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) -> *const c_char { 432 | use std::ffi::CString; 433 | let c_str = CString::new(s).unwrap(); 434 | lua_pushlstring(L, c_str.as_ptr(), c_str.as_bytes().len()) 435 | } 436 | 437 | #[inline(always)] 438 | pub unsafe fn lua_pushglobaltable(L: *mut lua_State) -> c_int { 439 | lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS) 440 | } 441 | 442 | #[inline(always)] 443 | pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char { 444 | lua_tolstring(L, i, ptr::null_mut()) 445 | } 446 | 447 | #[inline(always)] 448 | pub unsafe fn lua_insert(L: *mut lua_State, idx: c_int) { 449 | lua_rotate(L, idx, 1) 450 | } 451 | 452 | #[inline(always)] 453 | pub unsafe fn lua_remove(L: *mut lua_State, idx: c_int) { 454 | lua_rotate(L, idx, -1); 455 | lua_pop(L, 1) 456 | } 457 | 458 | #[inline(always)] 459 | pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) { 460 | lua_copy(L, -1, idx); 461 | lua_pop(L, 1) 462 | } 463 | 464 | #[inline(always)] 465 | pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) { 466 | lua_pushvalue(from, idx); 467 | lua_xmove(from, to, 1); 468 | } 469 | 470 | #[inline(always)] 471 | pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void { 472 | lua_newuserdatauv(L, sz, 1) 473 | } 474 | 475 | #[inline(always)] 476 | pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int { 477 | lua_getiuservalue(L, idx, 1) 478 | } 479 | 480 | #[inline(always)] 481 | pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) -> c_int { 482 | lua_setiuservalue(L, idx, 1) 483 | } 484 | 485 | // 486 | // Debug API 487 | // 488 | 489 | // Maximum size for the description of the source of a function in debug information. 490 | const LUA_IDSIZE: usize = 60; 491 | 492 | // Event codes 493 | pub const LUA_HOOKCALL: c_int = 0; 494 | pub const LUA_HOOKRET: c_int = 1; 495 | pub const LUA_HOOKLINE: c_int = 2; 496 | pub const LUA_HOOKCOUNT: c_int = 3; 497 | pub const LUA_HOOKTAILCALL: c_int = 4; 498 | 499 | // Event masks 500 | pub const LUA_MASKCALL: c_int = 1 << (LUA_HOOKCALL as usize); 501 | pub const LUA_MASKRET: c_int = 1 << (LUA_HOOKRET as usize); 502 | pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize); 503 | pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize); 504 | 505 | /// Type for functions to be called on debug events. 506 | pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug); 507 | 508 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 509 | extern "C-unwind" { 510 | pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int; 511 | pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int; 512 | pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char; 513 | pub fn lua_setlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char; 514 | pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char; 515 | pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char; 516 | 517 | pub fn lua_upvalueid(L: *mut lua_State, fidx: c_int, n: c_int) -> *mut c_void; 518 | pub fn lua_upvaluejoin(L: *mut lua_State, fidx1: c_int, n1: c_int, fidx2: c_int, n2: c_int); 519 | 520 | pub fn lua_sethook(L: *mut lua_State, func: Option, mask: c_int, count: c_int); 521 | pub fn lua_gethook(L: *mut lua_State) -> Option; 522 | pub fn lua_gethookmask(L: *mut lua_State) -> c_int; 523 | pub fn lua_gethookcount(L: *mut lua_State) -> c_int; 524 | } 525 | 526 | #[repr(C)] 527 | pub struct lua_Debug { 528 | pub event: c_int, 529 | pub name: *const c_char, 530 | pub namewhat: *const c_char, 531 | pub what: *const c_char, 532 | pub source: *const c_char, 533 | pub srclen: usize, 534 | pub currentline: c_int, 535 | pub linedefined: c_int, 536 | pub lastlinedefined: c_int, 537 | pub nups: c_uchar, 538 | pub nparams: c_uchar, 539 | pub isvararg: c_char, 540 | pub istailcall: c_char, 541 | pub ftransfer: c_ushort, 542 | pub ntransfer: c_ushort, 543 | pub short_src: [c_char; LUA_IDSIZE], 544 | // lua.h mentions this is for private use 545 | i_ci: *mut c_void, 546 | } 547 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/src/lua54/lualib.rs: -------------------------------------------------------------------------------- 1 | //! Contains definitions from `lualib.h`. 2 | 3 | use std::os::raw::c_int; 4 | 5 | use super::lua::lua_State; 6 | 7 | pub const LUA_COLIBNAME: &str = "coroutine"; 8 | pub const LUA_TABLIBNAME: &str = "table"; 9 | pub const LUA_IOLIBNAME: &str = "io"; 10 | pub const LUA_OSLIBNAME: &str = "os"; 11 | pub const LUA_STRLIBNAME: &str = "string"; 12 | pub const LUA_UTF8LIBNAME: &str = "utf8"; 13 | pub const LUA_MATHLIBNAME: &str = "math"; 14 | pub const LUA_DBLIBNAME: &str = "debug"; 15 | pub const LUA_LOADLIBNAME: &str = "package"; 16 | 17 | #[cfg_attr(all(windows, feature = "raw_dylib"), link(name = "lua54", kind = "raw-dylib"))] 18 | extern "C-unwind" { 19 | pub fn luaopen_base(L: *mut lua_State) -> c_int; 20 | pub fn luaopen_coroutine(L: *mut lua_State) -> c_int; 21 | pub fn luaopen_table(L: *mut lua_State) -> c_int; 22 | pub fn luaopen_io(L: *mut lua_State) -> c_int; 23 | pub fn luaopen_os(L: *mut lua_State) -> c_int; 24 | pub fn luaopen_string(L: *mut lua_State) -> c_int; 25 | pub fn luaopen_utf8(L: *mut lua_State) -> c_int; 26 | pub fn luaopen_math(L: *mut lua_State) -> c_int; 27 | pub fn luaopen_debug(L: *mut lua_State) -> c_int; 28 | pub fn luaopen_package(L: *mut lua_State) -> c_int; 29 | 30 | // open all builtin libraries 31 | pub fn luaL_openlibs(L: *mut lua_State); 32 | } 33 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/src/lua54/mod.rs: -------------------------------------------------------------------------------- 1 | //! Low level bindings to Lua 5.4. 2 | 3 | use std::os::raw::c_int; 4 | 5 | pub use lauxlib::*; 6 | pub use lua::*; 7 | pub use lualib::*; 8 | 9 | pub mod lauxlib; 10 | pub mod lua; 11 | pub mod lualib; 12 | 13 | pub type Callback = extern "C-unwind" fn(i32); 14 | 15 | extern "C-unwind" { 16 | pub fn lua_json_decode(L: *mut lua_State) -> c_int; 17 | pub fn luaopen_serialize(L: *mut lua_State) -> c_int; 18 | } 19 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_macros)] 2 | #[macro_export] 3 | macro_rules! cstr { 4 | ($s:expr) => { 5 | concat!($s, "\0") as *const str as *const [::std::os::raw::c_char] 6 | as *const ::std::os::raw::c_char 7 | }; 8 | } 9 | 10 | #[macro_export] 11 | macro_rules! lreg { 12 | ($name:expr, $func:expr) => { 13 | luaL_Reg { 14 | name: cstr!($name), 15 | func: $func, 16 | } 17 | }; 18 | } 19 | 20 | #[macro_export] 21 | macro_rules! lreg_null { 22 | () => { 23 | luaL_Reg { 24 | name: std::ptr::null(), 25 | func: laux::lua_null_function, 26 | } 27 | }; 28 | } 29 | 30 | #[macro_export] 31 | macro_rules! lua_rawsetfield { 32 | ($state:expr, $tbindex:expr, $kname:expr, $valueexp:expr) => { 33 | unsafe { 34 | ffi::lua_pushstring($state, cstr!($kname)); 35 | $valueexp; 36 | ffi::lua_rawset($state, $tbindex-2); 37 | } 38 | }; 39 | } 40 | 41 | #[macro_export] 42 | macro_rules! push_lua_table { 43 | ($state:expr, $( $key:expr => $value:expr ),* ) => { 44 | unsafe { 45 | ffi::lua_createtable($state, 0, 0); 46 | $( 47 | laux::lua_push($state, $key); 48 | laux::lua_push($state, $value); 49 | ffi::lua_settable($state, -3); 50 | )* 51 | } 52 | }; 53 | } 54 | 55 | #[macro_export] 56 | macro_rules! luaL_newlib { 57 | ($state:expr, $l:expr) => { 58 | unsafe { 59 | ffi::lua_createtable($state, 0, $l.len() as i32); 60 | ffi::luaL_setfuncs($state, $l.as_ptr(), 0); 61 | } 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lua-sys/wrapper_lua.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include -------------------------------------------------------------------------------- /rust/crates/libs/lib-lualib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lib-lualib" 3 | version = "0.1.0" 4 | edition = "2021" 5 | build = "build.rs" 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | name = "rust" 10 | crate-type = ["cdylib"] 11 | doctest = false 12 | 13 | [dependencies] 14 | lib-core = { path = "../../libs/lib-core"} 15 | lib-lua = {package = "lib-lua-sys", path = "../../libs/lib-lua-sys",features = ["lua54"]} 16 | tokio = { version = "1", features = ["full"] } 17 | log = "0.4.0" 18 | calamine = "^0.26" 19 | csv = "1.3.0" 20 | 21 | bytes = "1.5.0" 22 | base64 = "^0.22" 23 | 24 | reqwest = { version = "0.12", features = ["rustls-tls"], default-features = false} 25 | serde = { version = "1.0", features = ["derive"]} 26 | serde_json = {version = "1.0"} 27 | percent-encoding = "2.3.1" 28 | form_urlencoded = "1.2.1" 29 | url = "2.5.0" 30 | 31 | sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres", "sqlite", "mysql"] } 32 | mongodb = "3.2" 33 | futures = "0.3" 34 | 35 | dashmap = "6.1.0" 36 | lazy_static = "1.5.0" 37 | 38 | # opendal = { version = "0.50.1", features = [ 39 | # # These are default features before v0.46. TODO: change to optional features 40 | # "services-azblob", 41 | # "services-azdls", 42 | # "services-cos", 43 | # "services-fs", 44 | # "services-gcs", 45 | # "services-ghac", 46 | # "services-http", 47 | # "services-ipmfs", 48 | # "services-memory", 49 | # "services-obs", 50 | # "services-oss", 51 | # "services-s3", 52 | # "services-webdav", 53 | # "services-webhdfs", 54 | # "services-azfile", 55 | # "services-mysql" 56 | # ] } -------------------------------------------------------------------------------- /rust/crates/libs/lib-lualib/build.rs: -------------------------------------------------------------------------------- 1 | // extern crate cc; 2 | // use std::env; 3 | // use std::path::PathBuf; 4 | 5 | fn main() { 6 | println!("cargo:rerun-if-changed=lualib-src"); 7 | 8 | if cfg!(target_os = "windows") { 9 | println!(r"cargo:rustc-link-search=native=../moon/build/bin/Release"); 10 | println!("cargo:rustc-link-lib=dylib=moon"); 11 | println!("cargo:rustc-link-lib=moon"); 12 | } else if cfg!(target_os = "macos") { 13 | println!(r"cargo:rustc-link-search=native=../moon/build/bin/Release"); 14 | println!("cargo:rustc-cdylib-link-arg=-undefined"); 15 | println!("cargo:rustc-cdylib-link-arg=dynamic_lookup"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lualib/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod lua_excel; 2 | pub mod lua_http; 3 | // pub mod lua_opendal; 4 | pub mod lua_json; 5 | pub mod lua_runtime; 6 | pub mod lua_sqlx; 7 | pub mod lua_mongodb; 8 | 9 | pub fn moon_send(protocol_type: u8, owner: u32, session: i64, res: T) { 10 | unsafe extern "C-unwind" { 11 | unsafe fn send_integer_message(type_: u8, receiver: u32, session: i64, val: isize); 12 | } 13 | 14 | if session == 0 { 15 | return; 16 | } 17 | let ptr = Box::into_raw(Box::new(res)); 18 | 19 | unsafe { 20 | send_integer_message(protocol_type, owner, session, ptr as isize); 21 | } 22 | } 23 | 24 | pub fn moon_send_bytes(protocol_type: u8, owner: u32, session: i64, data: &[u8]) { 25 | unsafe extern "C-unwind" { 26 | unsafe fn send_message(type_: u8, receiver: u32, session: i64, data: *const i8, len: usize); 27 | } 28 | 29 | unsafe { 30 | send_message( 31 | protocol_type, 32 | owner, 33 | session, 34 | data.as_ptr() as *const i8, 35 | data.len(), 36 | ); 37 | } 38 | } 39 | 40 | pub const PTYPE_ERROR: u8 = 4; 41 | pub const PTYPE_LOG: u8 = 13; 42 | 43 | pub const LOG_LEVEL_ERROR: u8 = 1; 44 | pub const LOG_LEVEL_WARN: u8 = 2; 45 | pub const LOG_LEVEL_INFO: u8 = 3; 46 | pub const LOG_LEVEL_DEBUG: u8 = 4; 47 | 48 | pub fn moon_log(owner: u32, log_level: u8, data: String) { 49 | unsafe extern "C-unwind" { 50 | unsafe fn send_message(type_: u8, receiver: u32, session: i64, data: *const i8, len: usize); 51 | } 52 | let message = format!("{}{}", log_level, data); 53 | unsafe { 54 | send_message( 55 | PTYPE_LOG, 56 | owner, 57 | 0, 58 | message.as_ptr() as *const i8, 59 | message.len(), 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lualib/src/lua_excel.rs: -------------------------------------------------------------------------------- 1 | use calamine::{open_workbook, Data, Reader, Xlsx}; 2 | use csv::ReaderBuilder; 3 | use lib_lua::{ 4 | self, cstr, 5 | ffi::{self, luaL_Reg}, 6 | laux, lreg, lreg_null, luaL_newlib, 7 | }; 8 | use std::{os::raw::c_int, path::Path}; 9 | 10 | fn read_csv(state: *mut ffi::lua_State, path: &Path, max_row: usize) -> c_int { 11 | let res = ReaderBuilder::new().has_headers(false).from_path(path); 12 | unsafe { 13 | ffi::lua_createtable(state, 0, 0); 14 | } 15 | 16 | match res { 17 | Ok(mut reader) => { 18 | unsafe { 19 | ffi::lua_createtable(state, 0, 2); 20 | laux::lua_push( 21 | state, 22 | path.file_stem() 23 | .unwrap_or_default() 24 | .to_str() 25 | .unwrap_or_default(), 26 | ); 27 | ffi::lua_setfield(state, -2, cstr!("sheet_name")); 28 | ffi::lua_createtable(state, 1024, 0); 29 | } 30 | 31 | let mut idx: usize = 0; 32 | 33 | for result in reader.records() { 34 | if idx >= max_row { 35 | break; 36 | } 37 | match result { 38 | Ok(record) => unsafe { 39 | ffi::lua_createtable(state, 0, record.len() as i32); 40 | for (i, field) in record.iter().enumerate() { 41 | laux::lua_push(state, field); 42 | ffi::lua_rawseti(state, -2, (i + 1) as i64); 43 | } 44 | idx += 1; 45 | ffi::lua_rawseti(state, -2, idx as i64); 46 | }, 47 | Err(err) => unsafe { 48 | ffi::lua_pushboolean(state, 0); 49 | laux::lua_push( 50 | state, 51 | format!("read csv '{}' error: {}", path.to_string_lossy(), err) 52 | .as_str(), 53 | ); 54 | return 2; 55 | }, 56 | } 57 | } 58 | 59 | unsafe { 60 | ffi::lua_setfield(state, -2, cstr!("data")); 61 | ffi::lua_rawseti(state, -2, 1); 62 | } 63 | 1 64 | } 65 | Err(err) => { 66 | unsafe { 67 | ffi::lua_pushboolean(state, 0); 68 | } 69 | 70 | laux::lua_push( 71 | state, 72 | format!("open file '{}' error: {}", path.to_string_lossy(), err).as_str(), 73 | ); 74 | 2 75 | } 76 | } 77 | } 78 | 79 | fn read_xlxs(state: *mut ffi::lua_State, path: &Path, max_row: usize) -> c_int { 80 | let res: Result, _> = open_workbook(path); 81 | match res { 82 | Ok(mut workbook) => { 83 | unsafe { 84 | ffi::lua_createtable(state, 0, 0); 85 | } 86 | let mut sheet_counter = 0; 87 | workbook.sheet_names().iter().for_each(|sheet| { 88 | if let Ok(range) = workbook.worksheet_range(sheet) { 89 | unsafe { 90 | ffi::lua_createtable(state, 0, 2); 91 | laux::lua_push(state, sheet.as_str()); 92 | 93 | ffi::lua_setfield(state, -2, cstr!("sheet_name")); 94 | 95 | ffi::lua_createtable(state, range.rows().len() as i32, 0); 96 | for (i, row) in range.rows().enumerate() { 97 | if i >= max_row { 98 | break; 99 | } 100 | //rows 101 | ffi::lua_createtable(state, row.len() as i32, 0); 102 | 103 | for (j, cell) in row.iter().enumerate() { 104 | //columns 105 | 106 | match cell { 107 | Data::Int(v) => { 108 | ffi::lua_pushinteger(state, *v as ffi::lua_Integer) 109 | } 110 | Data::Float(v) => ffi::lua_pushnumber(state, *v), 111 | Data::String(v) => laux::lua_push(state, v.as_str()), 112 | Data::Bool(v) => ffi::lua_pushboolean(state, *v as i32), 113 | Data::Error(v) => laux::lua_push(state, v.to_string()), 114 | Data::Empty => ffi::lua_pushnil(state), 115 | Data::DateTime(v) => laux::lua_push(state, v.to_string()), 116 | _ => ffi::lua_pushnil(state), 117 | } 118 | ffi::lua_rawseti(state, -2, (j + 1) as i64); 119 | } 120 | ffi::lua_rawseti(state, -2, (i + 1) as i64); 121 | } 122 | ffi::lua_setfield(state, -2, cstr!("data")); 123 | } 124 | sheet_counter += 1; 125 | unsafe { 126 | ffi::lua_rawseti(state, -2, sheet_counter as i64); 127 | } 128 | } 129 | }); 130 | 1 131 | } 132 | Err(err) => unsafe { 133 | ffi::lua_pushboolean(state, 0); 134 | laux::lua_push(state, format!("{}", err).as_str()); 135 | 2 136 | }, 137 | } 138 | } 139 | 140 | extern "C-unwind" fn lua_excel_read(state: *mut ffi::lua_State) -> c_int { 141 | let filename: &str = laux::lua_get(state, 1); 142 | let max_row: usize = laux::lua_opt(state, 2).unwrap_or(usize::MAX); 143 | let path = Path::new(filename); 144 | 145 | match path.extension() { 146 | Some(ext) => { 147 | let ext = ext.to_string_lossy().to_string(); 148 | match ext.as_str() { 149 | "csv" => read_csv(state, path, max_row), 150 | "xlsx" => read_xlxs(state, path, max_row), 151 | _ => unsafe { 152 | ffi::lua_pushboolean(state, 0); 153 | laux::lua_push(state, format!("unsupport file type: {}", ext)); 154 | 2 155 | }, 156 | } 157 | } 158 | None => unsafe { 159 | ffi::lua_pushboolean(state, 0); 160 | laux::lua_push( 161 | state, 162 | format!("unsupport file type: {}", path.to_string_lossy()), 163 | ); 164 | 2 165 | }, 166 | } 167 | } 168 | 169 | #[no_mangle] 170 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 171 | pub extern "C-unwind" fn luaopen_rust_excel(state: *mut ffi::lua_State) -> c_int { 172 | let l = [lreg!("read", lua_excel_read), lreg_null!()]; 173 | 174 | luaL_newlib!(state, l); 175 | 1 176 | } 177 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lualib/src/lua_http.rs: -------------------------------------------------------------------------------- 1 | use lib_core::context::CONTEXT; 2 | use lib_lua::{ 3 | self, cstr, 4 | ffi::{self, luaL_Reg}, 5 | laux::{self, LuaTable, LuaValue}, 6 | lreg, lreg_null, luaL_newlib 7 | }; 8 | use reqwest::{header::HeaderMap, Method, Version}; 9 | use std::{error::Error, ffi::c_int, str::FromStr}; 10 | use url::form_urlencoded::{self}; 11 | 12 | use crate::moon_send; 13 | 14 | struct HttpRequest { 15 | owner: u32, 16 | session: i64, 17 | method: String, 18 | url: String, 19 | body: String, 20 | headers: HeaderMap, 21 | timeout: u64, 22 | proxy: String, 23 | } 24 | 25 | struct HttpResponse { 26 | version: Version, 27 | status_code: i32, 28 | headers: HeaderMap, 29 | body: bytes::Bytes, 30 | } 31 | 32 | fn version_to_string(version: &reqwest::Version) -> &str { 33 | match *version { 34 | reqwest::Version::HTTP_09 => "HTTP/0.9", 35 | reqwest::Version::HTTP_10 => "HTTP/1.0", 36 | reqwest::Version::HTTP_11 => "HTTP/1.1", 37 | reqwest::Version::HTTP_2 => "HTTP/2.0", 38 | reqwest::Version::HTTP_3 => "HTTP/3.0", 39 | _ => "Unknown", 40 | } 41 | } 42 | 43 | async fn http_request(req: HttpRequest, protocol_type: u8) -> Result<(), Box> { 44 | let http_client = &CONTEXT.get_http_client(req.timeout, &req.proxy); 45 | 46 | let response = http_client 47 | .request(Method::from_str(req.method.as_str())?, req.url) 48 | .headers(req.headers) 49 | .body(req.body) 50 | .send() 51 | .await?; 52 | 53 | let response = HttpResponse { 54 | version: response.version(), 55 | status_code: response.status().as_u16() as i32, 56 | headers: response.headers().clone(), 57 | body: response.bytes().await?, 58 | }; 59 | 60 | moon_send(protocol_type, req.owner, req.session, response); 61 | 62 | Ok(()) 63 | } 64 | 65 | fn extract_headers(state: *mut ffi::lua_State, index: i32) -> Result { 66 | let mut headers = HeaderMap::with_capacity(8); // Pre-allocate reasonable size 67 | 68 | let table = LuaTable::from_stack(state, index); 69 | let header_table = table.rawget("headers"); 70 | 71 | match &header_table.value { 72 | LuaValue::Table(header_table) => { 73 | header_table 74 | .iter() 75 | .try_for_each(|(key, value)| { 76 | let key_str = key.to_string(); 77 | let value_str = value.to_string(); 78 | 79 | // Parse header name and value 80 | let name = key_str 81 | .parse::() 82 | .map_err(|e| format!("Invalid header name '{}': {}", key_str, e))?; 83 | 84 | let value = value_str 85 | .parse::() 86 | .map_err(|e| format!("Invalid header value '{}': {}", value_str, e))?; 87 | 88 | headers.insert(name, value); 89 | Ok(()) 90 | }) 91 | .map_err(|e: String| e)?; 92 | } 93 | _ => return Ok(headers), // Empty headers if not a table 94 | } 95 | 96 | Ok(headers) 97 | } 98 | 99 | extern "C-unwind" fn lua_http_request(state: *mut ffi::lua_State) -> c_int { 100 | laux::lua_checktype(state, 1, ffi::LUA_TTABLE); 101 | 102 | let protocol_type = laux::lua_get::(state, 2); 103 | 104 | let headers = match extract_headers(state, 1) { 105 | Ok(headers) => headers, 106 | Err(err) => { 107 | laux::lua_push(state, false); 108 | laux::lua_push(state, err); 109 | return 2; 110 | } 111 | }; 112 | 113 | let session = laux::opt_field(state, 1, "session").unwrap_or(0); 114 | 115 | let req = HttpRequest { 116 | owner: laux::opt_field(state, 1, "owner").unwrap_or_default(), 117 | session, 118 | method: laux::opt_field(state, 1, "method").unwrap_or("GET".to_string()), 119 | url: laux::opt_field(state, 1, "url").unwrap_or_default(), 120 | body: laux::opt_field(state, 1, "body").unwrap_or_default(), 121 | headers, 122 | timeout: laux::opt_field(state, 1, "timeout").unwrap_or(5), 123 | proxy: laux::opt_field(state, 1, "proxy").unwrap_or_default(), 124 | }; 125 | 126 | CONTEXT.tokio_runtime.spawn(async move { 127 | let session = req.session; 128 | let owner = req.owner; 129 | if let Err(err) = http_request(req, protocol_type).await { 130 | let response = HttpResponse { 131 | version: Version::HTTP_11, 132 | status_code: -1, 133 | headers: HeaderMap::new(), 134 | body: err.to_string().into(), 135 | }; 136 | moon_send(protocol_type, owner, session, response); 137 | } 138 | }); 139 | 140 | laux::lua_push(state, session); 141 | 1 142 | } 143 | 144 | extern "C-unwind" fn decode(state: *mut ffi::lua_State) -> c_int { 145 | laux::luaL_checkstack(state, 4, std::ptr::null()); 146 | let p_as_isize: isize = laux::lua_get(state, 1); 147 | let response = unsafe { Box::from_raw(p_as_isize as *mut HttpResponse) }; 148 | 149 | LuaTable::new(state, 0, 6) 150 | .rawset("version", version_to_string(&response.version)) 151 | .rawset("status_code", response.status_code) 152 | .rawset("body", response.body.as_ref()) 153 | .rawset_x("headers", || { 154 | let headers = LuaTable::new(state, 0, response.headers.len()); 155 | for (key, value) in response.headers.iter() { 156 | headers.rawset(key.as_str(), value.to_str().unwrap_or("").trim()); 157 | } 158 | }); 159 | 1 160 | } 161 | 162 | extern "C-unwind" fn lua_http_form_urlencode(state: *mut ffi::lua_State) -> c_int { 163 | laux::lua_checktype(state, 1, ffi::LUA_TTABLE); 164 | 165 | let mut result = String::with_capacity(64); 166 | for (key, value) in LuaTable::from_stack(state, 1).iter() { 167 | if !result.is_empty() { 168 | result.push('&'); 169 | } 170 | result.push_str( 171 | form_urlencoded::byte_serialize(key.to_vec().as_ref()) 172 | .collect::() 173 | .as_str(), 174 | ); 175 | result.push('='); 176 | result.push_str( 177 | form_urlencoded::byte_serialize(value.to_vec().as_ref()) 178 | .collect::() 179 | .as_str(), 180 | ); 181 | } 182 | laux::lua_push(state, result); 183 | 1 184 | } 185 | 186 | extern "C-unwind" fn lua_http_form_urldecode(state: *mut ffi::lua_State) -> c_int { 187 | let query_string = laux::lua_get::<&str>(state, 1); 188 | 189 | let decoded: Vec<(String, String)> = form_urlencoded::parse(query_string.as_bytes()) 190 | .into_owned() 191 | .collect(); 192 | 193 | let table = LuaTable::new(state, 0, decoded.len()); 194 | 195 | for (key, value) in decoded { 196 | table.rawset(key, value); 197 | } 198 | 1 199 | } 200 | 201 | #[no_mangle] 202 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 203 | pub extern "C-unwind" fn luaopen_rust_httpc(state: *mut ffi::lua_State) -> c_int { 204 | let l = [ 205 | lreg!("request", lua_http_request), 206 | lreg!("decode", decode), 207 | lreg!("form_urlencode", lua_http_form_urlencode), 208 | lreg!("form_urldecode", lua_http_form_urldecode), 209 | lreg_null!(), 210 | ]; 211 | 212 | luaL_newlib!(state, l); 213 | 214 | 1 215 | } 216 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lualib/src/lua_json.rs: -------------------------------------------------------------------------------- 1 | use lib_lua::{ 2 | self, cstr, 3 | ffi::{self, luaL_Reg}, 4 | laux::{self, LuaStateRef, LuaTable, LuaType, LuaValue}, 5 | lreg, lreg_null, 6 | }; 7 | use serde::de::Error; 8 | use serde_json::Value; 9 | use std::{ 10 | ffi::{c_char, c_int, c_void}, 11 | fs::File, 12 | io::Read, 13 | }; 14 | 15 | use lib_core::buffer::Buffer; 16 | 17 | const JSON_NULL: &str = "null"; 18 | const JSON_TRUE: &str = "true"; 19 | const JSON_FALSE: &str = "false"; 20 | const CHAR2ESCAPE: [u8; 256] = [ 21 | b'u', b'u', b'u', b'u', b'u', b'u', b'u', b'u', b'b', b't', b'n', b'u', b'f', b'r', b'u', b'u', 22 | b'u', b'u', b'u', b'u', // 0~19 23 | b'u', b'u', b'u', b'u', b'u', b'u', b'u', b'u', b'u', b'u', b'u', b'u', 0, 0, b'"', 0, 0, 0, 0, 24 | 0, // 20~39 25 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40~59 26 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60~79 27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b'\\', 0, 0, 0, 0, 0, 0, 0, // 80~99 28 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 100~119 29 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 120~139 30 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 140~159 31 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160~179 32 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 180~199 33 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 200~219 34 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 220~239 35 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36 | ]; 37 | 38 | const HEX_DIGITS: [u8; 16] = [ 39 | b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', b'F', 40 | ]; 41 | 42 | pub struct JsonOptions { 43 | empty_as_array: bool, 44 | enable_number_key: bool, 45 | enable_sparse_array: bool, 46 | } 47 | 48 | impl Default for JsonOptions { 49 | fn default() -> Self { 50 | Self { 51 | empty_as_array: true, 52 | enable_number_key: true, 53 | enable_sparse_array: true, 54 | } 55 | } 56 | } 57 | 58 | extern "C-unwind" fn set_options(state: LuaStateRef) -> i32 { 59 | let options = fetch_options(state); 60 | let key = laux::lua_get::<&str>(state, 1); 61 | match key { 62 | "encode_empty_as_array" => { 63 | let v = options.empty_as_array; 64 | options.empty_as_array = laux::lua_opt(state, 2).unwrap_or(true); 65 | laux::lua_push(state, v); 66 | } 67 | "enable_number_key" => { 68 | let v = options.enable_number_key; 69 | options.enable_number_key = laux::lua_opt(state, 2).unwrap_or(true); 70 | laux::lua_push(state, v); 71 | } 72 | "enable_sparse_array" => { 73 | let v = options.enable_sparse_array; 74 | options.enable_sparse_array = laux::lua_opt(state, 2).unwrap_or(false); 75 | laux::lua_push(state, v); 76 | } 77 | _ => { 78 | laux::lua_error(state, format!("invalid json option key: {}", key).as_str()); 79 | } 80 | } 81 | 82 | 1 83 | } 84 | 85 | pub fn fetch_options(state: LuaStateRef) -> &'static mut JsonOptions { 86 | let opts = laux::lua_touserdata::(state, ffi::lua_upvalueindex(1)); 87 | if opts.is_none() { 88 | laux::lua_error(state, "expect json options"); 89 | } 90 | opts.unwrap() 91 | } 92 | 93 | pub fn encode_one( 94 | writer: &mut Vec, 95 | val: LuaValue, 96 | depth: i32, 97 | fmt: bool, 98 | options: &JsonOptions, 99 | ) -> Result<(), String> { 100 | match val { 101 | LuaValue::Boolean(val) => { 102 | if val { 103 | writer.extend_from_slice(JSON_TRUE.as_bytes()); 104 | } else { 105 | writer.extend_from_slice(JSON_FALSE.as_bytes()); 106 | } 107 | } 108 | LuaValue::Number(val) => writer.extend_from_slice(val.to_string().as_bytes()), 109 | LuaValue::Integer(val) => writer.extend_from_slice(val.to_string().as_bytes()), 110 | LuaValue::String(val) => { 111 | writer.reserve(val.len() * 6 + 2); 112 | writer.push(b'\"'); 113 | for ch in val { 114 | let esc = CHAR2ESCAPE[*ch as usize]; 115 | if esc == 0 { 116 | writer.push(*ch); 117 | } else { 118 | writer.push(b'\\'); 119 | writer.push(esc); 120 | if esc == b'u' { 121 | writer.push(b'0'); 122 | writer.push(b'0'); 123 | writer.push(HEX_DIGITS[(*ch >> 4) as usize & 0xF]); 124 | writer.push(HEX_DIGITS[*ch as usize & 0xF]); 125 | } 126 | } 127 | } 128 | 129 | writer.push(b'\"'); 130 | } 131 | LuaValue::Table(val) => { 132 | encode_table(writer, &val, depth, fmt, options)?; 133 | } 134 | LuaValue::Nil => { 135 | writer.extend_from_slice(JSON_NULL.as_bytes()); 136 | } 137 | LuaValue::LightUserData(val) => { 138 | if val.is_null() { 139 | writer.extend_from_slice(JSON_NULL.as_bytes()); 140 | } 141 | } 142 | val => { 143 | return Err(format!("json encode: unsupport value type :{}", val.name())); 144 | } 145 | } 146 | 147 | Ok(()) 148 | } 149 | 150 | #[inline] 151 | fn format_new_line(writer: &mut Vec, fmt: bool) { 152 | if fmt { 153 | writer.push(b'\n'); 154 | } 155 | } 156 | 157 | #[inline] 158 | fn format_space(writer: &mut Vec, fmt: bool, n: i32) { 159 | if fmt { 160 | for _ in 0..n { 161 | writer.push(b' '); 162 | writer.push(b' '); 163 | } 164 | } 165 | } 166 | 167 | fn encode_array( 168 | writer: &mut Vec, 169 | table: &LuaTable, 170 | size: usize, 171 | depth: i32, 172 | fmt: bool, 173 | options: &JsonOptions, 174 | ) -> Result<(), String> { 175 | let bsize = writer.len(); 176 | writer.push(b'['); 177 | 178 | for (i, val) in table.array_iter(size).enumerate() { 179 | if i == 0 { 180 | format_new_line(writer, fmt); 181 | } else { 182 | writer.push(b','); 183 | } 184 | format_space(writer, fmt, depth); 185 | 186 | if let LuaValue::Nil = val { 187 | if !options.enable_sparse_array { 188 | writer.truncate(bsize); 189 | return encode_object(writer, table, depth, fmt, options); 190 | } 191 | } 192 | encode_one(writer, val, depth, fmt, options)?; 193 | format_new_line(writer, fmt) 194 | } 195 | format_space(writer, fmt, depth - 1); 196 | writer.push(b']'); 197 | Ok(()) 198 | } 199 | 200 | fn encode_object( 201 | writer: &mut Vec, 202 | table: &LuaTable, 203 | depth: i32, 204 | fmt: bool, 205 | options: &JsonOptions, 206 | ) -> Result<(), String> { 207 | let mut i = 0; 208 | writer.push(b'{'); 209 | 210 | for (key, value) in table.iter() { 211 | if i > 0 { 212 | writer.push(b','); 213 | } 214 | i += 1; 215 | format_new_line(writer, fmt); 216 | 217 | match key { 218 | LuaValue::String(key) => { 219 | format_space(writer, fmt, depth); 220 | writer.push(b'\"'); 221 | writer.extend_from_slice(key); 222 | writer.extend_from_slice(b"\":"); 223 | if fmt { 224 | writer.push(b' '); 225 | } 226 | encode_one(writer, value, depth, fmt, options)?; 227 | } 228 | LuaValue::Integer(key) => { 229 | if options.enable_number_key { 230 | format_space(writer, fmt, depth); 231 | writer.push(b'\"'); 232 | writer.extend_from_slice(key.to_string().as_bytes()); 233 | writer.extend_from_slice(b"\":"); 234 | if fmt { 235 | writer.push(b' '); 236 | } 237 | encode_one(writer, value, depth, fmt, options)?; 238 | } else { 239 | return Err("json encode: unsupport number key type.".to_string()); 240 | } 241 | } 242 | _ => {} 243 | } 244 | } 245 | 246 | if i == 0 && options.empty_as_array { 247 | writer.pop(); 248 | writer.extend_from_slice(b"[]"); 249 | } else { 250 | if i > 0 { 251 | format_new_line(writer, fmt); 252 | format_space(writer, fmt, depth - 1); 253 | } 254 | writer.push(b'}'); 255 | } 256 | 257 | Ok(()) 258 | } 259 | 260 | pub fn encode_table( 261 | writer: &mut Vec, 262 | table: &LuaTable, 263 | depth: i32, 264 | fmt: bool, 265 | options: &JsonOptions, 266 | ) -> Result<(), String> { 267 | let depth = depth + 1; 268 | if depth > 64 { 269 | return Err("json encode: too depth".to_string()); 270 | } 271 | 272 | laux::luaL_checkstack(table.lua_state(), 6, cstr!("json.encode.table")); 273 | let arr_size = table.array_len(); 274 | if arr_size > 0 { 275 | encode_array(writer, table, arr_size, depth, fmt, options)?; 276 | } else { 277 | encode_object(writer, table, depth, fmt, options)?; 278 | } 279 | 280 | Ok(()) 281 | } 282 | 283 | unsafe extern "C-unwind" fn encode(state: *mut ffi::lua_State) -> c_int { 284 | ffi::luaL_checkany(state, 1); 285 | 286 | { 287 | let options = fetch_options(state); 288 | let fmt: bool = laux::lua_opt(state, 2).unwrap_or_default(); 289 | let mut writer = Vec::new(); 290 | match encode_one(&mut writer, LuaValue::from_stack(state, 1), 0, fmt, options) { 291 | Ok(_) => { 292 | laux::lua_push(state, writer.as_slice()); 293 | return 1; 294 | } 295 | Err(err) => { 296 | laux::lua_push(state, err.to_string()); 297 | } 298 | } 299 | } 300 | 301 | laux::throw_error(state) 302 | } 303 | 304 | #[inline] 305 | unsafe fn decode_one(state: *mut ffi::lua_State, val: &Value, options: &JsonOptions) { 306 | match val { 307 | Value::Object(map) => { 308 | laux::luaL_checkstack(state, 6, cstr!("json.decode.object")); 309 | ffi::lua_createtable(state, 0, map.len() as i32); 310 | for (k, v) in map { 311 | if !k.is_empty() { 312 | let c = k.as_bytes()[0]; 313 | if (c.is_ascii_digit() || c == b'-') && options.enable_number_key { 314 | if let Ok(n) = k.parse::() { 315 | //try convert k to integer 316 | ffi::lua_pushinteger(state, n); 317 | } else { 318 | ffi::lua_pushlstring(state, k.as_ptr() as *const c_char, k.len()); 319 | } 320 | } else { 321 | ffi::lua_pushlstring(state, k.as_ptr() as *const c_char, k.len()); 322 | } 323 | decode_one(state, v, options); 324 | ffi::lua_rawset(state, -3); 325 | } 326 | } 327 | } 328 | Value::Array(arr) => { 329 | ffi::luaL_checkstack(state, 6, cstr!("json.decode.array")); 330 | ffi::lua_createtable(state, arr.len() as i32, 0); 331 | for (i, v) in arr.iter().enumerate() { 332 | decode_one(state, v, options); 333 | ffi::lua_rawseti(state, -2, (i + 1) as ffi::lua_Integer); 334 | } 335 | } 336 | Value::Bool(b) => { 337 | ffi::lua_pushboolean( 338 | state, 339 | match b { 340 | true => 1, 341 | false => 0, 342 | }, 343 | ); 344 | } 345 | Value::Number(n) => { 346 | if n.is_f64() { 347 | ffi::lua_pushnumber(state, n.as_f64().unwrap_or_default()); 348 | } else { 349 | ffi::lua_pushinteger(state, n.as_i64().unwrap_or_default()); 350 | } 351 | } 352 | Value::Null => { 353 | ffi::lua_pushlightuserdata(state, std::ptr::null_mut()); 354 | } 355 | Value::String(s) => { 356 | ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len()); 357 | } 358 | } 359 | } 360 | 361 | extern "C-unwind" fn decode(state: *mut ffi::lua_State) -> c_int { 362 | let options = fetch_options(state); 363 | let str: &[u8] = laux::lua_get(state, 1); 364 | 365 | // Handle JSON decoding errors 366 | fn handle_error(state: *mut ffi::lua_State, e: serde_json::Error) -> c_int { 367 | laux::lua_pushnil(state); 368 | laux::lua_push(state, e.to_string()); 369 | 2 370 | } 371 | 372 | // Decode JSON data 373 | let result = if !str.is_empty() && str[0] == b'@' { 374 | match std::str::from_utf8(&str[1..]) { 375 | Ok(path) => { 376 | let mut file = match File::open(path) { 377 | Ok(file) => file, 378 | Err(e) => return handle_error(state, serde_json::Error::custom(e.to_string())), 379 | }; 380 | let mut contents = Vec::new(); 381 | if let Err(e) = file.read_to_end(&mut contents) { 382 | return handle_error(state, serde_json::Error::custom(e.to_string())); 383 | } 384 | serde_json::from_slice::(&contents) 385 | } 386 | Err(e) => return handle_error(state, serde_json::Error::custom(e.to_string())), 387 | } 388 | } else { 389 | serde_json::from_slice::(str) 390 | }; 391 | 392 | match result { 393 | Ok(val) => { 394 | unsafe { 395 | decode_one(state, &val, options); 396 | } 397 | 1 398 | } 399 | Err(e) => handle_error(state, e), 400 | } 401 | } 402 | 403 | unsafe extern "C-unwind" fn concat(state: *mut ffi::lua_State) -> c_int { 404 | let options = fetch_options(state); 405 | 406 | if laux::lua_type(state, 1) == LuaType::String { 407 | let slc = laux::lua_get::<&[u8]>(state, 1); 408 | let mut buf = Box::new(Buffer::with_capacity(slc.len())); 409 | buf.write_slice(slc); 410 | laux::lua_pushlightuserdata(state, Box::into_raw(buf) as *mut c_void); 411 | return 1; 412 | } 413 | ffi::luaL_checktype(state, 1, ffi::LUA_TTABLE); 414 | 415 | ffi::lua_settop(state, 1); 416 | 417 | let mut writer = Box::new(Buffer::new()); 418 | let array_len = ffi::lua_rawlen(state, 1); 419 | let mut has_error = false; 420 | 421 | for i in 1..=array_len { 422 | ffi::lua_rawgeti(state, 1, i as ffi::lua_Integer); 423 | match LuaValue::from_stack(state, -1) { 424 | LuaValue::String(val) => writer.write_slice(val), 425 | LuaValue::Number(val) => writer.write_chars(val), 426 | LuaValue::Integer(val) => writer.write_chars(val), 427 | LuaValue::Boolean(val) => { 428 | if val { 429 | writer.write_slice(JSON_TRUE.as_bytes()); 430 | } else { 431 | writer.write_slice(JSON_FALSE.as_bytes()); 432 | } 433 | } 434 | LuaValue::Table(val) => { 435 | if let Err(err) = encode_table(writer.as_mut_vec(), &val, 0, false, options) { 436 | has_error = true; 437 | laux::lua_push(state, err); 438 | break; 439 | } 440 | } 441 | _ => { 442 | has_error = true; 443 | laux::lua_push( 444 | state, 445 | format!( 446 | "json.concat: unsupport value type :{}", 447 | laux::type_name(state, -1) 448 | ), 449 | ); 450 | break; 451 | } 452 | } 453 | laux::lua_pop(state, 1); 454 | } 455 | 456 | if has_error { 457 | drop(writer); 458 | laux::throw_error(state); 459 | } 460 | 461 | ffi::lua_pushlightuserdata(state, Box::into_raw(writer) as *mut c_void); 462 | 463 | 1 464 | } 465 | 466 | pub fn hash_combine_u64(h: &mut u64, k: u64) { 467 | let m = 0xc6a4a7935bd1e995; 468 | let r = 47; 469 | 470 | let mut k = k; 471 | k = k.wrapping_mul(m); 472 | k ^= k >> r; 473 | k = k.wrapping_mul(m); 474 | 475 | *h ^= k; 476 | *h = h.wrapping_mul(m); 477 | 478 | // Completely arbitrary number, to prevent 0's 479 | // from hashing to 0. 480 | *h = h.wrapping_add(0xe6546b64); 481 | } 482 | 483 | fn hash_string(s: &str) -> u64 { 484 | let mut seed = 0; 485 | let mut basis: u64 = 14695981039346656037; 486 | for b in s.bytes() { 487 | basis ^= b as u64; 488 | basis = basis.wrapping_mul(1099511628211); 489 | hash_combine_u64(&mut seed, basis); 490 | } 491 | seed 492 | } 493 | 494 | #[inline] 495 | fn write_resp(writer: &mut Buffer, cmd: &str) { 496 | writer.write_slice(b"\r\n$"); 497 | writer.write_chars(cmd.len()); 498 | writer.write_slice(b"\r\n"); 499 | writer.write_str(cmd); 500 | } 501 | 502 | fn concat_resp_one( 503 | writer: &mut Buffer, 504 | value: LuaValue, 505 | options: &JsonOptions, 506 | ) -> Result<(), String> { 507 | match value { 508 | LuaValue::Nil => { 509 | writer.write_slice(b"\r\n$-1"); 510 | } 511 | LuaValue::Number(val) => write_resp(writer, val.to_string().as_str()), 512 | LuaValue::Integer(val) => write_resp(writer, val.to_string().as_str()), 513 | LuaValue::Boolean(val) => write_resp(writer, if val { JSON_TRUE } else { JSON_FALSE }), 514 | LuaValue::String(val) => write_resp(writer, unsafe { std::str::from_utf8_unchecked(val) }), 515 | 516 | LuaValue::Table(val) => { 517 | if val.getmetafield(cstr!("__redis")).is_some() { 518 | for value in val.array_iter(val.len()) { 519 | concat_resp_one(writer, value, options)?; 520 | } 521 | } else { 522 | let mut w = Buffer::new(); 523 | encode_table(w.as_mut_vec(), &val, 0, false, options)?; 524 | write_resp(writer, w.as_str()); 525 | } 526 | } 527 | val => { 528 | return Err(format!( 529 | "concat_resp_one: unsupport value type :{}", 530 | val.name() 531 | )); 532 | } 533 | } 534 | 535 | Ok(()) 536 | } 537 | 538 | extern "C-unwind" fn concat_resp(state: *mut ffi::lua_State) -> c_int { 539 | let n = laux::lua_top(state); 540 | if n == 0 { 541 | return 0; 542 | } 543 | 544 | let options = fetch_options(state); 545 | 546 | let mut writer = Box::new(Buffer::new()); 547 | let mut has_error = false; 548 | let mut hash = 1; 549 | if laux::lua_type(state, 2) != LuaType::Table { 550 | if let Some(key) = laux::lua_opt::<&str>(state, 1) { 551 | if !key.is_empty() { 552 | let hash_part = if n > 2 && (key.starts_with('h') || key.starts_with('H')) { 553 | laux::lua_opt::<&str>(state, 3) 554 | } else if n > 1 { 555 | laux::lua_opt::<&str>(state, 2) 556 | } else { 557 | None 558 | }; 559 | 560 | if let Some(part) = hash_part { 561 | hash = hash_string(part); 562 | } 563 | } 564 | } 565 | } 566 | 567 | writer.write(b'*'); 568 | writer.write_chars(n); 569 | for i in 1..=n { 570 | if let Err(err) = concat_resp_one(&mut writer, LuaValue::from_stack(state, i), options) { 571 | has_error = true; 572 | laux::lua_push(state, err); 573 | break; 574 | } 575 | } 576 | 577 | if has_error { 578 | drop(writer); 579 | laux::throw_error(state); 580 | } 581 | 582 | writer.write_slice(b"\r\n"); 583 | 584 | laux::lua_pushlightuserdata(state, Box::into_raw(writer) as *mut c_void); 585 | laux::lua_push(state, (hash as ffi::lua_Integer).abs()); 586 | 587 | 2 588 | } 589 | 590 | /// # Safety 591 | /// 592 | /// This function is unsafe because it dereferences a raw pointer `state`. 593 | /// The caller must ensure that `state` is a valid pointer to a `lua_State` 594 | /// and that it remains valid for the duration of the function call. 595 | #[no_mangle] 596 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 597 | pub unsafe extern "C-unwind" fn luaopen_json(state: *mut ffi::lua_State) -> c_int { 598 | let l = [ 599 | lreg!("decode", decode), 600 | lreg!("encode", encode), 601 | lreg!("concat", concat), 602 | lreg!("concat_resp", concat_resp), 603 | lreg!("options", set_options), 604 | lreg_null!(), 605 | ]; 606 | 607 | ffi::lua_createtable(state, 0, l.len() as c_int); 608 | laux::lua_newuserdata( 609 | state, 610 | JsonOptions { 611 | empty_as_array: true, 612 | enable_number_key: true, 613 | enable_sparse_array: false, 614 | }, 615 | cstr!("json_options_meta"), 616 | &[lreg_null!()], 617 | ); 618 | 619 | ffi::luaL_setfuncs(state, l.as_ptr(), 1); 620 | 621 | ffi::lua_pushlightuserdata(state, std::ptr::null_mut()); 622 | ffi::lua_setfield(state, -2, cstr!("null")); 623 | 624 | 1 625 | } 626 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lualib/src/lua_opendal.rs: -------------------------------------------------------------------------------- 1 | use serde_json::json; 2 | use std::str::FromStr; 3 | use std::{collections::HashMap, ffi::c_int}; 4 | 5 | use ::opendal as od; 6 | 7 | use lib_core::context::CONTEXT; 8 | use lib_lua::{self, cstr, ffi, ffi::luaL_Reg, laux, lreg, lreg_null}; 9 | 10 | use crate::{moon_send_bytes, PTYPE_ERROR}; 11 | 12 | fn lua_to_schema( 13 | state: *mut ffi::lua_State, 14 | index: i32, 15 | schema: &str, 16 | ) -> Result { 17 | // [+1] 18 | let mut map = HashMap::::default(); 19 | laux::lua_pushnil(state); 20 | while laux::lua_next(state, index) { 21 | let key: &str = laux::lua_opt(state, -2).unwrap_or_default(); 22 | let value: &str = laux::lua_opt(state, -1).unwrap_or_default(); 23 | map.insert(key.to_string(), value.to_string()); 24 | laux::lua_pop(state, 1); 25 | } 26 | 27 | let od_schema = od::Scheme::from_str(&schema)?; 28 | 29 | let op = od::Operator::via_iter(od_schema, map)?; 30 | op.blocking(); 31 | Ok(op) 32 | } 33 | 34 | extern "C-unwind" fn operator_new(state: *mut ffi::lua_State) -> c_int { 35 | laux::lua_checktype(state, 2, ffi::LUA_TTABLE); 36 | let schema: &str = laux::lua_get(state, 1); 37 | if schema.is_empty() { 38 | laux::lua_push(state, false); 39 | laux::lua_push(state, "schema is empty"); 40 | return 2; 41 | } 42 | 43 | let op: opendal::Operator = match lua_to_schema(state, 2, schema) { 44 | Ok(op) => op, 45 | Err(e) => { 46 | laux::lua_push(state, false); 47 | laux::lua_push(state, e.to_string()); 48 | return 2; 49 | } 50 | }; 51 | 52 | let l = [lreg!("operators", operators), lreg_null!()]; 53 | if laux::lua_newuserdata(state, op, cstr!("opendal_metatable"), l.as_ref()).is_none() { 54 | laux::lua_push(state, false); 55 | laux::lua_push(state, "laux::lua_newuserdata failed"); 56 | return 2; 57 | } 58 | 59 | 1 60 | } 61 | 62 | extern "C-unwind" fn operators(state: *mut ffi::lua_State) -> c_int { 63 | laux::lua_checktype(state, 1, ffi::LUA_TUSERDATA); 64 | 65 | let op = laux::lua_touserdata::(state, 1); 66 | if op.is_none() { 67 | laux::lua_error(state, "Invalid operator pointer"); 68 | } 69 | let op = op.unwrap(); 70 | 71 | let protocol_type = laux::lua_get::(state, 2); 72 | 73 | let session: i64 = laux::lua_get(state, 3); 74 | let owner = laux::lua_get(state, 4); 75 | let op_name = laux::lua_get::<&str>(state, 5); 76 | 77 | let path = laux::lua_get::<&str>(state, 6); 78 | if path.is_empty() { 79 | laux::lua_error(state, "path is empty"); 80 | } 81 | 82 | let handle_result = move |result: opendal::Result>| match result { 83 | Ok(data) => { 84 | let vec = data.to_vec(); 85 | moon_send_bytes( 86 | protocol_type, 87 | owner, 88 | session, 89 | vec.as_ref(), 90 | ); 91 | } 92 | Err(err) => { 93 | let err_str = err.to_string(); 94 | moon_send_bytes( 95 | PTYPE_ERROR, 96 | owner, 97 | session, 98 | err_str.as_bytes(), 99 | ); 100 | } 101 | }; 102 | 103 | match op_name { 104 | "read" => { 105 | CONTEXT.tokio_runtime.spawn(async move { 106 | handle_result(op.read(path).await.map(|v| v.to_vec())); 107 | }); 108 | } 109 | "write" => { 110 | let data = laux::lua_get::<&[u8]>(state, 8); 111 | CONTEXT.tokio_runtime.spawn(async move { 112 | handle_result(op.write(path, data).await.map(|_| vec![])); 113 | }); 114 | } 115 | "delete" => { 116 | CONTEXT.tokio_runtime.spawn(async move { 117 | handle_result(op.delete(path).await.map(|_| vec![])); 118 | }); 119 | } 120 | "exists" => { 121 | CONTEXT.tokio_runtime.spawn(async move { 122 | handle_result( 123 | op.exists(path) 124 | .await 125 | .map(|exist| exist.to_string().into_bytes()), 126 | ); 127 | }); 128 | } 129 | "create_dir" => { 130 | CONTEXT.tokio_runtime.spawn(async move { 131 | handle_result(op.create_dir(path).await.map(|_| vec![])); 132 | }); 133 | } 134 | "rename" => { 135 | let to = laux::lua_get::<&str>(state, 8); 136 | if to.is_empty() { 137 | laux::lua_error(state, "to is empty"); 138 | } 139 | CONTEXT.tokio_runtime.spawn(async move { 140 | handle_result(op.rename(path, to).await.map(|_| vec![])); 141 | }); 142 | } 143 | "stat" => { 144 | CONTEXT.tokio_runtime.spawn(async move { 145 | handle_result(op.stat(path).await.map(|stat| { 146 | let json_obj = json!({ 147 | "content_length": stat.content_length(), 148 | "content_md5": stat.content_md5(), 149 | "content_type": stat.content_type(), 150 | "is_dir": stat.is_dir(), 151 | "is_file": stat.is_file() 152 | }); 153 | json_obj.to_string().into_bytes() 154 | })); 155 | }); 156 | } 157 | "list" => { 158 | CONTEXT.tokio_runtime.spawn(async move { 159 | handle_result(op.list(path).await.map(|list| { 160 | let json_obj = list 161 | .into_iter() 162 | .map(|stat| { 163 | let (path, metadata) = stat.into_parts(); 164 | json!({ 165 | "path": path, 166 | "memtadata": { 167 | "content_length": metadata.content_length(), 168 | "content_md5": metadata.content_md5(), 169 | "content_type": metadata.content_type(), 170 | "is_dir": metadata.is_dir(), 171 | "is_file": metadata.is_file() 172 | } 173 | }) 174 | }) 175 | .collect::>(); 176 | serde_json::to_string(&json_obj) 177 | .unwrap_or_default() 178 | .into_bytes() 179 | })); 180 | }); 181 | } 182 | _ => { 183 | laux::lua_push(state, false); 184 | laux::lua_push(state, "Invalid operator name"); 185 | return 2; 186 | } 187 | } 188 | 189 | laux::lua_push(state, session); 190 | 1 191 | } 192 | 193 | /// # Safety 194 | /// 195 | /// This function is unsafe because it dereferences a raw pointer `state`. 196 | /// The caller must ensure that `state` is a valid pointer to a `lua_State` 197 | /// and that it remains valid for the duration of the function call. 198 | #[no_mangle] 199 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 200 | pub unsafe extern "C-unwind" fn luaopen_rust_opendal(state: *mut ffi::lua_State) -> c_int { 201 | let l = [lreg!("new", operator_new), lreg_null!()]; 202 | 203 | ffi::lua_createtable(state, 0, l.len() as c_int); 204 | ffi::luaL_setfuncs(state, l.as_ptr(), 0); 205 | 206 | 1 207 | } 208 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lualib/src/lua_runtime.rs: -------------------------------------------------------------------------------- 1 | use lib_core::context::CONTEXT; 2 | use lib_lua::{ 3 | self, cstr, 4 | ffi::{self, luaL_Reg}, 5 | laux, lreg, lreg_null, luaL_newlib, 6 | }; 7 | use std::ffi::c_int; 8 | 9 | extern "C-unwind" fn num_alive_tasks(state: *mut ffi::lua_State) -> c_int { 10 | laux::lua_push( 11 | state, 12 | CONTEXT.tokio_runtime.metrics().num_alive_tasks() as i64, 13 | ); 14 | 1 15 | } 16 | 17 | #[no_mangle] 18 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 19 | pub extern "C-unwind" fn luaopen_rust_runtime(state: *mut ffi::lua_State) -> c_int { 20 | let l = [lreg!("num_alive_tasks", num_alive_tasks), lreg_null!()]; 21 | luaL_newlib!(state, l); 22 | 1 23 | } 24 | -------------------------------------------------------------------------------- /rust/crates/libs/lib-lualib/src/lua_sqlx.rs: -------------------------------------------------------------------------------- 1 | use crate::lua_json::{encode_table, JsonOptions}; 2 | use crate::{moon_log, moon_send, LOG_LEVEL_ERROR, LOG_LEVEL_INFO}; 3 | use dashmap::DashMap; 4 | use lazy_static::lazy_static; 5 | use lib_core::context::CONTEXT; 6 | use lib_lua::laux::{lua_into_userdata, LuaArgs, LuaNil, LuaTable, LuaValue}; 7 | use lib_lua::luaL_newlib; 8 | use lib_lua::{self, cstr, ffi, ffi::luaL_Reg, laux, lreg, lreg_null, push_lua_table}; 9 | use sqlx::migrate::MigrateDatabase; 10 | use sqlx::mysql::MySqlRow; 11 | use sqlx::postgres::{PgPoolOptions, PgRow}; 12 | use sqlx::sqlite::SqliteRow; 13 | use sqlx::ColumnIndex; 14 | use sqlx::ValueRef; 15 | use sqlx::{ 16 | Column, Database, MySql, MySqlPool, PgPool, Postgres, Row, Sqlite, SqlitePool, TypeInfo, 17 | }; 18 | use std::ffi::c_int; 19 | use std::sync::atomic::AtomicI64; 20 | use std::sync::Arc; 21 | use std::time::Duration; 22 | use tokio::sync::mpsc; 23 | use tokio::time::timeout; 24 | 25 | lazy_static! { 26 | static ref DATABASE_CONNECTIONSS: DashMap = DashMap::new(); 27 | } 28 | 29 | enum DatabasePool { 30 | MySql(MySqlPool), 31 | Postgres(PgPool), 32 | Sqlite(SqlitePool), 33 | } 34 | 35 | impl DatabasePool { 36 | async fn connect(database_url: &str, timeout_duration: Duration) -> Result { 37 | async fn connect_with_timeout( 38 | timeout_duration: Duration, 39 | connect_future: F, 40 | ) -> Result 41 | where 42 | F: std::future::Future>, 43 | { 44 | timeout(timeout_duration, connect_future) 45 | .await 46 | .map_err(|err| { 47 | sqlx::Error::Io(std::io::Error::new( 48 | std::io::ErrorKind::Other, 49 | format!("Connection error: {}", err), 50 | )) 51 | })? 52 | } 53 | 54 | if database_url.starts_with("mysql://") { 55 | let pool = 56 | connect_with_timeout(timeout_duration, MySqlPool::connect(database_url)).await?; 57 | Ok(DatabasePool::MySql(pool)) 58 | } else if database_url.starts_with("postgres://") { 59 | let pool = connect_with_timeout( 60 | timeout_duration, 61 | PgPoolOptions::new() 62 | .max_connections(1) 63 | .acquire_timeout(Duration::from_secs(2)) 64 | .connect(database_url), 65 | ) 66 | .await?; 67 | Ok(DatabasePool::Postgres(pool)) 68 | } else if database_url.starts_with("sqlite://") { 69 | if !Sqlite::database_exists(database_url).await? { 70 | Sqlite::create_database(database_url).await?; 71 | } 72 | let pool = 73 | connect_with_timeout(timeout_duration, SqlitePool::connect(database_url)).await?; 74 | Ok(DatabasePool::Sqlite(pool)) 75 | } else { 76 | Err(sqlx::Error::Configuration( 77 | "Unsupported database type".into(), 78 | )) 79 | } 80 | } 81 | 82 | fn make_query<'a, DB: sqlx::Database>( 83 | sql: &'a str, 84 | binds: &'a [QueryParams], 85 | ) -> Result::Arguments<'a>>, sqlx::Error> 86 | where 87 | bool: sqlx::Encode<'a, DB> + sqlx::Type, 88 | i64: sqlx::Encode<'a, DB> + sqlx::Type, 89 | f64: sqlx::Encode<'a, DB> + sqlx::Type, 90 | &'a str: sqlx::Encode<'a, DB> + sqlx::Type, 91 | serde_json::Value: sqlx::Encode<'a, DB> + sqlx::Type, 92 | &'a Vec: sqlx::Encode<'a, DB> + sqlx::Type, 93 | { 94 | let mut query = sqlx::query(sql); 95 | for bind in binds { 96 | query = match bind { 97 | QueryParams::Bool(value) => query.bind(*value), 98 | QueryParams::Int(value) => query.bind(*value), 99 | QueryParams::Float(value) => query.bind(*value), 100 | QueryParams::Text(value) => query.bind(value.as_str()), 101 | QueryParams::Json(value) => query.bind(value), 102 | QueryParams::Bytes(value) => query.bind(value), 103 | }; 104 | } 105 | Ok(query) 106 | } 107 | 108 | async fn query(&self, request: &DatabaseQuery) -> Result { 109 | match self { 110 | DatabasePool::MySql(pool) => { 111 | let query = Self::make_query(&request.sql, &request.binds)?; 112 | let rows = query.fetch_all(pool).await?; 113 | Ok(DatabaseResponse::MysqlRows(rows)) 114 | } 115 | DatabasePool::Postgres(pool) => { 116 | let query = Self::make_query(&request.sql, &request.binds)?; 117 | let rows = query.fetch_all(pool).await?; 118 | Ok(DatabaseResponse::PgRows(rows)) 119 | } 120 | DatabasePool::Sqlite(pool) => { 121 | let query = Self::make_query(&request.sql, &request.binds)?; 122 | let rows = query.fetch_all(pool).await?; 123 | Ok(DatabaseResponse::SqliteRows(rows)) 124 | } 125 | } 126 | } 127 | 128 | async fn transaction( 129 | &self, 130 | requests: &[DatabaseQuery], 131 | ) -> Result { 132 | match self { 133 | DatabasePool::MySql(pool) => { 134 | let mut transaction = pool.begin().await?; 135 | for request in requests { 136 | let query = Self::make_query(&request.sql, &request.binds)?; 137 | query.execute(&mut *transaction).await?; 138 | } 139 | transaction.commit().await?; 140 | Ok(DatabaseResponse::Transaction) 141 | } 142 | DatabasePool::Postgres(pool) => { 143 | let mut transaction = pool.begin().await?; 144 | for request in requests { 145 | let query = Self::make_query(&request.sql, &request.binds)?; 146 | query.execute(&mut *transaction).await?; 147 | } 148 | transaction.commit().await?; 149 | Ok(DatabaseResponse::Transaction) 150 | } 151 | DatabasePool::Sqlite(pool) => { 152 | let mut transaction = pool.begin().await?; 153 | for request in requests { 154 | let query = Self::make_query(&request.sql, &request.binds)?; 155 | query.execute(&mut *transaction).await?; 156 | } 157 | transaction.commit().await?; 158 | Ok(DatabaseResponse::Transaction) 159 | } 160 | } 161 | } 162 | } 163 | 164 | enum DatabaseRequest { 165 | Query(u32, i64, DatabaseQuery), //owner, session, QueryBuilder 166 | Transaction(u32, i64, Vec), //owner, session, Vec 167 | Close(), 168 | } 169 | 170 | #[derive(Clone)] 171 | struct DatabaseConnection { 172 | tx: mpsc::Sender, 173 | counter: Arc, 174 | } 175 | 176 | enum DatabaseResponse { 177 | Connect, 178 | PgRows(Vec), 179 | MysqlRows(Vec), 180 | SqliteRows(Vec), 181 | Error(sqlx::Error), 182 | Timeout(String), 183 | Transaction, 184 | } 185 | 186 | #[derive(Debug, Clone)] 187 | enum QueryParams { 188 | Bool(bool), 189 | Int(i64), 190 | Float(f64), 191 | Text(String), 192 | Json(serde_json::Value), 193 | Bytes(Vec), 194 | } 195 | 196 | #[derive(Debug, Clone)] 197 | struct DatabaseQuery { 198 | sql: String, 199 | binds: Vec, 200 | } 201 | 202 | async fn handle_result( 203 | database_url: &str, 204 | failed_times: &mut i32, 205 | counter: &Arc, 206 | protocol_type: u8, 207 | owner: u32, 208 | session: i64, 209 | res: Result, 210 | ) -> bool { 211 | match res { 212 | Ok(rows) => { 213 | moon_send(protocol_type, owner, session, rows); 214 | if *failed_times > 0 { 215 | moon_log( 216 | owner, 217 | LOG_LEVEL_INFO, 218 | format!( 219 | "Database '{}' recover from error. Retry success.", 220 | database_url 221 | ), 222 | ); 223 | } 224 | counter.fetch_sub(1, std::sync::atomic::Ordering::Release); 225 | false 226 | } 227 | Err(err) => { 228 | if session != 0 { 229 | moon_send(protocol_type, owner, session, DatabaseResponse::Error(err)); 230 | counter.fetch_sub(1, std::sync::atomic::Ordering::Release); 231 | false 232 | } else { 233 | if *failed_times > 0 { 234 | moon_log( 235 | owner, 236 | LOG_LEVEL_ERROR, 237 | format!( 238 | "Database '{}' error: '{:?}'. Will retry.", 239 | database_url, 240 | err.to_string() 241 | ), 242 | ); 243 | } 244 | *failed_times += 1; 245 | tokio::time::sleep(Duration::from_secs(1)).await; 246 | true 247 | } 248 | } 249 | } 250 | } 251 | 252 | async fn database_handler( 253 | protocol_type: u8, 254 | pool: &DatabasePool, 255 | mut rx: mpsc::Receiver, 256 | database_url: &str, 257 | counter: Arc, 258 | ) { 259 | while let Some(op) = rx.recv().await { 260 | let mut failed_times = 0; 261 | match &op { 262 | DatabaseRequest::Query(owner, session, query_op) => { 263 | while handle_result( 264 | database_url, 265 | &mut failed_times, 266 | &counter, 267 | protocol_type, 268 | *owner, 269 | *session, 270 | pool.query(query_op).await, 271 | ) 272 | .await 273 | {} 274 | } 275 | DatabaseRequest::Transaction(owner, session, query_ops) => { 276 | while handle_result( 277 | database_url, 278 | &mut failed_times, 279 | &counter, 280 | protocol_type, 281 | *owner, 282 | *session, 283 | pool.transaction(query_ops).await, 284 | ) 285 | .await 286 | {} 287 | } 288 | DatabaseRequest::Close() => { 289 | break; 290 | } 291 | } 292 | } 293 | } 294 | 295 | extern "C-unwind" fn connect(state: *mut ffi::lua_State) -> c_int { 296 | let protocol_type: u8 = laux::lua_get(state, 1); 297 | let owner = laux::lua_get(state, 2); 298 | let session: i64 = laux::lua_get(state, 3); 299 | 300 | let database_url: &str = laux::lua_get(state, 4); 301 | let name: &str = laux::lua_get(state, 5); 302 | let connect_timeout: u64 = laux::lua_opt(state, 6).unwrap_or(5000); 303 | 304 | CONTEXT.tokio_runtime.spawn(async move { 305 | match DatabasePool::connect(database_url, Duration::from_millis(connect_timeout)).await { 306 | Ok(pool) => { 307 | let (tx, rx) = mpsc::channel(100); 308 | let counter = Arc::new(AtomicI64::new(0)); 309 | DATABASE_CONNECTIONSS.insert( 310 | name.to_string(), 311 | DatabaseConnection { 312 | tx: tx.clone(), 313 | counter: counter.clone(), 314 | }, 315 | ); 316 | moon_send(protocol_type, owner, session, DatabaseResponse::Connect); 317 | database_handler(protocol_type, &pool, rx, database_url, counter).await; 318 | } 319 | Err(err) => { 320 | moon_send( 321 | protocol_type, 322 | owner, 323 | session, 324 | DatabaseResponse::Timeout(err.to_string()), 325 | ); 326 | } 327 | }; 328 | }); 329 | 330 | laux::lua_push(state, session); 331 | 1 332 | } 333 | 334 | fn get_query_param(state: *mut ffi::lua_State, i: i32) -> Result { 335 | let options = JsonOptions::default(); 336 | 337 | let res = match LuaValue::from_stack(state, i) { 338 | LuaValue::Boolean(val) => QueryParams::Bool(val), 339 | LuaValue::Number(val) => QueryParams::Float(val), 340 | LuaValue::Integer(val) => QueryParams::Int(val), 341 | LuaValue::String(val) => { 342 | if val.starts_with(b"{") || val.starts_with(b"[") { 343 | if let Ok(value) = serde_json::from_slice::(val) { 344 | QueryParams::Json(value) 345 | } else { 346 | QueryParams::Text(unsafe { String::from_utf8_unchecked(val.to_vec()) }) 347 | } 348 | } else { 349 | QueryParams::Text(unsafe { String::from_utf8_unchecked(val.to_vec()) }) 350 | } 351 | } 352 | LuaValue::Table(val) => { 353 | let mut buffer = Vec::new(); 354 | if let Err(err) = encode_table(&mut buffer, &val, 0, false, &options) { 355 | drop(buffer); 356 | laux::lua_error(state, &err); 357 | } 358 | if buffer[0] == b'{' || buffer[0] == b'[' { 359 | if let Ok(value) = serde_json::from_slice::(buffer.as_slice()) { 360 | QueryParams::Json(value) 361 | } else { 362 | QueryParams::Bytes(buffer) 363 | } 364 | } else { 365 | QueryParams::Bytes(buffer) 366 | } 367 | } 368 | _t => { 369 | return Err(format!( 370 | "get_query_param: unsupport value type :{}", 371 | laux::type_name(state, i) 372 | )); 373 | } 374 | }; 375 | Ok(res) 376 | } 377 | 378 | extern "C-unwind" fn query(state: *mut ffi::lua_State) -> c_int { 379 | let mut args = LuaArgs::new(1); 380 | let conn = laux::lua_touserdata::(state, args.iter_arg()) 381 | .expect("Invalid database connect pointer"); 382 | 383 | let owner = laux::lua_get(state, args.iter_arg()); 384 | let session = laux::lua_get(state, args.iter_arg()); 385 | 386 | let sql = laux::lua_get::<&str>(state, args.iter_arg()); 387 | let mut params = Vec::new(); 388 | let top = laux::lua_top(state); 389 | for i in args.iter_arg()..=top { 390 | let param = get_query_param(state, i); 391 | match param { 392 | Ok(value) => { 393 | params.push(value); 394 | } 395 | Err(err) => { 396 | push_lua_table!( 397 | state, 398 | "kind" => "ERROR", 399 | "message" => err 400 | ); 401 | return 1; 402 | } 403 | } 404 | } 405 | 406 | match conn.tx.try_send(DatabaseRequest::Query( 407 | owner, 408 | session, 409 | DatabaseQuery { 410 | sql: sql.to_string(), 411 | binds: params, 412 | }, 413 | )) { 414 | Ok(_) => { 415 | conn.counter 416 | .fetch_add(1, std::sync::atomic::Ordering::Release); 417 | laux::lua_push(state, session); 418 | 1 419 | } 420 | Err(err) => { 421 | push_lua_table!( 422 | state, 423 | "kind" => "ERROR", 424 | "message" => err.to_string() 425 | ); 426 | 1 427 | } 428 | } 429 | } 430 | 431 | struct TransactionQuerys { 432 | querys: Vec, 433 | } 434 | 435 | extern "C-unwind" fn push_transaction_query(state: *mut ffi::lua_State) -> c_int { 436 | let querys = laux::lua_touserdata::(state, 1) 437 | .expect("Invalid transaction query pointer"); 438 | 439 | let sql = laux::lua_get::<&str>(state, 2); 440 | let mut params = Vec::new(); 441 | let top = laux::lua_top(state); 442 | for i in 3..=top { 443 | let param = get_query_param(state, i); 444 | match param { 445 | Ok(value) => { 446 | params.push(value); 447 | } 448 | Err(err) => { 449 | drop(params); 450 | laux::lua_error(state, err.as_ref()); 451 | } 452 | } 453 | } 454 | 455 | querys.querys.push(DatabaseQuery { 456 | sql: sql.to_string(), 457 | binds: params, 458 | }); 459 | 460 | 0 461 | } 462 | 463 | extern "C-unwind" fn make_transaction(state: *mut ffi::lua_State) -> c_int { 464 | laux::lua_newuserdata( 465 | state, 466 | TransactionQuerys { querys: Vec::new() }, 467 | cstr!("sqlx_transaction_metatable"), 468 | &[lreg!("push", push_transaction_query), lreg_null!()], 469 | ); 470 | 1 471 | } 472 | 473 | extern "C-unwind" fn transaction(state: *mut ffi::lua_State) -> c_int { 474 | let mut args = LuaArgs::new(1); 475 | let conn = laux::lua_touserdata::(state, args.iter_arg()) 476 | .expect("Invalid database connect pointer"); 477 | 478 | let owner = laux::lua_get(state, args.iter_arg()); 479 | let session = laux::lua_get(state, args.iter_arg()); 480 | 481 | let querys = laux::lua_touserdata::(state, args.iter_arg()) 482 | .expect("Invalid transaction query pointer"); 483 | 484 | match conn.tx.try_send(DatabaseRequest::Transaction( 485 | owner, 486 | session, 487 | std::mem::take(&mut querys.querys), 488 | )) { 489 | Ok(_) => { 490 | conn.counter 491 | .fetch_add(1, std::sync::atomic::Ordering::Release); 492 | laux::lua_push(state, session); 493 | 1 494 | } 495 | Err(err) => { 496 | push_lua_table!( 497 | state, 498 | "kind" => "ERROR", 499 | "message" => err.to_string() 500 | ); 501 | 1 502 | } 503 | } 504 | } 505 | 506 | extern "C-unwind" fn close(state: *mut ffi::lua_State) -> c_int { 507 | let conn = laux::lua_touserdata::(state, 1) 508 | .expect("Invalid database connect pointer"); 509 | 510 | match conn.tx.try_send(DatabaseRequest::Close()) { 511 | Ok(_) => { 512 | laux::lua_push(state, true); 513 | 1 514 | } 515 | Err(err) => { 516 | push_lua_table!( 517 | state, 518 | "kind" => "ERROR", 519 | "message" => err.to_string() 520 | ); 521 | 1 522 | } 523 | } 524 | } 525 | 526 | fn process_rows<'a, DB>( 527 | state: *mut ffi::lua_State, 528 | rows: &'a [::Row], 529 | ) -> Result 530 | where 531 | DB: sqlx::Database, 532 | usize: ColumnIndex<::Row>, 533 | bool: sqlx::Decode<'a, DB>, 534 | i64: sqlx::Decode<'a, DB>, 535 | f64: sqlx::Decode<'a, DB>, 536 | &'a str: sqlx::Decode<'a, DB>, 537 | &'a [u8]: sqlx::Decode<'a, DB>, 538 | { 539 | let table = LuaTable::new(state, rows.len(), 0); 540 | if rows.is_empty() { 541 | return Ok(1); 542 | } 543 | 544 | let mut column_info = Vec::new(); 545 | if column_info.is_empty() { 546 | rows.iter() 547 | .next() 548 | .unwrap() 549 | .columns() 550 | .iter() 551 | .enumerate() 552 | .for_each(|(index, column)| { 553 | column_info.push((index, column.name())); 554 | }); 555 | } 556 | 557 | let mut i = 0; 558 | for row in rows.iter() { 559 | let row_table = LuaTable::new(state, 0, row.len()); 560 | for (index, column_name) in column_info.iter() { 561 | match row.try_get_raw(*index) { 562 | Ok(value) => match value.type_info().name() { 563 | "NULL" => { 564 | row_table.rawset(*column_name, LuaNil {}); 565 | } 566 | "BOOL" | "BOOLEAN" => { 567 | row_table.rawset( 568 | *column_name, 569 | sqlx::decode::Decode::decode(value).unwrap_or(false), 570 | ); 571 | } 572 | "INT2" | "INT4" | "INT8" | "TINYINT" | "SMALLINT" | "INT" | "MEDIUMINT" 573 | | "BIGINT" | "INTEGER" => { 574 | row_table.rawset( 575 | *column_name, 576 | sqlx::decode::Decode::decode(value).unwrap_or(0), 577 | ); 578 | } 579 | "FLOAT4" | "FLOAT8" | "NUMERIC" | "FLOAT" | "DOUBLE" | "REAL" => { 580 | row_table.rawset( 581 | *column_name, 582 | sqlx::decode::Decode::decode(value).unwrap_or(0.0), 583 | ); 584 | } 585 | "TEXT" => { 586 | row_table.rawset( 587 | *column_name, 588 | sqlx::decode::Decode::decode(value).unwrap_or(""), 589 | ); 590 | } 591 | _ => { 592 | let column_value: &[u8] = 593 | sqlx::decode::Decode::decode(value).unwrap_or(b""); 594 | row_table.rawset(*column_name, column_value); 595 | } 596 | }, 597 | Err(error) => { 598 | laux::lua_push(state, false); 599 | laux::lua_push( 600 | state, 601 | format!("{:?} decode error: {:?}", column_name, error), 602 | ); 603 | return Ok(2); 604 | } 605 | } 606 | } 607 | i += 1; 608 | table.seti(i); 609 | } 610 | Ok(1) 611 | } 612 | 613 | extern "C-unwind" fn find_connection(state: *mut ffi::lua_State) -> c_int { 614 | let name = laux::lua_get::<&str>(state, 1); 615 | match DATABASE_CONNECTIONSS.get(name) { 616 | Some(pair) => { 617 | let l = [ 618 | lreg!("query", query), 619 | lreg!("transaction", transaction), 620 | lreg!("close", close), 621 | lreg_null!(), 622 | ]; 623 | if laux::lua_newuserdata( 624 | state, 625 | pair.value().clone(), 626 | cstr!("sqlx_connection_metatable"), 627 | l.as_ref(), 628 | ) 629 | .is_none() 630 | { 631 | laux::lua_pushnil(state); 632 | return 1; 633 | } 634 | } 635 | None => { 636 | laux::lua_pushnil(state); 637 | } 638 | } 639 | 1 640 | } 641 | 642 | extern "C-unwind" fn decode(state: *mut ffi::lua_State) -> c_int { 643 | laux::luaL_checkstack(state, 6, std::ptr::null()); 644 | let result = lua_into_userdata::(state, 1); 645 | 646 | match *result { 647 | DatabaseResponse::PgRows(rows) => { 648 | return process_rows::(state, &rows) 649 | .map_err(|e| { 650 | push_lua_table!( 651 | state, 652 | "kind" => "ERROR", 653 | "message" => e 654 | ); 655 | }) 656 | .unwrap_or(1); 657 | } 658 | DatabaseResponse::MysqlRows(rows) => { 659 | return process_rows::(state, &rows) 660 | .map_err(|e| { 661 | push_lua_table!( 662 | state, 663 | "kind" => "ERROR", 664 | "message" => e 665 | ); 666 | }) 667 | .unwrap_or(1); 668 | } 669 | DatabaseResponse::SqliteRows(rows) => { 670 | return process_rows::(state, &rows) 671 | .map_err(|e| { 672 | push_lua_table!( 673 | state, 674 | "kind" => "ERROR", 675 | "message" => e 676 | ); 677 | }) 678 | .unwrap_or(1); 679 | } 680 | DatabaseResponse::Transaction => { 681 | push_lua_table!( 682 | state, 683 | "message" => "ok" 684 | ); 685 | return 1; 686 | } 687 | DatabaseResponse::Connect => { 688 | push_lua_table!( 689 | state, 690 | "message" => "success" 691 | ); 692 | return 1; 693 | } 694 | DatabaseResponse::Error(err) => match err.as_database_error() { 695 | Some(db_err) => { 696 | push_lua_table!( 697 | state, 698 | "kind" => "DB", 699 | "message" => db_err.message() 700 | ); 701 | } 702 | None => { 703 | push_lua_table!( 704 | state, 705 | "kind" => "ERROR", 706 | "message" => err.to_string() 707 | ); 708 | } 709 | }, 710 | DatabaseResponse::Timeout(err) => { 711 | push_lua_table!( 712 | state, 713 | "kind" => "TIMEOUT", 714 | "message" => err.to_string() 715 | ); 716 | } 717 | } 718 | 719 | 1 720 | } 721 | 722 | extern "C-unwind" fn stats(state: *mut ffi::lua_State) -> c_int { 723 | let table = LuaTable::new(state, 0, DATABASE_CONNECTIONSS.len()); 724 | DATABASE_CONNECTIONSS.iter().for_each(|pair| { 725 | table.rawset( 726 | pair.key().as_str(), 727 | pair.value() 728 | .counter 729 | .load(std::sync::atomic::Ordering::Acquire), 730 | ); 731 | }); 732 | 1 733 | } 734 | 735 | #[no_mangle] 736 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 737 | pub extern "C-unwind" fn luaopen_rust_sqlx(state: *mut ffi::lua_State) -> c_int { 738 | let l = [ 739 | lreg!("connect", connect), 740 | lreg!("find_connection", find_connection), 741 | lreg!("decode", decode), 742 | lreg!("stats", stats), 743 | lreg!("make_transaction", make_transaction), 744 | lreg_null!(), 745 | ]; 746 | 747 | luaL_newlib!(state, l); 748 | 749 | 1 750 | } 751 | --------------------------------------------------------------------------------