├── .gitattributes ├── .gitignore ├── LICENSE ├── README.adoc ├── bench └── object_access.lua ├── bugs ├── coroutine_1.lua ├── coroutine_2.lua ├── error.lua ├── io_read.lua ├── multi-assignment.lua ├── self-assign.lua ├── string_format_1.lua └── string_format_2.lua ├── cmd ├── dsl │ └── main.go ├── ll │ └── main.go ├── standalone │ ├── main.go │ └── src │ │ └── main.lua └── test │ └── src │ └── main.lua ├── docs ├── bitcask.adoc ├── crypto.adoc ├── csv.adoc ├── date.adoc ├── exec.adoc ├── extension_exec.adoc ├── extension_json.adoc ├── extension_string.adoc ├── extension_table.adoc ├── fmt.adoc ├── fs.adoc ├── fsnotify.adoc ├── go-helper.adoc ├── go-loader.adoc ├── guard.adoc ├── http.adoc ├── json.adoc ├── list.adoc ├── logger.adoc ├── lz4.adoc ├── map.adoc ├── mysql.adoc ├── os.adoc ├── pushover.adoc ├── queue.adoc ├── redis.adoc ├── refmt.adoc ├── slack.adoc ├── ssh_config.adoc ├── telegram.adoc ├── template.adoc ├── tuple.adoc ├── uid.adoc ├── ulid.adoc └── uuid.adoc ├── dsl.go ├── external ├── gluacrypto │ ├── LICENSE │ └── crypto │ │ ├── base64.go │ │ ├── ch.go │ │ ├── crc32.go │ │ ├── crypto.go │ │ ├── decrypt.go │ │ ├── encrypt.go │ │ ├── hmac.go │ │ ├── md5.go │ │ ├── random.go │ │ ├── sha1.go │ │ ├── sha256.go │ │ └── sha512.go ├── gluahttp │ ├── LICENSE │ ├── gluahttp.go │ └── httpresponsetype.go ├── gluasocket │ ├── LICENSE │ ├── gluasocket.go │ ├── socket │ │ └── socket.go │ ├── socketcore │ │ ├── client.go │ │ ├── clientclose.go │ │ ├── clientdirty.go │ │ ├── clientgetfd.go │ │ ├── clientgetpeername.go │ │ ├── clientgetsockname.go │ │ ├── clientgetstats.go │ │ ├── clientreceive.go │ │ ├── clientsend.go │ │ ├── clientsetoption.go │ │ ├── clientsetstats.go │ │ ├── clientsettimeout.go │ │ ├── clientshutdown.go │ │ ├── connect.go │ │ ├── dns.go │ │ ├── dnsgetaddrinfo.go │ │ ├── dnsgethostname.go │ │ ├── dnstohostname.go │ │ ├── dnstoip.go │ │ ├── gettime.go │ │ ├── master.go │ │ ├── masteraccept.go │ │ ├── masterbind.go │ │ ├── masterclose.go │ │ ├── masterconnect.go │ │ ├── masterlisten.go │ │ ├── mastersetoption.go │ │ ├── mastersettimeout.go │ │ ├── select.go │ │ ├── skip.go │ │ ├── sleep.go │ │ ├── socketcore.go │ │ ├── tcp.go │ │ ├── tcp4.go │ │ ├── tcp6.go │ │ └── udp.go │ └── socketexcept │ │ └── socketexcept.go ├── gluasql │ ├── LICENSE │ ├── mysql │ │ ├── client.go │ │ ├── clientconnect.go │ │ ├── clientquery.go │ │ ├── clientsettimeout.go │ │ └── mysql.go │ └── util │ │ └── util.go ├── gopher-json │ ├── LICENSE │ └── json.go └── gopher-lfs │ ├── LICENSE │ ├── api.go │ ├── api_darwin.go │ ├── api_linux.go │ ├── api_other.go │ ├── api_windows.go │ └── util.go ├── go.mod ├── go.sum ├── helper.go ├── internal ├── bitcask.go ├── dsl │ ├── lbuildah.lua │ └── lopper.lua ├── exec.go ├── fs.go ├── fsnotify.go ├── global.go ├── ksuid.go ├── logger.go ├── lua │ ├── argparse.lua │ ├── csv.lua │ ├── date.lua │ ├── extension_exec.lua │ ├── extension_json.lua │ ├── extension_string.lua │ ├── extension_table.lua │ ├── fmt.lua │ ├── graph.lua │ ├── guard.lua │ ├── list.lua │ ├── map.lua │ ├── object.lua │ ├── queue.lua │ ├── state.lua │ ├── template.lua │ ├── test.lua │ ├── tuple.lua │ └── util.lua ├── lz4.go ├── os.go ├── pushover.go ├── redis.go ├── refmt.go ├── slack.go ├── ssh_config.go ├── telegram.go ├── ulid.go └── uuid.go ├── ll.svg ├── ll.toml ├── loader.go ├── scripts ├── .lib │ ├── 000-header.sh │ └── 001-go.sh ├── build-dsl │ └── script ├── build-pie │ └── script ├── build-standalone │ └── script ├── build-test │ └── script ├── build │ └── script ├── docs │ └── script ├── godocs │ └── script ├── install │ └── script ├── lint │ └── script ├── show-functions │ └── script ├── template-test │ ├── script │ └── template.txt └── test │ └── script ├── selene.toml ├── stylua.toml └── tests ├── argparse.lua ├── bitcask.lua ├── crypto.lua ├── csv.lua ├── date.lua ├── exec.lua ├── exec_cmd.lua ├── extension_exec.lua ├── extension_json.lua ├── extension_string.lua ├── extension_table.lua ├── fmt.lua ├── fs.lua ├── fsnotify.lua ├── graph.lua ├── guard.lua ├── http.lua ├── json.lua ├── ksuid.lua ├── list.lua ├── logger.lua ├── lua.lua ├── lz4.lua ├── map.lua ├── mysql.lua ├── object.lua ├── os.lua ├── pushover.lua ├── queue.lua ├── redis.lua ├── refmt.lua ├── slack.lua ├── ssh_config.lua ├── state.lua ├── state ├── composite.lua ├── debug_plain.lua └── helloworld.lua ├── telegram.lua ├── template.lua ├── tuple.lua ├── ulid.lua └── uuid.lua /.gitattributes: -------------------------------------------------------------------------------- 1 | *.lua linguist-detectable=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /ll 2 | /experiments 3 | /external/gopher-lua 4 | /cmd/lopper 5 | /cmd/lbuildah 6 | /cmd/test/main.go 7 | /*.lua 8 | /bin 9 | /rr.json 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Eduardo Tongson 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 | -------------------------------------------------------------------------------- /bench/object_access.lua: -------------------------------------------------------------------------------- 1 | -- http://lua-users.org/wiki/ObjectBenchmarkTests 2 | --[[ This is an evaluation of object access time for various approaches to object orientation in Lua. The primary concern was raw performance speed, not memory use or object creation time. 3 | ]] 4 | 5 | do 6 | local function runbenchmark(name, code, count, ob) 7 | local f = loadstring([[ 8 | local count,ob = ... 9 | local clock = os.clock 10 | local start = clock() 11 | for i=1,count do ]] .. code .. [[ end 12 | return clock() - start 13 | ]]) 14 | io.write(f(count, ob), "\t", name, "\n") 15 | end 16 | 17 | local nameof = {} 18 | local codeof = {} 19 | local tests = {} 20 | function addbenchmark(name, code, ob) 21 | nameof[ob] = name 22 | codeof[ob] = code 23 | tests[#tests+1] = ob 24 | end 25 | function runbenchmarks(count) 26 | for _,ob in ipairs(tests) do 27 | runbenchmark(nameof[ob], codeof[ob], count, ob) 28 | end 29 | end 30 | end 31 | 32 | function makeob1() 33 | local self = {data = 0} 34 | function self:test() self.data = self.data + 1 end 35 | return self 36 | end 37 | addbenchmark("Standard (solid)", "ob:test()", makeob1()) 38 | 39 | local ob2mt = {} 40 | ob2mt.__index = ob2mt 41 | function ob2mt:test() self.data = self.data + 1 end 42 | function makeob2() 43 | return setmetatable({data = 0}, ob2mt) 44 | end 45 | addbenchmark("Standard (metatable)", "ob:test()", makeob2()) 46 | 47 | function makeob3() 48 | local self = {data = 0}; 49 | function self.test() self.data = self.data + 1 end 50 | return self 51 | end 52 | addbenchmark("Object using closures (PiL 16.4)", "ob.test()", makeob3()) 53 | 54 | function makeob4() 55 | local public = {} 56 | local data = 0 57 | function public.test() data = data + 1 end 58 | function public.getdata() return data end 59 | function public.setdata(d) data = d end 60 | return public 61 | end 62 | addbenchmark("Object using closures (noself)", "ob.test()", makeob4()) 63 | 64 | addbenchmark("Direct Access", "ob.data = ob.data + 1", makeob1()) 65 | 66 | addbenchmark("Local Variable", "ob = ob + 1", 0) 67 | 68 | 69 | runbenchmarks(select(1,...) or 100000000) 70 | -------------------------------------------------------------------------------- /bugs/coroutine_1.lua: -------------------------------------------------------------------------------- 1 | mt = { 2 | __le = function (a,b) 3 | coroutine.yield("yield") 4 | return a.x <= b.x 5 | end 6 | } 7 | t1 = setmetatable({x=1}, mt) 8 | t2 = {x=2} 9 | fn = function (a,b) return t2 <= t1 end 10 | co = coroutine.wrap(fn) 11 | co() 12 | print(co()) 13 | -------------------------------------------------------------------------------- /bugs/coroutine_2.lua: -------------------------------------------------------------------------------- 1 | local f = string.gmatch("1 2 3 4 5", "%d+") 2 | print(f()) --> 1 3 | co = coroutine.wrap(f) 4 | print(co()) --> ??? (should be 2) 5 | -------------------------------------------------------------------------------- /bugs/error.lua: -------------------------------------------------------------------------------- 1 | error(coroutine.create(function() end)) 2 | -------------------------------------------------------------------------------- /bugs/io_read.lua: -------------------------------------------------------------------------------- 1 | print(io.read("*n", "*n")) 2 | -------------------------------------------------------------------------------- /bugs/multi-assignment.lua: -------------------------------------------------------------------------------- 1 | --#315 2 | local a = {} 3 | local d = 'e' 4 | local f = 1 5 | 6 | f, a.d = f, d 7 | 8 | print(f..", "..a.d) 9 | -------------------------------------------------------------------------------- /bugs/self-assign.lua: -------------------------------------------------------------------------------- 1 | --#280 2 | function conver(str) 3 | str = f() and str or str 4 | print(type(str)) 5 | end 6 | function f() 7 | return false 8 | end 9 | conver("main") 10 | -------------------------------------------------------------------------------- /bugs/string_format_1.lua: -------------------------------------------------------------------------------- 1 | print(string.format("%")) 2 | -------------------------------------------------------------------------------- /bugs/string_format_2.lua: -------------------------------------------------------------------------------- 1 | x = string.rep("x", 10000) .. "%d" 2 | print(string.format(x)) 3 | -------------------------------------------------------------------------------- /cmd/standalone/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "github.com/tongson/LadyLua" 6 | "github.com/tongson/gl" 7 | "github.com/yuin/gopher-lua" 8 | "os" 9 | "runtime" 10 | ) 11 | 12 | //go:embed src/* 13 | var mainSrc embed.FS 14 | 15 | func main() { 16 | runtime.MemProfileRate = 0 17 | defer gl.RecoverPanic() 18 | L := lua.NewState() 19 | defer L.Close() 20 | ll.GlobalGo(L, "fs") 21 | ll.GlobalGo(L, "os") 22 | ll.GlobalGo(L, "extend") 23 | ll.GlobalGo(L, "exec") 24 | ll.PreloadGo(L, "http") 25 | ll.PreloadGo(L, "json") 26 | ll.PreloadGo(L, "crypto") 27 | ll.PreloadGo(L, "ksuid") 28 | ll.PreloadGo(L, "mysql") 29 | ll.PreloadGo(L, "lz4") 30 | ll.PreloadGo(L, "telegram") 31 | ll.PreloadGo(L, "pushover") 32 | ll.PreloadGo(L, "slack") 33 | ll.PreloadGo(L, "logger") 34 | ll.PreloadGo(L, "fsnotify") 35 | ll.PreloadGo(L, "bitcask") 36 | ll.PreloadGo(L, "refmt") 37 | ll.PreloadGo(L, "uuid") 38 | ll.PreloadGo(L, "ulid") 39 | ll.PreloadGo(L, "redis") 40 | ll.PreloadGo(L, "ssh_config") 41 | ll.Preload(L) 42 | ll.FillArg(L, os.Args) 43 | ll.Main(L, ll.ReadFile(mainSrc, "src/main.lua")) 44 | os.Exit(0) 45 | } 46 | -------------------------------------------------------------------------------- /cmd/standalone/src/main.lua: -------------------------------------------------------------------------------- 1 | local argparse = require("argparse") 2 | local parser = argparse() 3 | parser:argument("first") 4 | parser:argument("second") 5 | local a = parser:parse(arg) 6 | print(a.first) 7 | print(a.second) 8 | os.exit(1) 9 | -------------------------------------------------------------------------------- /cmd/test/src/main.lua: -------------------------------------------------------------------------------- 1 | local all = false 2 | if arg[1] == "all" then 3 | all = true 4 | end 5 | os.setenv("MYSQL_PASSWORD", "irbj0O3Bn1j8Ezja21NdfcMzj7ZFd2lz") 6 | local T = require("test") 7 | local errstr = function(tested, str, ...) 8 | local n, s = tested(...) 9 | if n ~= nil then 10 | return false, "First return value is not nil." 11 | end 12 | if nil == (string.find(s, str, 1, true)) then 13 | return false, string.format('Not found in error string: "%s".', str) 14 | end 15 | return true 16 | end 17 | T.register_assert("error", errstr) 18 | 19 | T["built-in => os.hostname"] = dofile("tests/os.lua") 20 | T["built-in => fmt"] = dofile("tests/fmt.lua") 21 | T["built-in => exec"] = dofile("tests/exec.lua") 22 | T["internal => lua"] = dofile("tests/lua.lua") 23 | T["extension => table"] = dofile("tests/extension_table.lua") 24 | T["extension => string"] = dofile("tests/extension_string.lua") 25 | T["extension => exec"] = dofile("tests/extension_exec.lua") 26 | T["module => list"] = dofile("tests/list.lua") 27 | T["module => guard"] = dofile("tests/guard.lua") 28 | T["module => queue"] = dofile("tests/queue.lua") 29 | T["module => map"] = dofile("tests/map.lua") 30 | T["module => tuple"] = dofile("tests/tuple.lua") 31 | T["module => graph"] = dofile("tests/graph.lua") 32 | T["module => object"] = dofile("tests/object.lua") 33 | T["module => date"] = dofile("tests/date.lua") 34 | T["module => csv"] = dofile("tests/csv.lua") 35 | T["module => ssh_config"] = dofile("tests/ssh_config.lua") 36 | T["module => ulid"] = dofile("tests/ulid.lua") 37 | T["module => refmt"] = dofile("tests/refmt.lua") 38 | T["module => bitcask"] = dofile("tests/bitcask.lua") 39 | T["module => fsnotify"] = dofile("tests/fsnotify.lua") 40 | T["module => logger"] = dofile("tests/logger.lua") 41 | T["module => lz4"] = dofile("tests/lz4.lua") 42 | if all then 43 | T["module => slack"] = dofile("tests/slack.lua") 44 | T["module => pushover"] = dofile("tests/pushover.lua") 45 | T["module => telegram"] = dofile("tests/telegram.lua") 46 | T["module => mysql"] = dofile("tests/mysql.lua") 47 | T["module => redis"] = dofile("tests/redis.lua") 48 | end 49 | T["module => ksuid"] = dofile("tests/ksuid.lua") 50 | T["module => tengattack/gluacrypto"] = dofile("tests/crypto.lua") 51 | T["module => leafo/etlua"] = dofile("tests/template.lua") 52 | T["module => layeh/gopher-json"] = dofile("tests/json.lua") 53 | T["extension => json"] = dofile("tests/extension_json.lua") 54 | T["module => cjoudrey/gluahttp"] = dofile("tests/http.lua") 55 | T["module => luarocks/argparse"] = dofile("tests/argparse.lua") 56 | T["global => extend"] = function() 57 | T.is_function(extend) 58 | end 59 | T["module => UIdalov/u-test"] = function() 60 | T.is_function(T.is_function) 61 | end 62 | T["built-in => fs"] = dofile("tests/fs.lua") 63 | T.summary() 64 | -------------------------------------------------------------------------------- /docs/bitcask.adoc: -------------------------------------------------------------------------------- 1 | = bitcask 2 | :toc: 3 | :toc-placement!: 4 | 5 | Wrapper to https://git.mills.io/prologic/bitcask[Bitcask]. 6 | 7 | [NOTE] 8 | ==== 9 | Maximum key size is *64* bytes + 10 | Default maximum value size is *64* KiB 11 | ==== 12 | 13 | toc::[] 14 | 15 | == *bitcask.open*(_String_[, _Number_]) -> _Userdata_ 16 | Open a database. Creates a new database if it does not exists. 17 | 18 | === Arguments 19 | [options="header",width="72%"] 20 | |=== 21 | |Type |Description 22 | |string |Path for directory hierarchy 23 | |number |Maximum value size in bytes; default 64KiB 24 | |=== 25 | 26 | === Returns 27 | [options="header",width="72%"] 28 | |=== 29 | |Type |Description 30 | |userdata |Database lock 31 | |=== 32 | 33 | == *:put*(_String_, _String_) -> _Boolean_ 34 | Write key-value to database. 35 | 36 | === Arguments 37 | [options="header",width="72%"] 38 | |=== 39 | |Type |Description 40 | |string |Key 41 | |string |Value 42 | |=== 43 | 44 | === Returns 45 | [options="header",width="72%"] 46 | |=== 47 | |Type |Description 48 | |boolean |`true` if succesful 49 | |=== 50 | 51 | == *:keys*() -> _Table_ 52 | List of keys in database. 53 | 54 | === Returns 55 | [options="header",width="72%"] 56 | |=== 57 | |Type |Description 58 | |table |List of keys 59 | |=== 60 | 61 | == *:get*(_String_) -> _String_ 62 | Fetch value with matching key. 63 | 64 | === Arguments 65 | [options="header",width="72%"] 66 | |=== 67 | |Type |Description 68 | |string |Key 69 | |=== 70 | 71 | === Returns 72 | [options="header",width="72%"] 73 | |=== 74 | |Type |Description 75 | |string |Value 76 | |=== 77 | 78 | == *:has*(_String_) -> _Boolean_ 79 | Returns true if the key exists in the database, false otherwise. 80 | 81 | === Arguments 82 | [options="header",width="72%"] 83 | |=== 84 | |Type |Description 85 | |string |Key 86 | |=== 87 | 88 | === Returns 89 | [options="header",width="72%"] 90 | |=== 91 | |Type |Description 92 | |boolean |`true` if found, `false` otherwise 93 | |=== 94 | 95 | == *:delete*(_String_) -> _Boolean_ 96 | Delete key and value from database. 97 | 98 | === Arguments 99 | [options="header",width="72%"] 100 | |=== 101 | |Type |Description 102 | |string |Key 103 | |=== 104 | 105 | === Returns 106 | [options="header",width="72%"] 107 | |=== 108 | |Type |Description 109 | |boolean |`true` if successful 110 | |=== 111 | 112 | == *:sync*() -> _Boolean_ 113 | Flush buffers to disk ensuring all data is written 114 | 115 | === Returns 116 | [options="header",width="72%"] 117 | |=== 118 | |Type |Description 119 | |boolean |`true` if successful 120 | |=== 121 | 122 | == *:close*() -> _Boolean_ 123 | Release database lock. 124 | 125 | === Returns 126 | [options="header",width="72%"] 127 | |=== 128 | |Type |Description 129 | |boolean |`true` if successful 130 | |=== 131 | -------------------------------------------------------------------------------- /docs/crypto.adoc: -------------------------------------------------------------------------------- 1 | = crypto 2 | :toc: 3 | :toc-placement!: 4 | 5 | Cryptography operations and base64 encoding/decoding. + 6 | 7 | Not in the global namespace. Load with `require('crypto')`. 8 | 9 | toc::[] 10 | 11 | == *crypto.base64_encode*(_String_) -> _String_ 12 | Encode data to base64. 13 | 14 | === Arguments 15 | [width="72%"] 16 | |=== 17 | |string |Data to encode 18 | |=== 19 | 20 | === Returns 21 | [width="72%"] 22 | |=== 23 | |string |Encoded base64 string 24 | |=== 25 | 26 | == *crypto.base64_decode*(_String_) -> _String_ 27 | Decode base64 string. 28 | 29 | === Arguments 30 | [width="72%"] 31 | |=== 32 | |string |String to decode 33 | |=== 34 | 35 | === Returns 36 | [width="72%"] 37 | |=== 38 | |string |Decoded data 39 | |=== 40 | 41 | == *crypto.crc32*(_String_[, _Boolean_]) -> _String_ 42 | Perform CRC32 on data. 43 | 44 | === Arguments 45 | [width="72%"] 46 | |=== 47 | |string |String to check 48 | |boolean|If `true`, returns raw bytes instead of the default hex encoding string 49 | |=== 50 | 51 | === Returns 52 | [width="72%"] 53 | |=== 54 | |string |Checksum 55 | |=== 56 | 57 | == *crypto.sha256*(_String_[, _Boolean_]) -> _String_ 58 | Compute the SHA256 hash code from data. 59 | 60 | === Arguments 61 | [width="72%"] 62 | |=== 63 | |string |Data 64 | |boolean|If `true`, returns raw bytes instead of the default hex encoding string 65 | |=== 66 | 67 | === Returns 68 | [width="72%"] 69 | |=== 70 | |string |Hash code 71 | |=== 72 | 73 | == *crypto.sha512*(_String_[, _Boolean_]) -> _String_ 74 | Compute the SHA512 hash code from data. 75 | 76 | === Arguments 77 | [width="72%"] 78 | |=== 79 | |string |Data 80 | |boolean|If `true`, returns raw bytes instead of the default hex encoding string 81 | |=== 82 | 83 | === Returns 84 | [width="72%"] 85 | |=== 86 | |string |Hash code 87 | |=== 88 | 89 | == *crypto.hmac*(_String_, _String_, _String_[, _Boolean_]) -> _String_ 90 | Data integrity and authenticity code computation. 91 | 92 | === Arguments 93 | [width="72%"] 94 | |=== 95 | |string |Hash function, example: `sha256` 96 | |string |Message 97 | |string |Secret key 98 | |boolean|If `true`, returns raw bytes instead of the default hex encoding string 99 | |=== 100 | 101 | === Returns 102 | [width="72%"] 103 | |=== 104 | |string |Code 105 | |=== 106 | 107 | == *crypto.valid_hmac*(_String_, _String_, _String_, _String_) -> _Boolean_ 108 | Compare MACs in a way that avoids side-channel attacks. 109 | 110 | === Arguments 111 | [width="72%"] 112 | |=== 113 | |string |Hash function, example: `sha256` 114 | |string |Message 115 | |string |Secret key 116 | |string |Raw output from crypto.hmac() 117 | |=== 118 | 119 | === Returns 120 | [width="72%"] 121 | |=== 122 | |boolean |`true` if valid 123 | |=== 124 | 125 | == *crypto.random*([_Number_]) -> _String_ 126 | Generate random Hexadecimal string. 127 | 128 | === Arguments 129 | [width="72%"] 130 | |=== 131 | |number |Optional hexadecimal length, default: 8 (16 characters) 132 | |=== 133 | 134 | === Returns 135 | [width="72%"] 136 | |=== 137 | |string |Hexadecimal string 138 | |=== 139 | 140 | == *crypto.fast_random*() -> _String_ 141 | Generate random Hexadecimal string(16-character string). 142 | 143 | === Returns 144 | [width="72%"] 145 | |=== 146 | |string |Hexadecimal string 147 | |=== 148 | -------------------------------------------------------------------------------- /docs/csv.adoc: -------------------------------------------------------------------------------- 1 | = csv 2 | :toc: 3 | :toc-placement!: 4 | 5 | Parse and encode CSV. 6 | 7 | toc::[] 8 | 9 | == *csv.parse*(_String_) -> _Table_ 10 | Decode a CSV into a table. 11 | 12 | === Arguments 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |string |CSV 17 | |=== 18 | 19 | === Returns 20 | [options="header",width="72%"] 21 | |=== 22 | |Type |Description 23 | |table |Table 24 | |=== 25 | 26 | == *csv.encode*(_Table_) -> _String_ 27 | Encode table into CSV string. 28 | 29 | === Arguments 30 | [options="header",width="72%"] 31 | |=== 32 | |Type |Description 33 | |table |Table 34 | |=== 35 | 36 | === Returns 37 | [options="header",width="72%"] 38 | |=== 39 | |Type |Description 40 | |string |CSV 41 | |=== 42 | -------------------------------------------------------------------------------- /docs/exec.adoc: -------------------------------------------------------------------------------- 1 | = exec 2 | :toc: 3 | :toc-placement!: 4 | 5 | Execute external programs. 6 | 7 | toc::[] 8 | 9 | == *exec.command*(_String_[, _Table_][, _Table_][, _Table_][, _String_][, _String_]) -> _Boolean_, _String_, _String_, _String_ 10 | Execute a program. Base function of the the other functions in this module. 11 | 12 | === Arguments 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |string |Executable 17 | |table |Arguments 18 | |table |Environment 19 | |string |Working directory 20 | |string |STDIN 21 | |number |Timeout in seconds 22 | |=== 23 | 24 | === Returns 25 | [options="header",width="72%"] 26 | |=== 27 | |Type |Description 28 | |boolean |`true` if no errors encountered, `false` otherwise 29 | |string |STDOUT output from program 30 | |string |STDERR output from program 31 | |string |Error from Go 32 | |=== 33 | 34 | == *exec.ctx*(_String_) -> _Function_ 35 | Execute program under a context. The returned function takes a table(list) for arguments. 36 | 37 | === Arguments 38 | [options="header",width="72%"] 39 | |=== 40 | |Type |Description 41 | |string |Executable 42 | |=== 43 | 44 | === Returns 45 | [options="header",width="72%"] 46 | |=== 47 | |Type |Description 48 | |function| A function that can be called and set values; the function also returns the same values as `exec.command` 49 | |=== 50 | 51 | === Map 52 | [options="header",width="72%"] 53 | |=== 54 | |Value |Description 55 | |env |Environment 56 | |cwd |Working directory 57 | |stdin |STDIN 58 | |timeout |Timeout in seconds 59 | |=== 60 | 61 | === Example 62 | ---- 63 | local ls = exec.ctx'/bin/ls' 64 | ls.env = {'LC_ALL=C'} 65 | local r, o = ls{'/tmp', '/dev'} 66 | ---- 67 | -------------------------------------------------------------------------------- /docs/extension_exec.adoc: -------------------------------------------------------------------------------- 1 | = extension: exec 2 | :toc: 3 | :toc-placement!: 4 | 5 | Additional functions for the exec namespace. 6 | 7 | To load and patch global `exec` namespace: 8 | ---- 9 | extend("exec") 10 | ---- 11 | 12 | toc::[] 13 | 14 | == *exec.cmd*(_String_) -> _Function_ 15 | Execute program under a context. Difference with `exec.ctx` is this takes two additional settings; `errexit` and `error`. When `errexit` is set to `true`, the programs exits immediately when an error is encountered. The `error` setting takes a string to show when `errexit` is triggered. + 16 | The returned function's also accepts a format string OR a table(list) for building the argument. 17 | 18 | === Arguments 19 | [options="header",width="72%"] 20 | |=== 21 | |Type |Description 22 | |string |Executable 23 | |=== 24 | 25 | === Returns 26 | [options="header",width="72%"] 27 | |=== 28 | |Type |Description 29 | |function| A function that can be called and set values; the function also returns the same values as `exec.command` 30 | |=== 31 | 32 | === Map 33 | [options="header",width="72%"] 34 | |=== 35 | |Value |Description 36 | |env |Environment 37 | |cwd |Working directory 38 | |stdin |STDIN 39 | |timeout |Timeout in seconds 40 | |errexit |Exit immediately when an error is encountered 41 | |error |Custom error message when errexit is triggered 42 | |=== 43 | 44 | === Example 45 | ---- 46 | local ls = exec.cmd'/bin/ls' 47 | ls.env = {'LC_ALL=C'} 48 | local tmp = '/tmp' 49 | local dev = '/dev' 50 | local r, o = ls('%s %s', tmp, dev) 51 | ---- 52 | 53 | == *exec.run*(_String_) -> _Function_ 54 | A quick way run programs if you only need to set arguments. 55 | 56 | === Arguments 57 | [options="header",width="72%"] 58 | |=== 59 | |Type |Description 60 | |string |Executable 61 | |=== 62 | 63 | === Returns 64 | [options="header",width="72%"] 65 | |=== 66 | |Type |Description 67 | |function| A function that can be called; the function also returns the same values as `exec.command` 68 | |=== 69 | 70 | === Example 71 | ---- 72 | local rm = exec.run 'rm' 73 | rm'/tmp/test' 74 | ---- 75 | -------------------------------------------------------------------------------- /docs/extension_json.adoc: -------------------------------------------------------------------------------- 1 | = extension: json 2 | :toc: 3 | :toc-placement!: 4 | 5 | Additional functions for the `json` module. 6 | 7 | To load and patch `json` namespace: 8 | ---- 9 | extend("json") 10 | ---- 11 | 12 | toc::[] 13 | 14 | == *json.array*(_String_) 15 | JSON array iterator 16 | 17 | === Arguments 18 | [options="header",width="72%"] 19 | |=== 20 | |Type |Description 21 | |string |JSON 22 | |=== 23 | 24 | == *json.object*(_String_) 25 | JSON object iterator. 26 | 27 | === Arguments 28 | [options="header",width="72%"] 29 | |=== 30 | |Type |Description 31 | |string |JSON 32 | |=== 33 | -------------------------------------------------------------------------------- /docs/fmt.adoc: -------------------------------------------------------------------------------- 1 | = fmt 2 | :toc: 3 | :toc-placement!: 4 | 5 | Format string variants that wraps `string.format`. + 6 | 7 | toc::[] 8 | 9 | == *fmt.print*(_String_, _..._) 10 | Print formatted string to io.stdout. 11 | 12 | === Arguments 13 | [width="72%"] 14 | |=== 15 | |string| Format string 16 | |...| Values for the format string 17 | |=== 18 | 19 | == *fmt.warn*(_String_, _..._) 20 | Print formatted string to io.stderr. 21 | 22 | === Arguments 23 | [width="72%"] 24 | |=== 25 | |string| Format string 26 | |...| Values for the format string 27 | |=== 28 | 29 | == *fmt.error*(_String_, _..._) -> _Nil_, _String_ 30 | Shortcut for following the Lua convention of returning `nil` and `string` during error conditions. 31 | 32 | === Arguments 33 | [width="72%"] 34 | |=== 35 | |string| Format string 36 | |...| Values for the format string 37 | |=== 38 | 39 | === Returns 40 | [width="72%"] 41 | |=== 42 | |nil| nil 43 | |string| Error message 44 | |=== 45 | 46 | == *fmt.panic*(_String_, _..._) 47 | Print formatted string to io.stderr and exit immediately with code 1. 48 | 49 | === Arguments 50 | [width="72%"] 51 | |=== 52 | |string| Format string 53 | |...| Values for the format string 54 | |=== 55 | 56 | == *fmt.assert*(_Value_, _String_, _..._) 57 | Print formatted string to io.stderr and exit immediately with code 1 if argument #1 is falsy(nil or false). 58 | 59 | === Arguments 60 | [width="72%"] 61 | |=== 62 | |value| Any Lua type that can return nil or false 63 | |string| Format string 64 | |...| Values for the format string 65 | |=== 66 | -------------------------------------------------------------------------------- /docs/fsnotify.adoc: -------------------------------------------------------------------------------- 1 | = fsnotify 2 | :toc: 3 | :toc-placement!: 4 | 5 | Wait for filesystem create, delete, and write events. All functions block until the specified event is detected. 6 | 7 | toc::[] 8 | 9 | == *fsnotify.create*(_String_) -> _Boolean_ 10 | Wait for a create event on path. 11 | 12 | === Arguments 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |string |Path to wait on 17 | |=== 18 | 19 | === Returns 20 | [options="header",width="72%"] 21 | |=== 22 | |Type |Description 23 | |boolean |`true` if create event happened 24 | |=== 25 | 26 | == *fsnotify.write*(_String_) -> _Boolean_ 27 | Wait for a write event on path. 28 | 29 | === Arguments 30 | [options="header",width="72%"] 31 | |=== 32 | |Type |Description 33 | |string |Path to wait on 34 | |=== 35 | 36 | === Returns 37 | [options="header",width="72%"] 38 | |=== 39 | |Type |Description 40 | |boolean |`true` if write event happened 41 | |=== 42 | 43 | == *fsnotify.remove*(_String_) -> _Boolean_ 44 | Wait for a remove event on path. 45 | 46 | === Arguments 47 | [options="header",width="72%"] 48 | |=== 49 | |Type |Description 50 | |string |Path to wait on 51 | |=== 52 | 53 | === Returns 54 | [options="header",width="72%"] 55 | |=== 56 | |Type |Description 57 | |boolean |`true` if remove event happened 58 | |=== 59 | -------------------------------------------------------------------------------- /docs/go-helper.adoc: -------------------------------------------------------------------------------- 1 | 2 | == *ll.FillArg*(*lua.LState, []string) 3 | Capture command line arguments as the `arg` table in the global `_G` environment. 4 | 5 | === Arguments 6 | [width="72%"] 7 | |=== 8 | |*lua.LState|The current `LState`; usually the result of `lua.NewState()` 9 | |[]string |Usually `os.Args` 10 | |=== 11 | 12 | == *ll.ReadFile*(embed.FS, string) -> string 13 | Read file from an `embed.FS` location. 14 | 15 | === Arguments 16 | [width="72%"] 17 | |=== 18 | |embed.FS |Variable of embedded filesystem 19 | |string |Filename 20 | |=== 21 | 22 | === Returns 23 | [width="72%"] 24 | |=== 25 | |string |Contents of file 26 | |=== 27 | -------------------------------------------------------------------------------- /docs/go-loader.adoc: -------------------------------------------------------------------------------- 1 | = loader.go 2 | :toc: 3 | :toc-placement!: 4 | 5 | Module loaders. 6 | 7 | toc::[] 8 | 9 | == *ll.Preload*(*lua.LState) 10 | Add `package.loaders` entry for loading plain Lua modules from `internal/lua`. + 11 | This allows Lua code to `require()` these modules. 12 | 13 | == *ll.LoadPatch*(*lua.LState, string) 14 | For monkey-patching Lua values. 15 | 16 | [NOTE] 17 | ==== 18 | Severely degrades VM start up time. 19 | ==== 20 | 21 | === Arguments 22 | [width="72%"] 23 | |=== 24 | |*lua.LState|The current `LState`; usually the result of `lua.NewState()` 25 | |string |Basename of Lua source in `internal/lua` 26 | |=== 27 | 28 | == *ll.LoadGlobalLua*(*lua.LState, string) 29 | For adding Lua values into the global `_G` environment. 30 | 31 | === Arguments 32 | [width="72%"] 33 | |=== 34 | |*lua.LState|The current `LState`; usually the result of `lua.NewState()` 35 | |string |Basename of Lua source in `internal/lua` 36 | |=== 37 | 38 | == *ll.Main*(*lua.LState, string) 39 | The entrypoint(main) Lua code for standalone projects. 40 | 41 | === Arguments 42 | [width="72%"] 43 | |=== 44 | |*lua.LState|The current `LState`; usually the result of `lua.NewState()` 45 | |string |Lua source code 46 | |=== 47 | 48 | == *ll.PreloadModule*(*lua.LState, string, string) 49 | Load plain Lua modules into `package.preload`. Useful for your own Lua modules loaded from standalone projects. 50 | 51 | === Arguments 52 | [width="72%"] 53 | |=== 54 | |*lua.LState|The current `LState`; usually the result of `lua.NewState()` 55 | |string |Name of the module 56 | |string |Lua source code 57 | |=== 58 | 59 | == *ll.LoadGlobalGo*(*lua.LState, string) 60 | Load gopher-lua (Go) module into the global `_G` environment. + 61 | 62 | === Arguments 63 | [width="72%"] 64 | |=== 65 | |*lua.LState|The current `LState`; usually the result of `lua.NewState()` 66 | |string |Name of the module 67 | |=== 68 | 69 | == *ll.PreloadGo*(*lua.LState, string) 70 | Load gopher-lua (Go) module into `package.preload`. + 71 | 72 | === Arguments 73 | [width="72%"] 74 | |=== 75 | |*lua.LState|The current `LState`; usually the result of `lua.NewState()` 76 | |string |Name of the module 77 | |=== 78 | -------------------------------------------------------------------------------- /docs/guard.adoc: -------------------------------------------------------------------------------- 1 | = guard 2 | :toc: 3 | :toc-placement!: 4 | 5 | Elixir-style guards. One way to avoid nested conditionals. 6 | 7 | toc::[] 8 | 9 | == *guard*() -> _Table_ 10 | Returns new guard factory. 11 | 12 | === Returns 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |table |Guardian table 17 | |=== 18 | 19 | == *.any*(_function_) 20 | Fallthrough for a guard chain. 21 | 22 | === Arguments 23 | [options="header",width="72%"] 24 | |=== 25 | |type |description 26 | |function |Default case function 27 | |=== 28 | 29 | == *.when*(_function_, _function_) 30 | Expects two functions arguments: the first one being a filter function, and the second one being a function to be evaluated. the filter function should return a boolean. if it returns `true`, the second function argument is evaluated and the guard returns itself right after. 31 | 32 | === Arguments 33 | [options="header",width="72%"] 34 | |=== 35 | |type |description 36 | |function |filter 37 | |function |main function 38 | |=== 39 | -------------------------------------------------------------------------------- /docs/http.adoc: -------------------------------------------------------------------------------- 1 | = http 2 | :toc: 3 | :toc-placement!: 4 | 5 | Perform HTTP requests from Lua. From https://github.com/cjoudrey/gluahttp[gluahttp]. 6 | 7 | toc::[] 8 | 9 | === Common Options Map 10 | [options="header",width="88%"] 11 | |=== 12 | |Name |Type | Description 13 | |query |String | URL encoded query params 14 | |cookies |Table | Additional cookies to send with the request 15 | |headers |Table | Additional headers to send with the request 16 | |timeout |Number/String |Request timeout. Number of seconds or String such as "1h" 17 | |auth |Table |Username and password for HTTP basic auth. Table keys are *user* for username, *pass* for passwod. `auth={user="user", pass="pass"}` 18 | |=== 19 | 20 | === Additional Options for HTTP POST, PUT, PATCH 21 | [options="header",width="88%"] 22 | |=== 23 | |Name |Type | Description 24 | |body |String |Request body 25 | |=== 26 | 27 | === Common Response Map 28 | [options="header",width="88%"] 29 | |=== 30 | |Name | Type | Description 31 | |body | String | The HTTP response body 32 | |body_size | Number | The size of the HTTP response body in bytes 33 | |headers | Table | The HTTP response headers 34 | |cookies | Table | The cookies sent by the server in the HTTP response 35 | |status_code | Number | The HTTP response status code 36 | |url | String | The final URL the request ended pointing to after redirects 37 | |=== 38 | 39 | == *http.get*(_String_, _Table_) -> _Table_ 40 | HTTP GET. 41 | 42 | === Arguments 43 | [options="header",width="72%"] 44 | |=== 45 | |Type |Description 46 | |string |URL 47 | |table |Options, see map above 48 | |=== 49 | 50 | === Returns 51 | [options="header",width="72%"] 52 | |=== 53 | |Type |Description 54 | |table |Response, see map above 55 | |=== 56 | 57 | == *http.head*(_String_, _Table_) -> _Table_ 58 | HTTP HEAD. 59 | 60 | === Arguments 61 | [options="header",width="72%"] 62 | |=== 63 | |Type |Description 64 | |string |URL 65 | |table |Options, see map above 66 | |=== 67 | 68 | === Returns 69 | [options="header",width="72%"] 70 | |=== 71 | |Type |Description 72 | |table |Response, see map above 73 | |=== 74 | 75 | == *http.delete*(_String_, _Table_) -> _Table_ 76 | HTTP DELETE. 77 | 78 | === Arguments 79 | [options="header",width="72%"] 80 | |=== 81 | |Type |Description 82 | |string |URL 83 | |table |Options, see map above 84 | |=== 85 | 86 | === Returns 87 | [options="header",width="72%"] 88 | |=== 89 | |Type |Description 90 | |table |Response, see map above 91 | |=== 92 | 93 | == *http.patch*(_String_, _Table_) -> _Table_ 94 | HTTP PATCH. 95 | 96 | === Arguments 97 | [options="header",width="72%"] 98 | |=== 99 | |Type |Description 100 | |string |URL 101 | |table |Options, see map above 102 | |=== 103 | 104 | === Returns 105 | [options="header",width="72%"] 106 | |=== 107 | |Type |Description 108 | |table |Response, see map above 109 | |=== 110 | 111 | == *http.put*(_String_, _Table_) -> _Table_ 112 | HTTP PUT. 113 | 114 | === Arguments 115 | [options="header",width="72%"] 116 | |=== 117 | |Type |Description 118 | |string |URL 119 | |table |Options, see map above 120 | |=== 121 | 122 | === Returns 123 | [options="header",width="72%"] 124 | |=== 125 | |Type |Description 126 | |table |Response, see map above 127 | |=== 128 | 129 | == *http.post*(_String_, _Table_) -> _Table_ 130 | HTTP POST. 131 | 132 | === Arguments 133 | [options="header",width="72%"] 134 | |=== 135 | |Type |Description 136 | |string |URL 137 | |table |Options, see map above 138 | |=== 139 | 140 | === Returns 141 | [options="header",width="72%"] 142 | |=== 143 | |Type |Description 144 | |table |Response, see map above 145 | |=== 146 | -------------------------------------------------------------------------------- /docs/json.adoc: -------------------------------------------------------------------------------- 1 | = json 2 | :toc: 3 | :toc-placement!: 4 | 5 | JSON encoding and decoding of Lua values. 6 | 7 | toc::[] 8 | 9 | == *json.encode*(_Any_) -> _String_ 10 | Encode value to JSON. 11 | 12 | === Arguments 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |any |Any value 17 | |=== 18 | 19 | === Returns 20 | [options="header",width="72%"] 21 | |=== 22 | |Type |Description 23 | |string |JSON representation 24 | |=== 25 | 26 | == *json.decode*(_String_) -> _Any_ 27 | Decode JSON to Lua value 28 | 29 | === Arguments 30 | [options="header",width="72%"] 31 | |=== 32 | |Type |Description 33 | |string |JSON string 34 | |=== 35 | 36 | === Returns 37 | [options="header",width="72%"] 38 | |=== 39 | |Type |Description 40 | |any |Lua value 41 | |=== 42 | -------------------------------------------------------------------------------- /docs/list.adoc: -------------------------------------------------------------------------------- 1 | = list 2 | :toc: 3 | :toc-placement!: 4 | 5 | Doubly linked list data structures. Stores one instance of a value. Push tables for more leeway. 6 | 7 | toc::[] 8 | 9 | == *list.new*() -> _Table_ 10 | Create a new list. 11 | 12 | === Returns 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |table |List 17 | |=== 18 | 19 | 20 | == *:push_front*(_Value_) 21 | Push to beginning of list. 22 | 23 | === Arguments 24 | [options="header",width="72%"] 25 | |=== 26 | |Type |Description 27 | |value |String, Boolean, Number, or Table 28 | |=== 29 | 30 | == *:push_back*(_Value_) 31 | Push to end of list. 32 | 33 | Alias: `:push` 34 | 35 | === Arguments 36 | [options="header",width="72%"] 37 | |=== 38 | |Type |Description 39 | |value |String, Boolean, Number, or Table 40 | |=== 41 | 42 | == *:pop_front*() -> _Value_ 43 | Pop value from beginning of list. 44 | 45 | === Returns 46 | [options="header",width="72%"] 47 | |=== 48 | |Type |Description 49 | |value |String, Boolean, Number, or Table 50 | |=== 51 | 52 | == *:pop_back*() -> _Value_ 53 | Pop value from end of list. 54 | 55 | Alias: `:pop` 56 | 57 | === Returns 58 | [options="header",width="72%"] 59 | |=== 60 | |Type |Description 61 | |value |String, Boolean, Number, or Table 62 | |=== 63 | 64 | == *:contains*() -> _Boolean_ 65 | Check if list contains an instance of number, string, or boolean 66 | 67 | === Returns 68 | [options="header",width="72%"] 69 | |=== 70 | |Type |Description 71 | |boolean |`true` if value is found, `false` otherwise 72 | |=== 73 | 74 | == *:size*() -> _Number_ 75 | Count items in list. 76 | 77 | === Returns 78 | [options="header",width="72%"] 79 | |=== 80 | |Type |Description 81 | |number |Count 82 | |=== 83 | 84 | == *:front*() -> _Value_ 85 | Return first value in the list. Does not pop() the value. 86 | 87 | === Returns 88 | [options="header",width="72%"] 89 | |=== 90 | |Type |Description 91 | |value |Value 92 | |=== 93 | 94 | == *:back*() -> _Value_ 95 | Return last value in the list. Does not pop() the value. 96 | 97 | === Returns 98 | [options="header",width="72%"] 99 | |=== 100 | |Type |Description 101 | |value |Value 102 | |=== 103 | 104 | == *:walk*([_Boolean_]) -> _Iterator_ 105 | Iterate over list. 106 | 107 | === Arguments 108 | [options="header",width="72%"] 109 | |=== 110 | |Type |Description 111 | |boolean |if `false`, does a reverse iteration 112 | |=== 113 | 114 | == *:range*(_Number_, _Number_) -> _Table_ 115 | Return a table for ranged iteration. 116 | 117 | === Arguments 118 | [options="header",width="72%"] 119 | |=== 120 | |Type |Description 121 | |number |Index start 122 | |number |Index end 123 | |=== 124 | 125 | === Returns 126 | [options="header",width="72%"] 127 | |=== 128 | |Type |Description 129 | |table |Table with values 130 | |=== 131 | -------------------------------------------------------------------------------- /docs/logger.adoc: -------------------------------------------------------------------------------- 1 | = logger 2 | :toc: 3 | :toc-placement!: 4 | 5 | Structured logging to STDERR, STDOUT, or file. 6 | A https://github.com/rs/zerolog[zerolog] wrapper. 7 | 8 | toc::[] 9 | 10 | == *logger.new*() -> _Userdata_ 11 | 12 | Initialize object to access methods below. 13 | 14 | === Arguments 15 | [options="header",width="72%"] 16 | |=== 17 | |Type |Description 18 | |string |`stdout`, `stderr`, or path to a file, default is `stderr` 19 | |=== 20 | 21 | === Returns 22 | [options="header",width="72%"] 23 | |=== 24 | |Type |Description 25 | |userdata| Userdata with methods below 26 | |=== 27 | 28 | == *logger.time*() -> _String_ 29 | 30 | Get same timestamp format used in logs. 31 | 32 | === Returns 33 | [options="header",width="72%"] 34 | |=== 35 | |Type |Description 36 | |string| Timestamp 37 | |=== 38 | 39 | == *:{info, debug, warn, error}* (_String_, _Table_) 40 | 41 | Log to specified log level. 42 | 43 | === Arguments 44 | [options="header",width="72%"] 45 | |=== 46 | |Type |Description 47 | |string| message 48 | |table | key-value map 49 | |=== 50 | -------------------------------------------------------------------------------- /docs/lz4.adoc: -------------------------------------------------------------------------------- 1 | = lz4 2 | :toc: 3 | :toc-placement!: 4 | 5 | LZ4 compression and decompression. 6 | 7 | toc::[] 8 | 9 | == *lz4.compress*(_String_) -> _String_ 10 | Compress data. 11 | 12 | === Arguments 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |string |Data 17 | |=== 18 | 19 | === Returns 20 | [options="header",width="72%"] 21 | |=== 22 | |Type |Description 23 | |string |Compressed binary data 24 | |=== 25 | 26 | == *lz4.decompress*(_String_) -> _String_ 27 | Decompress lz4 data. 28 | 29 | === Arguments 30 | [options="header",width="72%"] 31 | |=== 32 | |Type |Description 33 | |string |Compressed 34 | |=== 35 | 36 | === Returns 37 | [options="header",width="72%"] 38 | |=== 39 | |Type |Description 40 | |string |Decompressed data 41 | |=== 42 | -------------------------------------------------------------------------------- /docs/map.adoc: -------------------------------------------------------------------------------- 1 | = map 2 | :toc: 3 | :toc-placement!: 4 | 5 | Bidirectional map implementation. 6 | 7 | toc::[] 8 | 9 | == *map.new*() -> _Table_, _Table_ 10 | Create a new map. 11 | 12 | === Returns 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |table |Array side 17 | |table |Map side 18 | |=== 19 | -------------------------------------------------------------------------------- /docs/mysql.adoc: -------------------------------------------------------------------------------- 1 | = mysql 2 | :toc: 3 | :toc-placement!: 4 | 5 | Access MySQL or MariaDB databases. 6 | 7 | toc::[] 8 | 9 | == *mysql.escape*(_String_) -> _String_ 10 | Escape a query. 11 | 12 | == *mysql.new* 13 | Initialize mysql instance. 14 | 15 | === Returns 16 | [options="header",width="72%"] 17 | |=== 18 | |Type |Description 19 | |object |Instance of mysql that you can index into 20 | |=== 21 | 22 | == *close* 23 | Close mysql instance. 24 | 25 | == *set_timeout*(_Number_) 26 | Set timeout. 27 | 28 | === Arguments 29 | [options="header",width="72%"] 30 | |=== 31 | |Type |Description 32 | |number |Timeout in ms 33 | |=== 34 | 35 | == *set_keepalive*(_Number_, _Number_) 36 | 37 | === Arguments 38 | [options="header",width="72%"] 39 | |=== 40 | |Type |Description 41 | |number |Timeout in ms 42 | |number |Max idle connections(poolSize) 43 | |=== 44 | 45 | == *connect*(_Table_) 46 | 47 | === Arguments 48 | [options="header",width="72%"] 49 | |=== 50 | |Type |Description 51 | |table |See map below 52 | |=== 53 | 54 | === Map 55 | [options="header",width="72%"] 56 | |=== 57 | |host | 58 | |port | 59 | |database | 60 | |user | 61 | |password | 62 | |=== 63 | 64 | == *query*(_String_[, ...]) -> _Table_ 65 | 66 | === Arguments 67 | [options="header",width="72%"] 68 | |=== 69 | |Type |Description 70 | |string |SQL query 71 | |=== 72 | 73 | === Returns 74 | [options="header",width="72%"] 75 | |=== 76 | |Type |Description 77 | |table |Query results, empty table if no results 78 | |=== 79 | -------------------------------------------------------------------------------- /docs/os.adoc: -------------------------------------------------------------------------------- 1 | = os 2 | :toc: 3 | :toc-placement!: 4 | 5 | Extensions to the `os` namespace. 6 | 7 | toc::[] 8 | 9 | == *os.hostname*() -> _String_ 10 | Get current hostname. 11 | 12 | === Returns 13 | [width="72%"] 14 | |=== 15 | |string |Hostname 16 | |=== 17 | 18 | == *os.outbound_ip*() -> _String_ 19 | Get IP used for outbound connections. 20 | 21 | === Returns 22 | [width="72%"] 23 | |=== 24 | |string |IP 25 | |=== 26 | 27 | == *os.sleep(_Number_) -> _Boolean_ 28 | Sleep for a number of milliseconds. 29 | 30 | === Arguments 31 | [width="72%"] 32 | |=== 33 | |number |Milliseconds 34 | |=== 35 | 36 | === Returns 37 | [width="72%"] 38 | |=== 39 | |boolean |`true` 40 | |=== 41 | 42 | == *os.setenv*(_String_, _String_) -> _Boolean_ 43 | Set environment variable. 44 | 45 | === Arguments 46 | [width="72%"] 47 | |=== 48 | |string |Variable 49 | |string |Value 50 | |=== 51 | 52 | === Returns 53 | [width="72%"] 54 | |=== 55 | |boolean |`true` if successful 56 | |=== 57 | -------------------------------------------------------------------------------- /docs/pushover.adoc: -------------------------------------------------------------------------------- 1 | = pushover 2 | :toc: 3 | :toc-placement!: 4 | 5 | Send Pushover messages via the API. 6 | 7 | toc::[] 8 | 9 | == *pushover.new*() 10 | 11 | Initialize object to access methods below. Requires a valid token in the environment variable `PUSHOVER_TOKEN`. 12 | 13 | === Returns 14 | [options="header",width="72%"] 15 | |=== 16 | |Type |Description 17 | |userdata| Userdata with methods below 18 | |=== 19 | 20 | == *:message*(_String_, _String_) 21 | 22 | Send a message to device. 23 | 24 | === Arguments 25 | [options="header",width="72%"] 26 | |=== 27 | |Type |Description 28 | |string| Device ID 29 | |string| message 30 | |=== 31 | 32 | === Returns 33 | [options="header",width="72%"] 34 | |=== 35 | |Type |Description 36 | |string| Response from API 37 | |=== 38 | -------------------------------------------------------------------------------- /docs/refmt.adoc: -------------------------------------------------------------------------------- 1 | = refmt 2 | :toc: 3 | :toc-placement!: 4 | 5 | Convert between JSON and YAML. 6 | 7 | toc::[] 8 | 9 | == *refmt.json_to_yaml*(_String_) -> _String_ 10 | Convert JSON to YAML. 11 | 12 | === Arguments 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |string |JSON 17 | |=== 18 | 19 | === Returns 20 | [options="header",width="72%"] 21 | |=== 22 | |Type |Description 23 | |string |YAML 24 | |=== 25 | 26 | == *refmt.yaml_to_json*(_String_) -> _String_ 27 | Convert YAML to JSON. 28 | 29 | === Arguments 30 | [options="header",width="72%"] 31 | |=== 32 | |Type |Description 33 | |string |YAML 34 | |=== 35 | 36 | === Returns 37 | [options="header",width="72%"] 38 | |=== 39 | |Type |Description 40 | |string |JSON 41 | |=== 42 | 43 | == *refmt.toml_to_json*(_String_) -> _String_ 44 | Convert TOML to JSON. 45 | 46 | === Arguments 47 | [options="header",width="72%"] 48 | |=== 49 | |Type |Description 50 | |string |TOML 51 | |=== 52 | 53 | === Returns 54 | [options="header",width="72%"] 55 | |=== 56 | |Type |Description 57 | |string |JSON 58 | |=== 59 | 60 | == *refmt.json_to_toml*(_String_) -> _String_ 61 | Convert JSON to TOML. 62 | 63 | === Arguments 64 | [options="header",width="72%"] 65 | |=== 66 | |Type |Description 67 | |string |JSON 68 | |=== 69 | 70 | === Returns 71 | [options="header",width="72%"] 72 | |=== 73 | |Type |Description 74 | |string |TOML 75 | |=== 76 | -------------------------------------------------------------------------------- /docs/slack.adoc: -------------------------------------------------------------------------------- 1 | = slack 2 | :toc: 3 | :toc-placement!: 4 | 5 | Wrapper for the Slack API. 6 | 7 | toc::[] 8 | 9 | == *slack.message*(_String_) -> _Boolean_ 10 | Send webhook text. Requires the string after the `https://hooks.slack.com/services/` URL in the `SLACK_WEBHOOK` environment variable. 11 | 12 | === Arguments 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |string |Message 17 | |=== 18 | 19 | === Returns 20 | [options="header",width="72%"] 21 | |=== 22 | |Type |Description 23 | |boolean| `true` if successful 24 | |=== 25 | == *slack.attachment*(_Table_) -> _Boolean_ 26 | Send webhook attachment containing values from the argument. Requires the string after the `https://hooks.slack.com/services/` URL in the `SLACK_WEBHOOK` environment variable. 27 | 28 | === Arguments 29 | [options="header",width="72%"] 30 | |=== 31 | |Type |Description 32 | |table |See valid values from https://github.com/slack-go/slack[repo] 33 | |=== 34 | 35 | === Returns 36 | [options="header",width="72%"] 37 | |=== 38 | |Type |Description 39 | |boolean| `true` if successful 40 | |=== 41 | -------------------------------------------------------------------------------- /docs/ssh_config.adoc: -------------------------------------------------------------------------------- 1 | = ssh_config 2 | :toc: 3 | :toc-placement!: 4 | 5 | Get values from ~/.ssh/config 6 | 7 | toc::[] 8 | 9 | == *ssh_config.port*(_String_) -> _String_ 10 | Get configured Port for Host. 11 | 12 | === Arguments 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |string |Host 17 | |=== 18 | 19 | === Returns 20 | [options="header",width="72%"] 21 | |=== 22 | |Type |Description 23 | |string |Port 24 | |=== 25 | 26 | == *ssh_config.hostname*(_String_) -> _String_ 27 | Get configured Hostname for Host. 28 | 29 | === Arguments 30 | [options="header",width="72%"] 31 | |=== 32 | |Type |Description 33 | |string |Host 34 | |=== 35 | 36 | === Returns 37 | [options="header",width="72%"] 38 | |=== 39 | |Type |Description 40 | |string |Hostname 41 | |=== 42 | 43 | == *ssh_config.identity_file*(_String_) -> _String_ 44 | Get configured IdentityFile for Host. 45 | 46 | === Arguments 47 | [options="header",width="72%"] 48 | |=== 49 | |Type |Description 50 | |string |Host 51 | |=== 52 | 53 | === Returns 54 | [options="header",width="72%"] 55 | |=== 56 | |Type |Description 57 | |string |Path of key 58 | |=== 59 | 60 | == *ssh_config.hosts*() -> _Table_ 61 | Get all hosts configured in ~/.ssh/config 62 | 63 | === Returns 64 | [options="header",width="72%"] 65 | |=== 66 | |Type |Description 67 | |table|List of hosts 68 | |=== 69 | -------------------------------------------------------------------------------- /docs/telegram.adoc: -------------------------------------------------------------------------------- 1 | = telegram 2 | :toc: 3 | :toc-placement!: 4 | 5 | Send Telegram messages via the Bot API. 6 | 7 | toc::[] 8 | 9 | == *telegram.new*() 10 | 11 | Initialize object to access methods below. Requires a valid BOT token in the environment variable `TELEGRAM_TOKEN`. 12 | 13 | === Returns 14 | [options="header",width="72%"] 15 | |=== 16 | |Type |Description 17 | |userdata| Userdata containing methods below 18 | |=== 19 | 20 | == *:channel*(_String_, _String_) 21 | 22 | Post a channel message. 23 | 24 | === Arguments 25 | [options="header",width="72%"] 26 | |=== 27 | |Type |Description 28 | |string| channel id e.g. "-12312313" 29 | |string| message 30 | |=== 31 | 32 | === Returns 33 | [options="header",width="72%"] 34 | |=== 35 | |Type |Description 36 | |boolean| true 37 | |=== 38 | 39 | 40 | == *:message*(_String_, _String_) 41 | 42 | Send a message to Telegram user. 43 | 44 | === Arguments 45 | [options="header",width="72%"] 46 | |=== 47 | |Type |Description 48 | |number| user id e.g. 9348484 49 | |string| message 50 | |=== 51 | 52 | === Returns 53 | [options="header",width="72%"] 54 | |=== 55 | |Type |Description 56 | |boolean| true 57 | |=== 58 | -------------------------------------------------------------------------------- /docs/template.adoc: -------------------------------------------------------------------------------- 1 | = template 2 | :toc: 3 | :toc-placement!: 4 | 5 | Embedded Lua templating. This is etlua(github.com/leafo/etlua). + 6 | 7 | Not in the global namespace. Load with `require('template')`. 8 | 9 | toc::[] 10 | 11 | == *template.compile*(_String_) -> _Function_ 12 | Compiles the template into a function, the returned function can be called to render the template. The function takes one argument: a table to use as the environment within the template. `_G` is used to look up a variable if it can't be found in the environment. 13 | 14 | === Arguments 15 | [width="72%"] 16 | |=== 17 | |string| String to compile 18 | |=== 19 | 20 | === Returns 21 | [width="72%"] 22 | |=== 23 | |function| (_Table_) -> _String_ 24 | |=== 25 | 26 | == *template.render*(_String_, _Table_) -> _String_ 27 | Compiles and renders the template in a single call. If you are concerned about high performance this should be avoided in favor of `compile` if it's possible to cache the compiled template. 28 | 29 | === Arguments 30 | [width="72%"] 31 | |=== 32 | |string| String to compile 33 | |=== 34 | 35 | === Returns 36 | [width="72%"] 37 | |=== 38 | |function| (_Table_) -> _String_ 39 | |=== 40 | 41 | :note-caption: :information_source: 42 | [NOTE] 43 | ==== 44 | Documentation and tests from etlua project page. 45 | Check github.com/leafo/etlua for information on the `Parser` raw API. 46 | ==== 47 | -------------------------------------------------------------------------------- /docs/tuple.adoc: -------------------------------------------------------------------------------- 1 | = tuple 2 | :toc: 3 | :toc-placement!: 4 | 5 | Implementation of ordered n-tuples. 6 | 7 | . Fixed sized 8 | . May contain `nil` 9 | . Values can be changed for existing keys 10 | . Tables passed are copied 11 | . Maximum of 199 values 12 | 13 | toc::[] 14 | 15 | == *tuple([...])* -> _Table_ 16 | Create a new tuple. 17 | 18 | === Returns 19 | [options="header",width="72%"] 20 | |=== 21 | |Type |Description 22 | |table |Tuple 23 | |=== 24 | 25 | == *:iterator()* 26 | Iterator function to traverse a tuple. Returns a count and a value at each step of iteration, until the end of the tuple is reached. 27 | 28 | Use this instead of `ipairs` since the tuple may contain `nil`. 29 | 30 | == *:includes(_Table_)* -> _Boolean_ 31 | Returns `true` if the tuple argument is included in the tuple, i.e when all elements found in tuple argument were found in the original tuple. Otherwise, returns `false`. 32 | 33 | === Arguments 34 | [options="header",width="72%"] 35 | |=== 36 | |Type |Description 37 | |table |Tuple 38 | |=== 39 | 40 | === Returns 41 | [options="header",width="72%"] 42 | |=== 43 | |Type |Description 44 | |boolean |Result 45 | |=== 46 | 47 | == *:has(_Table_)* -> _Boolean_ 48 | Returns `true` when the given value was found in the tuple. Otherwhise, returns `false`. 49 | 50 | === Arguments 51 | [options="header",width="72%"] 52 | |=== 53 | |Type |Description 54 | |value |Any valid value for comparison 55 | |=== 56 | 57 | === Returns 58 | [options="header",width="72%"] 59 | |=== 60 | |Type |Description 61 | |boolean |Result 62 | |=== 63 | 64 | == *:size(_Table_)* -> _Number_ 65 | Returns the size (the count of values) of the tuple. 66 | 67 | === Returns 68 | [options="header",width="72%"] 69 | |=== 70 | |Type |Description 71 | |number |Size of tuple 72 | |=== 73 | 74 | == *:contents(_Table_)* -> _Table_ 75 | Converts the tuple contents to a read-only array. 76 | 77 | [NOTE] 78 | ==== 79 | Lower level tables can be modified. 80 | ==== 81 | 82 | === Arguments 83 | [options="header",width="72%"] 84 | |=== 85 | |Type |Description 86 | |table |Tuple 87 | |=== 88 | 89 | === Returns 90 | [options="header",width="72%"] 91 | |=== 92 | |Type |Description 93 | |table |New table 94 | |=== 95 | -------------------------------------------------------------------------------- /docs/uid.adoc: -------------------------------------------------------------------------------- 1 | = uid 2 | :toc: 3 | :toc-placement!: 4 | 5 | Generate unique IDs. This is a https://github.com/segmentio/ksuid[ksuid] wrapper. 6 | 7 | toc::[] 8 | 9 | == *uid.new*() -> _String_ 10 | Generate an ID. 11 | 12 | === Returns 13 | [options="header",width="72%"] 14 | |=== 15 | |Type |Description 16 | |string |ksuid string 17 | |=== 18 | -------------------------------------------------------------------------------- /docs/ulid.adoc: -------------------------------------------------------------------------------- 1 | = ulid 2 | :toc: 3 | :toc-placement!: 4 | 5 | Generate random strings in ULID format. 6 | This is a https://github.com/oklog/ulid/[ulid] wrapper. 7 | 8 | toc::[] 9 | 10 | == *ulid.new*() -> _String_ 11 | Generate an ID. 12 | 13 | === Returns 14 | [options="header",width="72%"] 15 | |=== 16 | |Type |Description 17 | |string |ULID string 18 | |=== 19 | -------------------------------------------------------------------------------- /docs/uuid.adoc: -------------------------------------------------------------------------------- 1 | = uuid 2 | :toc: 3 | :toc-placement!: 4 | 5 | Generate random strings in UUID format. 6 | This is a https://github.com/hashicorp/go-uuid[go-uuid] wrapper. 7 | 8 | toc::[] 9 | 10 | == *uuid.new*() -> _String_ 11 | Generate an ID. 12 | 13 | === Returns 14 | [options="header",width="72%"] 15 | |=== 16 | |Type |Description 17 | |string |uuid string 18 | |=== 19 | -------------------------------------------------------------------------------- /dsl.go: -------------------------------------------------------------------------------- 1 | // +build dsl 2 | package ll 3 | 4 | import ( 5 | "embed" 6 | "fmt" 7 | 8 | "github.com/yuin/gopher-lua" 9 | ) 10 | 11 | //go:embed internal/dsl/* 12 | var dslSrc embed.FS 13 | 14 | func DslLoader(L *lua.LState, mod string) { 15 | src, _ := dslSrc.ReadFile(fmt.Sprintf("internal/dsl/%s.lua", mod)) 16 | fn, _ := L.LoadString(string(src)) 17 | L.Push(fn) 18 | L.Call(0, 0) 19 | } 20 | -------------------------------------------------------------------------------- /external/gluacrypto/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/base64.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "encoding/base64" 5 | 6 | lua "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func base64EncodeFn(L *lua.LState) int { 10 | s := lua.LVAsString(L.Get(1)) 11 | result := base64.StdEncoding.EncodeToString([]byte(s)) 12 | L.Push(lua.LString(result)) 13 | return 1 14 | } 15 | 16 | func base64DecodeFn(L *lua.LState) int { 17 | s := lua.LVAsString(L.Get(1)) 18 | result, err := base64.StdEncoding.DecodeString(s) 19 | if err != nil { 20 | L.Push(lua.LNil) 21 | L.Push(lua.LString(err.Error())) 22 | return 2 23 | } 24 | L.Push(lua.LString(result)) 25 | return 1 26 | } 27 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/ch.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "strconv" 7 | "strings" 8 | "unicode/utf8" 9 | 10 | "github.com/yuin/gopher-lua" 11 | ) 12 | 13 | // xor 14 | // xor two numbers 15 | // (number, number) -> (number) 16 | func xorFn(L *lua.LState) int { 17 | i := L.CheckNumber(1) 18 | x := L.CheckNumber(2) 19 | L.Push(lua.LNumber(uint8(i) ^ uint8(x))) 20 | return 1 21 | } 22 | 23 | // htos 24 | // Hexadecimal string to string 25 | // (string) -> string 26 | func hextostrFn(L *lua.LState) int { 27 | h := L.CheckString(1) 28 | decodedByteArray, err := hex.DecodeString(h) 29 | if err != nil { 30 | L.Push(lua.LNil) 31 | L.Push(lua.LString(err.Error())) 32 | return 2 33 | } 34 | L.Push(lua.LString(string(decodedByteArray))) 35 | return 1 36 | } 37 | 38 | // stoh 39 | // String to Hexadecimal string 40 | // (string) -> string 41 | func strtohexFn(L *lua.LState) int { 42 | h := L.CheckString(1) 43 | encodedString := hex.EncodeToString([]byte(h)) 44 | L.Push(lua.LString(encodedString)) 45 | return 1 46 | } 47 | 48 | // htoi 49 | // Hexadecimal string to int 50 | // (string) -> number 51 | func hextointFn(L *lua.LState) int { 52 | h := L.CheckString(1) 53 | value, err := strconv.ParseInt(h, 16, 64) 54 | if err != nil { 55 | L.Push(lua.LNil) 56 | L.Push(lua.LString(err.Error())) 57 | return 2 58 | } 59 | L.Push(lua.LNumber(value)) 60 | return 1 61 | } 62 | 63 | // htot 64 | // Hexadecimal string to int array 65 | // (string) -> table[number] 66 | func hextotblFn(L *lua.LState) int { 67 | h := L.CheckString(1) 68 | decodedByteArray, err := hex.DecodeString(h) 69 | if err != nil { 70 | L.Push(lua.LNil) 71 | L.Push(lua.LString(err.Error())) 72 | return 2 73 | } 74 | t := L.NewTable() 75 | for _, element := range decodedByteArray { 76 | t.Append(lua.LNumber(element)) 77 | } 78 | L.Push(t) 79 | return 1 80 | } 81 | 82 | // hexor 83 | // XOR two hex strings 84 | // (string, string) -> string, table[number] 85 | func hexorFn(L *lua.LState) int { 86 | a := L.CheckString(1) 87 | b := L.CheckString(2) 88 | aa, err := hex.DecodeString(a) 89 | if err != nil { 90 | L.Push(lua.LNil) 91 | L.Push(lua.LString(err.Error())) 92 | return 2 93 | } 94 | ba, err := hex.DecodeString(b) 95 | if err != nil { 96 | L.Push(lua.LNil) 97 | L.Push(lua.LString(err.Error())) 98 | return 2 99 | } 100 | hexa := strings.Builder{} 101 | var ax *[]byte 102 | var bx *[]byte 103 | var sz int 104 | la := len(aa) 105 | lb := len(ba) 106 | switch la > lb { 107 | case true: 108 | ax = &aa 109 | bx = &ba 110 | sz = lb - 1 111 | case false: 112 | ax = &ba 113 | bx = &aa 114 | sz = la - 1 115 | } 116 | t := L.NewTable() 117 | var r int = 0 118 | var v uint8 119 | for _, element := range *ax { 120 | v = element ^ (*bx)[r] 121 | hexa.WriteString(fmt.Sprintf("%02x", v)) 122 | t.Append(lua.LNumber(v)) 123 | if r < sz { 124 | r++ 125 | } else { 126 | r = 0 127 | } 128 | } 129 | L.Push(lua.LString(hexa.String())) 130 | L.Push(t) 131 | return 2 132 | } 133 | 134 | // ord 135 | func ordFn(L *lua.LState) int { 136 | s := L.CheckString(1) 137 | r, _ := utf8.DecodeRuneInString(s) 138 | L.Push(lua.LNumber(r)) 139 | return 1 140 | } 141 | 142 | // char 143 | func charFn(L *lua.LState) int { 144 | L.Push(lua.LString(string(int32(L.CheckNumber(1))))) 145 | return 1 146 | } 147 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/crc32.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "encoding/hex" 5 | "hash/crc32" 6 | 7 | lua "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func crc32Fn(L *lua.LState) int { 11 | h := crc32.NewIEEE() 12 | s := lua.LVAsString(L.Get(1)) 13 | raw := lua.LVAsBool(L.Get(2)) 14 | _, err := h.Write([]byte(s)) 15 | if err != nil { 16 | L.Push(lua.LNil) 17 | L.Push(lua.LString(err.Error())) 18 | return 2 19 | } 20 | 21 | var result string 22 | if !raw { 23 | result = hex.EncodeToString(h.Sum(nil)) 24 | } else { 25 | result = string(h.Sum(nil)) 26 | } 27 | L.Push(lua.LString(result)) 28 | return 1 29 | } 30 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/crypto.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | lua "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | var exports = map[string]lua.LGFunction{ 8 | "base64_encode": base64EncodeFn, 9 | "base64_decode": base64DecodeFn, 10 | "crc32": crc32Fn, 11 | "md5": md5Fn, 12 | "sha1": sha1Fn, 13 | "sha256": sha256Fn, 14 | "sha512": sha512Fn, 15 | "hmac": hmacFn, 16 | "valid_hmac": validHmacFn, 17 | "encrypt": encryptFn, 18 | "decrypt": decryptFn, 19 | "random": randomFn, 20 | "fast_random": fastRandomFn, 21 | "xor": xorFn, 22 | "stoh": strtohexFn, 23 | "htos": hextostrFn, 24 | "htoi": hextointFn, 25 | "htot": hextotblFn, 26 | "hexor": hexorFn, 27 | "ord": ordFn, 28 | "char": charFn, 29 | } 30 | 31 | func Loader(L *lua.LState) int { 32 | mod := L.SetFuncs(L.NewTable(), exports) 33 | L.Push(mod) 34 | 35 | L.SetField(mod, "_DEBUG", lua.LBool(false)) 36 | L.SetField(mod, "_VERSION", lua.LString("0.0.0")) 37 | 38 | // consts 39 | L.SetField(mod, "RAW_DATA", lua.LNumber(1)) 40 | L.SetField(mod, "ZERO_PADDING", lua.LNumber(2)) 41 | 42 | return 1 43 | } 44 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/decrypt.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/des" 7 | "encoding/hex" 8 | "errors" 9 | 10 | lua "github.com/yuin/gopher-lua" 11 | ) 12 | 13 | // PKCS5Unpadding unpad data 14 | func PKCS5Unpadding(origData []byte) []byte { 15 | length := len(origData) 16 | unpadding := int(origData[length-1]) 17 | return origData[:(length - unpadding)] 18 | } 19 | 20 | // Decrypt data by specified method: `des-ecb`, `des-cbc`, `aes-cbc` 21 | func Decrypt(data []byte, method string, key, iv []byte) ([]byte, error) { 22 | var out []byte 23 | switch method { 24 | case "des-ecb": 25 | block, err := des.NewCipher([]byte(key)) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | bs := block.BlockSize() 31 | if len(data)%bs != 0 { 32 | return nil, errors.New("crypto/cipher: input not full blocks") 33 | } 34 | 35 | out = make([]byte, len(data)) 36 | dst := out 37 | for len(data) > 0 { 38 | block.Decrypt(dst, data[:bs]) 39 | data = data[bs:] 40 | dst = dst[bs:] 41 | } 42 | out = PKCS5Unpadding(out) 43 | case "des-cbc": 44 | block, err := des.NewCipher([]byte(key)) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | // CBC mode always works in whole blocks. 50 | if len(data)%block.BlockSize() != 0 { 51 | return nil, ErrCiphertextNotMultipleBlockSize 52 | } 53 | 54 | mode := cipher.NewCBCDecrypter(block, []byte(iv)) 55 | plaintext := make([]byte, len(data)) 56 | mode.CryptBlocks(plaintext, data) 57 | out = PKCS5Unpadding(plaintext) 58 | case "aes-cbc": 59 | block, err := aes.NewCipher([]byte(key)) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | // CBC mode always works in whole blocks. 65 | if len(data)%block.BlockSize() != 0 { 66 | return nil, ErrCiphertextNotMultipleBlockSize 67 | } 68 | 69 | mode := cipher.NewCBCDecrypter(block, []byte(iv)) 70 | plaintext := make([]byte, len(data)) 71 | mode.CryptBlocks(plaintext, data) 72 | out = PKCS5Unpadding(plaintext) 73 | default: 74 | return nil, ErrNotSupport 75 | } 76 | return out, nil 77 | } 78 | 79 | func decryptFn(L *lua.LState) int { 80 | s := lua.LVAsString(L.Get(1)) 81 | method := lua.LVAsString(L.Get(2)) 82 | key := lua.LVAsString(L.Get(3)) 83 | options := L.ToInt(4) 84 | iv := lua.LVAsString(L.Get(5)) 85 | 86 | var data []byte 87 | var err error 88 | if options&RawData == 0 { 89 | data, err = hex.DecodeString(s) 90 | } else { 91 | data = []byte(s) 92 | } 93 | if err != nil { 94 | L.Push(lua.LNil) 95 | L.Push(lua.LString(err.Error())) 96 | return 2 97 | } 98 | 99 | out, err := Decrypt(data, method, []byte(key), []byte(iv)) 100 | if err != nil { 101 | L.Push(lua.LNil) 102 | L.Push(lua.LString(err.Error())) 103 | return 2 104 | } 105 | 106 | L.Push(lua.LString(out)) 107 | return 1 108 | } 109 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/encrypt.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/des" 8 | "encoding/hex" 9 | "errors" 10 | 11 | lua "github.com/yuin/gopher-lua" 12 | ) 13 | 14 | // options const 15 | const ( 16 | RawData = 1 << iota 17 | // not implement 18 | // ZeroPadding = 1 << iota 19 | ) 20 | 21 | // errors 22 | var ( 23 | ErrNotSupport = errors.New("unsupported encrypt method") 24 | ErrCiphertextNotMultipleBlockSize = errors.New("ciphertext is not a multiple of the block size") 25 | ) 26 | 27 | // PKCS5Padding pad data 28 | func PKCS5Padding(ciphertext []byte, blockSize int) []byte { 29 | padding := blockSize - len(ciphertext)%blockSize 30 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 31 | return append(ciphertext, padtext...) 32 | } 33 | 34 | // Encrypt data by specified method: `des-ecb`, `des-cbc`, `aes-cbc` 35 | func Encrypt(data []byte, method string, key, iv []byte) ([]byte, error) { 36 | var out []byte 37 | switch method { 38 | case "des-ecb": 39 | block, err := des.NewCipher(key) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | bs := block.BlockSize() 45 | data := PKCS5Padding(data, bs) 46 | out = make([]byte, len(data)) 47 | 48 | dst := out 49 | for len(data) > 0 { 50 | // The message is divided into blocks, 51 | // and each block is encrypted separately. 52 | block.Encrypt(dst, data[:bs]) 53 | data = data[bs:] 54 | dst = dst[bs:] 55 | } 56 | case "des-cbc": 57 | block, err := des.NewCipher(key) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | data := PKCS5Padding(data, block.BlockSize()) 63 | mode := cipher.NewCBCEncrypter(block, iv) 64 | out = make([]byte, len(data)) 65 | mode.CryptBlocks(out, data) 66 | case "aes-cbc": 67 | block, err := aes.NewCipher(key) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | data := PKCS5Padding(data, block.BlockSize()) 73 | mode := cipher.NewCBCEncrypter(block, iv) 74 | out = make([]byte, len(data)) 75 | mode.CryptBlocks(out, data) 76 | default: 77 | return nil, ErrNotSupport 78 | } 79 | return out, nil 80 | } 81 | 82 | func encryptFn(L *lua.LState) int { 83 | s := lua.LVAsString(L.Get(1)) 84 | method := lua.LVAsString(L.Get(2)) 85 | key := lua.LVAsString(L.Get(3)) 86 | options := L.ToInt(4) 87 | iv := lua.LVAsString(L.Get(5)) 88 | 89 | out, err := Encrypt([]byte(s), method, []byte(key), []byte(iv)) 90 | if err != nil { 91 | L.Push(lua.LNil) 92 | L.Push(lua.LString(err.Error())) 93 | return 2 94 | } 95 | 96 | var result string 97 | if options&RawData == 0 { 98 | result = hex.EncodeToString(out) 99 | } else { 100 | result = string(out) 101 | } 102 | L.Push(lua.LString(result)) 103 | return 1 104 | } 105 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/hmac.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/md5" 6 | "crypto/sha1" 7 | "crypto/sha256" 8 | "crypto/sha512" 9 | "encoding/hex" 10 | "hash" 11 | 12 | lua "github.com/yuin/gopher-lua" 13 | ) 14 | 15 | func hmacFn(L *lua.LState) int { 16 | algorithm := lua.LVAsString(L.Get(1)) 17 | s := lua.LVAsString(L.Get(2)) 18 | key := lua.LVAsString(L.Get(3)) 19 | raw := lua.LVAsBool(L.Get(4)) 20 | 21 | var h hash.Hash 22 | switch algorithm { 23 | case "md5": 24 | h = hmac.New(md5.New, []byte(key)) 25 | case "sha1": 26 | h = hmac.New(sha1.New, []byte(key)) 27 | case "sha256": 28 | h = hmac.New(sha256.New, []byte(key)) 29 | case "sha512": 30 | h = hmac.New(sha512.New, []byte(key)) 31 | default: 32 | L.Push(lua.LNil) 33 | L.Push(lua.LString("unsupported algorithm")) 34 | return 2 35 | } 36 | 37 | _, err := h.Write([]byte(s)) 38 | if err != nil { 39 | L.Push(lua.LNil) 40 | L.Push(lua.LString(err.Error())) 41 | return 2 42 | } 43 | 44 | var result string 45 | if !raw { 46 | result = hex.EncodeToString(h.Sum(nil)) 47 | } else { 48 | result = string(h.Sum(nil)) 49 | } 50 | L.Push(lua.LString(result)) 51 | return 1 52 | } 53 | 54 | func validHmacFn(L *lua.LState) int { 55 | algorithm := L.CheckString(1) 56 | message := L.CheckString(2) 57 | key := L.CheckString(3) 58 | mmac := L.CheckString(4) 59 | var mac hash.Hash 60 | switch algorithm { 61 | case "md5": 62 | mac = hmac.New(md5.New, []byte(key)) 63 | case "sha1": 64 | mac = hmac.New(sha1.New, []byte(key)) 65 | case "sha256": 66 | mac = hmac.New(sha256.New, []byte(key)) 67 | case "sha512": 68 | mac = hmac.New(sha512.New, []byte(key)) 69 | default: 70 | L.Push(lua.LNil) 71 | L.Push(lua.LString("unsupported algorithm")) 72 | return 2 73 | } 74 | mac.Write([]byte(message)) 75 | expectedMAC := mac.Sum(nil) 76 | if hmac.Equal([]byte(mmac), expectedMAC) { 77 | L.Push(lua.LTrue) 78 | } else { 79 | L.Push(lua.LFalse) 80 | } 81 | return 1 82 | } 83 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/md5.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | 7 | lua "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func md5Fn(L *lua.LState) int { 11 | h := md5.New() 12 | s := lua.LVAsString(L.Get(1)) 13 | raw := lua.LVAsBool(L.Get(2)) 14 | _, err := h.Write([]byte(s)) 15 | if err != nil { 16 | L.Push(lua.LNil) 17 | L.Push(lua.LString(err.Error())) 18 | return 2 19 | } 20 | 21 | var result string 22 | if !raw { 23 | result = hex.EncodeToString(h.Sum(nil)) 24 | } else { 25 | result = string(h.Sum(nil)) 26 | } 27 | L.Push(lua.LString(result)) 28 | return 1 29 | } 30 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/random.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "hash/maphash" 7 | "io" 8 | 9 | lua "github.com/yuin/gopher-lua" 10 | ) 11 | 12 | func randomFn(L *lua.LState) int { 13 | size := int(L.OptNumber(1, 8)) 14 | buf := make([]byte, size) 15 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 16 | L.Push(lua.LNil) 17 | L.Push(lua.LString("Failed to read bytes.")) 18 | return 2 19 | } 20 | L.Push(lua.LString(fmt.Sprintf("%016x", buf[0:size]))) 21 | return 1 22 | } 23 | 24 | func fastRandomFn(L *lua.LState) int { 25 | h := new(maphash.Hash) 26 | L.Push(lua.LString(fmt.Sprintf("%016X", h.Sum64()))) 27 | return 1 28 | } 29 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/sha1.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | 7 | lua "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func sha1Fn(L *lua.LState) int { 11 | h := sha1.New() 12 | s := lua.LVAsString(L.Get(1)) 13 | raw := lua.LVAsBool(L.Get(2)) 14 | _, err := h.Write([]byte(s)) 15 | if err != nil { 16 | L.Push(lua.LNil) 17 | L.Push(lua.LString(err.Error())) 18 | return 2 19 | } 20 | 21 | var result string 22 | if !raw { 23 | result = hex.EncodeToString(h.Sum(nil)) 24 | } else { 25 | result = string(h.Sum(nil)) 26 | } 27 | L.Push(lua.LString(result)) 28 | return 1 29 | } 30 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/sha256.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | 7 | lua "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func sha256Fn(L *lua.LState) int { 11 | h := sha256.New() 12 | s := lua.LVAsString(L.Get(1)) 13 | raw := lua.LVAsBool(L.Get(2)) 14 | _, err := h.Write([]byte(s)) 15 | if err != nil { 16 | L.Push(lua.LNil) 17 | L.Push(lua.LString(err.Error())) 18 | return 2 19 | } 20 | 21 | var result string 22 | if !raw { 23 | result = hex.EncodeToString(h.Sum(nil)) 24 | } else { 25 | result = string(h.Sum(nil)) 26 | } 27 | L.Push(lua.LString(result)) 28 | return 1 29 | } 30 | -------------------------------------------------------------------------------- /external/gluacrypto/crypto/sha512.go: -------------------------------------------------------------------------------- 1 | package gluacrypto_crypto 2 | 3 | import ( 4 | "crypto/sha512" 5 | "encoding/hex" 6 | 7 | lua "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func sha512Fn(L *lua.LState) int { 11 | h := sha512.New() 12 | s := lua.LVAsString(L.Get(1)) 13 | raw := lua.LVAsBool(L.Get(2)) 14 | _, err := h.Write([]byte(s)) 15 | if err != nil { 16 | L.Push(lua.LNil) 17 | L.Push(lua.LString(err.Error())) 18 | return 2 19 | } 20 | 21 | var result string 22 | if !raw { 23 | result = hex.EncodeToString(h.Sum(nil)) 24 | } else { 25 | result = string(h.Sum(nil)) 26 | } 27 | L.Push(lua.LString(result)) 28 | return 1 29 | } 30 | -------------------------------------------------------------------------------- /external/gluahttp/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Christian Joudrey 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 | 23 | -------------------------------------------------------------------------------- /external/gluahttp/httpresponsetype.go: -------------------------------------------------------------------------------- 1 | package gluahttp 2 | 3 | import "github.com/yuin/gopher-lua" 4 | import "net/http" 5 | 6 | const luaHttpResponseTypeName = "http.response" 7 | 8 | type luaHttpResponse struct { 9 | res *http.Response 10 | body lua.LString 11 | bodySize int 12 | } 13 | 14 | func registerHttpResponseType(module *lua.LTable, L *lua.LState) { 15 | mt := L.NewTypeMetatable(luaHttpResponseTypeName) 16 | L.SetField(mt, "__index", L.NewFunction(httpResponseIndex)) 17 | 18 | L.SetField(module, "response", mt) 19 | } 20 | 21 | func newHttpResponse(res *http.Response, body *[]byte, bodySize int, L *lua.LState) *lua.LUserData { 22 | ud := L.NewUserData() 23 | ud.Value = &luaHttpResponse{ 24 | res: res, 25 | body: lua.LString(*body), 26 | bodySize: bodySize, 27 | } 28 | L.SetMetatable(ud, L.GetTypeMetatable(luaHttpResponseTypeName)) 29 | return ud 30 | } 31 | 32 | func checkHttpResponse(L *lua.LState) *luaHttpResponse { 33 | ud := L.CheckUserData(1) 34 | if v, ok := ud.Value.(*luaHttpResponse); ok { 35 | return v 36 | } 37 | L.ArgError(1, "http.response expected") 38 | return nil 39 | } 40 | 41 | func httpResponseIndex(L *lua.LState) int { 42 | res := checkHttpResponse(L) 43 | 44 | switch L.CheckString(2) { 45 | case "headers": 46 | return httpResponseHeaders(res, L) 47 | case "cookies": 48 | return httpResponseCookies(res, L) 49 | case "status_code": 50 | return httpResponseStatusCode(res, L) 51 | case "url": 52 | return httpResponseUrl(res, L) 53 | case "body": 54 | return httpResponseBody(res, L) 55 | case "body_size": 56 | return httpResponseBodySize(res, L) 57 | } 58 | 59 | return 0 60 | } 61 | 62 | func httpResponseHeaders(res *luaHttpResponse, L *lua.LState) int { 63 | headers := L.NewTable() 64 | for key, _ := range res.res.Header { 65 | headers.RawSetString(key, lua.LString(res.res.Header.Get(key))) 66 | } 67 | L.Push(headers) 68 | return 1 69 | } 70 | 71 | func httpResponseCookies(res *luaHttpResponse, L *lua.LState) int { 72 | cookies := L.NewTable() 73 | for _, cookie := range res.res.Cookies() { 74 | cookies.RawSetString(cookie.Name, lua.LString(cookie.Value)) 75 | } 76 | L.Push(cookies) 77 | return 1 78 | } 79 | 80 | func httpResponseStatusCode(res *luaHttpResponse, L *lua.LState) int { 81 | L.Push(lua.LNumber(res.res.StatusCode)) 82 | return 1 83 | } 84 | 85 | func httpResponseUrl(res *luaHttpResponse, L *lua.LState) int { 86 | L.Push(lua.LString(res.res.Request.URL.String())) 87 | return 1 88 | } 89 | 90 | func httpResponseBody(res *luaHttpResponse, L *lua.LState) int { 91 | L.Push(res.body) 92 | return 1 93 | } 94 | 95 | func httpResponseBodySize(res *luaHttpResponse, L *lua.LState) int { 96 | L.Push(lua.LNumber(res.bodySize)) 97 | return 1 98 | } 99 | -------------------------------------------------------------------------------- /external/gluasocket/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2019 Nubix https://nubix.io 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 | -------------------------------------------------------------------------------- /external/gluasocket/gluasocket.go: -------------------------------------------------------------------------------- 1 | package gluasocket 2 | 3 | import ( 4 | "github.com/tongson/LadyLua/external/gluasocket/socket" 5 | "github.com/tongson/LadyLua/external/gluasocket/socketcore" 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func Preload(L *lua.LState) { 10 | L.PreloadModule("socket", gluasocket_socket.Loader) 11 | L.PreloadModule("socket.core", gluasocket_socketcore.Loader) 12 | } 13 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/client.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "bufio" 5 | "net" 6 | "time" 7 | 8 | "github.com/yuin/gopher-lua" 9 | ) 10 | 11 | const ( 12 | CLIENT_TYPENAME = "tcp{client}" 13 | ) 14 | 15 | type Client struct { 16 | Conn net.Conn 17 | Timeout time.Duration 18 | Reader *bufio.Reader 19 | } 20 | 21 | var clientMethods = map[string]lua.LGFunction{ 22 | "close": clientCloseMethod, 23 | "dirty": clientDirtyMethod, 24 | "getfd": clientGetFdMethod, 25 | "getpeername": clientGetPeerNameMethod, 26 | "getsockname": clientGetSockNameMethod, 27 | "getstats": clientGetStatsMethod, 28 | "receive": clientReceiveMethod, 29 | "settimeout": clientSetTimeoutMethod, 30 | "send": clientSendMethod, 31 | "setoption": clientSetOptionMethod, 32 | "setstats": clientSetStatsMethod, 33 | "shutdown": clientShutdownMethod, 34 | } 35 | 36 | // ---------------------------------------------------------------------------- 37 | 38 | func checkClient(L *lua.LState) *Client { 39 | ud := L.CheckUserData(1) 40 | if v, ok := ud.Value.(*Client); ok { 41 | return v 42 | } 43 | L.ArgError(1, "client expected") 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientclose.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func clientCloseMethod(L *lua.LState) int { 8 | client := checkClient(L) 9 | if err := client.Conn.Close(); err != nil { 10 | L.RaiseError(err.Error()) 11 | } 12 | return 0 13 | } 14 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientdirty.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func clientDirtyMethod(L *lua.LState) int { 8 | L.Push(lua.LBool(false)) 9 | return 1 10 | } 11 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientgetfd.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func clientGetFdMethod(L *lua.LState) int { 10 | client := checkClient(L) 11 | if file, err := client.Conn.(*net.TCPConn).File(); err != nil { 12 | L.RaiseError(err.Error()) 13 | return 0 14 | } else { 15 | L.Push(lua.LNumber(file.Fd())) 16 | return 1 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientgetpeername.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func clientGetPeerNameMethod(L *lua.LState) int { 8 | L.RaiseError("client:getpeername() not implemented yet") 9 | return 0 10 | } 11 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientgetsockname.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func clientGetSockNameMethod(L *lua.LState) int { 8 | L.RaiseError("client:getsockname() not implemented yet") 9 | return 0 10 | } 11 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientgetstats.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func clientGetStatsMethod(L *lua.LState) int { 8 | L.RaiseError("client:getstats() not implemented yet") 9 | return 0 10 | } 11 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientreceive.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net" 7 | "time" 8 | 9 | "github.com/yuin/gopher-lua" 10 | ) 11 | 12 | func clientReceiveMethod(L *lua.LState) int { 13 | client := checkClient(L) 14 | pattern := L.Get(2) 15 | //prefix := "" // TODO L.CheckString(3) 16 | 17 | // Read a number of bytes from the socket 18 | if pattern.Type() == lua.LTNumber { 19 | if client.Timeout <= 0 { 20 | client.Conn.SetDeadline(time.Time{}) 21 | } else { 22 | client.Conn.SetDeadline(time.Now().Add(client.Timeout)) 23 | } 24 | var buf bytes.Buffer 25 | bytesToRead := L.ToInt(2) 26 | for i := 0; i < bytesToRead; i++ { 27 | byte, err := client.Reader.ReadByte() 28 | if err == io.EOF { 29 | break 30 | } 31 | if err != nil { 32 | errstr := err.Error() 33 | if err, ok := err.(net.Error); ok && err.Timeout() { 34 | errstr = "timeout" 35 | } 36 | L.Push(lua.LNil) 37 | L.Push(lua.LString(errstr)) 38 | return 2 39 | } 40 | buf.WriteByte(byte) 41 | } 42 | L.Push(lua.LString(string(buf.Bytes()))) 43 | return 1 44 | } 45 | 46 | // Read a line of text from the socket. Line separators are not returned. 47 | // This is the default pattern so nil is the same as "*l". 48 | if pattern.Type() == lua.LTNil || (pattern.Type() == lua.LTString && pattern.String() == "*l") { 49 | var buf bytes.Buffer 50 | for { 51 | if client.Timeout <= 0 { 52 | client.Conn.SetDeadline(time.Time{}) 53 | } else { 54 | client.Conn.SetDeadline(time.Now().Add(client.Timeout)) 55 | } 56 | line, isPrefix, err := client.Reader.ReadLine() 57 | if err != nil { 58 | errstr := err.Error() 59 | if err, ok := err.(net.Error); ok && err.Timeout() { 60 | errstr = "timeout" 61 | } 62 | L.Push(lua.LNil) 63 | L.Push(lua.LString(errstr)) 64 | return 2 65 | } 66 | buf.Write(line) 67 | if !isPrefix { 68 | break 69 | } 70 | } 71 | L.Push(lua.LString(string(buf.Bytes()))) 72 | return 1 73 | } 74 | 75 | // Read until the connection is closed 76 | if pattern.Type() == lua.LTString && pattern.String() == "*a" { 77 | if client.Timeout <= 0 { 78 | client.Conn.SetDeadline(time.Time{}) 79 | } else { 80 | client.Conn.SetDeadline(time.Now().Add(client.Timeout)) 81 | } 82 | var buf bytes.Buffer 83 | for { 84 | byte, err := client.Reader.ReadByte() 85 | if err == io.EOF { 86 | break 87 | } 88 | if err != nil { 89 | errstr := err.Error() 90 | if err, ok := err.(net.Error); ok && err.Timeout() { 91 | errstr = "timeout" 92 | } 93 | L.Push(lua.LNil) 94 | L.Push(lua.LString(errstr)) 95 | return 2 96 | } 97 | buf.WriteByte(byte) 98 | } 99 | L.Push(lua.LString(string(buf.Bytes()))) 100 | return 1 101 | } 102 | 103 | L.RaiseError("client:receive() not implemented yet") 104 | return 0 105 | } 106 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientsend.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func clientSendMethod(L *lua.LState) int { 8 | client := checkClient(L) 9 | data := L.ToString(2) 10 | i := L.OptInt(3, 1) 11 | j := L.OptInt(4, len(data)+1) 12 | 13 | dataBytes := []byte(data) 14 | if bytesSent, err := client.Conn.Write(dataBytes[i-1 : j-1]); err != nil { 15 | L.Push(lua.LNil) 16 | L.Push(lua.LString(err.Error())) 17 | return 2 18 | } else { 19 | L.Push(lua.LNumber(bytesSent)) 20 | return 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientsetoption.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func clientSetOptionMethod(L *lua.LState) int { 8 | L.RaiseError("client:setoption() not implemented yet") 9 | return 0 10 | } 11 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientsetstats.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func clientSetStatsMethod(L *lua.LState) int { 8 | L.RaiseError("client:setstats() not implemented yet") 9 | return 0 10 | } 11 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientsettimeout.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func clientSetTimeoutMethod(L *lua.LState) int { 10 | client := checkClient(L) 11 | timeout := L.CheckNumber(2) 12 | client.Timeout = time.Duration(timeout * 1.0e9) 13 | L.Push(lua.LNumber(1)) 14 | return 1 15 | } 16 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/clientshutdown.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func clientShutdownMethod(L *lua.LState) int { 8 | L.RaiseError("client:shutdown() not implemented yet") 9 | return 0 10 | } 11 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/connect.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | 8 | "github.com/yuin/gopher-lua" 9 | ) 10 | 11 | func connectFn(L *lua.LState) int { 12 | hostname := L.ToString(1) 13 | port := L.ToInt(2) 14 | 15 | conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", hostname, port)) 16 | if err != nil { 17 | L.Push(lua.LNil) 18 | L.Push(lua.LString(err.Error())) 19 | return 2 20 | } 21 | 22 | reader := bufio.NewReader(conn) 23 | client := &Client{Conn: conn, Reader: reader} 24 | ud := L.NewUserData() 25 | ud.Value = client 26 | L.SetMetatable(ud, L.GetTypeMetatable(CLIENT_TYPENAME)) 27 | L.Push(ud) 28 | return 1 29 | } 30 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/dns.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | type DNS struct { 8 | } 9 | 10 | var dnsMethods = map[string]lua.LGFunction{ 11 | "getaddrinfo": dnsGetAddrInfo, 12 | "gethostname": dnsGetHostName, 13 | "tohostname": dnsToHostName, 14 | "toip": dnsToIp, 15 | } 16 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/dnsgetaddrinfo.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func dnsGetAddrInfo(L *lua.LState) int { 10 | 11 | // Read arguments 12 | host := L.CheckString(1) 13 | 14 | // Handle 15 | if host == "" { 16 | return 0 17 | } 18 | addrs, err := net.LookupHost(host) 19 | if err != nil { 20 | L.RaiseError(err.Error()) 21 | return 0 22 | } 23 | result := &lua.LTable{} 24 | for _, addr := range addrs { 25 | if addr == "::1" { 26 | // best guess, according to https://stackoverflow.com/questions/5956516/getaddrinfo-and-ipv6 27 | continue 28 | } 29 | t := &lua.LTable{} 30 | t.RawSetString("family", lua.LString("inet")) 31 | t.RawSetString("addr", lua.LString(addr)) 32 | result.Append(t) 33 | } 34 | 35 | L.Push(result) 36 | return 1 37 | } 38 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/dnsgethostname.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func dnsGetHostName(L *lua.LState) int { 11 | hostname, err := os.Hostname() 12 | if err != nil { 13 | L.RaiseError(fmt.Sprintf("Failure detecting hostname: %v", err)) 14 | return 0 15 | } 16 | 17 | L.Push(lua.LString(hostname)) 18 | return 1 19 | } 20 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/dnstohostname.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func dnsToHostName(L *lua.LState) int { 10 | ip := L.ToString(1) 11 | host, err := net.LookupAddr(ip) 12 | if err != nil { 13 | L.Push(lua.LNil) 14 | L.Push(lua.LString(err.Error())) 15 | return 2 16 | } 17 | // Generally only one result is returned by LookupAddr, so we just assume this is the case 18 | // TODO: Return a table containing all hostnames mapping to the address 19 | if len(host) > 0 { 20 | L.Push(lua.LString(host[0])) 21 | } else { 22 | L.Push(lua.LNil) 23 | } 24 | L.Push(lua.LNil) 25 | return 2 26 | } 27 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/dnstoip.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | 7 | "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func dnsToIp(L *lua.LState) int { 11 | host := L.ToString(1) 12 | if addrs, err := net.LookupHost(host); err != nil { 13 | L.RaiseError(err.Error()) 14 | return 0 15 | } else { 16 | if len(addrs) < 1 { 17 | L.Push(lua.LNil) 18 | return 1 19 | } 20 | for _, addr := range addrs { 21 | if !strings.Contains(addr, ":") { 22 | L.Push(lua.LString(addr)) 23 | return 1 24 | } 25 | } 26 | L.Push(lua.LString(addrs[0])) 27 | return 1 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/gettime.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func gettimeFn(l *lua.LState) int { 10 | now := time.Now() 11 | l.Push(lua.LNumber(float64(now.UnixNano()) / 1.0e9)) 12 | return 1 13 | } 14 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/master.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "net" 5 | "time" 6 | 7 | "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | const ( 11 | MASTER_TYPENAME = "tcp{master}" 12 | ) 13 | 14 | type Master struct { 15 | Listener net.Listener 16 | BindAddr string 17 | BindPort lua.LValue 18 | Timeout time.Duration 19 | Family int 20 | Options map[string]lua.LValue 21 | } 22 | 23 | var masterMethods = map[string]lua.LGFunction{ 24 | "accept": masterAcceptMethod, 25 | "bind": masterBindMethod, 26 | "close": masterCloseMethod, 27 | "connect": masterConnectMethod, 28 | "listen": masterListenMethod, 29 | "setoption": masterSetOptionMethod, 30 | "settimeout": masterSetTimeoutMethod, 31 | } 32 | 33 | // ---------------------------------------------------------------------------- 34 | 35 | func checkMaster(L *lua.LState) (*Master, *lua.LUserData) { 36 | ud := L.CheckUserData(1) 37 | if v, ok := ud.Value.(*Master); ok { 38 | return v, ud 39 | } 40 | L.ArgError(1, "master expected") 41 | return nil, nil 42 | } 43 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/masteraccept.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "bufio" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func masterAcceptMethod(L *lua.LState) int { 10 | master, ud := checkMaster(L) 11 | conn, err := master.Listener.Accept() 12 | if err != nil { 13 | L.Push(lua.LNil) 14 | L.Push(lua.LString(err.Error())) 15 | return 2 16 | } 17 | reader := bufio.NewReader(conn) 18 | client := &Client{Conn: conn, Reader: reader, Timeout: master.Timeout} 19 | ud.Value = client 20 | L.SetMetatable(ud, L.GetTypeMetatable(CLIENT_TYPENAME)) 21 | L.Push(ud) 22 | return 1 23 | } 24 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/masterbind.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func masterBindMethod(L *lua.LState) int { 8 | master, _ := checkMaster(L) 9 | master.BindAddr = L.CheckString(2) 10 | master.BindPort = L.Get(3) 11 | L.Push(lua.LNumber(1)) 12 | return 1 13 | } 14 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/masterclose.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func masterCloseMethod(L *lua.LState) int { 8 | master, _ := checkMaster(L) 9 | master.Listener.Close() 10 | return 0 11 | } 12 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/masterconnect.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | 8 | "github.com/yuin/gopher-lua" 9 | ) 10 | 11 | func masterConnectMethod(L *lua.LState) int { 12 | master, ud := checkMaster(L) 13 | hostname := L.ToString(2) 14 | port := L.ToInt(3) 15 | 16 | conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", hostname, port), master.Timeout) 17 | if err != nil { 18 | L.Push(lua.LNil) 19 | L.Push(lua.LString(err.Error())) 20 | return 2 21 | } 22 | 23 | reader := bufio.NewReader(conn) 24 | client := &Client{Conn: conn, Reader: reader, Timeout: master.Timeout} 25 | ud.Value = client 26 | L.SetMetatable(ud, L.GetTypeMetatable(CLIENT_TYPENAME)) 27 | L.Push(ud) 28 | return 1 29 | } 30 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/masterlisten.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func masterListenMethod(L *lua.LState) int { 11 | master, _ := checkMaster(L) 12 | //backlog := L.CheckNumber(1) 13 | 14 | listener, err := net.Listen("tcp", fmt.Sprintf("%s:%s", master.BindAddr, master.BindPort)) 15 | if err != nil { 16 | L.Push(lua.LNil) 17 | L.Push(lua.LString(err.Error())) 18 | return 2 19 | } 20 | 21 | master.Listener = listener 22 | L.Push(lua.LNumber(1)) 23 | return 1 24 | } 25 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/mastersetoption.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func masterSetOptionMethod(L *lua.LState) int { 8 | master, _ := checkMaster(L) 9 | optionName := L.CheckString(2) /* obj, name, ... */ 10 | if master.Options == nil { 11 | master.Options = map[string]lua.LValue{} 12 | } 13 | master.Options[optionName] = L.Get(3) 14 | return 0 15 | } 16 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/mastersettimeout.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func masterSetTimeoutMethod(L *lua.LState) int { 10 | master, _ := checkMaster(L) 11 | timeout := L.CheckNumber(2) 12 | master.Timeout = time.Duration(timeout * 1.0e9) 13 | L.Push(lua.LNumber(1)) 14 | return 1 15 | } 16 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/select.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func selectFn(L *lua.LState) int { 10 | 11 | // Read arguments 12 | recvt := L.Get(1) 13 | sendt := L.Get(2) 14 | timeout := L.Get(3) 15 | 16 | // Handle select(nil, nil, timeout) 17 | if recvt.Type() == lua.LTNil && sendt.Type() == lua.LTNil { 18 | timeoutVal, ok := timeout.(lua.LNumber) 19 | if !ok { 20 | L.RaiseError("Malformed timeout in call to socket.select(?,?,timeout)") 21 | return 0 22 | } 23 | timeoutDuration := time.Duration(timeoutVal * 1.0e9) 24 | time.Sleep(timeoutDuration) 25 | L.Push(lua.LString("timeout")) 26 | return 1 27 | } 28 | 29 | // TODO Handle socket select 30 | L.RaiseError("socket.select(recvt,sendt,timeout) not implemented yet") 31 | return 0 32 | } 33 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/skip.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func skipFn(L *lua.LState) int { 8 | d := L.ToInt(1) 9 | for i := 0; i <= d; i++ { 10 | L.Remove(1) 11 | } 12 | return L.GetTop() 13 | } 14 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/sleep.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func sleepFn(L *lua.LState) int { 10 | 11 | // Read arguments 12 | timeout := L.Get(1) 13 | 14 | // Handle 15 | timeoutVal, ok := timeout.(lua.LNumber) 16 | if !ok { 17 | L.RaiseError("Malformed timeout in call to socket.sleep(time)") 18 | return 0 19 | } 20 | timeoutDuration := time.Duration(timeoutVal * 1.0e9) 21 | time.Sleep(timeoutDuration) 22 | 23 | return 0 24 | } 25 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/socketcore.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | const ( 8 | AF_UNSPEC = 0 9 | AF_INET = 2 10 | AF_INET6 = 23 11 | ) 12 | 13 | // ---------------------------------------------------------------------------- 14 | 15 | var exports = map[string]lua.LGFunction{ 16 | "connect": connectFn, 17 | "gettime": gettimeFn, 18 | "select": selectFn, 19 | "skip": skipFn, 20 | "sleep": sleepFn, 21 | "tcp": tcpFn, 22 | "tcp4": tcp4Fn, 23 | "tcp6": tcp6Fn, 24 | "udp": udpFn, 25 | } 26 | 27 | // ---------------------------------------------------------------------------- 28 | 29 | func Loader(L *lua.LState) int { 30 | mod := L.SetFuncs(L.NewTable(), exports) 31 | L.Push(mod) 32 | 33 | L.SetField(mod, "_DEBUG", lua.LBool(false)) 34 | L.SetField(mod, "_VERSION", lua.LString("0.0.0")) // TODO 35 | 36 | registerClientType(L) 37 | registerMasterType(L) 38 | 39 | registerDNSTable(L, mod) 40 | 41 | return 1 42 | } 43 | 44 | // ---------------------------------------------------------------------------- 45 | 46 | func registerClientType(L *lua.LState) { 47 | mt := L.NewTypeMetatable(CLIENT_TYPENAME) 48 | L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), clientMethods)) 49 | } 50 | 51 | // ---------------------------------------------------------------------------- 52 | 53 | func registerDNSTable(L *lua.LState, mod *lua.LTable) { 54 | table := L.NewTable() 55 | L.SetFuncs(table, dnsMethods) 56 | L.SetField(mod, "dns", table) 57 | } 58 | 59 | // ---------------------------------------------------------------------------- 60 | 61 | func registerMasterType(L *lua.LState) { 62 | mt := L.NewTypeMetatable(MASTER_TYPENAME) 63 | L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), masterMethods)) 64 | } 65 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/tcp.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func tcpFn(L *lua.LState) int { 8 | master := &Master{Family: AF_UNSPEC} 9 | ud := L.NewUserData() 10 | ud.Value = master 11 | L.SetMetatable(ud, L.GetTypeMetatable(MASTER_TYPENAME)) 12 | L.Push(ud) 13 | return 1 14 | } 15 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/tcp4.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func tcp4Fn(L *lua.LState) int { 8 | master := &Master{Family: AF_INET} 9 | ud := L.NewUserData() 10 | ud.Value = master 11 | L.SetMetatable(ud, L.GetTypeMetatable(MASTER_TYPENAME)) 12 | L.Push(ud) 13 | return 1 14 | } 15 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/tcp6.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func tcp6Fn(L *lua.LState) int { 8 | master := &Master{Family: AF_INET6} 9 | ud := L.NewUserData() 10 | ud.Value = master 11 | L.SetMetatable(ud, L.GetTypeMetatable(MASTER_TYPENAME)) 12 | L.Push(ud) 13 | return 1 14 | } 15 | -------------------------------------------------------------------------------- /external/gluasocket/socketcore/udp.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketcore 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | func udpFn(L *lua.LState) int { 8 | L.RaiseError("socket.udp() not implemented yet") 9 | return 0 10 | } 11 | -------------------------------------------------------------------------------- /external/gluasocket/socketexcept/socketexcept.go: -------------------------------------------------------------------------------- 1 | package gluasocket_socketexcept 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | // ---------------------------------------------------------------------------- 8 | 9 | func Loader(l *lua.LState) int { 10 | if err := l.DoString(exceptDotLua); err != nil { 11 | l.RaiseError("Error loading except.lua: %v", err) 12 | return 0 13 | } 14 | return 1 15 | } 16 | 17 | const exceptDotLua = `----------------------------------------------------------------------------- 18 | -- Exception control 19 | -- LuaSocket toolkit (but completely independent from other modules) 20 | -- Author: Diego Nehab 21 | 22 | -- This provides support for simple exceptions in Lua. During the 23 | -- development of the HTTP/FTP/SMTP support, it became aparent that 24 | -- error checking was taking a substantial amount of the coding. These 25 | -- function greatly simplify the task of checking errors. 26 | 27 | -- The main idea is that functions should return nil as its first return 28 | -- value when it finds an error, and return an error message (or value) 29 | -- following nil. In case of success, as long as the first value is not nil, 30 | -- the other values don't matter. 31 | 32 | -- The idea is to nest function calls with the "try" function. This function 33 | -- checks the first value, and, if it's falsy, wraps the second value 34 | -- in a table with metatable and calls "error" on it. Otherwise, 35 | -- it returns all values it received. 36 | 37 | -- The "newtry" function is a factory for "try" functions that call a finalizer 38 | -- in protected mode before calling "error". 39 | 40 | -- The "protect" function returns a new function that behaves exactly like the 41 | -- function it receives, but the new function catches exceptions 42 | -- thrown by "try" functions and returns nil followed by the error message 43 | -- instead. 44 | 45 | -- With these three function, it's easy to write functions that throw 46 | -- exceptions on error, but that don't interrupt the user script. 47 | ----------------------------------------------------------------------------- 48 | 49 | local base = _G 50 | local _M = {} 51 | 52 | local exception_metat = {} 53 | 54 | function exception_metat:__tostring() 55 | return base.tostring(self[1]) 56 | end 57 | 58 | local function do_nothing() end 59 | 60 | function _M.newtry(finalizer) 61 | if finalizer == nil then finalizer = do_nothing end 62 | return function(...) 63 | local ok, err = ... 64 | if ok then 65 | return ... 66 | else 67 | base.pcall(finalizer) 68 | base.error(base.setmetatable({err}, exception_metat)) 69 | end 70 | end 71 | end 72 | 73 | local function handle_pcall_returns(ok, ...) 74 | if ok then 75 | return ... 76 | else 77 | local err = ... 78 | if base.getmetatable(err) == exception_metat then 79 | return nil, err[1] 80 | else 81 | base.error(err, 0) 82 | end 83 | end 84 | end 85 | 86 | function _M.protect(func) 87 | return function(...) 88 | return handle_pcall_returns(base.pcall(func, ...)) 89 | end 90 | end 91 | 92 | return _M 93 | ` 94 | -------------------------------------------------------------------------------- /external/gluasql/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 | -------------------------------------------------------------------------------- /external/gluasql/mysql/client.go: -------------------------------------------------------------------------------- 1 | package gluasql_mysql 2 | 3 | import ( 4 | "database/sql" 5 | "time" 6 | 7 | _ "github.com/go-sql-driver/mysql" 8 | "github.com/yuin/gopher-lua" 9 | ) 10 | 11 | const ( 12 | CLIENT_TYPENAME = "mysql{client}" 13 | ) 14 | 15 | // Client mysql 16 | type Client struct { 17 | DB *sql.DB 18 | Timeout time.Duration 19 | } 20 | 21 | var clientMethods = map[string]lua.LGFunction{ 22 | "connect": clientConnectMethod, 23 | "set_timeout": clientSetTimeoutMethod, 24 | "set_keepalive": clientSetKeepaliveMethod, 25 | "close": clientCloseMethod, 26 | "query": clientQueryMethod, 27 | } 28 | 29 | func checkClient(L *lua.LState) *Client { 30 | ud := L.CheckUserData(1) 31 | if v, ok := ud.Value.(*Client); ok { 32 | return v 33 | } 34 | L.ArgError(1, "client expected") 35 | return nil 36 | } 37 | 38 | func clientCloseMethod(L *lua.LState) int { 39 | client := checkClient(L) 40 | 41 | if client.DB == nil { 42 | L.Push(lua.LBool(true)) 43 | return 1 44 | } 45 | 46 | err := client.DB.Close() 47 | // always clean 48 | client.DB = nil 49 | if err != nil { 50 | L.Push(lua.LBool(false)) 51 | L.Push(lua.LString(err.Error())) 52 | return 2 53 | } 54 | 55 | L.Push(lua.LBool(true)) 56 | return 1 57 | } 58 | 59 | func clientSetKeepaliveMethod(L *lua.LState) int { 60 | client := checkClient(L) 61 | idleTimeout := L.ToInt64(2) // timeout (in ms) 62 | poolSize := L.ToInt(3) 63 | 64 | if client.DB == nil { 65 | L.Push(lua.LBool(true)) 66 | L.Push(lua.LString("connect required")) 67 | return 2 68 | } 69 | 70 | client.DB.SetConnMaxLifetime(time.Millisecond * time.Duration(idleTimeout)) 71 | client.DB.SetMaxIdleConns(poolSize) 72 | 73 | L.Push(lua.LBool(true)) 74 | return 1 75 | } 76 | -------------------------------------------------------------------------------- /external/gluasql/mysql/clientconnect.go: -------------------------------------------------------------------------------- 1 | package gluasql_mysql 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "net/url" 7 | 8 | util "github.com/tongson/LadyLua/external/gluasql/util" 9 | "github.com/yuin/gopher-lua" 10 | ) 11 | 12 | func clientConnectMethod(L *lua.LState) int { 13 | client := checkClient(L) 14 | tb := util.GetValue(L, 2) 15 | options, ok := tb.(map[string]interface{}) 16 | 17 | if tb == nil || !ok { 18 | L.ArgError(2, "options excepted") 19 | return 0 20 | } 21 | 22 | host, _ := options["host"].(string) 23 | if host == "" { 24 | host = "127.0.0.1" 25 | } 26 | port, _ := options["port"].(int) 27 | if port == 0 { 28 | port = 3306 29 | } 30 | database, _ := options["database"].(string) 31 | user, _ := options["user"].(string) 32 | password, _ := options["password"].(string) 33 | charset, _ := options["charset"].(string) 34 | 35 | // current support tcp connection only 36 | dsn := fmt.Sprintf("tcp(%s:%d)/%s", host, port, database) 37 | if user != "" { 38 | if password != "" { 39 | dsn = fmt.Sprintf("%s:%s@", user, password) + dsn 40 | } else { 41 | dsn = fmt.Sprintf("%s@", user) + dsn 42 | } 43 | } 44 | 45 | query := url.Values{} 46 | if charset != "" { 47 | query.Set("charset", charset) 48 | } 49 | if client.Timeout > 0 { 50 | stimeout := client.Timeout.String() 51 | query.Set("readTimeout", stimeout) 52 | query.Set("writeTimeout", stimeout) 53 | } 54 | 55 | s := query.Encode() 56 | if s != "" { 57 | dsn += "?" + s 58 | } 59 | 60 | var err error 61 | client.DB, err = sql.Open("mysql", dsn) 62 | if err != nil { 63 | L.Push(lua.LBool(false)) 64 | L.Push(lua.LString(err.Error())) 65 | return 2 66 | } 67 | 68 | L.Push(lua.LBool(true)) 69 | return 1 70 | } 71 | -------------------------------------------------------------------------------- /external/gluasql/mysql/clientquery.go: -------------------------------------------------------------------------------- 1 | package gluasql_mysql 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/junhsieh/goexamples/fieldbinding/fieldbinding" 7 | util "github.com/tongson/LadyLua/external/gluasql/util" 8 | "github.com/yuin/gopher-lua" 9 | ) 10 | 11 | func clientQueryMethod(L *lua.LState) int { 12 | client := checkClient(L) 13 | query := L.ToString(2) 14 | 15 | if client.DB == nil { 16 | return 0 17 | } 18 | 19 | if query == "" { 20 | L.ArgError(2, "query string required") 21 | return 0 22 | } 23 | 24 | top := L.GetTop() 25 | args := make([]interface{}, 0, top-2) 26 | for i := 3; i <= top; i++ { 27 | args = append(args, util.GetValue(L, i)) 28 | } 29 | 30 | rows, err := client.DB.Query(query, args...) 31 | if err != nil { 32 | L.Push(lua.LNil) 33 | L.Push(lua.LString(err.Error())) 34 | return 2 35 | } 36 | defer rows.Close() 37 | 38 | fb := fieldbinding.NewFieldBinding() 39 | cols, err := rows.Columns() 40 | if err != nil { 41 | L.Push(lua.LNil) 42 | L.Push(lua.LString(err.Error())) 43 | return 2 44 | } 45 | 46 | fb.PutFields(cols) 47 | 48 | tb := L.NewTable() 49 | for rows.Next() { 50 | if err := rows.Scan(fb.GetFieldPtrArr()...); err != nil { 51 | L.Push(lua.LNil) 52 | L.Push(lua.LString(err.Error())) 53 | return 2 54 | } 55 | 56 | tbRow := util.ToTableFromMap(L, reflect.ValueOf(fb.GetFieldArr())) 57 | tb.Append(tbRow) 58 | } 59 | 60 | L.Push(tb) 61 | return 1 62 | } 63 | -------------------------------------------------------------------------------- /external/gluasql/mysql/clientsettimeout.go: -------------------------------------------------------------------------------- 1 | package gluasql_mysql 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | func clientSetTimeoutMethod(L *lua.LState) int { 10 | client := checkClient(L) 11 | timeout := L.ToInt64(2) // timeout (in ms) 12 | 13 | client.Timeout = time.Millisecond * time.Duration(timeout) 14 | return 0 15 | } 16 | -------------------------------------------------------------------------------- /external/gluasql/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package gluasql_mysql 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | ) 6 | 7 | var exports = map[string]lua.LGFunction{ 8 | "new": newFn, 9 | "escape": escapeFn, 10 | } 11 | 12 | func Loader(L *lua.LState) int { 13 | mod := L.SetFuncs(L.NewTable(), exports) 14 | L.Push(mod) 15 | 16 | L.SetField(mod, "_DEBUG", lua.LBool(false)) 17 | L.SetField(mod, "_VERSION", lua.LString("0.0.0")) 18 | 19 | registerClientType(L) 20 | 21 | return 1 22 | } 23 | 24 | func registerClientType(L *lua.LState) { 25 | mt := L.NewTypeMetatable(CLIENT_TYPENAME) 26 | L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), clientMethods)) 27 | } 28 | 29 | func newFn(L *lua.LState) int { 30 | client := &Client{} 31 | ud := L.NewUserData() 32 | ud.Value = client 33 | L.SetMetatable(ud, L.GetTypeMetatable(CLIENT_TYPENAME)) 34 | L.Push(ud) 35 | return 1 36 | } 37 | 38 | func escape(source string) string { 39 | var j int = 0 40 | if len(source) == 0 { 41 | return "" 42 | } 43 | tempStr := source[:] 44 | desc := make([]byte, len(tempStr)*2) 45 | for i := 0; i < len(tempStr); i++ { 46 | flag := false 47 | var escape byte 48 | switch tempStr[i] { 49 | case '\r': 50 | flag = true 51 | escape = 'r' 52 | break 53 | case '\n': 54 | flag = true 55 | escape = 'n' 56 | break 57 | case '\\': 58 | flag = true 59 | escape = '\\' 60 | break 61 | case '\'': 62 | flag = true 63 | escape = '\'' 64 | break 65 | case '"': 66 | flag = true 67 | escape = '"' 68 | break 69 | case '\032': 70 | flag = true 71 | escape = 'Z' 72 | break 73 | default: 74 | } 75 | if flag { 76 | desc[j] = '\\' 77 | desc[j+1] = escape 78 | j = j + 2 79 | } else { 80 | desc[j] = tempStr[i] 81 | j = j + 1 82 | } 83 | } 84 | return string(desc[0:j]) 85 | } 86 | 87 | func escapeFn(L *lua.LState) int { 88 | s := L.ToString(1) 89 | L.Push(lua.LString(escape(s))) 90 | return 1 91 | } 92 | -------------------------------------------------------------------------------- /external/gopher-json/LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /external/gopher-lfs/LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /external/gopher-lfs/api_darwin.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | package lfs 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | 9 | "github.com/yuin/gopher-lua" 10 | ) 11 | 12 | func attributesFill(tbl *lua.LTable, stat os.FileInfo) error { 13 | sys := stat.Sys().(*syscall.Stat_t) 14 | tbl.RawSetString("dev", lua.LNumber(sys.Dev)) 15 | tbl.RawSetString("ino", lua.LNumber(sys.Ino)) 16 | { 17 | var mode string 18 | switch sys.Mode & syscall.S_IFMT { 19 | case syscall.S_IFREG: 20 | mode = "file" 21 | case syscall.S_IFDIR: 22 | mode = "directory" 23 | case syscall.S_IFLNK: 24 | mode = "link" 25 | case syscall.S_IFSOCK: 26 | mode = "socket" 27 | case syscall.S_IFIFO: 28 | mode = "named pipe" 29 | case syscall.S_IFCHR: 30 | mode = "char device" 31 | case syscall.S_IFBLK: 32 | mode = "block device" 33 | default: 34 | mode = "other" 35 | } 36 | tbl.RawSetString("mode", lua.LString(mode)) 37 | } 38 | tbl.RawSetString("nlink", lua.LNumber(sys.Nlink)) 39 | tbl.RawSetString("uid", lua.LNumber(sys.Uid)) 40 | tbl.RawSetString("gid", lua.LNumber(sys.Gid)) 41 | tbl.RawSetString("rdev", lua.LNumber(sys.Rdev)) 42 | tbl.RawSetString("access", lua.LNumber(sys.Atimespec.Sec)) 43 | tbl.RawSetString("modification", lua.LNumber(sys.Mtimespec.Sec)) 44 | tbl.RawSetString("change", lua.LNumber(sys.Ctimespec.Sec)) 45 | tbl.RawSetString("size", lua.LNumber(sys.Size)) 46 | tbl.RawSetString("blocks", lua.LNumber(sys.Blocks)) 47 | tbl.RawSetString("blksize", lua.LNumber(sys.Blksize)) 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /external/gopher-lfs/api_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package lfs 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | 9 | "github.com/yuin/gopher-lua" 10 | ) 11 | 12 | func attributesFill(tbl *lua.LTable, stat os.FileInfo) error { 13 | sys := stat.Sys().(*syscall.Stat_t) 14 | tbl.RawSetString("dev", lua.LNumber(sys.Dev)) 15 | tbl.RawSetString("ino", lua.LNumber(sys.Ino)) 16 | { 17 | var mode string 18 | switch sys.Mode & syscall.S_IFMT { 19 | case syscall.S_IFREG: 20 | mode = "file" 21 | case syscall.S_IFDIR: 22 | mode = "directory" 23 | case syscall.S_IFLNK: 24 | mode = "link" 25 | case syscall.S_IFSOCK: 26 | mode = "socket" 27 | case syscall.S_IFIFO: 28 | mode = "named pipe" 29 | case syscall.S_IFCHR: 30 | mode = "char device" 31 | case syscall.S_IFBLK: 32 | mode = "block device" 33 | default: 34 | mode = "other" 35 | } 36 | tbl.RawSetString("mode", lua.LString(mode)) 37 | } 38 | tbl.RawSetString("nlink", lua.LNumber(sys.Nlink)) 39 | tbl.RawSetString("uid", lua.LNumber(sys.Uid)) 40 | tbl.RawSetString("gid", lua.LNumber(sys.Gid)) 41 | tbl.RawSetString("rdev", lua.LNumber(sys.Rdev)) 42 | tbl.RawSetString("access", lua.LNumber(sys.Atim.Sec)) 43 | tbl.RawSetString("modification", lua.LNumber(sys.Mtim.Sec)) 44 | tbl.RawSetString("change", lua.LNumber(sys.Ctim.Sec)) 45 | tbl.RawSetString("size", lua.LNumber(sys.Size)) 46 | tbl.RawSetString("blocks", lua.LNumber(sys.Blocks)) 47 | tbl.RawSetString("blksize", lua.LNumber(sys.Blksize)) 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /external/gopher-lfs/api_other.go: -------------------------------------------------------------------------------- 1 | // +build !darwin,!linux,!windows 2 | 3 | package lfs 4 | 5 | import ( 6 | "errors" 7 | "os" 8 | "runtime" 9 | 10 | "github.com/yuin/gopher-lua" 11 | ) 12 | 13 | func attributesFill(tbl *lua.LTable, stat os.FileInfo) error { 14 | return errors.New("unsupported operating system " + runtime.GOOS) 15 | } 16 | -------------------------------------------------------------------------------- /external/gopher-lfs/api_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package lfs 4 | 5 | import ( 6 | "os" 7 | "syscall" 8 | "time" 9 | 10 | "github.com/yuin/gopher-lua" 11 | ) 12 | 13 | func attributesFill(tbl *lua.LTable, stat os.FileInfo) error { 14 | sys := stat.Sys().(*syscall.Win32FileAttributeData) 15 | tbl.RawSetString("dev", lua.LNumber(0)) 16 | tbl.RawSetString("ino", lua.LNumber(0)) 17 | 18 | if stat.IsDir() { 19 | tbl.RawSetString("mode", lua.LString("directory")) 20 | } else { 21 | tbl.RawSetString("mode", lua.LString("file")) 22 | } 23 | 24 | tbl.RawSetString("nlink", lua.LNumber(0)) 25 | tbl.RawSetString("uid", lua.LNumber(0)) 26 | tbl.RawSetString("gid", lua.LNumber(0)) 27 | tbl.RawSetString("rdev", lua.LNumber(0)) 28 | 29 | tbl.RawSetString("access", lua.LNumber(time.Unix(0, sys.LastAccessTime.Nanoseconds()/1e9).Second())) 30 | tbl.RawSetString("modification", lua.LNumber(time.Unix(0, sys.CreationTime.Nanoseconds()/1e9).Second())) 31 | tbl.RawSetString("change", lua.LNumber(time.Unix(0, sys.LastWriteTime.Nanoseconds()/1e9).Second())) 32 | tbl.RawSetString("size", lua.LNumber(stat.Size())) 33 | 34 | tbl.RawSetString("blocks", lua.LNumber(0)) 35 | tbl.RawSetString("blksize", lua.LNumber(0)) 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /external/gopher-lfs/util.go: -------------------------------------------------------------------------------- 1 | package lfs 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/yuin/gopher-lua" 8 | ) 9 | 10 | func attributes(L *lua.LState, statFunc func(string) (os.FileInfo, error)) int { 11 | fp := L.CheckString(1) 12 | 13 | stat, err := statFunc(fp) 14 | if err != nil { 15 | L.Push(lua.LNil) 16 | L.Push(lua.LString(err.Error())) 17 | return 2 18 | } 19 | table := L.NewTable() 20 | if err := attributesFill(table, stat); err != nil { 21 | L.Push(lua.LNil) 22 | L.Push(lua.LString(err.Error())) 23 | return 2 24 | } 25 | if table.RawGetString("mode").String() == "link" { 26 | if path, err := filepath.EvalSymlinks(fp); err == nil { 27 | table.RawSetString("target", lua.LString(path)) 28 | } 29 | } 30 | if L.GetTop() > 1 { 31 | requestName := L.CheckString(2) 32 | L.Push(table.RawGetString(requestName)) 33 | return 1 34 | } 35 | L.Push(table) 36 | return 1 37 | } 38 | 39 | func dirItr(L *lua.LState) int { 40 | ud := L.CheckUserData(1) 41 | 42 | f, ok := ud.Value.(*os.File) 43 | if !ok { 44 | return 0 45 | } 46 | names, err := f.Readdirnames(1) 47 | if err != nil { 48 | return 0 49 | } 50 | L.Push(lua.LString(names[0])) 51 | return 1 52 | } 53 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/tongson/LadyLua 2 | 3 | go 1.16 4 | 5 | require ( 6 | git.mills.io/prologic/bitcask v0.3.14 7 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e 8 | github.com/frankban/quicktest v1.13.0 // indirect 9 | github.com/fsnotify/fsnotify v1.4.9 10 | github.com/ghodss/yaml v1.0.0 11 | github.com/go-redis/redis/v8 v8.11.0 12 | github.com/go-sql-driver/mysql v1.6.0 13 | github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible 14 | github.com/gregdel/pushover v1.1.0 15 | github.com/hashicorp/go-uuid v1.0.2 16 | github.com/junhsieh/goexamples v0.0.0-20210713004924-3d9f14ba676d 17 | github.com/kevinburke/ssh_config v1.1.0 18 | github.com/oklog/ulid/v2 v2.0.2 19 | github.com/pelletier/go-toml v1.9.3 20 | github.com/pierrec/lz4 v2.6.1+incompatible 21 | github.com/rs/zerolog v1.23.0 22 | github.com/segmentio/ksuid v1.0.4 23 | github.com/slack-go/slack v0.9.3 24 | github.com/technoweenie/multipartstreamer v1.0.1 // indirect 25 | github.com/tongson/gl v0.0.0-20210722053448-dfbc7abd31bf 26 | github.com/yuin/gluamapper v0.0.0-20150323120927-d836955830e7 27 | github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 28 | ) 29 | 30 | replace github.com/yuin/gopher-lua => github.com/tongson/gopher-lua v0.0.0-20210610051759-53ab9600e09f 31 | -------------------------------------------------------------------------------- /helper.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "embed" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/yuin/gopher-lua" 9 | ) 10 | 11 | //# 12 | //# == *ll.FillArg*(*lua.LState, []string) 13 | //# Capture command line arguments as the `arg` table in the global `_G` environment. 14 | //# 15 | //# === Arguments 16 | //# [width="72%"] 17 | //# |=== 18 | //# |*lua.LState|The current `LState`; usually the result of `lua.NewState()` 19 | //# |[]string |Usually `os.Args` 20 | //# |=== 21 | func FillArg(L *lua.LState, args []string) { 22 | argtb := L.NewTable() 23 | for i := 0; i < len(args); i++ { 24 | L.RawSet(argtb, lua.LNumber(i), lua.LString(args[i])) 25 | } 26 | L.SetGlobal("arg", argtb) 27 | } 28 | 29 | //# 30 | //# == *ll.ReadFile*(embed.FS, string) -> string 31 | //# Read file from an `embed.FS` location. 32 | //# 33 | //# === Arguments 34 | //# [width="72%"] 35 | //# |=== 36 | //# |embed.FS |Variable of embedded filesystem 37 | //# |string |Filename 38 | //# |=== 39 | //# 40 | //# === Returns 41 | //# [width="72%"] 42 | //# |=== 43 | //# |string |Contents of file 44 | //# |=== 45 | func ReadFile(f embed.FS, p string) string { 46 | c, err := f.ReadFile(p) 47 | if err != nil { 48 | fmt.Println(err.Error()) 49 | os.Exit(1) 50 | } 51 | return string(c) 52 | } 53 | -------------------------------------------------------------------------------- /internal/exec.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/tongson/gl" 5 | "github.com/yuin/gopher-lua" 6 | ) 7 | 8 | func ExecCommand(L *lua.LState) int { 9 | targ := []string{} 10 | tenv := []string{} 11 | tbl := L.NewTable() 12 | exe := L.CheckString(1) 13 | arg := L.OptTable(2, tbl) 14 | env := L.OptTable(3, tbl) 15 | cwd := L.OptString(4, "") 16 | sin := L.OptString(5, "") 17 | tme := L.OptNumber(6, 0) 18 | 19 | if arg.Len() > 0 { 20 | arg.ForEach(func(_, value lua.LValue) { 21 | targ = append(targ, lua.LVAsString(value)) 22 | }) 23 | } 24 | if env.Len() > 0 { 25 | env.ForEach(func(_, value lua.LValue) { 26 | tenv = append(tenv, lua.LVAsString(value)) 27 | }) 28 | } 29 | cmd := gl.RunArgs{Exe: exe, Args: targ, Env: tenv, Dir: cwd, Stdin: []byte(sin), Timeout: int(tme)} 30 | ret, stdout, stderr, err := cmd.Run() 31 | 32 | if ret { 33 | L.Push(lua.LTrue) 34 | } else { 35 | L.Push(lua.LNil) 36 | } 37 | L.Push(lua.LString(stdout)) 38 | L.Push(lua.LString(stderr)) 39 | L.Push(lua.LString(err)) 40 | return 4 41 | } 42 | 43 | func ExecCtx(L *lua.LState) int { 44 | exe := L.CheckString(1) 45 | set := L.NewTable() 46 | mt := L.NewTable() 47 | L.SetField(mt, "__call", L.NewFunction(func(L *lua.LState) int { 48 | deftbl := L.NewTable() 49 | args := L.OptTable(2, deftbl) 50 | L.Push(L.NewFunction(ExecCommand)) 51 | L.Push(lua.LString(exe)) 52 | L.Push(args) 53 | L.Push(L.GetField(set, "env")) 54 | L.Push(L.GetField(set, "cwd")) 55 | L.Push(L.GetField(set, "stdin")) 56 | L.Push(L.GetField(set, "timeout")) 57 | L.Call(6, 4) 58 | return 4 59 | })) 60 | L.SetMetatable(set, mt) 61 | L.Push(set) 62 | return 1 63 | } 64 | -------------------------------------------------------------------------------- /internal/fs.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/tongson/gl" 5 | "github.com/yuin/gopher-lua" 6 | "io" 7 | "os" 8 | ) 9 | 10 | func FsIsdir(L *lua.LState) int { 11 | dir := L.CheckString(1) 12 | is := gl.StatPath("directory") 13 | if is(dir) { 14 | L.Push(lua.LTrue) 15 | return 1 16 | } else { 17 | L.Push(lua.LNil) 18 | L.Push(lua.LString("Not a directory.")) 19 | return 2 20 | } 21 | } 22 | 23 | func FsIsfile(L *lua.LState) int { 24 | f := L.CheckString(1) 25 | is := gl.StatPath("") 26 | if is(f) { 27 | L.Push(lua.LTrue) 28 | return 1 29 | } else { 30 | L.Push(lua.LNil) 31 | L.Push(lua.LString("Not a file.")) 32 | return 2 33 | } 34 | } 35 | 36 | func FsRead(L *lua.LState) int { 37 | path := L.CheckString(1) 38 | isFile := gl.StatPath("file") 39 | /* #nosec G304 */ 40 | if isFile(path) { 41 | file, err := os.Open(path) 42 | defer func() { 43 | _ = file.Close() 44 | }() 45 | if err != nil { 46 | L.Push(lua.LNil) 47 | L.Push(lua.LString(err.Error())) 48 | return 2 49 | } 50 | if str, err := io.ReadAll(file); err != nil { 51 | L.Push(lua.LNil) 52 | L.Push(lua.LString(err.Error())) 53 | return 2 54 | } else { 55 | L.Push(lua.LString(string(str))) 56 | return 1 57 | } 58 | } else { 59 | L.Push(lua.LNil) 60 | L.Push(lua.LString("Not a file or file does not exist.")) 61 | return 2 62 | } 63 | } 64 | 65 | func FsWrite(L *lua.LState) int { 66 | f := L.CheckString(1) 67 | s := L.CheckString(2) 68 | err := gl.StringToFile(f, s) 69 | if err != nil { 70 | L.Push(lua.LNil) 71 | L.Push(lua.LString(err.Error())) 72 | return 2 73 | } else { 74 | L.Push(lua.LTrue) 75 | return 1 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /internal/fsnotify.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/fsnotify/fsnotify" 5 | "github.com/yuin/gopher-lua" 6 | ) 7 | 8 | func fsnCreate(L *lua.LState) int { 9 | var got bool 10 | var ero string 11 | watcher, err := fsnotify.NewWatcher() 12 | if err != nil { 13 | L.Push(lua.LNil) 14 | L.Push(lua.LString(err.Error())) 15 | return 2 16 | } 17 | defer watcher.Close() 18 | f := L.CheckString(1) 19 | done := make(chan bool) 20 | go func() { 21 | for { 22 | select { 23 | case event, ok := <-watcher.Events: 24 | if !ok { 25 | return 26 | } 27 | if event.Op&fsnotify.Create == fsnotify.Create { 28 | got = true 29 | close(done) 30 | return 31 | } 32 | case err, ok := <-watcher.Errors: 33 | if !ok { 34 | return 35 | } 36 | ero = err.Error() 37 | return 38 | } 39 | } 40 | }() 41 | err = watcher.Add(f) 42 | <-done 43 | if err != nil { 44 | L.Push(lua.LNil) 45 | L.Push(lua.LString(err.Error())) 46 | return 2 47 | } 48 | if got == true { 49 | L.Push(lua.LTrue) 50 | return 1 51 | } else { 52 | L.Push(lua.LNil) 53 | L.Push(lua.LString(ero)) 54 | return 2 55 | } 56 | } 57 | 58 | func fsnWrite(L *lua.LState) int { 59 | var got bool 60 | var ero string 61 | watcher, err := fsnotify.NewWatcher() 62 | if err != nil { 63 | L.Push(lua.LNil) 64 | L.Push(lua.LString(err.Error())) 65 | return 2 66 | } 67 | defer watcher.Close() 68 | f := L.CheckString(1) 69 | done := make(chan bool) 70 | go func() { 71 | for { 72 | select { 73 | case event, ok := <-watcher.Events: 74 | if !ok { 75 | return 76 | } 77 | if event.Op&fsnotify.Write == fsnotify.Write { 78 | got = true 79 | close(done) 80 | return 81 | } 82 | case err, ok := <-watcher.Errors: 83 | if !ok { 84 | return 85 | } 86 | ero = err.Error() 87 | return 88 | } 89 | } 90 | }() 91 | err = watcher.Add(f) 92 | <-done 93 | if err != nil { 94 | L.Push(lua.LNil) 95 | L.Push(lua.LString(err.Error())) 96 | return 2 97 | } 98 | if got == true { 99 | L.Push(lua.LTrue) 100 | return 1 101 | } else { 102 | L.Push(lua.LNil) 103 | L.Push(lua.LString(ero)) 104 | return 2 105 | } 106 | } 107 | 108 | func fsnRemove(L *lua.LState) int { 109 | var got bool 110 | var ero string 111 | watcher, err := fsnotify.NewWatcher() 112 | if err != nil { 113 | L.Push(lua.LNil) 114 | L.Push(lua.LString(err.Error())) 115 | return 2 116 | } 117 | defer watcher.Close() 118 | f := L.CheckString(1) 119 | done := make(chan bool) 120 | go func() { 121 | for { 122 | select { 123 | case event, ok := <-watcher.Events: 124 | if !ok { 125 | return 126 | } 127 | if event.Op&fsnotify.Remove == fsnotify.Remove { 128 | got = true 129 | close(done) 130 | return 131 | } 132 | case err, ok := <-watcher.Errors: 133 | if !ok { 134 | return 135 | } 136 | ero = err.Error() 137 | return 138 | } 139 | } 140 | }() 141 | err = watcher.Add(f) 142 | <-done 143 | if err != nil { 144 | L.Push(lua.LNil) 145 | L.Push(lua.LString(err.Error())) 146 | return 2 147 | } 148 | if got == true { 149 | L.Push(lua.LTrue) 150 | return 1 151 | } else { 152 | L.Push(lua.LNil) 153 | L.Push(lua.LString(ero)) 154 | return 2 155 | } 156 | } 157 | 158 | func FsnLoader(L *lua.LState) int { 159 | t := L.NewTable() 160 | L.SetFuncs(t, fsnApi) 161 | L.Push(t) 162 | return 1 163 | } 164 | 165 | var fsnApi = map[string]lua.LGFunction{ 166 | "create": fsnCreate, 167 | "write": fsnWrite, 168 | "remove": fsnRemove, 169 | } 170 | -------------------------------------------------------------------------------- /internal/global.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/yuin/gopher-lua" 7 | ) 8 | 9 | // Prettier equivalent of `require("xstring")()` --> `extend("string")` 10 | func Extend(L *lua.LState) int { 11 | mod := L.CheckString(1) 12 | req := L.GetField(L.Get(lua.GlobalsIndex), "require").(*lua.LFunction) 13 | L.Push(req) 14 | L.Push(lua.LString(fmt.Sprintf("extension_%s", mod))) 15 | L.Call(1, 1) 16 | L.Call(0, 0) 17 | return 0 18 | } 19 | -------------------------------------------------------------------------------- /internal/ksuid.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/segmentio/ksuid" 5 | "github.com/yuin/gopher-lua" 6 | ) 7 | 8 | func ksuidNew(L *lua.LState) int { 9 | L.Push(lua.LString(ksuid.New().String())) 10 | return 1 11 | } 12 | 13 | func KsuidLoader(L *lua.LState) int { 14 | t := L.NewTable() 15 | L.SetFuncs(t, ksuidApi) 16 | L.Push(t) 17 | return 1 18 | } 19 | 20 | var ksuidApi = map[string]lua.LGFunction{ 21 | "new": ksuidNew, 22 | } 23 | -------------------------------------------------------------------------------- /internal/logger.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/rs/zerolog" 5 | "github.com/yuin/gluamapper" 6 | "github.com/yuin/gopher-lua" 7 | "os" 8 | "strings" 9 | "time" 10 | "fmt" 11 | ) 12 | 13 | var loggerFile *os.File 14 | 15 | const ( 16 | LOGGER_TYPE = "logger{api}" 17 | ) 18 | 19 | type loggerT struct { 20 | logger *zerolog.Logger 21 | } 22 | 23 | func loggerInit(logger zerolog.Logger) *loggerT { 24 | return &loggerT{ 25 | &logger, 26 | } 27 | } 28 | 29 | var loggerExports = map[string]lua.LGFunction{ 30 | "new": loggerNew, 31 | "time": loggerTime, 32 | } 33 | 34 | func loggerTime(L *lua.LState) int { 35 | p := fmt.Sprintf 36 | t := time.Now() 37 | L.Push(lua.LString(p(t.Format(time.RFC3339)))) 38 | return 1 39 | } 40 | 41 | func loggerCheck(L *lua.LState) *loggerT { 42 | ud := L.CheckUserData(1) 43 | if v, ok := ud.Value.(*loggerT); ok { 44 | return v 45 | } else { 46 | return nil 47 | } 48 | } 49 | 50 | func LoggerLoader(L *lua.LState) int { 51 | mod := L.SetFuncs(L.NewTable(), loggerExports) 52 | L.Push(mod) 53 | loggerRegister(L) 54 | return 1 55 | } 56 | 57 | func loggerRegister(L *lua.LState) { 58 | mt := L.NewTypeMetatable(LOGGER_TYPE) 59 | L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), loggerMethods)) 60 | } 61 | 62 | func loggerNew(L *lua.LState) int { 63 | zerolog.TimeFieldFormat = time.RFC3339 64 | ud := L.NewUserData() 65 | a := L.OptString(1, "stderr") 66 | switch a { 67 | case "stdout": 68 | ud.Value = loggerInit(zerolog.New(os.Stdout).With().Timestamp().Logger()) 69 | case "stderr": 70 | ud.Value = loggerInit(zerolog.New(os.Stderr).With().Timestamp().Logger()) 71 | default: 72 | loggerFile, err := os.OpenFile(a, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) 73 | if err != nil { 74 | ud.Value = nil 75 | } else { 76 | ud.Value = loggerInit(zerolog.New(loggerFile).With().Timestamp().Logger()) 77 | } 78 | } 79 | L.SetMetatable(ud, L.GetTypeMetatable(LOGGER_TYPE)) 80 | L.Push(ud) 81 | return 1 82 | } 83 | 84 | func loggerPush(L *lua.LState, event *zerolog.Event) int { 85 | msg := L.CheckString(2) 86 | stubs := L.CheckTable(3) 87 | 88 | gostubs := make(map[string]interface{}) 89 | err := gluamapper.Map(stubs, &gostubs) 90 | 91 | if err != nil { 92 | L.Push(lua.LNil) 93 | L.Push(lua.LString(err.Error())) 94 | return 2 95 | } 96 | 97 | for str, val := range gostubs { 98 | event.Interface(strings.ToLower(str), val) 99 | } 100 | event.Msg(msg) 101 | loggerFile.Close() 102 | return 0 103 | } 104 | 105 | var loggerMethods = map[string]lua.LGFunction{ 106 | "info": func(L *lua.LState) int { 107 | logger := loggerCheck(L) 108 | if logger == nil { 109 | L.Push(lua.LNil) 110 | L.Push(lua.LString("info: Invalid logger.")) 111 | return 2 112 | } 113 | event := logger.logger.Info() 114 | return loggerPush(L, event) 115 | }, 116 | "debug": func(L *lua.LState) int { 117 | logger := loggerCheck(L) 118 | if logger == nil { 119 | L.Push(lua.LNil) 120 | L.Push(lua.LString("info: Invalid logger.")) 121 | return 2 122 | } 123 | 124 | event := logger.logger.Debug() 125 | return loggerPush(L, event) 126 | }, 127 | "warn": func(L *lua.LState) int { 128 | logger := loggerCheck(L) 129 | if logger == nil { 130 | L.Push(lua.LNil) 131 | L.Push(lua.LString("info: Invalid logger.")) 132 | return 2 133 | } 134 | event := logger.logger.Warn() 135 | return loggerPush(L, event) 136 | }, 137 | "error": func(L *lua.LState) int { 138 | logger := loggerCheck(L) 139 | if logger == nil { 140 | L.Push(lua.LNil) 141 | L.Push(lua.LString("info: Invalid logger.")) 142 | return 2 143 | } 144 | event := logger.logger.Error() 145 | return loggerPush(L, event) 146 | }, 147 | } 148 | -------------------------------------------------------------------------------- /internal/lua/extension_exec.lua: -------------------------------------------------------------------------------- 1 | return setmetatable({}, { 2 | __call = function() 3 | exec.cmd = function(exe) 4 | local format, len, gsub, gmatch = string.format, string.len, string.gsub, string.gmatch 5 | local set = {} 6 | return setmetatable(set, { 7 | __call = function(_, a, ...) 8 | local args 9 | if a and type(a) == "string" then 10 | local ergs = format(a, ...) 11 | args = {} 12 | for k in gmatch(ergs, "%S+") do 13 | args[#args + 1] = k 14 | end 15 | end 16 | if a and type(a) == "table" then 17 | args = a 18 | end 19 | local r, so, se, cerr = exec.command( 20 | exe, 21 | args, 22 | set.env, 23 | set.cwd, 24 | set.stdin, 25 | set.timeout 26 | ) 27 | local pretty_prefix = function(header, prefix, str) 28 | local n 29 | if len(str) > 0 then 30 | local replacement = format("\n %s > ", prefix) 31 | str, n = gsub(str, "\n", replacement) 32 | if n == 0 then 33 | str = str .. ("\n %s > "):format(prefix) 34 | end 35 | return format( 36 | "%s\n %s >\n %s > %s\n", 37 | header, 38 | prefix, 39 | prefix, 40 | str 41 | ) 42 | else 43 | return "" 44 | end 45 | end 46 | if set.errexit == true and r == nil then 47 | local err = cerr or set.error or "execution failed" 48 | local header = format('exec.cmd: `%s` => "%s"', exe, err) 49 | if len(so) < 1 and len(se) < 1 then 50 | return fmt.panic("%s\n", header) 51 | else 52 | fmt.print("%s", pretty_prefix(header, "stdout", so)) 53 | return fmt.panic("%s", pretty_prefix(header, "stderr", se)) 54 | end 55 | else 56 | return r, so, se 57 | end 58 | end, 59 | }) 60 | end 61 | 62 | exec.run = setmetatable({}, { 63 | __index = function(_, exe) 64 | return function(...) 65 | local args 66 | if not (...) then 67 | args = {} 68 | elseif type(...) == "table" then 69 | args = ... 70 | else 71 | args = { ... } 72 | end 73 | return exec.command(exe, args) 74 | end 75 | end, 76 | }) 77 | end, 78 | }) 79 | -------------------------------------------------------------------------------- /internal/lua/extension_json.lua: -------------------------------------------------------------------------------- 1 | return setmetatable({}, { 2 | __call = function() 3 | local json = require("json") 4 | local decode = json.decode 5 | json.array = function(j) 6 | local t = decode(j) 7 | local jagen = function(x, i) 8 | i = i + 1 9 | local v = x[i] 10 | if v ~= nil then 11 | return i, v 12 | else 13 | return nil 14 | end 15 | end 16 | return jagen, t, 0 17 | end 18 | json.object = function(j) 19 | local next = next 20 | local t = decode(j) 21 | return next, t, nil 22 | end 23 | end, 24 | }) 25 | -------------------------------------------------------------------------------- /internal/lua/extension_string.lua: -------------------------------------------------------------------------------- 1 | return setmetatable({}, { 2 | __call = function() 3 | local F = string.format 4 | local gmatch = string.gmatch 5 | local find = string.find 6 | local sub = string.sub 7 | local reverse = string.reverse 8 | local gsub = string.gsub 9 | local append = table.insert 10 | local unpack = unpack 11 | local escape = function(s) 12 | return (gsub(s, "[%-%.%+%[%]%(%)%$%^%%%?%*]", "%%%1")) 13 | end 14 | local _strip = function(s, left, right, chrs) 15 | if not chrs then 16 | chrs = "%s" 17 | else 18 | chrs = "[" .. escape(chrs) .. "]" 19 | end 20 | local f = 1 21 | local t 22 | if left then 23 | local i1, i2 = find(s, "^" .. chrs .. "*") 24 | if i2 >= i1 then 25 | f = i2 + 1 26 | end 27 | end 28 | if right then 29 | if #s < 200 then 30 | local i1, i2 = find(s, chrs .. "*$", f) 31 | if i2 >= i1 then 32 | t = i1 - 1 33 | end 34 | else 35 | local rs = reverse(s) 36 | local i1, i2 = find(rs, "^" .. chrs .. "*") 37 | if i2 >= i1 then 38 | t = -i2 - 1 39 | end 40 | end 41 | end 42 | return sub(s, f, t) 43 | end 44 | 45 | string.trim_start = function(s, chrs) 46 | return _strip(s, true, false, chrs) 47 | end 48 | 49 | string.trim_end = function(s, chrs) 50 | return _strip(s, false, true, chrs) 51 | end 52 | 53 | string.split = function(s, re, plain, n) 54 | local i1, ls = 1, {} 55 | if not re then 56 | re = "%s+" 57 | end 58 | if re == "" then 59 | return { s } 60 | end 61 | while true do 62 | local i2, i3 = find(s, re, i1, plain) 63 | if not i2 then 64 | local last = sub(s, i1) 65 | if last ~= "" then 66 | append(ls, last) 67 | end 68 | if #ls == 1 and ls[1] == "" then 69 | return {} 70 | else 71 | return ls 72 | end 73 | end 74 | append(ls, sub(s, i1, i2 - 1)) 75 | if n and #ls == n then 76 | ls[#ls] = sub(s, i1) 77 | return ls 78 | end 79 | i1 = i3 + 1 80 | end 81 | end 82 | 83 | string.splitv = function(s, re, plain, n) 84 | return unpack(string.split(s, re, plain, n)) 85 | end 86 | 87 | string.contains = function(str, a) 88 | return find(str, a, 1, true) 89 | end 90 | 91 | string.append = function(str, a) 92 | return F("%s\n%s", str, a) 93 | end 94 | 95 | string.word_to_list = function(str) 96 | local t = {} 97 | for s in gmatch(str, "%w+") do 98 | t[#t + 1] = s 99 | end 100 | return t 101 | end 102 | 103 | string.to_list = function(str) 104 | local t = {} 105 | for s in gmatch(str, "%S+") do 106 | t[#t + 1] = s 107 | end 108 | return t 109 | end 110 | 111 | string.to_map = function(str, def) 112 | def = def or true 113 | local t = {} 114 | for s in gmatch(str, "%S+") do 115 | t[s] = def 116 | end 117 | return t 118 | end 119 | end, 120 | }) 121 | -------------------------------------------------------------------------------- /internal/lua/fmt.lua: -------------------------------------------------------------------------------- 1 | local fmt = {} 2 | local F = string.format 3 | 4 | fmt.print = function(str, ...) 5 | local stdout = io.stdout 6 | stdout:write(F(str, ...)) 7 | return stdout:flush() 8 | end 9 | 10 | fmt.fprint = function(file, str, ...) 11 | local o = io.output() 12 | io.output(file) 13 | local ret, err = io.write(F(str, ...)) 14 | io.output(o) 15 | return ret, err 16 | end 17 | 18 | local warnf = function(str, ...) 19 | local stderr = io.stderr 20 | stderr:write(F(str, ...)) 21 | stderr:flush() 22 | end 23 | fmt.warn = warnf 24 | 25 | local panicf = function(str, ...) 26 | warnf(str, ...) 27 | os.exit(1) 28 | end 29 | fmt.panic = panicf 30 | 31 | fmt.error = function(str, ...) 32 | return nil, F(str, ...) 33 | end 34 | 35 | fmt.assert = function(v, str, ...) 36 | if v then 37 | return true 38 | else 39 | panicf(str, ...) 40 | end 41 | end 42 | return fmt 43 | -------------------------------------------------------------------------------- /internal/lua/graph.lua: -------------------------------------------------------------------------------- 1 | local setmetatable = setmetatable 2 | local pairs = pairs 3 | local type = type 4 | local function visit(k, n, m, s) 5 | if m[k] == 0 then return 1 end 6 | if m[k] == 1 then return end 7 | m[k] = 0 8 | local f = n[k] 9 | for i=1, #f do 10 | if visit(f[i], n, m, s) then return 1 end 11 | end 12 | m[k] = 1 13 | s[#s+1] = k 14 | end 15 | local tsort = {} 16 | tsort.__index = tsort 17 | function tsort.new() 18 | return setmetatable({ n = {} }, tsort) 19 | end 20 | function tsort:add(...) 21 | local p = { ... } 22 | local c = #p 23 | if c == 0 then return self end 24 | if c == 1 then 25 | p = p[1] 26 | if type(p) == "table" then 27 | c = #p 28 | else 29 | p = { p } 30 | end 31 | end 32 | local n = self.n 33 | for i=1, c do 34 | local f = p[i] 35 | if n[f] == nil then n[f] = {} end 36 | end 37 | for i=2, c, 1 do 38 | local f = p[i] 39 | local t = p[i-1] 40 | local o = n[f] 41 | o[#o+1] = t 42 | end 43 | return self 44 | end 45 | function tsort:sort() 46 | local n = self.n 47 | local s = {} 48 | local m = {} 49 | for k in pairs(n) do 50 | if m[k] == nil then 51 | if visit(k, n, m, s) then 52 | return nil, "There is a circular dependency in the graph. It is not possible to derive a topological sort." 53 | end 54 | end 55 | end 56 | return s 57 | end 58 | return tsort -------------------------------------------------------------------------------- /internal/lua/guard.lua: -------------------------------------------------------------------------------- 1 | local setmetatable = setmetatable 2 | local ipairs = ipairs 3 | local error = error 4 | 5 | local _guardian = function() 6 | local guard = { __when = {} } 7 | 8 | guard.when = function(filter, f) 9 | guard.__when[#guard.__when + 1] = { filter = filter, f = f } 10 | return guard 11 | end 12 | 13 | guard.any = function(f) 14 | guard.__any = f 15 | return guard 16 | end 17 | 18 | return setmetatable(guard, { 19 | __call = function(gg, ...) 20 | for _, g in ipairs(gg.__when) do 21 | if g.filter(...) then 22 | return g.f(...) 23 | end 24 | end 25 | if gg.__any then 26 | return gg.__any(...) 27 | end 28 | return error("guard: No guard defined for given arguments.", 0) 29 | end, 30 | }) 31 | end 32 | 33 | return setmetatable({ 34 | _DESCRIPTION = "Elixir-style guards for Lua", 35 | _VERSION = "guard v0.0.1", 36 | _URL = "http://github.com/Yonaba/guard.lua", 37 | _LICENSE = "MIT LICENSE ", 38 | }, { __call = function() 39 | return _guardian() 40 | end }) 41 | -------------------------------------------------------------------------------- /internal/lua/map.lua: -------------------------------------------------------------------------------- 1 | --- Bimap implementation by Pierre 'catwell' Chapuis 2 | --- MIT licensed (see LICENSE.txt) 3 | 4 | local _newindex = function(l, r) 5 | return function(_, k, v) 6 | if v ~= nil then 7 | if r[v] then 8 | error( 9 | string.format( 10 | "cannot assign value %q to key %q: already assigned to key %q", 11 | tostring(v), tostring(k), tostring(r[v]) 12 | ), 13 | 2 14 | ) 15 | end 16 | r[v] = k 17 | end 18 | if l[k] ~= nil then 19 | r[l[k]] = nil 20 | end 21 | l[k] = v 22 | end 23 | end 24 | 25 | local _call = function(l) 26 | return function(_, x) 27 | if x == "len" then 28 | return #l 29 | elseif x == "raw" then 30 | return l 31 | end 32 | end 33 | end 34 | 35 | local new = function(l_val) 36 | if l_val == nil then 37 | l_val = {} 38 | end 39 | assert(type(l_val) == "table") 40 | local r_val = {} 41 | for k,v in pairs(l_val) do 42 | r_val[v] = k 43 | end 44 | local l_proxy = setmetatable({}, { 45 | __index = l_val, 46 | __len = function(_) 47 | return #l_val 48 | end, 49 | __newindex = _newindex(l_val, r_val), 50 | __call = _call(l_val, r_val), 51 | }) 52 | local r_proxy = setmetatable({}, { 53 | __index = r_val, 54 | __len = function(_) 55 | return #r_val 56 | end, 57 | __newindex = _newindex(r_val, l_val), 58 | __call = _call(r_val, l_val), 59 | }) 60 | return l_proxy, r_proxy 61 | end 62 | 63 | return { 64 | new = new, 65 | } 66 | -------------------------------------------------------------------------------- /internal/lua/queue.lua: -------------------------------------------------------------------------------- 1 | --- Deque implementation by Pierre 'catwell' Chapuis 2 | --- MIT licensed (see LICENSE.txt) 3 | 4 | local push_back = function(self, x) 5 | assert(x ~= nil, "deque.push_back: Value cannot be nil.") 6 | self.tail = self.tail + 1 7 | self[self.tail] = x 8 | end 9 | 10 | local push_front = function(self, x) 11 | assert(x ~= nil, "deque.push_front: Value cannot be nil.") 12 | self[self.head] = x 13 | self.head = self.head - 1 14 | end 15 | 16 | local peek_back = function(self) 17 | return self[self.tail] 18 | end 19 | 20 | local peek_front = function(self) 21 | return self[self.head+1] 22 | end 23 | 24 | local pop_back = function(self) 25 | if self:is_empty() then return nil end 26 | local r = self[self.tail] 27 | self[self.tail] = nil 28 | self.tail = self.tail - 1 29 | return r 30 | end 31 | 32 | local pop_front = function(self) 33 | if self:is_empty() then return nil end 34 | local r = self[self.head+1] 35 | self.head = self.head + 1 36 | r = self[self.head] 37 | self[self.head] = nil 38 | return r 39 | end 40 | 41 | local rotate_back = function(self, n) 42 | n = n or 1 43 | if self:is_empty() then return nil end 44 | for _=1,n do 45 | self:push_front(self:pop_back()) 46 | end 47 | end 48 | 49 | local rotate_front = function(self, n) 50 | n = n or 1 51 | if self:is_empty() then return nil end 52 | for _=1,n do 53 | self:push_back(self:pop_front()) 54 | end 55 | end 56 | 57 | local _remove_at_internal = function(self, idx) 58 | for i=idx, self.tail do 59 | self[i] = self[i+1] 60 | end 61 | self.tail = self.tail - 1 62 | end 63 | 64 | local remove_back = function(self, x) 65 | for i=self.tail,self.head+1,-1 do 66 | if self[i] == x then 67 | _remove_at_internal(self, i) 68 | return true 69 | end 70 | end 71 | return false 72 | end 73 | 74 | local remove_front = function(self, x) 75 | for i=self.head+1,self.tail do 76 | if self[i] == x then 77 | _remove_at_internal(self, i) 78 | return true 79 | end 80 | end 81 | return false 82 | end 83 | 84 | local size = function(self) 85 | return self.tail - self.head 86 | end 87 | 88 | local is_empty = function(self) 89 | return self:size() == 0 90 | end 91 | 92 | local contents = function(self) 93 | local r = {} 94 | for i=self.head+1,self.tail do 95 | r[i-self.head] = self[i] 96 | end 97 | return r 98 | end 99 | 100 | local iter_back = function(self) 101 | local i = self.tail+1 102 | return function() 103 | if i > self.head+1 then 104 | i = i-1 105 | return self[i] 106 | end 107 | end 108 | end 109 | 110 | local iter_front = function(self) 111 | local i = self.head 112 | return function() 113 | if i < self.tail then 114 | i = i+1 115 | return self[i] 116 | end 117 | end 118 | end 119 | 120 | local methods = { 121 | push_back = push_back, 122 | push = push_back, 123 | push_front = push_front, 124 | peek_back = peek_back, 125 | peek_front = peek_front, 126 | pop_back = pop_back, 127 | pop = pop_back, 128 | pop_front = pop_front, 129 | rotate_back = rotate_back, 130 | rotate_front = rotate_front, 131 | remove_back = remove_back, 132 | remove_front = remove_front, 133 | iter_back = iter_back, 134 | iter_front = iter_front, 135 | iterator = iter_front, 136 | size = size, 137 | is_empty = is_empty, 138 | contents = contents, 139 | } 140 | 141 | local new = function() 142 | local r = {head = 0, tail = 0} 143 | return setmetatable(r, {__index = methods}) 144 | end 145 | 146 | return { 147 | new = new, 148 | } 149 | -------------------------------------------------------------------------------- /internal/lz4.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "bytes" 5 | "github.com/pierrec/lz4" 6 | "github.com/yuin/gopher-lua" 7 | "io" 8 | "strings" 9 | ) 10 | 11 | func lz4Compress(L *lua.LState) int { 12 | s := L.CheckString(1) 13 | r := strings.NewReader(s) 14 | pr, pw := io.Pipe() 15 | zw := lz4.NewWriter(pw) 16 | go func() { 17 | _, _ = io.Copy(zw, r) 18 | _ = zw.Close() 19 | _ = pw.Close() 20 | }() 21 | if b, err := io.ReadAll(pr); err == nil { 22 | L.Push(lua.LString(b)) 23 | return 1 24 | } else { 25 | L.Push(lua.LNil) 26 | L.Push(lua.LString("Unable to compress.")) 27 | return 2 28 | } 29 | } 30 | 31 | func lz4Decompress(L *lua.LState) int { 32 | s := L.CheckString(1) 33 | r := strings.NewReader(s) 34 | var out bytes.Buffer 35 | zr := lz4.NewReader(r) 36 | _, err := io.Copy(&out, zr) 37 | if err == nil { 38 | L.Push(lua.LString(out.String())) 39 | return 1 40 | } else { 41 | L.Push(lua.LNil) 42 | L.Push(lua.LString("Unable to decompress.")) 43 | return 2 44 | } 45 | } 46 | 47 | func Lz4Loader(L *lua.LState) int { 48 | t := L.NewTable() 49 | L.SetFuncs(t, lz4Api) 50 | L.Push(t) 51 | return 1 52 | } 53 | 54 | var lz4Api = map[string]lua.LGFunction{ 55 | "compress": lz4Compress, 56 | "decompress": lz4Decompress, 57 | } 58 | -------------------------------------------------------------------------------- /internal/os.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/yuin/gopher-lua" 5 | "net" 6 | "os" 7 | "time" 8 | ) 9 | 10 | func OsHostname(L *lua.LState) int { 11 | name, err := os.Hostname() 12 | if err != nil { 13 | L.Push(lua.LNil) 14 | L.Push(lua.LString(err.Error())) 15 | return 2 16 | } 17 | L.Push(lua.LString(name)) 18 | return 1 19 | } 20 | 21 | func OsSleep(L *lua.LState) int { 22 | n := L.CheckNumber(1) 23 | time.Sleep(time.Duration(n) * time.Millisecond) 24 | L.Push(lua.LTrue) 25 | return 1 26 | } 27 | 28 | func OsOutboundIP(L *lua.LState) int { 29 | conn, err := net.Dial("udp", "1.1.1.1:53") 30 | if err != nil { 31 | L.Push(lua.LNil) 32 | L.Push(lua.LString(err.Error())) 33 | return 2 34 | } 35 | defer conn.Close() 36 | localAddr := conn.LocalAddr().(*net.UDPAddr) 37 | L.Push(lua.LString(localAddr.IP.String())) 38 | return 1 39 | } 40 | -------------------------------------------------------------------------------- /internal/pushover.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/gregdel/pushover" 5 | "github.com/yuin/gopher-lua" 6 | "os" 7 | ) 8 | 9 | const ( 10 | PUSHOVER_TYPE = "pushover{api}" 11 | ) 12 | 13 | func pushoverCheck(L *lua.LState) *pushover.Pushover { 14 | ud := L.CheckUserData(1) 15 | if v, ok := ud.Value.(*pushover.Pushover); ok { 16 | return v 17 | } else { 18 | return nil 19 | } 20 | } 21 | 22 | func pushoverMessage(L *lua.LState) int { 23 | p := pushoverCheck(L) 24 | if p == nil { 25 | L.Push(lua.LNil) 26 | L.Push(lua.LString("pushover.message: Initializing with Pushover token failed.")) 27 | return 2 28 | } 29 | r := L.CheckString(2) 30 | m := L.CheckString(3) 31 | recipient := pushover.NewRecipient(r) 32 | message := pushover.NewMessage(m) 33 | response, err := p.SendMessage(message, recipient) 34 | if err != nil { 35 | L.Push(lua.LNil) 36 | L.Push(lua.LString(err.Error())) 37 | return 2 38 | } 39 | L.Push(lua.LString(response.String())) 40 | return 1 41 | } 42 | 43 | var pushoverMethods = map[string]lua.LGFunction{ 44 | "message": pushoverMessage, 45 | } 46 | 47 | var pushoverExports = map[string]lua.LGFunction{ 48 | "new": pushoverNew, 49 | } 50 | 51 | func PushoverLoader(L *lua.LState) int { 52 | mod := L.SetFuncs(L.NewTable(), pushoverExports) 53 | L.Push(mod) 54 | pushoverRegister(L) 55 | return 1 56 | } 57 | 58 | func pushoverRegister(L *lua.LState) { 59 | mt := L.NewTypeMetatable(PUSHOVER_TYPE) 60 | L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), pushoverMethods)) 61 | } 62 | 63 | func pushoverNew(L *lua.LState) int { 64 | token := os.Getenv("PUSHOVER_TOKEN") 65 | ud := L.NewUserData() 66 | p := pushover.New(token) 67 | ud.Value = p 68 | L.SetMetatable(ud, L.GetTypeMetatable(PUSHOVER_TYPE)) 69 | L.Push(ud) 70 | return 1 71 | } 72 | -------------------------------------------------------------------------------- /internal/refmt.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | gyaml "github.com/ghodss/yaml" 7 | toml "github.com/pelletier/go-toml" 8 | lua "github.com/yuin/gopher-lua" 9 | ) 10 | 11 | func refmtToYAML(L *lua.LState) int { 12 | j := L.CheckString(1) 13 | y, err := gyaml.JSONToYAML([]byte(j)) 14 | if err != nil { 15 | L.Push(lua.LNil) 16 | L.Push(lua.LString(err.Error())) 17 | return 2 18 | } else { 19 | L.Push(lua.LString(y)) 20 | return 1 21 | } 22 | } 23 | 24 | func refmtToJSON(L *lua.LState) int { 25 | y := L.CheckString(1) 26 | j, err := gyaml.YAMLToJSON([]byte(y)) 27 | if err != nil { 28 | L.Push(lua.LNil) 29 | L.Push(lua.LString(err.Error())) 30 | return 2 31 | } else { 32 | L.Push(lua.LString(j)) 33 | return 1 34 | } 35 | } 36 | 37 | func refmtTOMLToJSON(L *lua.LState) int { 38 | s := L.CheckString(1) 39 | var tree *toml.Tree 40 | var err error 41 | if tree, err = toml.LoadBytes([]byte(s)); err != nil { 42 | L.Push(lua.LNil) 43 | L.Push(lua.LString(err.Error())) 44 | return 2 45 | } 46 | var bytes []byte 47 | treeMap := tree.ToMap() 48 | if bytes, err = json.MarshalIndent(treeMap, "", " "); err != nil { 49 | L.Push(lua.LNil) 50 | L.Push(lua.LString(err.Error())) 51 | return 2 52 | } 53 | L.Push(lua.LString(string(bytes[:]))) 54 | return 1 55 | } 56 | 57 | func refmtJSONToTOML(L *lua.LState) int { 58 | s := L.CheckString(1) 59 | jsonMap := make(map[string]interface{}) 60 | var err error 61 | jsonBytes := []byte(s) 62 | if err != nil { 63 | L.Push(lua.LNil) 64 | L.Push(lua.LString(err.Error())) 65 | return 2 66 | } 67 | err = json.Unmarshal(jsonBytes, &jsonMap) 68 | if err != nil { 69 | L.Push(lua.LNil) 70 | L.Push(lua.LString(err.Error())) 71 | return 2 72 | } 73 | tree, err := toml.TreeFromMap(jsonMap) 74 | if err != nil { 75 | L.Push(lua.LNil) 76 | L.Push(lua.LString(err.Error())) 77 | return 2 78 | } 79 | tomlBytes, err := tree.ToTomlString() 80 | if err != nil { 81 | L.Push(lua.LNil) 82 | L.Push(lua.LString(err.Error())) 83 | return 2 84 | } 85 | L.Push(lua.LString(string(tomlBytes[:]))) 86 | return 1 87 | } 88 | 89 | func RefmtLoader(L *lua.LState) int { 90 | t := L.NewTable() 91 | L.SetFuncs(t, refmtApi) 92 | L.Push(t) 93 | return 1 94 | } 95 | 96 | var refmtApi = map[string]lua.LGFunction{ 97 | "json": refmtToJSON, 98 | "yaml": refmtToYAML, 99 | "yaml_to_json": refmtToJSON, 100 | "json_to_yaml": refmtToYAML, 101 | "toml_to_json": refmtTOMLToJSON, 102 | "json_to_toml": refmtJSONToTOML, 103 | } 104 | -------------------------------------------------------------------------------- /internal/slack.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/slack-go/slack" 5 | "github.com/yuin/gluamapper" 6 | "github.com/yuin/gopher-lua" 7 | "os" 8 | ) 9 | 10 | func slackWebhookMessage(L *lua.LState) int { 11 | a := L.CheckString(1) 12 | msg := slack.WebhookMessage{ 13 | Text: a, 14 | } 15 | token := "https://hooks.slack.com/services/" + os.Getenv("SLACK_WEBHOOK") 16 | err := slack.PostWebhook(token, &msg) 17 | if err != nil { 18 | L.Push(lua.LNil) 19 | L.Push(lua.LString(err.Error())) 20 | return 2 21 | } 22 | L.Push(lua.LTrue) 23 | return 1 24 | } 25 | 26 | func slackWebhookAttachment(L *lua.LState) int { 27 | a := L.CheckTable(1) 28 | var gostubs slack.Attachment 29 | { 30 | err := gluamapper.Map(a, &gostubs) 31 | 32 | if err != nil { 33 | L.Push(lua.LNil) 34 | L.Push(lua.LString(err.Error())) 35 | return 2 36 | } 37 | } 38 | msg := slack.WebhookMessage{ 39 | Attachments: []slack.Attachment{gostubs}, 40 | } 41 | token := "https://hooks.slack.com/services/" + os.Getenv("SLACK_WEBHOOK") 42 | err := slack.PostWebhook(token, &msg) 43 | if err != nil { 44 | L.Push(lua.LNil) 45 | L.Push(lua.LString(err.Error())) 46 | return 2 47 | } 48 | L.Push(lua.LTrue) 49 | return 1 50 | } 51 | 52 | func SlackLoader(L *lua.LState) int { 53 | t := L.NewTable() 54 | L.SetFuncs(t, slackApi) 55 | L.Push(t) 56 | return 1 57 | } 58 | 59 | var slackApi = map[string]lua.LGFunction{ 60 | "attachment": slackWebhookAttachment, 61 | "message": slackWebhookMessage, 62 | } 63 | -------------------------------------------------------------------------------- /internal/ssh_config.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/kevinburke/ssh_config" 8 | "github.com/yuin/gopher-lua" 9 | ) 10 | 11 | func sshconfigPort(L *lua.LState) int { 12 | host := L.CheckString(1) 13 | port := ssh_config.Get(host, "Port") 14 | if port != "" { 15 | L.Push(lua.LString(port)) 16 | return 1 17 | } else { 18 | L.Push(lua.LNil) 19 | L.Push(lua.LString("ssh_config: No such host.")) 20 | return 2 21 | } 22 | 23 | } 24 | 25 | func sshconfigIdentityFile(L *lua.LState) int { 26 | host := L.CheckString(1) 27 | key := ssh_config.Get(host, "IdentityFile") 28 | if key != "" { 29 | L.Push(lua.LString(key)) 30 | return 1 31 | } else { 32 | L.Push(lua.LNil) 33 | L.Push(lua.LString("ssh_config: No such host.")) 34 | return 2 35 | } 36 | } 37 | 38 | func sshconfigHostname(L *lua.LState) int { 39 | host := L.CheckString(1) 40 | hn := ssh_config.Get(host, "Hostname") 41 | if hn != "" { 42 | L.Push(lua.LString(hn)) 43 | return 1 44 | } else { 45 | L.Push(lua.LNil) 46 | L.Push(lua.LString("ssh_config: No such host.")) 47 | return 2 48 | } 49 | } 50 | 51 | func sshconfigHosts(L *lua.LState) int { 52 | f, ferr := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "config")) 53 | if ferr != nil { 54 | L.Push(lua.LNil) 55 | L.Push(lua.LString(ferr.Error())) 56 | return 2 57 | } 58 | // TODO: cerr does not do anything? 59 | c, cerr := ssh_config.Decode(f) 60 | if cerr != nil { 61 | L.Push(lua.LNil) 62 | L.Push(lua.LString(cerr.Error())) 63 | return 2 64 | } 65 | t := L.NewTable() 66 | for _, host := range c.Hosts { 67 | for _, pat := range host.Patterns { 68 | s := pat.String() 69 | if s != "*" { 70 | t.Append(lua.LString(s)) 71 | } 72 | } 73 | } 74 | L.Push(t) 75 | return 1 76 | } 77 | 78 | func SSHconfigLoader(L *lua.LState) int { 79 | t := L.NewTable() 80 | L.SetFuncs(t, sshconfigApi) 81 | L.Push(t) 82 | return 1 83 | } 84 | 85 | var sshconfigApi = map[string]lua.LGFunction{ 86 | "port": sshconfigPort, 87 | "identity_file": sshconfigIdentityFile, 88 | "hostname": sshconfigHostname, 89 | "hosts": sshconfigHosts, 90 | } 91 | -------------------------------------------------------------------------------- /internal/telegram.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/go-telegram-bot-api/telegram-bot-api" 5 | "github.com/yuin/gopher-lua" 6 | "os" 7 | ) 8 | 9 | const ( 10 | TELEGRAM_TYPE = "telegram{bot}" 11 | ) 12 | 13 | func telegramCheck(L *lua.LState) *tgbotapi.BotAPI { 14 | ud := L.CheckUserData(1) 15 | if v, ok := ud.Value.(*tgbotapi.BotAPI); ok { 16 | return v 17 | } else { 18 | return nil 19 | } 20 | } 21 | 22 | func telegramMessage(L *lua.LState) int { 23 | bot := telegramCheck(L) 24 | if bot == nil { 25 | L.Push(lua.LNil) 26 | L.Push(lua.LString("telegram.message: Initializing with Telegram token failed.")) 27 | return 2 28 | } 29 | i := L.CheckInt64(2) 30 | m := L.CheckString(3) 31 | msg := tgbotapi.NewMessage(i, m) 32 | if _, err := bot.Send(msg); err != nil { 33 | L.Push(lua.LNil) 34 | L.Push(lua.LString(err.Error())) 35 | return 2 36 | } 37 | L.Push(lua.LTrue) 38 | return 1 39 | } 40 | 41 | func telegramChannelMessage(L *lua.LState) int { 42 | bot := telegramCheck(L) 43 | if bot == nil { 44 | L.Push(lua.LNil) 45 | L.Push(lua.LString("telegram.channel: Initializing with Telegram token failed.")) 46 | return 1 47 | } 48 | c := L.CheckString(2) 49 | m := L.CheckString(3) 50 | msg := tgbotapi.NewMessageToChannel(c, m) 51 | if _, err := bot.Send(msg); err != nil { 52 | L.Push(lua.LNil) 53 | L.Push(lua.LString(err.Error())) 54 | return 2 55 | } 56 | L.Push(lua.LTrue) 57 | return 1 58 | } 59 | 60 | var telegramMethods = map[string]lua.LGFunction{ 61 | "message": telegramMessage, 62 | "channel": telegramChannelMessage, 63 | } 64 | 65 | var telegramExports = map[string]lua.LGFunction{ 66 | "new": telegramNew, 67 | } 68 | 69 | func TelegramLoader(L *lua.LState) int { 70 | mod := L.SetFuncs(L.NewTable(), telegramExports) 71 | L.Push(mod) 72 | telegramRegister(L) 73 | return 1 74 | } 75 | 76 | func telegramRegister(L *lua.LState) { 77 | mt := L.NewTypeMetatable(TELEGRAM_TYPE) 78 | L.SetField(mt, "__index", L.SetFuncs(L.NewTable(), telegramMethods)) 79 | } 80 | 81 | func telegramNew(L *lua.LState) int { 82 | token := os.Getenv("TELEGRAM_TOKEN") 83 | ud := L.NewUserData() 84 | bot, err := tgbotapi.NewBotAPI(token) 85 | if err == nil { 86 | ud.Value = bot 87 | } else { 88 | ud.Value = nil 89 | } 90 | L.SetMetatable(ud, L.GetTypeMetatable(TELEGRAM_TYPE)) 91 | L.Push(ud) 92 | return 1 93 | } 94 | -------------------------------------------------------------------------------- /internal/ulid.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "bytes" 5 | "github.com/oklog/ulid/v2" 6 | "github.com/yuin/gopher-lua" 7 | "hash/maphash" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | func ulidNew(L *lua.LState) int { 13 | b := []byte(strconv.FormatUint(new(maphash.Hash).Sum64(), 10)) 14 | e := bytes.NewReader(b) 15 | id, err := ulid.New(ulid.Timestamp(time.Now()), e) 16 | if err != nil { 17 | L.Push(lua.LNil) 18 | L.Push(lua.LString(err.Error())) 19 | return 2 20 | } 21 | L.Push(lua.LString(id.String())) 22 | return 1 23 | } 24 | 25 | func UlidLoader(L *lua.LState) int { 26 | t := L.NewTable() 27 | L.SetFuncs(t, ulidApi) 28 | L.Push(t) 29 | return 1 30 | } 31 | 32 | var ulidApi = map[string]lua.LGFunction{ 33 | "new": ulidNew, 34 | } 35 | -------------------------------------------------------------------------------- /internal/uuid.go: -------------------------------------------------------------------------------- 1 | package ll 2 | 3 | import ( 4 | "github.com/hashicorp/go-uuid" 5 | "github.com/yuin/gopher-lua" 6 | ) 7 | 8 | func uuidNew(L *lua.LState) int { 9 | id, err := uuid.GenerateUUID() 10 | if err != nil { 11 | L.Push(lua.LNil) 12 | L.Push(lua.LString(err.Error())) 13 | return 2 14 | } 15 | L.Push(lua.LString(id)) 16 | return 1 17 | } 18 | 19 | func UuidLoader(L *lua.LState) int { 20 | t := L.NewTable() 21 | L.SetFuncs(t, uuidApi) 22 | L.Push(t) 23 | return 1 24 | } 25 | 26 | var uuidApi = map[string]lua.LGFunction{ 27 | "new": uuidNew, 28 | } 29 | -------------------------------------------------------------------------------- /scripts/.lib/000-header.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | unset IFS 3 | set -o errexit -o nounset -o noglob 4 | export PATH=/bin:/sbin:/usr/bin:/usr/sbin 5 | export LC_ALL=C 6 | 7 | __mark() 8 | { 9 | printf >&2 "► “%s”\\n" "$*" 10 | printf "► “%s”\\n" "$*" 11 | } 12 | -------------------------------------------------------------------------------- /scripts/.lib/001-go.sh: -------------------------------------------------------------------------------- 1 | _go_os() 2 | { 3 | local os 4 | os=$(uname -s | tr '[:upper:]' '[:lower:]') 5 | printf "%s" "${os}" 6 | } 7 | -------------------------------------------------------------------------------- /scripts/build-dsl/script: -------------------------------------------------------------------------------- 1 | mkdir -p "cmd/$1" 2 | sed "s|//__DSL__||" cmd/dsl/main.go > "cmd/$1/main.go" 3 | sed -i "s|__DSLMOD__|$1|g" "cmd/$1/main.go" 4 | GOOS=$(_go_os) CGO_ENABLED=0 go build \ 5 | -trimpath -ldflags '-s -w -extldflags "-static"' \ 6 | -o "bin/$1" "./cmd/$1" 7 | -------------------------------------------------------------------------------- /scripts/build-pie/script: -------------------------------------------------------------------------------- 1 | GOOS=linux CGO_ENABLED=0 go build \ 2 | -buildmode=pie -trimpath -ldflags '-s -w -extldflags "-static -Wl,-z,now,-z,relro"' \ 3 | -o "ll" . 4 | -------------------------------------------------------------------------------- /scripts/build-standalone/script: -------------------------------------------------------------------------------- 1 | GOOS=$(_go_os) CGO_ENABLED=0 go build \ 2 | -trimpath -ldflags '-s -w -extldflags "-static"' \ 3 | -o "bin/$1" "./cmd/$1" 4 | -------------------------------------------------------------------------------- /scripts/build-test/script: -------------------------------------------------------------------------------- 1 | cp cmd/standalone/main.go cmd/test 2 | GOOS=$(_go_os) CGO_ENABLED=0 go build \ 3 | -trimpath -ldflags '-s -w -extldflags "-static"' \ 4 | -o bin/test "./cmd/test" 5 | -------------------------------------------------------------------------------- /scripts/build/script: -------------------------------------------------------------------------------- 1 | GOOS=$(_go_os) CGO_ENABLED=0 go build \ 2 | -trimpath -ldflags '-s -w -extldflags "-static"' \ 3 | -o "bin/ll" ./cmd/ll 4 | -------------------------------------------------------------------------------- /scripts/docs/script: -------------------------------------------------------------------------------- 1 | awk '/^.*--#/{$1="";print $0}/^.*--#$/{print "\\n"}' "tests/${1}.lua" | cut -f2- -d' ' > "docs/${1}.adoc" 2 | -------------------------------------------------------------------------------- /scripts/godocs/script: -------------------------------------------------------------------------------- 1 | awk '/^.*\/\/#/{$1="";print $0}/^.*\/\/#$/{print "\\n"}' "${1}.go" | cut -f2- -d' ' > "docs/go-${1}.adoc" 2 | -------------------------------------------------------------------------------- /scripts/install/script: -------------------------------------------------------------------------------- 1 | cp "$1/github/LadyLua/ll" /usr/bin/ll 2 | -------------------------------------------------------------------------------- /scripts/lint/script: -------------------------------------------------------------------------------- 1 | __mark gosec -exlude-dir external . 2 | $HOME/bin/gosec.v2.7.0 -exclude-dir external . 3 | __mark staticcheck *.go 4 | $HOME/bin/staticcheck.2020.2.3 exec.go fs.go global.go html.go lib.go ll.go os.go uid.go 5 | __mark selene lua/*.lua 6 | $HOME/bin/selene.0.11.0 lua/exec.lua lua/fmt.lua lua/table.lua lua/string.lua 7 | -------------------------------------------------------------------------------- /scripts/show-functions/script: -------------------------------------------------------------------------------- 1 | grep -F "function(" "lua/${1}.lua" 2 | -------------------------------------------------------------------------------- /scripts/template-test/script: -------------------------------------------------------------------------------- 1 | cp scripts/template-test/template.txt "tests/$1.lua" 2 | -------------------------------------------------------------------------------- /scripts/template-test/template.txt: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require 'test' 3 | --# = ?? 4 | --# :toc: 5 | --# :toc-placement!: 6 | --# 7 | --# ???/ 8 | --# 9 | --# toc::[] 10 | --# 11 | --# == *??.??*(_String_, _String_) 12 | --# ?? 13 | --# 14 | --# === Arguments 15 | --# [options="header",width="72%"] 16 | --# |=== 17 | --# |Type |Description 18 | --# |??| ?? 19 | --# |??| ?? 20 | --# |=== 21 | --# 22 | --# === Returns 23 | --# [options="header",width="72%"] 24 | --# |=== 25 | --# |Type |Description 26 | --# |??| ?? 27 | --# |=== 28 | local ??_?? = function() 29 | T.is_function(??.??) 30 | local x = "one" 31 | local y = "two" 32 | local z = ??.??(x, y) 33 | T.equal(z, "one\ntwo") 34 | end 35 | if included then 36 | return function() 37 | T["??.??"] = ??_?? 38 | end 39 | else 40 | T["??.??"] = ??_?? 41 | end 42 | -------------------------------------------------------------------------------- /scripts/test/script: -------------------------------------------------------------------------------- 1 | bin/test 2 | -------------------------------------------------------------------------------- /selene.toml: -------------------------------------------------------------------------------- 1 | std = "ll" 2 | 3 | [rules] 4 | global_usage = "allow" 5 | -------------------------------------------------------------------------------- /stylua.toml: -------------------------------------------------------------------------------- 1 | quote_style = "AutoPreferDouble" 2 | indent_width = 8 3 | indent_type = "Tabs" 4 | column_width = 120 5 | -------------------------------------------------------------------------------- /tests/argparse.lua: -------------------------------------------------------------------------------- 1 | package.path = "internal/lua/?.lua" 2 | local included = pcall(debug.getlocal, 4, 1) 3 | local T = require("test") 4 | local A = require("argparse") 5 | local expect = T.expect 6 | local is_table = T.is_table 7 | local new = function() 8 | local parser = A() 9 | local p = parser:parse({}) 10 | is_table(p) 11 | end 12 | local one_arg = function() 13 | local parser = A() 14 | parser:argument("foo") 15 | local args = parser:parse({"bar"}) 16 | expect("bar")(args.foo) 17 | end 18 | local opt_arg = function() 19 | local parser = A() 20 | parser:argument("foo"):args("?") 21 | local args = parser:parse({"bar"}) 22 | expect("bar")(args.foo) 23 | end 24 | local many_args = function() 25 | local parser = A() 26 | parser:argument("foo1") 27 | parser:argument("foo2") 28 | local args = parser:parse({"bar", "baz"}) 29 | expect("bar")(args.foo1) 30 | expect("baz")(args.foo2) 31 | end 32 | local wildcard_args = function() 33 | local parser = A() 34 | parser:argument("foo"):args("*") 35 | local args = parser:parse({"bar", "baz", "qu"}) 36 | expect("bar")(args.foo[1]) 37 | expect("baz")(args.foo[2]) 38 | expect("qu")(args.foo[3]) 39 | end 40 | local hyphens = function() 41 | local parser = A() 42 | parser:argument("foo") 43 | local args = parser:parse({"-"}) 44 | expect("-")(args.foo) 45 | args = parser:parse({"--", "-q"}) 46 | expect("-q")(args.foo) 47 | end 48 | local commands_after_args = function() 49 | local parser = A("name") 50 | parser:argument("file") 51 | parser:command("create") 52 | parser:command("remove") 53 | local args = parser:parse{"temp.txt", "remove"} 54 | expect("temp.txt")(args.file) 55 | expect(true)(args.remove) 56 | end 57 | local command_flags = function() 58 | local parser = A("name") 59 | local install = parser:command("install") 60 | install:flag "-q" "--quiet" 61 | local args = parser:parse{"install", "-q"} 62 | expect(true)(args.install) 63 | expect(true)(args.quiet) 64 | end 65 | local nested_commands = function() 66 | local parser = A("name") 67 | local install = parser:command("install") 68 | install:command("bar") 69 | local args = parser:parse{"install", "bar"} 70 | expect(true)(args.install) 71 | expect(true)(args.bar) 72 | end 73 | local action_args = function() 74 | local parser = A() 75 | local foo 76 | parser:argument("foo"):action(function(_, _, passed) 77 | foo = passed 78 | end) 79 | local baz 80 | parser:argument("baz"):args("*"):action(function(_, _, passed) 81 | baz = passed 82 | end) 83 | parser:parse({"a"}) 84 | expect("a")(foo) 85 | expect(0)(#baz) 86 | parser:parse({"b", "c"}) 87 | expect("b")(foo) 88 | expect("c")(baz[1]) 89 | parser:parse({"d", "e", "f"}) 90 | expect("d")(foo) 91 | expect("e")(baz[1]) 92 | expect("f")(baz[2]) 93 | end 94 | if included then 95 | return function() 96 | T["new parser"] = new 97 | T["one argument"] = one_arg 98 | T["optional argument"] = opt_arg 99 | T["several arguments"] = many_args 100 | T["wildcard arguments"] = wildcard_args 101 | T["hyphens"] = hyphens 102 | T["commands after arguments"] = commands_after_args 103 | T["command flags"] = command_flags 104 | T["nested commands"] = nested_commands 105 | T["action on arguments"] = action_args 106 | end 107 | else 108 | T["new parser"] = new 109 | T["one argument"] = one_arg 110 | T["optional argument"] = opt_arg 111 | T["several arguments"] = many_args 112 | T["wildcard arguments"] = wildcard_args 113 | T["hyphens"] = hyphens 114 | T["commands after arguments"] = commands_after_args 115 | T["command flags"] = command_flags 116 | T["nested commands"] = nested_commands 117 | T["action on arguments"] = action_args 118 | end 119 | -------------------------------------------------------------------------------- /tests/exec_cmd.lua: -------------------------------------------------------------------------------- 1 | local l = exec.cmd("l") 2 | l() 3 | fmt.print("first l() ok\n") 4 | fmt.print("next l() will fail\n") 5 | l.error = "custom error" 6 | l.errexit = true 7 | l() 8 | -------------------------------------------------------------------------------- /tests/extension_json.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local J = require("json") 4 | extend("json") 5 | local expect = T.expect 6 | --# = extension: json 7 | --# :toc: 8 | --# :toc-placement!: 9 | --# 10 | --# Additional functions for the `json` module. 11 | --# 12 | --# To load and patch `json` namespace: 13 | --# ---- 14 | --# extend("json") 15 | --# ---- 16 | --# 17 | --# toc::[] 18 | --# 19 | --# == *json.array*(_String_) 20 | --# JSON array iterator 21 | --# 22 | --# === Arguments 23 | --# [options="header",width="72%"] 24 | --# |=== 25 | --# |Type |Description 26 | --# |string |JSON 27 | --# |=== 28 | local json_array = function() 29 | T.is_function(J.array) 30 | local t = { 31 | "one", 32 | "two", 33 | "three", 34 | } 35 | local e = {} 36 | for x, y in J.array(J.encode(t)) do 37 | e[x] = y 38 | end 39 | expect(t[1])(e[1]) 40 | expect(t[2])(e[2]) 41 | expect(t[3])(e[3]) 42 | end 43 | --# 44 | --# == *json.object*(_String_) 45 | --# JSON object iterator. 46 | --# 47 | --# === Arguments 48 | --# [options="header",width="72%"] 49 | --# |=== 50 | --# |Type |Description 51 | --# |string |JSON 52 | --# |=== 53 | local json_object = function() 54 | T.is_function(J.object) 55 | local t = { 56 | one = 1, 57 | two = 2, 58 | three = 3, 59 | } 60 | local e = {} 61 | for x, y in J.object(J.encode(t)) do 62 | e[x] = y 63 | end 64 | expect(t.one)(e.one) 65 | expect(t.two)(e.two) 66 | expect(t.three)(e.three) 67 | end 68 | if included then 69 | return function() 70 | T["json array iterator"] = json_array 71 | T["json object iterator"] = json_object 72 | end 73 | else 74 | T["json array iterator"] = json_array 75 | T["json object iterator"] = json_object 76 | end 77 | -------------------------------------------------------------------------------- /tests/fmt.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local fmt = require("fmt") 3 | local T = require("test") 4 | --# = fmt 5 | --# :toc: 6 | --# :toc-placement!: 7 | --# 8 | --# Format string variants that wraps `string.format`. + 9 | --# 10 | --# toc::[] 11 | --# 12 | --# == *fmt.print*(_String_, _..._) 13 | --# Print formatted string to io.stdout. 14 | --# 15 | --# === Arguments 16 | --# [width="72%"] 17 | --# |=== 18 | --# |string| Format string 19 | --# |...| Values for the format string 20 | --# |=== 21 | local fmt_print = function() 22 | T.is_function(fmt.print) 23 | local x = "prints to STDOUT" 24 | if not included then 25 | fmt.print("%s\n", x) 26 | end 27 | end 28 | --# 29 | --# == *fmt.warn*(_String_, _..._) 30 | --# Print formatted string to io.stderr. 31 | --# 32 | --# === Arguments 33 | --# [width="72%"] 34 | --# |=== 35 | --# |string| Format string 36 | --# |...| Values for the format string 37 | --# |=== 38 | local fmt_warn = function() 39 | T.is_function(fmt.warn) 40 | local x = "prints to STDERR" 41 | if not included then 42 | fmt.warn("%s\n", x) 43 | end 44 | end 45 | --# 46 | --# == *fmt.error*(_String_, _..._) -> _Nil_, _String_ 47 | --# Shortcut for following the Lua convention of returning `nil` and `string` during error conditions. 48 | --# 49 | --# === Arguments 50 | --# [width="72%"] 51 | --# |=== 52 | --# |string| Format string 53 | --# |...| Values for the format string 54 | --# |=== 55 | --# 56 | --# === Returns 57 | --# [width="72%"] 58 | --# |=== 59 | --# |nil| nil 60 | --# |string| Error message 61 | --# |=== 62 | local fmt_error = function() 63 | T.is_function(fmt.warn) 64 | local x, y = fmt.error("%s", "message") 65 | T.is_nil(x) 66 | T.equal(y, "message") 67 | end 68 | --# 69 | --# == *fmt.panic*(_String_, _..._) 70 | --# Print formatted string to io.stderr and exit immediately with code 1. 71 | --# 72 | --# === Arguments 73 | --# [width="72%"] 74 | --# |=== 75 | --# |string| Format string 76 | --# |...| Values for the format string 77 | --# |=== 78 | local fmt_panic = function() 79 | T.is_function(fmt.panic) 80 | local x = "prints to STDERR and exit with code 1" 81 | if not included then 82 | fmt.panic("%s\n", x) 83 | end 84 | end 85 | --# 86 | --# == *fmt.assert*(_Value_, _String_, _..._) 87 | --# Print formatted string to io.stderr and exit immediately with code 1 if argument #1 is falsy(nil or false). 88 | --# 89 | --# === Arguments 90 | --# [width="72%"] 91 | --# |=== 92 | --# |value| Any Lua type that can return nil or false 93 | --# |string| Format string 94 | --# |...| Values for the format string 95 | --# |=== 96 | local fmt_assert = function() 97 | T.is_function(fmt.assert) 98 | local x = "prints to STDERR when argument #1 is falsy" 99 | if not included then 100 | fmt.assert(false, "%s\n", x) 101 | end 102 | end 103 | if included then 104 | return function() 105 | T["fmt.print"] = fmt_print 106 | T["fmt.warn"] = fmt_warn 107 | T["fmt.error"] = fmt_error 108 | T["fmt.panic"] = fmt_panic 109 | T["fmt.assert"] = fmt_assert 110 | end 111 | else -- ran 112 | fmt_print() 113 | fmt_warn() 114 | fmt_error() 115 | fmt_panic() 116 | fmt_assert() 117 | end 118 | -------------------------------------------------------------------------------- /tests/fsnotify.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local fsnotify = require("fsnotify") 4 | --# = fsnotify 5 | --# :toc: 6 | --# :toc-placement!: 7 | --# 8 | --# Wait for filesystem create, delete, and write events. All functions block until the specified event is detected. 9 | --# 10 | --# toc::[] 11 | --# 12 | --# == *fsnotify.create*(_String_) -> _Boolean_ 13 | --# Wait for a create event on path. 14 | --# 15 | --# === Arguments 16 | --# [options="header",width="72%"] 17 | --# |=== 18 | --# |Type |Description 19 | --# |string |Path to wait on 20 | --# |=== 21 | --# 22 | --# === Returns 23 | --# [options="header",width="72%"] 24 | --# |=== 25 | --# |Type |Description 26 | --# |boolean |`true` if create event happened 27 | --# |=== 28 | local fsnotify_create = function() 29 | T.is_function(fsnotify.create) 30 | end 31 | --# 32 | --# == *fsnotify.write*(_String_) -> _Boolean_ 33 | --# Wait for a write event on path. 34 | --# 35 | --# === Arguments 36 | --# [options="header",width="72%"] 37 | --# |=== 38 | --# |Type |Description 39 | --# |string |Path to wait on 40 | --# |=== 41 | --# 42 | --# === Returns 43 | --# [options="header",width="72%"] 44 | --# |=== 45 | --# |Type |Description 46 | --# |boolean |`true` if write event happened 47 | --# |=== 48 | local fsnotify_write = function() 49 | T.is_function(fsnotify.write) 50 | end 51 | --# 52 | --# == *fsnotify.remove*(_String_) -> _Boolean_ 53 | --# Wait for a remove event on path. 54 | --# 55 | --# === Arguments 56 | --# [options="header",width="72%"] 57 | --# |=== 58 | --# |Type |Description 59 | --# |string |Path to wait on 60 | --# |=== 61 | --# 62 | --# === Returns 63 | --# [options="header",width="72%"] 64 | --# |=== 65 | --# |Type |Description 66 | --# |boolean |`true` if remove event happened 67 | --# |=== 68 | local fsnotify_remove = function() 69 | T.is_function(fsnotify.remove) 70 | end 71 | if included then 72 | return function() 73 | T["fsnotify.create"] = fsnotify_create 74 | T["fsnotify.write"] = fsnotify_write 75 | T["fsnotify.remove"] = fsnotify_remove 76 | end 77 | else 78 | T["fsnotify.create"] = fsnotify_create 79 | T["fsnotify.write"] = fsnotify_write 80 | T["fsnotify.remove"] = fsnotify_remove 81 | end 82 | -------------------------------------------------------------------------------- /tests/graph.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local graph = require("graph") 3 | local G = graph.new() 4 | local T = require("test") 5 | local expect = T.expect 6 | 7 | local one = function() 8 | G:add("a", "b") 9 | G:add("b", "c") 10 | G:add("0", "a") 11 | local t1 = G:sort() 12 | expect("0")(t1[1]) 13 | expect("a")(t1[2]) 14 | expect("b")(t1[3]) 15 | expect("c")(t1[4]) 16 | end 17 | local two = function() 18 | G:add("1", "2", "3", "a") 19 | local t2 = G:sort() 20 | expect("0")(t2[1]) 21 | expect("1")(t2[2]) 22 | expect("2")(t2[3]) 23 | expect("3")(t2[4]) 24 | expect("a")(t2[5]) 25 | expect("b")(t2[6]) 26 | expect("c")(t2[7]) 27 | end 28 | local three = function() 29 | G:add({ "1", "1.5" }) 30 | G:add({ "1.5", "a" }) 31 | local t3 = G:sort() 32 | expect("0")(t3[1]) 33 | expect("1")(t3[2]) 34 | expect("2")(t3[3]) 35 | expect("3")(t3[4]) 36 | expect("1.5")(t3[5]) 37 | expect("a")(t3[6]) 38 | expect("b")(t3[7]) 39 | expect("c")(t3[8]) 40 | end 41 | local fail = function() 42 | G:add("first", "second") 43 | G:add("second", "third", "first") 44 | local sorted, err = G:sort() 45 | expect(nil)(sorted) 46 | expect("There is a circular dependency in the graph. It is not possible to derive a topological sort.")(err) 47 | end 48 | if included then 49 | return function() 50 | T["graph #1"] = one 51 | T["graph #2"] = two 52 | T["graph #3"] = three 53 | T["graph fail"] = fail 54 | end 55 | else 56 | T["graph #1"] = one 57 | T["graph #2"] = two 58 | T["graph #3"] = three 59 | T["graph fail"] = fail 60 | end 61 | -------------------------------------------------------------------------------- /tests/guard.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local G = require("guard") 4 | local expect = T.expect 5 | --# = guard 6 | --# :toc: 7 | --# :toc-placement!: 8 | --# 9 | --# Elixir-style guards. One way to avoid nested conditionals. 10 | --# 11 | --# toc::[] 12 | --# 13 | --# == *guard*() -> _Table_ 14 | --# Returns new guard factory. 15 | --# 16 | --# === Returns 17 | --# [options="header",width="72%"] 18 | --# |=== 19 | --# |Type |Description 20 | --# |table |Guardian table 21 | --# |=== 22 | local guard = function() 23 | T.is_table(G) 24 | local tbl = G() 25 | T.is_table(tbl) 26 | end 27 | --# 28 | --# == *.any*(_function_) 29 | --# Fallthrough for a guard chain. 30 | --# 31 | --# === Arguments 32 | --# [options="header",width="72%"] 33 | --# |=== 34 | --# |type |description 35 | --# |function |Default case function 36 | --# |=== 37 | local any = function() 38 | local tbl = G() 39 | local f = function() 40 | return "default" 41 | end 42 | local f2 = function() 43 | return "new_any" 44 | end 45 | local g = tbl.any(f) 46 | expect("default")(g()) 47 | g.any(f2) 48 | expect("new_any")(g()) 49 | end 50 | --# 51 | --# == *.when*(_function_, _function_) 52 | --# Expects two functions arguments: the first one being a filter function, and the second one being a function to be evaluated. the filter function should return a boolean. if it returns `true`, the second function argument is evaluated and the guard returns itself right after. 53 | --# 54 | --# === Arguments 55 | --# [options="header",width="72%"] 56 | --# |=== 57 | --# |type |description 58 | --# |function |filter 59 | --# |function |main function 60 | --# |=== 61 | local when = function() 62 | local tbl = G() 63 | T.is_function(tbl.when) 64 | local x = tbl.when(function() 65 | end, function() 66 | end) 67 | T.is_table(x) 68 | T.error_raised(x) 69 | local is_odd = function(n) 70 | return n % 2 ~= 0 71 | end 72 | local double = function(n) 73 | return n * 2 74 | end 75 | local g = G().when(is_odd, double) 76 | expect(6)(g(3)) 77 | T.error_raised(g, "guard: No guard defined for given arguments.", 0) 78 | g.any(function() 79 | return "default case" 80 | end) 81 | expect("default case")(g(2)) 82 | end 83 | local chain = function() 84 | local g = G() 85 | local truthy = function() 86 | return true 87 | end 88 | local falsy = function() 89 | return false 90 | end 91 | local f_one = function() 92 | return 1 93 | end 94 | local f_two = function() 95 | return 2 96 | end 97 | local f_three = function() 98 | return 3 99 | end 100 | local f_four = function() 101 | return 4 102 | end 103 | g.when(falsy, f_one).when(truthy, f_two).when(f_three).when(f_four) 104 | local h = G().when(falsy, f_one).when(falsy, f_two).when(falsy, f_three) 105 | expect(2)(g()) 106 | T.not_equal(1, g()) --falsy 107 | T.not_equal(3, g()) --falsy 108 | T.not_equal(4, g()) --FIFO 109 | T.error_raised(h) 110 | h.any(f_four) 111 | expect(4)(h()) 112 | end 113 | if included then 114 | return function() 115 | T["guard"] = guard 116 | T["any"] = any 117 | T["when"] = when 118 | T["chain"] = chain 119 | end 120 | else 121 | T["guard"] = guard 122 | T["any"] = any 123 | T["when"] = when 124 | T["chain"] = chain 125 | end 126 | -------------------------------------------------------------------------------- /tests/ksuid.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | --# = ksuid 4 | --# :toc: 5 | --# :toc-placement!: 6 | --# 7 | --# Generate unique IDs. This is a https://github.com/segmentio/ksuid[ksuid] wrapper. 8 | --# 9 | --# toc::[] 10 | --# 11 | --# == *ksuid.new*() -> _String_ 12 | --# Generate an ID. 13 | --# 14 | --# === Returns 15 | --# [options="header",width="72%"] 16 | --# |=== 17 | --# |Type |Description 18 | --# |string |ksuid string 19 | --# |=== 20 | local ksuid_new = function() 21 | local U = require("ksuid") 22 | T.is_function(U.new) 23 | local s = U.new() 24 | local n = U.new() 25 | T.is_string(s) 26 | T.equal(tonumber(#s), 27) 27 | T.not_equal(s, n) 28 | end 29 | if included then 30 | return function() 31 | T["ksuid.new"] = ksuid_new 32 | end 33 | else 34 | T["ksuid.new"] = ksuid_new 35 | end 36 | -------------------------------------------------------------------------------- /tests/logger.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local logger = require("logger") 4 | local api 5 | --# = logger 6 | --# :toc: 7 | --# :toc-placement!: 8 | --# 9 | --# Structured logging to STDERR, STDOUT, or file. 10 | --# A https://github.com/rs/zerolog[zerolog] wrapper. 11 | --# 12 | --# toc::[] 13 | --# 14 | --# == *logger.new*() -> _Userdata_ 15 | --# 16 | --# Initialize object to access methods below. 17 | --# 18 | --# === Arguments 19 | --# [options="header",width="72%"] 20 | --# |=== 21 | --# |Type |Description 22 | --# |string |`stdout`, `stderr`, or path to a file, default is `stderr` 23 | --# |=== 24 | --# 25 | --# === Returns 26 | --# [options="header",width="72%"] 27 | --# |=== 28 | --# |Type |Description 29 | --# |userdata| Userdata with methods below 30 | --# |=== 31 | local logger_new = function() 32 | T.is_function(logger.new) 33 | api = logger.new() 34 | T.is_userdata(api) 35 | end 36 | --# 37 | --# == *logger.time*() -> _String_ 38 | --# 39 | --# Get same timestamp format used in logs. 40 | --# 41 | --# === Returns 42 | --# [options="header",width="72%"] 43 | --# |=== 44 | --# |Type |Description 45 | --# |string| Timestamp 46 | --# |=== 47 | local logger_time = function() 48 | T.is_function(logger.time) 49 | local t = logger.time() 50 | T.is_string(t) 51 | end 52 | 53 | --# 54 | --# == *:{info, debug, warn, error}* (_String_, _Table_) 55 | --# 56 | --# Log to specified log level. 57 | --# 58 | --# === Arguments 59 | --# [options="header",width="72%"] 60 | --# |=== 61 | --# |Type |Description 62 | --# |string| message 63 | --# |table | key-value map 64 | --# |=== 65 | local logger_info = function() 66 | T.is_function(api.info) 67 | end 68 | local logger_debug = function() 69 | T.is_function(api.debug) 70 | end 71 | local logger_warn = function() 72 | T.is_function(api.warn) 73 | end 74 | local logger_error = function() 75 | T.is_function(api.error) 76 | end 77 | local logger_try = function() 78 | local l = logger.new("/tmp/logger_try") 79 | local json = require("json") 80 | l:info("test", { one = "a", two = "b" }) 81 | local s = fs.read("/tmp/logger_try") 82 | local t = json.decode(s) 83 | T.equal(t.one, "a") 84 | T.equal(t.two, "b") 85 | T.equal(t.message, "test") 86 | T.equal(t.level, "info") 87 | T.is_string(t.time) 88 | os.remove("/tmp/logger_try") 89 | end 90 | if included then 91 | return function() 92 | T["logger.new"] = logger_new 93 | T["logger.time"] = logger_time 94 | T[":info"] = logger_info 95 | T[":debug"] = logger_debug 96 | T[":warn"] = logger_warn 97 | T[":error"] = logger_error 98 | T["file"] = logger_try 99 | end 100 | else 101 | T["logger.new"] = logger_new 102 | T["logger.time"] = logger_time 103 | T[":info"] = logger_info 104 | T[":debug"] = logger_debug 105 | T[":warn"] = logger_warn 106 | T[":error"] = logger_error 107 | T["file"] = logger_try 108 | end 109 | -------------------------------------------------------------------------------- /tests/lua.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local expect = T.expect 4 | local multiple_assignment = function() 5 | local x = function() 6 | return 0 7 | end 8 | local t = { 9 } 9 | local z = { z = 8 } 10 | local a, b, c, d, e = 1, t[1], x(), z.z, 4 11 | expect(1)(a) 12 | expect(9)(b) 13 | expect(0)(c) 14 | expect(8)(d) 15 | expect(4)(e) 16 | end 17 | local complex_multi_assign = function() 18 | local a = {} 19 | local d = "e" 20 | local f = 1 21 | f, a.d = f, d 22 | expect("e")(a.d) 23 | expect(false)(f == 1) -- bug 24 | end 25 | local map_overwrite = function() 26 | local t = { 27 | [1] = 1, 28 | "2" 29 | } 30 | expect(1)(#t) 31 | end 32 | if included then 33 | return function() 34 | T["simple multiple assignment"] = multiple_assignment 35 | T["complex multi assign #315"] = complex_multi_assign 36 | T["map vs list"] = map_overwrite 37 | end 38 | else 39 | T["simple multiple assignment"] = multiple_assignment 40 | T["complex multi assign #315"] = complex_multi_assign 41 | T["map vs list"] = map_overwrite 42 | end 43 | -------------------------------------------------------------------------------- /tests/lz4.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local lz4 = require("lz4") 4 | --# = lz4 5 | --# :toc: 6 | --# :toc-placement!: 7 | --# 8 | --# LZ4 compression and decompression. 9 | --# 10 | --# toc::[] 11 | --# 12 | --# == *lz4.compress*(_String_) -> _String_ 13 | --# Compress data. 14 | --# 15 | --# === Arguments 16 | --# [options="header",width="72%"] 17 | --# |=== 18 | --# |Type |Description 19 | --# |string |Data 20 | --# |=== 21 | --# 22 | --# === Returns 23 | --# [options="header",width="72%"] 24 | --# |=== 25 | --# |Type |Description 26 | --# |string |Compressed binary data 27 | --# |=== 28 | local lz4_compress = function() 29 | T.is_function(lz4.compress) 30 | local lz4c = exec.ctx("lz4") 31 | local data = "AAA111ZZZ" 32 | local compressed = lz4.compress(data) 33 | lz4c.stdin = compressed 34 | local r, so = lz4c({ "-d", "-c" }) 35 | T.is_true(r) 36 | T.equal(so, data) 37 | end 38 | --# 39 | --# == *lz4.decompress*(_String_) -> _String_ 40 | --# Decompress lz4 data. 41 | --# 42 | --# === Arguments 43 | --# [options="header",width="72%"] 44 | --# |=== 45 | --# |Type |Description 46 | --# |string |Compressed 47 | --# |=== 48 | --# 49 | --# === Returns 50 | --# [options="header",width="72%"] 51 | --# |=== 52 | --# |Type |Description 53 | --# |string |Decompressed data 54 | --# |=== 55 | local lz4_decompress = function() 56 | local crypto = require("crypto") 57 | T.is_function(lz4.decompress) 58 | local ls = fs.read("/bin/ls") 59 | local sum = crypto.sha256(ls) 60 | local compressed = lz4.compress(ls) 61 | local data = lz4.decompress(compressed) 62 | T.equal(sum, crypto.sha256(data)) 63 | end 64 | if included then 65 | return function() 66 | T["lz4.compress"] = lz4_compress 67 | T["lz4.decompress"] = lz4_decompress 68 | end 69 | else 70 | T["lz4.compress"] = lz4_compress 71 | T["lz4.decompress"] = lz4_decompress 72 | end 73 | -------------------------------------------------------------------------------- /tests/map.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local bimap = require("map") 3 | local T = require("test") 4 | local expect = T.expect 5 | local func = T.is_function 6 | local tbl = T.is_table 7 | --# = map 8 | --# :toc: 9 | --# :toc-placement!: 10 | --# 11 | --# Bidirectional map implementation. 12 | --# 13 | --# toc::[] 14 | --# 15 | --# == *map.new*() -> _Table_, _Table_ 16 | --# Create a new map. 17 | --# 18 | --# === Returns 19 | --# [options="header",width="72%"] 20 | --# |=== 21 | --# |Type |Description 22 | --# |table |Array side 23 | --# |table |Map side 24 | --# |=== 25 | local new = function() 26 | func(bimap.new) 27 | local l, r = bimap.new() 28 | tbl(l) 29 | tbl(r) 30 | end 31 | local raw = function() 32 | local l, r = bimap.new() 33 | l.test = true 34 | l.nope = false 35 | local left = l("raw") 36 | local right = r("raw") 37 | expect(true)(left["test"]) 38 | expect(false)(left["nope"]) 39 | expect("test")(right[true]) 40 | expect("nope")(right[false]) 41 | end 42 | local len = function() 43 | local t = { 1 } 44 | expect(1)(#t) 45 | local l, r = bimap.new() 46 | l.test = 1 47 | l.nope = 2 48 | expect(0)(l("len")) 49 | expect(2)(r("len")) 50 | end 51 | local testing = function(l, r) 52 | expect(2)(l.bar) 53 | expect("bar")(r[2]) 54 | local r1 = r("raw") 55 | expect("foo")(r1[1]) 56 | expect("bar")(r1[2]) 57 | expect("baz")(r1[3]) 58 | local t1 = l("raw") 59 | expect(1)(t1.foo) 60 | expect(2)(t1.bar) 61 | expect(3)(t1.baz) 62 | expect(3)(r("len")) 63 | l.baz = nil 64 | expect(2)(#(r("raw"))) 65 | r[r("len")] = nil 66 | local r2 = r("raw") 67 | expect("foo")(r2[1]) 68 | local l1 = l("raw") 69 | expect(1)(l1.foo) 70 | expect(1)(r("len")) 71 | l.spam = "eggs" 72 | r.eggs = "chunky" 73 | l["chunky"] = "bacon" 74 | expect("bacon")(l["chunky"]) 75 | expect("chunky")(r["bacon"]) 76 | expect(nil)(l["spam"]) 77 | expect(nil)(r["eggs"]) 78 | local r3 = r("raw") 79 | local l2 = l("raw") 80 | expect("foo")(r3[1]) 81 | expect("chunky")(r3.bacon) 82 | expect(1)(l2.foo) 83 | expect("bacon")(l2.chunky) 84 | local fn = function() 85 | l.evil = 1 86 | end 87 | T.error_raised( 88 | fn, 89 | 'cannot assign value "1" to key "evil": ' .. 'already assigned to key "foo"' 90 | ) 91 | end 92 | local left = function() 93 | local l, r = bimap.new() 94 | l.foo = 1 95 | l.bar = 2 96 | l.baz = 3 97 | testing(l, r) 98 | end 99 | local right = function() 100 | local l, r = bimap.new{"foo", "bar", "baz"} 101 | testing(r, l) 102 | end 103 | local iter = function() 104 | local l, r = bimap.new{"foo", "bar", "baz"} 105 | local t = {} 106 | local x = l("raw") 107 | for n = 1, l("len") do 108 | t[n] = x[n] 109 | end 110 | expect("foo")(t[1]) 111 | expect("bar")(t[2]) 112 | expect("baz")(t[3]) 113 | end 114 | if included then 115 | return function() 116 | T["new"] = new 117 | T["raw argument"] = raw 118 | T["len argument"] = len 119 | T["left"] = left 120 | T["right"] = right 121 | T["iteration"] = iter 122 | end 123 | else 124 | T["new"] = new 125 | T["raw argument"] = raw 126 | T["len argument"] = len 127 | T["left"] = left 128 | T["right"] = right 129 | T["iteration"] = iter 130 | end 131 | -------------------------------------------------------------------------------- /tests/mysql.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local mysql = require("mysql") 4 | --# = mysql 5 | --# :toc: 6 | --# :toc-placement!: 7 | --# 8 | --# Access MySQL or MariaDB databases. 9 | --# 10 | --# toc::[] 11 | --# 12 | --# == *mysql.escape*(_String_) -> _String_ 13 | --# Escape a query. 14 | --# 15 | --# == *mysql.new* 16 | --# Initialize mysql instance. 17 | --# 18 | --# === Returns 19 | --# [options="header",width="72%"] 20 | --# |=== 21 | --# |Type |Description 22 | --# |object |Instance of mysql that you can index into 23 | --# |=== 24 | --# 25 | --# == *close* 26 | --# Close mysql instance. 27 | --# 28 | --# == *set_timeout*(_Number_) 29 | --# Set timeout. 30 | --# 31 | --# === Arguments 32 | --# [options="header",width="72%"] 33 | --# |=== 34 | --# |Type |Description 35 | --# |number |Timeout in ms 36 | --# |=== 37 | --# 38 | --# == *set_keepalive*(_Number_, _Number_) 39 | --# 40 | --# === Arguments 41 | --# [options="header",width="72%"] 42 | --# |=== 43 | --# |Type |Description 44 | --# |number |Timeout in ms 45 | --# |number |Max idle connections(poolSize) 46 | --# |=== 47 | --# 48 | --# == *connect*(_Table_) 49 | --# 50 | --# === Arguments 51 | --# [options="header",width="72%"] 52 | --# |=== 53 | --# |Type |Description 54 | --# |table |See map below 55 | --# |=== 56 | --# 57 | --# === Map 58 | --# [options="header",width="72%"] 59 | --# |=== 60 | --# |host | 61 | --# |port | 62 | --# |database | 63 | --# |user | 64 | --# |password | 65 | --# |=== 66 | --# 67 | --# == *query*(_String_[, ...]) -> _Table_ 68 | --# 69 | --# === Arguments 70 | --# [options="header",width="72%"] 71 | --# |=== 72 | --# |Type |Description 73 | --# |string |SQL query 74 | --# |=== 75 | --# 76 | --# === Returns 77 | --# [options="header",width="72%"] 78 | --# |=== 79 | --# |Type |Description 80 | --# |table |Query results, empty table if no results 81 | --# |=== 82 | local mysql_new = function() 83 | T.is_function(mysql.new) 84 | local c = mysql.new() 85 | T.is_userdata(c) 86 | c:close() 87 | end 88 | local mysql_close = function() 89 | local c = mysql.new() 90 | T.is_function(c.close) 91 | c:close() 92 | end 93 | local mysql_connect = function() 94 | local c = mysql.new() 95 | T.is_function(c.connect) 96 | local password = os.getenv("MYSQL_PASSWORD") 97 | local ok, err = c:connect({ 98 | host = "", 99 | port = "", 100 | database = "", 101 | user = "", 102 | password = password, 103 | }) 104 | T.is_true(ok) 105 | T.is_nil(err) 106 | c:close() 107 | end 108 | local mysql_query = function() 109 | local c = mysql.new() 110 | T.is_function(c.connect) 111 | local password = os.getenv("MYSQL_PASSWORD") 112 | local ok, err = c:connect({ 113 | host = "127.0.0.1", 114 | port = "3306", 115 | database = "mysql", 116 | user = "root", 117 | password = password, 118 | }) 119 | T.is_true(ok) 120 | T.is_nil(err) 121 | local qok, qerr = c:query("SELECT * FROM user LIMIT 1") 122 | T.is_nil(qerr) 123 | T.is_table(qok) 124 | T.is_table(qok[1]) 125 | T.equal(qok[1].password_expired, "N") 126 | local cok, cerr = c:query([[SELECT * FROM user where max_user_connections=?]], 0) 127 | T.is_nil(cerr) 128 | T.is_table(cok) 129 | T.equal(next(cok), 1) 130 | local nok, nerr = c:query([[SELECT * FROM user where max_user_connections=?]], 1) 131 | T.is_nil(nerr) 132 | T.is_table(nok) 133 | T.is_nil(next(nok)) 134 | c:close() 135 | end 136 | if included then 137 | return function() 138 | T["mysql.new"] = mysql_new 139 | T["close"] = mysql_close 140 | T["connect"] = mysql_connect 141 | T["query"] = mysql_query 142 | end 143 | else 144 | T["mysql.new"] = mysql_new 145 | T["close"] = mysql_close 146 | T["connect"] = mysql_connect 147 | T["query"] = mysql_query 148 | end 149 | -------------------------------------------------------------------------------- /tests/object.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local Object = require("object") 4 | local Point = Object:extend("Point") 5 | Point.scale = 2 -- Class field! 6 | 7 | function Point:init(x, y) 8 | self.x = x or 0 9 | self.y = y or 0 10 | end 11 | 12 | function Point:resize() 13 | self.x = self.x * self.scale 14 | self.y = self.y * self.scale 15 | end 16 | 17 | function Point.__call() 18 | return "called" 19 | end 20 | 21 | local Rectangle = Point:extend("Rectangle") 22 | 23 | function Rectangle:resize() 24 | Rectangle.super.resize(self) -- Extend Point's `resize()`. 25 | self.w = self.w * self.scale 26 | self.h = self.h * self.scale 27 | end 28 | 29 | function Rectangle:init(x, y, w, h) 30 | Rectangle.super.init(self, x, y) -- Initialize Point first! 31 | self.w = w or 0 32 | self.h = h or 0 33 | end 34 | 35 | function Rectangle:__index(key) 36 | if key == "width" then 37 | return self.w 38 | end 39 | if key == "height" then 40 | return self.h 41 | end 42 | end 43 | 44 | function Rectangle:__newindex(key, value) 45 | if key == "width" then 46 | self.w = value 47 | elseif key == "height" then 48 | self.h = value 49 | end 50 | end 51 | 52 | local rect = Rectangle:new(2, 4, 6, 8) 53 | local extend = function() 54 | T.expect(6)(rect.w) 55 | end 56 | local is = function() 57 | T.expect(true)(rect:is(Rectangle)) 58 | T.expect(true)(rect:is("Rectangle")) 59 | end 60 | local is_not = function() 61 | T.expect(false)(rect:is(Point)) 62 | end 63 | local has = function() 64 | T.expect(1)(rect:has("Point")) 65 | end 66 | local has_object = function() 67 | T.expect(2)(Rectangle:has(Object)) 68 | end 69 | local called = function() 70 | T.expect("called")(rect()) 71 | end 72 | local extend2 = function() 73 | T.expect(666)(rect.w) 74 | T.expect(8)(rect.height) 75 | end 76 | 77 | if included then 78 | return function() 79 | T["extend"] = extend 80 | T["is"] = is 81 | T["is not"] = is_not 82 | T["has"] = has 83 | T["has object"] = has_object 84 | T["called"] = called 85 | rect.width = 666 86 | T["extend2"] = extend2 87 | end 88 | else 89 | T["extend"] = extend 90 | T["is"] = is 91 | T["is not"] = is_not 92 | T["has"] = has 93 | T["has object"] = has_object 94 | T["called"] = called 95 | rect.width = 666 96 | T["extend2"] = extend2 97 | end 98 | -------------------------------------------------------------------------------- /tests/os.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | --# = os 4 | --# :toc: 5 | --# :toc-placement!: 6 | --# 7 | --# Extensions to the `os` namespace. 8 | --# 9 | --# toc::[] 10 | --# 11 | --# == *os.hostname*() -> _String_ 12 | --# Get current hostname. 13 | --# 14 | --# === Returns 15 | --# [width="72%"] 16 | --# |=== 17 | --# |string |Hostname 18 | --# |=== 19 | local os_hostname = function() 20 | T.is_function(os.hostname) 21 | T.is_string(os.hostname()) 22 | end 23 | --# 24 | --# == *os.outbound_ip*() -> _String_ 25 | --# Get IP used for outbound connections. 26 | --# 27 | --# === Returns 28 | --# [width="72%"] 29 | --# |=== 30 | --# |string |IP 31 | --# |=== 32 | local os_outbound_ip = function() 33 | T.is_function(os.outbound_ip) 34 | T.is_string(os.outbound_ip()) 35 | end 36 | --# 37 | --# == *os.sleep(_Number_) -> _Boolean_ 38 | --# Sleep for a number of milliseconds. 39 | --# 40 | --# === Arguments 41 | --# [width="72%"] 42 | --# |=== 43 | --# |number |Milliseconds 44 | --# |=== 45 | --# 46 | --# === Returns 47 | --# [width="72%"] 48 | --# |=== 49 | --# |boolean |`true` 50 | --# |=== 51 | local os_sleep = function() 52 | T.is_function(os.sleep) 53 | T.is_true(os.sleep(500)) 54 | end 55 | --# 56 | --# == *os.setenv*(_String_, _String_) -> _Boolean_ 57 | --# Set environment variable. 58 | --# 59 | --# === Arguments 60 | --# [width="72%"] 61 | --# |=== 62 | --# |string |Variable 63 | --# |string |Value 64 | --# |=== 65 | --# 66 | --# === Returns 67 | --# [width="72%"] 68 | --# |=== 69 | --# |boolean |`true` if successful 70 | --# |=== 71 | local os_setenv = function() 72 | T.is_function(os.setenv) 73 | T.is_true(os.setenv("TESTLADYLUAOSSETENV", "1")) 74 | local e = os.getenv("TESTLADYLUAOSSETENV") 75 | T.equal(e, "1") 76 | end 77 | if included then 78 | return function() 79 | T["os.hostname"] = os_hostname 80 | T["os.outbound_ip"] = os_outbound_ip 81 | T["os.sleep"] = os_sleep 82 | T["os.setenv"] = os_setenv 83 | end 84 | else 85 | T["os.hostname"] = os_hostname 86 | T["os.outbound_ip"] = os_outbound_ip 87 | T["os.sleep"] = os_sleep 88 | T["os.setenv"] = os_setenv 89 | end 90 | -------------------------------------------------------------------------------- /tests/pushover.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local pushover = require("pushover") 4 | local api 5 | --# = pushover 6 | --# :toc: 7 | --# :toc-placement!: 8 | --# 9 | --# Send Pushover messages via the API. 10 | --# 11 | --# toc::[] 12 | --# 13 | --# == *pushover.new*() 14 | --# 15 | --# Initialize object to access methods below. Requires a valid token in the environment variable `PUSHOVER_TOKEN`. 16 | --# 17 | --# === Returns 18 | --# [options="header",width="72%"] 19 | --# |=== 20 | --# |Type |Description 21 | --# |userdata| Userdata with methods below 22 | --# |=== 23 | local pushover_new = function() 24 | T.is_function(pushover.new) 25 | api = pushover.new() 26 | T.is_userdata(api) 27 | end 28 | --# 29 | --# == *:message*(_String_, _String_) 30 | --# 31 | --# Send a message to device. 32 | --# 33 | --# === Arguments 34 | --# [options="header",width="72%"] 35 | --# |=== 36 | --# |Type |Description 37 | --# |string| Device ID 38 | --# |string| message 39 | --# |=== 40 | --# 41 | --# === Returns 42 | --# [options="header",width="72%"] 43 | --# |=== 44 | --# |Type |Description 45 | --# |string| Response from API 46 | --# |=== 47 | local pushover_message = function() 48 | T.is_function(api.message) 49 | end 50 | if included then 51 | return function() 52 | T["pushover.new"] = pushover_new 53 | T[":message"] = pushover_message 54 | end 55 | else 56 | T["pushover.new"] = pushover_new 57 | T[":message"] = pushover_message 58 | end 59 | -------------------------------------------------------------------------------- /tests/slack.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local slack = require("slack") 4 | --# = slack 5 | --# :toc: 6 | --# :toc-placement!: 7 | --# 8 | --# Wrapper for the Slack API. 9 | --# 10 | --# toc::[] 11 | --# 12 | --# == *slack.message*(_String_) -> _Boolean_ 13 | --# Send webhook text. Requires the string after the `https://hooks.slack.com/services/` URL in the `SLACK_WEBHOOK` environment variable. 14 | --# 15 | --# === Arguments 16 | --# [options="header",width="72%"] 17 | --# |=== 18 | --# |Type |Description 19 | --# |string |Message 20 | --# |=== 21 | --# 22 | --# === Returns 23 | --# [options="header",width="72%"] 24 | --# |=== 25 | --# |Type |Description 26 | --# |boolean| `true` if successful 27 | --# |=== 28 | local slack_message = function() 29 | T.is_function(slack.message) 30 | end 31 | --# == *slack.attachment*(_Table_) -> _Boolean_ 32 | --# Send webhook attachment containing values from the argument. Requires the string after the `https://hooks.slack.com/services/` URL in the `SLACK_WEBHOOK` environment variable. 33 | --# 34 | --# === Arguments 35 | --# [options="header",width="72%"] 36 | --# |=== 37 | --# |Type |Description 38 | --# |table |See valid values from https://github.com/slack-go/slack[repo] 39 | --# |=== 40 | --# 41 | --# === Returns 42 | --# [options="header",width="72%"] 43 | --# |=== 44 | --# |Type |Description 45 | --# |boolean| `true` if successful 46 | --# |=== 47 | local slack_attachment = function() 48 | T.is_function(slack.attachment) 49 | end 50 | if included then 51 | return function() 52 | T["slack.message"] = slack_message 53 | T["slack.attachment"] = slack_attachment 54 | end 55 | else 56 | T["slack.message"] = slack_message 57 | T["slack.attachment"] = slack_attachment 58 | end 59 | -------------------------------------------------------------------------------- /tests/ssh_config.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local ssh_config = require("ssh_config") 4 | --# = ssh_config 5 | --# :toc: 6 | --# :toc-placement!: 7 | --# 8 | --# Get values from ~/.ssh/config 9 | --# 10 | --# toc::[] 11 | --# 12 | --# == *ssh_config.port*(_String_) -> _String_ 13 | --# Get configured Port for Host. 14 | --# 15 | --# === Arguments 16 | --# [options="header",width="72%"] 17 | --# |=== 18 | --# |Type |Description 19 | --# |string |Host 20 | --# |=== 21 | --# 22 | --# === Returns 23 | --# [options="header",width="72%"] 24 | --# |=== 25 | --# |Type |Description 26 | --# |string |Port 27 | --# |=== 28 | local ssh_config_port = function() 29 | T.is_function(ssh_config.port) 30 | end 31 | --# 32 | --# == *ssh_config.hostname*(_String_) -> _String_ 33 | --# Get configured Hostname for Host. 34 | --# 35 | --# === Arguments 36 | --# [options="header",width="72%"] 37 | --# |=== 38 | --# |Type |Description 39 | --# |string |Host 40 | --# |=== 41 | --# 42 | --# === Returns 43 | --# [options="header",width="72%"] 44 | --# |=== 45 | --# |Type |Description 46 | --# |string |Hostname 47 | --# |=== 48 | local ssh_config_hostname = function() 49 | T.is_function(ssh_config.hostname) 50 | end 51 | --# 52 | --# == *ssh_config.identity_file*(_String_) -> _String_ 53 | --# Get configured IdentityFile for Host. 54 | --# 55 | --# === Arguments 56 | --# [options="header",width="72%"] 57 | --# |=== 58 | --# |Type |Description 59 | --# |string |Host 60 | --# |=== 61 | --# 62 | --# === Returns 63 | --# [options="header",width="72%"] 64 | --# |=== 65 | --# |Type |Description 66 | --# |string |Path of key 67 | --# |=== 68 | local ssh_config_identityfile = function() 69 | T.is_function(ssh_config.identity_file) 70 | end 71 | --# 72 | --# == *ssh_config.hosts*() -> _Table_ 73 | --# Get all hosts configured in ~/.ssh/config 74 | --# 75 | --# === Returns 76 | --# [options="header",width="72%"] 77 | --# |=== 78 | --# |Type |Description 79 | --# |table|List of hosts 80 | --# |=== 81 | local ssh_config_hosts = function() 82 | T.is_function(ssh_config.hosts) 83 | T.is_table(ssh_config.hosts()) 84 | end 85 | if included then 86 | return function() 87 | T["ssh_config.port"] = ssh_config_port 88 | T["ssh_config.hostname"] = ssh_config_hostname 89 | T["ssh_config.identityfile"] = ssh_config_identityfile 90 | T["ssh_config.hosts"] = ssh_config_hosts 91 | end 92 | else 93 | T["ssh_config.port"] = ssh_config_port 94 | T["ssh_config.hostname"] = ssh_config_hostname 95 | T["ssh_config.identityfile"] = ssh_config_identityfile 96 | T["ssh_config.hosts"] = ssh_config_hosts 97 | end 98 | -------------------------------------------------------------------------------- /tests/state.lua: -------------------------------------------------------------------------------- 1 | local state = require("state") 2 | state.debug = require("tests.state.debug_plain").out 3 | 4 | local composite_s = require("tests.state.composite") 5 | composite_s.before = function() 6 | print("MACHINE STARTED") 7 | end 8 | 9 | local hsm = state.init(composite_s) -- create hsm from root composite state 10 | 11 | local function send(e) 12 | print("TEST sending event", e) 13 | hsm.send(e) 14 | end 15 | local function loop(e) 16 | print("===", hsm.loop()) 17 | end 18 | 19 | --package.path = package.path .. ";;;tools/?.lua;tools/?/init.lua" 20 | --local to_dot = require 'to_dot' 21 | 22 | send(composite_s.events.e_on) 23 | send("e_restart") 24 | 25 | --to_dot.to_function(composite_s, print) 26 | 27 | send("e_off") 28 | send(composite_s.events.e_on) 29 | while hsm.loop() do 30 | end 31 | -------------------------------------------------------------------------------- /tests/state/composite.lua: -------------------------------------------------------------------------------- 1 | --- Uses an embedded hsm in a state. 2 | 3 | local state = require("state") 4 | 5 | local helloworld_s = require("tests.state.helloworld") -- load a fsm from a library 6 | 7 | local off_s = state.state({ 8 | before = function() --[[print "TEST STATE off"]] 9 | end, 10 | op = function() --[[return true]] 11 | end, --single shot doo function, uncomment return for polling 12 | }) 13 | local e_on = {} 14 | local t21 = state.transition({ 15 | from = off_s, 16 | to = helloworld_s, --target is a composite state, will start it 17 | events = { e_on }, --event is an object 18 | guard = function(e) 19 | return true 20 | end, --guard function 21 | effect = function(e) --function called on transition 22 | --print ('TEST switching on', os.time()) 23 | end, 24 | }) 25 | local t22 = state.transition({ 26 | from = helloworld_s, 27 | to = off_s, 28 | events = { "e_off" }, 29 | timeout = 7.0, 30 | effect = function(e) --function called on transition 31 | --print ('TEST switching off', os.time(), 'timeout:'..tostring(e==state.EV_TIMEOUT)) 32 | end, 33 | }) 34 | 35 | local composite_s = state.state({ 36 | events = { e_on = e_on }, -- publish events 37 | states = { off = off_s, helloworld = helloworld_s }, --can provide names to states 38 | transitions = { ON = t21, OFF = t22 }, --can provide names to transitions 39 | initial = off_s, 40 | }) 41 | 42 | return composite_s 43 | -------------------------------------------------------------------------------- /tests/state/debug_plain.lua: -------------------------------------------------------------------------------- 1 | --- Debug formatter that generates readable output. 2 | -- @usage local state = require 'state' 3 | -- local debug_plain = require 'tools.debug_plain' 4 | -- state.debug = debug_plain.out 5 | 6 | local M = {} 7 | 8 | local debug_names = {} 9 | 10 | local function pick_debug_name(v, nv) 11 | if debug_names[v] then 12 | return debug_names[v] 13 | end 14 | if type(nv) == "string" then 15 | debug_names[v] = nv 16 | else 17 | debug_names[v] = tostring(v._name or v) 18 | end 19 | if v.container and v.container._name ~= "." then -- build path for states 20 | debug_names[v] = debug_names[v.container] .. "." .. debug_names[v] 21 | end 22 | return debug_names[v] 23 | end 24 | 25 | --- Function to be used to write. 26 | -- Defaults to `print` 27 | M.print = print 28 | 29 | -- -- Function to be passed assigned to `state.debug`. 30 | M.out = function(action, p1, p2, p3, p4) 31 | if action == "step" then 32 | M.print( 33 | action 34 | .. "\t" 35 | .. debug_names[p1.from] 36 | .. "\t--" 37 | .. debug_names[p1] 38 | .. "[" 39 | .. pick_debug_name(p2) 40 | .. "]->\t" 41 | .. debug_names[p1.to] 42 | ) 43 | elseif action == "init" then 44 | M.print(action .. "\t" .. debug_names[p1]) 45 | elseif action == "tmout" then 46 | --M.print(action, p1, p2, debug_names[p3],'--'..debug_names[p4]..'->', debug_names[p4.to]) 47 | --called before init 48 | M.print(action .. "\t" .. (debug_names[p1] or tostring(p1)) .. "\t" .. tostring(p2)) 49 | elseif action == "event" then 50 | M.print(action .. tostring(p1) .. '\t"' .. pick_debug_name(p1, p2) .. '"') 51 | elseif action == "state" then 52 | debug_names[p1.EV_DONE] = "EV_DONE" 53 | M.print(action .. "\t" .. tostring(p1) .. '\t"' .. pick_debug_name(p1, p2) .. '"') 54 | elseif action == "trans" then 55 | M.print(action .. "\t" .. tostring(p1) .. '\t"' .. pick_debug_name(p1, p2) .. '"') 56 | elseif action == "trsel" then 57 | M.print( 58 | action 59 | .. "\t" 60 | .. debug_names[p1] 61 | .. "\t--" 62 | .. debug_names[p2] 63 | .. "[" 64 | .. pick_debug_name(p3) 65 | .. "]->\t" 66 | .. debug_names[p2.to] 67 | ) 68 | end 69 | end 70 | 71 | return M 72 | -------------------------------------------------------------------------------- /tests/state/helloworld.lua: -------------------------------------------------------------------------------- 1 | --- Two state flip-flop. 2 | 3 | local state = require("state") 4 | local debug_plain = require("tests.state.debug_plain") 5 | state.debug = debug_plain.out 6 | 7 | local hello_s = state.state({ 8 | after = function() 9 | --print("HW STATE hello") 10 | end, 11 | }) --state with exit func 12 | local world_s = state.state({ 13 | before = function() 14 | --print("HW STATE world") 15 | end, 16 | }) --state with entry func 17 | local t11 = state.transition({ 18 | from = hello_s, 19 | to = world_s, 20 | events = { hello_s.EV_DONE }, 21 | }) --transition on state completion 22 | local t12 = state.transition({ 23 | from = world_s, 24 | to = hello_s, 25 | events = { "e_restart" }, 26 | timeout = 2.0, 27 | }) --transition with timeout, event is a string 28 | 29 | local a = 0 30 | local helloworld_s = state.state({ 31 | states = { hello = hello_s, world = world_s }, --composite state 32 | transitions = { to_world = t11, to_hello = t12 }, 33 | initial = hello_s, --initial state for machine 34 | op = coroutine.wrap(function() -- a long running doo with yields 35 | while true do 36 | a = a + 1 37 | coroutine.yield(true) 38 | end 39 | end), 40 | before = function() 41 | --print("HW doo running") 42 | end, 43 | after = function() 44 | --print("HW doo iteration count", a) 45 | end, -- will show efect of doo on exit 46 | }) 47 | 48 | return helloworld_s 49 | -------------------------------------------------------------------------------- /tests/telegram.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | local telegram = require("telegram") 4 | local bot 5 | --# = telegram 6 | --# :toc: 7 | --# :toc-placement!: 8 | --# 9 | --# Send Telegram messages via the Bot API. 10 | --# 11 | --# toc::[] 12 | --# 13 | --# == *telegram.new*() 14 | --# 15 | --# Initialize object to access methods below. Requires a valid BOT token in the environment variable `TELEGRAM_TOKEN`. 16 | --# 17 | --# === Returns 18 | --# [options="header",width="72%"] 19 | --# |=== 20 | --# |Type |Description 21 | --# |userdata| Userdata containing methods below 22 | --# |=== 23 | local telegram_new = function() 24 | T.is_function(telegram.new) 25 | bot = telegram.new() 26 | T.is_userdata(bot) 27 | end 28 | --# 29 | --# == *:channel*(_String_, _String_) 30 | --# 31 | --# Post a channel message. 32 | --# 33 | --# === Arguments 34 | --# [options="header",width="72%"] 35 | --# |=== 36 | --# |Type |Description 37 | --# |string| channel id e.g. "-12312313" 38 | --# |string| message 39 | --# |=== 40 | --# 41 | --# === Returns 42 | --# [options="header",width="72%"] 43 | --# |=== 44 | --# |Type |Description 45 | --# |boolean| true 46 | --# |=== 47 | --# 48 | local telegram_channel = function() 49 | T.is_function(bot.channel) 50 | end 51 | --# 52 | --# == *:message*(_String_, _String_) 53 | --# 54 | --# Send a message to Telegram user. 55 | --# 56 | --# === Arguments 57 | --# [options="header",width="72%"] 58 | --# |=== 59 | --# |Type |Description 60 | --# |number| user id e.g. 9348484 61 | --# |string| message 62 | --# |=== 63 | --# 64 | --# === Returns 65 | --# [options="header",width="72%"] 66 | --# |=== 67 | --# |Type |Description 68 | --# |boolean| true 69 | --# |=== 70 | local telegram_message = function() 71 | T.is_function(bot.message) 72 | end 73 | if included then 74 | return function() 75 | T["telegram.new"] = telegram_new 76 | T[":channel"] = telegram_channel 77 | T[":message"] = telegram_message 78 | end 79 | else 80 | T["telegram.new"] = telegram_new 81 | T[":channel"] = telegram_channel 82 | T[":message"] = telegram_message 83 | end 84 | -------------------------------------------------------------------------------- /tests/ulid.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | --# = ulid 4 | --# :toc: 5 | --# :toc-placement!: 6 | --# 7 | --# Generate random strings in ULID format. 8 | --# This is a https://github.com/oklog/ulid/[ulid] wrapper. 9 | --# 10 | --# toc::[] 11 | --# 12 | --# == *ulid.new*() -> _String_ 13 | --# Generate an ID. 14 | --# 15 | --# === Returns 16 | --# [options="header",width="72%"] 17 | --# |=== 18 | --# |Type |Description 19 | --# |string |ULID string 20 | --# |=== 21 | local ulid_new = function() 22 | local U = require("ulid") 23 | T.is_function(U.new) 24 | local s = U.new() 25 | local n = U.new() 26 | T.is_string(s) 27 | T.equal(tonumber(#s), 26) 28 | T.not_equal(s, n) 29 | end 30 | if included then 31 | return function() 32 | T["ulid.new"] = ulid_new 33 | end 34 | else 35 | T["ulid.new"] = ulid_new 36 | end 37 | -------------------------------------------------------------------------------- /tests/uuid.lua: -------------------------------------------------------------------------------- 1 | local included = pcall(debug.getlocal, 4, 1) 2 | local T = require("test") 3 | --# = uuid 4 | --# :toc: 5 | --# :toc-placement!: 6 | --# 7 | --# Generate random strings in UUID format. 8 | --# This is a https://github.com/hashicorp/go-uuid[go-uuid] wrapper. 9 | --# 10 | --# toc::[] 11 | --# 12 | --# == *uuid.new*() -> _String_ 13 | --# Generate an ID. 14 | --# 15 | --# === Returns 16 | --# [options="header",width="72%"] 17 | --# |=== 18 | --# |Type |Description 19 | --# |string |uuid string 20 | --# |=== 21 | local uuid_new = function() 22 | local U = require("uuid") 23 | T.is_function(U.new) 24 | local s = U.new() 25 | local n = U.new() 26 | T.is_string(s) 27 | T.equal(tonumber(#s), 36) 28 | T.not_equal(s, n) 29 | end 30 | if included then 31 | return function() 32 | T["uuid.new"] = uuid_new 33 | end 34 | else 35 | T["uuid.new"] = uuid_new 36 | end 37 | --------------------------------------------------------------------------------