commit d0a663b2bb2c5003069d51fa2118c12548f44e9d Author: ato Date: Sat Feb 11 14:37:27 2023 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5dd00d9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "Lua.diagnostics.globals": [ + "name", + "version", + "description", + "scripts", + "sleep", + "create_app" + ] +} diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9acc1db --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,729 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "erased-serde" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ca605381c017ec7a5fef5e548f1cfaa419ed0f6df6367339300db74c92aa7d" +dependencies = [ + "serde", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-executor" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-macro" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lua-src" +version = "544.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "708ba3c844d5e9d38def4a09dd871c17c370f519b3c4b7261fbabe4a613a814c" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.4.5+resty2cf5186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b7992a40e602786272d84c6f2beca44a588ededcfd57b48ec6f82008a7cb97" +dependencies = [ + "cc", +] + +[[package]] +name = "matchit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.42.0", +] + +[[package]] +name = "mlua" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee2ad7a9aa69056b148d9d590344bc155d3ce0d2200e3b2838f7034f6ba33c1" +dependencies = [ + "bstr", + "cc", + "erased-serde", + "futures-core", + "futures-task", + "futures-util", + "lua-src", + "luajit-src", + "num-traits", + "once_cell", + "pkg-config", + "rustc-hash", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" + +[[package]] +name = "serde_json" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tchoutchou" +version = "0.1.0" +dependencies = [ + "futures", + "hyper", + "matchit", + "mlua", + "serde_json", + "tokio", +] + +[[package]] +name = "tokio" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.42.0", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6a3b08b64e6dfad376fa2432c7b1f01522e37a623c3050bc95db2d3ff21583" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b2e7402 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "tchoutchou" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[[bin]] +path = "src/cli/main.rs" +name = "tt" +bench = false + +[profile.release] +strip = true # Automatically strip symbols from the binary. +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" + +[dependencies] +futures = { version = "0.3" } +hyper = { version = "0.14", features = ["full"] } +matchit = { version = "0.7" } +mlua = { version = "0.8", features = [ + "lua54", + "vendored", + "async", + "serialize", +] } +serde_json = { version = "1.0" } +tokio = { version = "1.0", features = ["full"] } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..29b4390 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM rust:alpine as builder +RUN apk add --no-cache musl-dev +WORKDIR / +RUN USER=root cargo new app +COPY Cargo.toml Cargo.lock /app +WORKDIR /app +RUN cargo build --release +COPY src /app/src +RUN touch /app/src/main.rs +RUN cargo build --release + +FROM scratch +COPY --from=builder /app/target/release/tchoutchou /tchoutchou +CMD ["/tchoutchou"] diff --git a/omg.lua b/omg.lua new file mode 100644 index 0000000..e9ee66a --- /dev/null +++ b/omg.lua @@ -0,0 +1,5 @@ +function omg() + print("OMG") +end + +return omg diff --git a/server.lua b/server.lua new file mode 100644 index 0000000..c389fec --- /dev/null +++ b/server.lua @@ -0,0 +1,72 @@ +local omg = require("omg") +omg() + +-- function sleep(a) +-- local sec = tonumber(os.clock() + a); +-- while (os.clock() < sec) do +-- end +-- end + +--[[ + Admin Section +]] +local admin = create_app() +admin:get("/", function(req, res) + res.status = 200 + res.body = "Admin" + return res +end) + +--[[ + Main App +]] +local app = create_app() +app:get("/foo/:number", function(req, res) + res.body = { data = req.params.number } + return res +end) +app:get("/:one/:two/:three/:four/:five", function(req, res) + print(req.params.five) + res.body = { data = req.params.one } + return res +end) +app:get("/bar", function(req, res) + res.status = 200 + res.body = "Hello, World!" + sleep(3) + return res +end) +app:post("/submit", function(req, res) + print(req.body) + return res +end) + +--[[ wip ]] +-- app:ws("/ws", function(ws) +-- ws:on("message", function(msg) +-- print(msg) +-- end) +-- return ws +-- end) +-- app:ws("/ws", { +-- on_connect = function(ws) +-- print("lua connect cb") +-- table.insert(ws_clients, ws) +-- end, +-- on_message = function(ws, message) +-- print("lua message cb") +-- end, +-- }) + +app:use("/admin", admin) + +-- Run App +app:run(3000) + +local otherapp = create_app() +otherapp:get("/", function(req, res) + res.status = 200 + res.body = "Admin" + return res +end) +otherapp:run(3001) diff --git a/src/api/builtins.rs b/src/api/builtins.rs new file mode 100644 index 0000000..73dd1d0 --- /dev/null +++ b/src/api/builtins.rs @@ -0,0 +1,14 @@ +use mlua::Lua; +use std::time::Duration; +use tokio::time::sleep; + +pub fn load_builtins(lua: &Lua) { + let sleep = lua + .create_async_function(|_l, n: u64| async move { + sleep(Duration::from_secs(n)).await; + Ok(()) + }) + .unwrap(); + + lua.globals().set("sleep", sleep).unwrap(); +} diff --git a/src/api/mod.rs b/src/api/mod.rs new file mode 100644 index 0000000..5085ee9 --- /dev/null +++ b/src/api/mod.rs @@ -0,0 +1 @@ +pub mod builtins; diff --git a/src/cli/main.rs b/src/cli/main.rs new file mode 100644 index 0000000..82e0233 --- /dev/null +++ b/src/cli/main.rs @@ -0,0 +1,36 @@ +use std::error::Error; +use std::{env, fs}; + +use tchoutchou::api::builtins::load_builtins; +use tchoutchou::internal::core::runtime::Runtime; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let args: Vec = env::args().collect(); + + if args.len() != 2 { + eprintln!("usage: tchoutchou [file.lua]"); + std::process::exit(1); + } + + let lua_script = match fs::read(&args[1]) { + Ok(content) => content, + Err(error) => { + eprintln!("{}", error); + std::process::exit(1); + } + }; + + let runtime = match Runtime::from_script(&lua_script) { + Ok(runtime) => runtime, + Err(error) => { + eprintln!("{}", error); + std::process::exit(1); + } + }; + + runtime.load(&load_builtins); + runtime.start().await; + + Ok(()) +} diff --git a/src/internal/core/mod.rs b/src/internal/core/mod.rs new file mode 100644 index 0000000..a26e031 --- /dev/null +++ b/src/internal/core/mod.rs @@ -0,0 +1,3 @@ +pub mod routers; +pub mod runtime; +pub mod service; diff --git a/src/internal/core/routers.rs b/src/internal/core/routers.rs new file mode 100644 index 0000000..d73c1bf --- /dev/null +++ b/src/internal/core/routers.rs @@ -0,0 +1,49 @@ +use hyper::Method; +use matchit::Router; +use std::collections::HashMap; + +#[derive(Clone)] +pub struct LuaHandle { + pub method: String, + pub handler_identifier: String, +} + +#[derive(Clone)] +pub struct Routers { + routers: HashMap>, +} + +impl Routers { + pub fn from(apps: HashMap)>) -> Self { + Self { + routers: { + let mut routers: HashMap> = HashMap::new(); + for (port, (_id, lua_paths)) in apps { + for (method, path, handler_identifier) in lua_paths.iter() { + let lua_handle = LuaHandle { + method: method.to_string(), + handler_identifier: handler_identifier.to_owned(), + }; + if let Some(router) = routers.get_mut(&port) { + router.insert(path, lua_handle).unwrap(); + } else { + let mut new_router = Router::new(); + new_router.insert(path, lua_handle).unwrap(); + routers.insert(port, new_router); + } + } + } + routers + }, + } + } +} + +impl IntoIterator for Routers { + type Item = (u16, Router); + type IntoIter = > as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.routers.to_owned().into_iter() + } +} diff --git a/src/internal/core/runtime.rs b/src/internal/core/runtime.rs new file mode 100644 index 0000000..7b87508 --- /dev/null +++ b/src/internal/core/runtime.rs @@ -0,0 +1,75 @@ +use std::{collections::HashMap, net::SocketAddr, rc::Rc}; + +use futures::future::join_all; +use hyper::{Method, Server}; +use mlua::Lua; +use tokio::task::LocalSet; + +use crate::internal::{ + core::service::{LocalExec, MakeSvc}, + lua::app::App, +}; + +use super::routers::Routers; + +pub struct Runtime { + pub lua: Rc, + pub routers: Routers, +} + +impl Runtime { + pub fn from_script(lua_script: &Vec) -> Result { + let lua = Rc::new(Lua::new()); + + lua.globals().set("internal_app_counter", 0).unwrap(); + + let app_constructor = lua.create_function(|l, ()| { + let app_counter: u16 = l.globals().get("internal_app_counter")?; + l.globals().set("internal_app_counter", app_counter + 1)?; + Ok(App { + id: app_counter + 1, + handlers: Vec::new(), + }) + }); + + lua.globals() + .set("create_app", app_constructor.unwrap()) + .unwrap(); + + lua.load(std::str::from_utf8(lua_script).unwrap()) + .eval::<()>() + .unwrap(); + + let apps = lua + .to_owned() + .app_data_ref::)>>() + .unwrap() + .to_owned(); + + Ok(Self { + lua: lua.to_owned(), + routers: Routers::from(apps), + }) + } + + pub fn load(&self, f: &dyn Fn(&Lua) -> ()) { + f(&self.lua); + } + + pub async fn start(&self) { + let mut servers = Vec::new(); + + for (port, router) in self.routers.to_owned() { + println!("Server listening at http://localhost:{}", port); + let addr = SocketAddr::from(([127, 0, 0, 1], port)); + let server = Server::bind(&addr) + .executor(LocalExec) + .serve(MakeSvc(self.lua.to_owned(), router)); + servers.push(server); + } + + // Create `LocalSet` to spawn !Send futures + let local = LocalSet::new(); + local.run_until(join_all(servers)).await; + } +} diff --git a/src/internal/core/service.rs b/src/internal/core/service.rs new file mode 100644 index 0000000..ef1cf9a --- /dev/null +++ b/src/internal/core/service.rs @@ -0,0 +1,227 @@ +use hyper::body::to_bytes; +use hyper::server::conn::AddrStream; +use hyper::service::Service; +use hyper::{Body, Request, Response, StatusCode}; +use matchit::Router; +use mlua::{Error as LuaError, Function, Lua}; +use std::collections::HashMap; +use std::future::Future; +use std::net::SocketAddr; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll}; +use tokio::task::spawn_local; + +use crate::internal::lua::req::Req; +use crate::internal::lua::res::Res; + +use super::routers::LuaHandle; + +pub struct Svc(Rc, SocketAddr, Router); + +impl Service> for Svc { + type Response = Response; + type Error = LuaError; + type Future = Pin>>>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Request) -> Self::Future { + let (parts, body) = req.into_parts(); + let lua = self.0.clone(); + let addr = self.1.clone(); + let router = self.2.clone(); + Box::pin(async move { + let uri = parts.uri.to_string(); + let lua_handle_match = match router.at(uri.as_str()) { + Ok(res) => res, + Err(_) => { + return Ok(Response::builder() + .status(404) + .body(Body::from("Not Found")) + .unwrap()) + } + }; + + if lua_handle_match.value.method != parts.method.to_string() { + return Ok(Response::builder() + .status(405) + .body(Body::from("Method Not Allowed")) + .unwrap()); + } + + let body_bytes = to_bytes(body).await.unwrap(); + let body_str = String::from_utf8(body_bytes.to_vec()).unwrap(); + + let mut headers_map: HashMap = HashMap::new(); + for (k, v) in parts.headers { + headers_map.insert(k.unwrap().to_string(), v.to_str().unwrap().to_string()); + } + + let mut params_map: HashMap = HashMap::new(); + for (k, v) in lua_handle_match.params.iter() { + params_map.insert(k.to_string(), v.to_string()); + } + + let req = Req { + body: body_str, + headers: headers_map, + method: lua_handle_match.value.method.to_string(), + params: params_map, + socket_addr: addr.to_string(), + }; + + let res = Res { + body: "".to_string(), + status: StatusCode::OK, + }; + + let handler: Function = lua + .named_registry_value(&lua_handle_match.value.handler_identifier) + .unwrap(); + + match handler.call_async::<_, Res>((req, res)).await { + Ok(res) => Ok(Response::builder() + .status(res.status) + .body(Body::from(res.body)) + .unwrap()), + Err(err) => { + eprintln!("{}", err); + Ok(Response::builder() + .status(500) + .body(Body::from("Internal Server Error")) + .unwrap()) + } + } + }) + } +} + +pub struct MakeSvc(pub Rc, pub Router); + +impl Service<&AddrStream> for MakeSvc { + type Response = Svc; + type Error = hyper::Error; + type Future = Pin>>>; + + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, stream: &AddrStream) -> Self::Future { + let lua = self.0.clone(); + let router = self.1.clone(); + let remote_addr = stream.remote_addr(); + Box::pin(async move { Ok(Svc(lua, remote_addr, router)) }) + } +} + +#[derive(Clone, Copy, Debug)] +pub struct LocalExec; + +impl hyper::rt::Executor for LocalExec +where + F: std::future::Future + 'static, // not requiring `Send` +{ + fn execute(&self, fut: F) { + spawn_local(fut); + } +} + +#[cfg(test)] +mod tests { + use hyper::Method; + + use super::*; + + #[tokio::test] + async fn test_default() { + let lua = Rc::new(Lua::new()); + let router = Router::new(); + let addr = SocketAddr::from(([127, 0, 0, 1], 42)); + let mut service = Svc(lua, addr, router); + + let mut f = service + .call(Request::get("/").body(Body::from("".to_string())).unwrap()) + .await + .unwrap(); + + assert_eq!(f.status().as_u16(), 404); + + let body_bytes = to_bytes(f.body_mut()).await.unwrap(); + let body_str = String::from_utf8(body_bytes.to_vec()).unwrap(); + + assert_eq!(body_str, "Not Found"); + } + + #[tokio::test] + async fn test_complete() { + let lua = Rc::new(Lua::new()); + let mut router = Router::new(); + let addr = SocketAddr::from(([127, 0, 0, 1], 42)); + + // Create a lua handler + let handler = lua + .create_async_function(|_, ()| async { + Ok(Res { + status: StatusCode::IM_A_TEAPOT, + body: "Hello, World!".to_string(), + }) + }) + .unwrap(); + + let handler_identifier = "response_handler"; + + // Register handler in lua state + lua.set_named_registry_value(handler_identifier, handler) + .unwrap(); + + // Register `matchit` handler + router + .insert( + "/root", + LuaHandle { + method: Method::GET.to_string(), + handler_identifier: handler_identifier.to_string(), + }, + ) + .unwrap(); + + // Create service + let mut service = Svc(lua.clone(), addr, router); + + // 405 Method Not Allowed + let mut response = service + .call( + Request::post("/root") + .body(Body::from("".to_string())) + .unwrap(), + ) + .await + .unwrap(); + + let status = response.status(); + let body = to_bytes(response.body_mut()).await.unwrap(); + + assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); + assert_eq!(body, "Method Not Allowed"); + + // Happy path GET + let mut response = service + .call( + Request::get("/root") + .body(Body::from("".to_string())) + .unwrap(), + ) + .await + .unwrap(); + + let status = response.status(); + let body = to_bytes(response.body_mut()).await.unwrap(); + + assert_eq!(status, StatusCode::IM_A_TEAPOT); + assert_eq!(body, "Hello, World!"); + } +} diff --git a/src/internal/lua/app.rs b/src/internal/lua/app.rs new file mode 100644 index 0000000..36abefa --- /dev/null +++ b/src/internal/lua/app.rs @@ -0,0 +1,53 @@ +use std::collections::HashMap; + +use hyper::Method; +use mlua::{Function, UserData, UserDataMethods}; + +#[derive(Clone)] +pub struct App { + pub id: u16, + pub handlers: Vec<(Method, String, String)>, +} + +impl UserData for App { + fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_method_mut("get", |l, this, (path, cb): (String, Function)| { + let handler_identifier = format!("{}-get-{}", this.id, path); + l.set_named_registry_value(&handler_identifier, cb)?; + this.handlers.push((Method::GET, path, handler_identifier)); + Ok(()) + }); + + methods.add_method_mut("post", |l, this, (path, cb): (String, Function)| { + let handler_identifier = format!("{}-post-{}", this.id, path); + l.set_named_registry_value(&handler_identifier, cb)?; + this.handlers.push((Method::POST, path, handler_identifier)); + Ok(()) + }); + + methods.add_method_mut("use", |_l, this, (root, app): (String, App)| { + for (method, path, handler_identifier) in app.handlers { + let sub_path = if path == "/" { + root.to_owned() + } else { + [root.to_owned(), path].join("") + }; + this.handlers.push((method, sub_path, handler_identifier)); + } + Ok(()) + }); + + methods.add_method_mut("run", |l, this, port: u16| { + if let Some(mut apps) = + l.app_data_mut::)>>() + { + apps.insert(port, (this.id.to_string(), this.handlers.to_owned())); + } else { + let mut map = HashMap::new(); + map.insert(port, (this.id.to_string(), this.handlers.to_owned())); + l.set_app_data(map); + } + Ok(()) + }); + } +} diff --git a/src/internal/lua/mod.rs b/src/internal/lua/mod.rs new file mode 100644 index 0000000..5c1694a --- /dev/null +++ b/src/internal/lua/mod.rs @@ -0,0 +1,3 @@ +pub mod app; +pub mod req; +pub mod res; diff --git a/src/internal/lua/req.rs b/src/internal/lua/req.rs new file mode 100644 index 0000000..174f1dd --- /dev/null +++ b/src/internal/lua/req.rs @@ -0,0 +1,45 @@ +use std::collections::HashMap; + +use mlua::{LuaSerdeExt, UserData, UserDataFields}; + +#[derive(Clone)] +pub struct Req { + pub body: String, + pub headers: HashMap, + pub method: String, + pub params: HashMap, + pub socket_addr: String, +} + +impl UserData for Req { + fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_set("body", |_, this, value| { + this.body = value; + Ok(()) + }); + fields.add_field_method_get("body", |l, this| { + match serde_json::from_str::(&this.body) { + Ok(value) => Ok(l.to_value(&value)?), + Err(_) => Ok(l.to_value(&this.body)?), + } + }); + + fields.add_field_method_set("headers", |_, this, value| { + this.headers = value; + Ok(()) + }); + fields.add_field_method_get("headers", |_, this| Ok(this.headers.to_owned())); + + fields.add_field_method_set("method", |_, this, value| { + this.method = value; + Ok(()) + }); + fields.add_field_method_get("method", |_, this| Ok(this.method.to_owned())); + + fields.add_field_method_set("params", |_, this, value| { + this.params = value; + Ok(()) + }); + fields.add_field_method_get("params", |_, this| Ok(this.params.to_owned())); + } +} diff --git a/src/internal/lua/res.rs b/src/internal/lua/res.rs new file mode 100644 index 0000000..e6fbae0 --- /dev/null +++ b/src/internal/lua/res.rs @@ -0,0 +1,39 @@ +use std::str::FromStr; + +use hyper::StatusCode; +use mlua::{Error as LuaError, UserData, UserDataFields, Value as LuaValue}; + +pub fn json_encode(value: LuaValue, pretty: Option) -> Result, LuaError> { + match pretty { + Some(true) => match serde_json::to_string_pretty(&value) { + Ok(s) => Ok(Some(s)), + Err(e) => Err(LuaError::SerializeError(e.to_string())), + }, + _ => match serde_json::to_string(&value) { + Ok(s) => Ok(Some(s)), + Err(e) => Err(LuaError::SerializeError(e.to_string())), + }, + } +} + +#[derive(Clone)] +pub struct Res { + pub status: StatusCode, + pub body: String, +} + +impl UserData for Res { + fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_set("body", |_, this, value| { + this.body = json_encode(value, None).unwrap().unwrap(); + Ok(()) + }); + fields.add_field_method_get("body", |_, this| Ok(this.body.to_owned())); + + fields.add_field_method_set("status", |_, this, value: String| { + this.status = StatusCode::from_str(value.as_str()).unwrap(); + Ok(()) + }); + fields.add_field_method_get("status", |_, this| Ok(this.status.as_str().to_string())); + } +} diff --git a/src/internal/mod.rs b/src/internal/mod.rs new file mode 100644 index 0000000..bc362cd --- /dev/null +++ b/src/internal/mod.rs @@ -0,0 +1,2 @@ +pub mod core; +pub mod lua; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6a595f4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod api; +pub mod internal;