Compare commits
53 Commits
working
...
3a7f2366e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
3a7f2366e4
|
|||
|
8443c44671
|
|||
|
72b3afaeea
|
|||
|
503985269a
|
|||
|
0839e7ce9a
|
|||
|
cdfb2522ac
|
|||
|
0c4639c3e9
|
|||
|
4f548bf9e9
|
|||
|
40829cdfc6
|
|||
|
db4faea821
|
|||
|
2964ebbe1b
|
|||
|
b1572cc9f1
|
|||
|
862fcfe891
|
|||
|
6a194e98e8
|
|||
|
d2e06c9a70
|
|||
|
679ffed807
|
|||
|
549f96d4dc
|
|||
|
09d7e51345
|
|||
|
8a74ade6a6
|
|||
|
9338be7eb0
|
|||
|
240e0111bf
|
|||
|
01f459ffaf
|
|||
|
9b7dbcc141
|
|||
|
24c5e9edc2
|
|||
|
0cafac0948
|
|||
|
1c1753234d
|
|||
|
31b5ff5ab9
|
|||
|
2078dd0d8e
|
|||
|
dfd57e0ad0
|
|||
|
2a6015c19a
|
|||
|
88ab48cddb
|
|||
|
bcb734846d
|
|||
|
2fe9515702
|
|||
|
f456a544e1
|
|||
|
ba969b9e56
|
|||
|
da5acd6bc8
|
|||
|
cbb47840e4
|
|||
|
2967513cbb
|
|||
|
014687068f
|
|||
|
85a0110e1a
|
|||
|
91302db725
|
|||
|
c249549b3c
|
|||
|
30596d9331
|
|||
|
530a1530ba
|
|||
|
a81271c0a8
|
|||
|
7ec60f0e6e
|
|||
|
82726ebb5d
|
|||
|
86bfc7ad34
|
|||
|
6cdf186b61
|
|||
|
98100d02fa
|
|||
|
681dd332ab
|
|||
|
af3dbe60cb
|
|||
|
a07f62ecc5
|
@@ -1,3 +1,2 @@
|
||||
[build]
|
||||
rustflags = ["--cfg", "tokio_unstable"]
|
||||
rustdocflags = ["--cfg", "tokio_unstable"]
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||
"runtime.version": "LuaJIT",
|
||||
"diagnostics.disable": ["redefined-local", "lowercase-global"]
|
||||
"diagnostics.disable": [
|
||||
"undefined-global",
|
||||
"redefined-local",
|
||||
"lowercase-global"
|
||||
]
|
||||
}
|
||||
|
||||
707
Cargo.lock
generated
707
Cargo.lock
generated
@@ -255,6 +255,32 @@ name = "camino"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
@@ -262,6 +288,8 @@ version = "1.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
@@ -444,6 +472,46 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.0.1"
|
||||
@@ -466,6 +534,23 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
@@ -494,6 +579,12 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.2"
|
||||
@@ -510,6 +601,15 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
@@ -557,7 +657,19 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -566,12 +678,38 @@ version = "0.31.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"log",
|
||||
"regex-automata 0.4.9",
|
||||
"regex-syntax 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.10"
|
||||
@@ -729,12 +867,119 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_collections"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"potential_utf",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_locale_core"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"litemap",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_normalizer_data"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_collections",
|
||||
"icu_locale_core",
|
||||
"icu_properties_data",
|
||||
"icu_provider",
|
||||
"potential_utf",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "icu_properties_data"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
|
||||
|
||||
[[package]]
|
||||
name = "icu_provider"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"icu_locale_core",
|
||||
"stable_deref_trait",
|
||||
"tinystr",
|
||||
"writeable",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerotrie",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -785,6 +1030,16 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a"
|
||||
dependencies = [
|
||||
"getrandom 0.3.3",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -793,15 +1048,17 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "lb"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"derive_more",
|
||||
"globset",
|
||||
"luaffi",
|
||||
"luaify",
|
||||
"luajit",
|
||||
"sysexits",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -810,6 +1067,18 @@ version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.18.2+1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.8"
|
||||
@@ -830,6 +1099,18 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
@@ -837,14 +1118,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.13"
|
||||
name = "litemap"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -854,7 +1131,7 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "luaffi"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"luaffi_impl",
|
||||
@@ -865,7 +1142,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "luaffi_impl"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
@@ -875,8 +1152,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "luaify"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@@ -884,7 +1162,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "luajit"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bstr",
|
||||
@@ -895,7 +1173,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "luajit-sys"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
@@ -904,7 +1182,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "luby"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"console-subscriber",
|
||||
@@ -916,6 +1194,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"vergen-git2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -976,7 +1255,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -1000,6 +1279,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@@ -1009,6 +1294,15 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.7"
|
||||
@@ -1038,32 +1332,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "4.2.1"
|
||||
version = "4.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
@@ -1103,6 +1374,27 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585"
|
||||
dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -1112,6 +1404,16 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||
dependencies = [
|
||||
"diff",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.35"
|
||||
@@ -1172,6 +1474,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -1199,16 +1507,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1267,6 +1566,15 @@ version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.7"
|
||||
@@ -1293,10 +1601,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@@ -1345,15 +1665,6 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdutf8"
|
||||
version = "0.1.5"
|
||||
@@ -1382,6 +1693,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@@ -1405,12 +1722,36 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sysexits"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "198f60d1f7f003f168507691e42d082df109ef0f05c6fd006e22528371a5f1b4"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
@@ -1440,6 +1781,49 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinystr"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.45.1"
|
||||
@@ -1450,9 +1834,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
@@ -1655,6 +2037,23 @@ version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
@@ -1667,6 +2066,63 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "9.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b2bf58be11fc9414104c6d3a2e464163db5ef74b12296bda593cac37b6e4777"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_metadata",
|
||||
"derive_builder",
|
||||
"regex",
|
||||
"rustc_version",
|
||||
"rustversion",
|
||||
"vergen-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vergen-git2"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f6ee511ec45098eabade8a0750e76eec671e7fb2d9360c563911336bea9cac1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_builder",
|
||||
"git2",
|
||||
"rustversion",
|
||||
"time",
|
||||
"vergen",
|
||||
"vergen-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vergen-lib"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"derive_builder",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
@@ -1682,6 +2138,15 @@ version = "0.11.1+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "8.0.0"
|
||||
@@ -1709,6 +2174,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
@@ -1876,6 +2350,51 @@ version = "0.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"stable_deref_trait",
|
||||
"yoke-derive",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke-derive"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.26"
|
||||
@@ -1895,3 +2414,57 @@ dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
|
||||
dependencies = [
|
||||
"zerofrom-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerofrom-derive"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
|
||||
dependencies = [
|
||||
"displaydoc",
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
|
||||
dependencies = [
|
||||
"yoke",
|
||||
"zerofrom",
|
||||
"zerovec-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerovec-derive"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
47
Cargo.toml
47
Cargo.toml
@@ -1,4 +1,5 @@
|
||||
[workspace]
|
||||
resolver = "3"
|
||||
members = [
|
||||
"crates/lb",
|
||||
"crates/luaffi",
|
||||
@@ -8,23 +9,49 @@ members = [
|
||||
"crates/luajit-sys",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.0.1"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
authors = ["luaneko <lumi@lua.re>"]
|
||||
homepage = "https://git.lua.re/luaneko/luby/"
|
||||
repository = "https://git.lua.re/luaneko/luby/"
|
||||
|
||||
[package]
|
||||
name = "luby"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[profile]
|
||||
dev.panic = "abort"
|
||||
release.panic = "abort"
|
||||
|
||||
[package]
|
||||
name = "luby"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
[[test]]
|
||||
name = "main"
|
||||
harness = false
|
||||
|
||||
[features]
|
||||
default = ["task", "fs", "net"]
|
||||
task = ["lb/task"]
|
||||
fs = ["lb/fs"]
|
||||
net = ["lb/net"]
|
||||
tokio-console = ["dep:console-subscriber"]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.40", features = ["derive"] }
|
||||
console-subscriber = "0.4.1"
|
||||
lb = { version = "0.1.0", path = "crates/lb" }
|
||||
luajit = { version = "0.1.0", path = "crates/luajit", features = ["runtime"] }
|
||||
clap = { version = "4.5.40", features = ["derive", "env"] }
|
||||
console-subscriber = { version = "0.4.1", optional = true }
|
||||
lb = { path = "crates/lb" }
|
||||
luajit = { path = "crates/luajit", features = ["runtime"] }
|
||||
mimalloc = "0.1.47"
|
||||
owo-colors = "4.2.1"
|
||||
sysexits = "0.9.0"
|
||||
tokio = { version = "1.45.1", features = ["full", "tracing"] }
|
||||
tokio = { version = "1.45.1", features = ["rt", "rt-multi-thread"] }
|
||||
tracing = "0.1.41"
|
||||
tracing-subscriber = "0.3.19"
|
||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||
|
||||
[build-dependencies]
|
||||
vergen-git2 = { version = "1.0.7", features = ["cargo", "rustc"] }
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
[jobs.test]
|
||||
command = ["cargo", "test", "--workspace"]
|
||||
need_stdout = true
|
||||
|
||||
[jobs.doc]
|
||||
command = ["cargo", "doc", "--workspace"]
|
||||
|
||||
|
||||
13
build.rs
Normal file
13
build.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
use vergen_git2::{CargoBuilder, Emitter, Git2Builder, RustcBuilder};
|
||||
|
||||
fn main() {
|
||||
Emitter::default()
|
||||
.add_instructions(&CargoBuilder::all_cargo().unwrap())
|
||||
.unwrap()
|
||||
.add_instructions(&Git2Builder::all_git().unwrap())
|
||||
.unwrap()
|
||||
.add_instructions(&RustcBuilder::all_rustc().unwrap())
|
||||
.unwrap()
|
||||
.emit()
|
||||
.unwrap();
|
||||
}
|
||||
@@ -1,16 +1,24 @@
|
||||
[package]
|
||||
name = "lb"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[features]
|
||||
task = ["tokio/rt", "tokio/time"]
|
||||
fs = ["tokio/fs", "dep:walkdir", "dep:globset", "dep:tempfile"]
|
||||
net = ["tokio/net"]
|
||||
|
||||
[dependencies]
|
||||
camino = "1.1.10"
|
||||
derive_more = { version = "2.0.1", features = ["full"] }
|
||||
luaffi = { version = "0.1.0", path = "../luaffi" }
|
||||
luajit = { version = "0.1.0", path = "../luajit" }
|
||||
globset = { version = "0.4.16", optional = true }
|
||||
luaffi = { path = "../luaffi" }
|
||||
luajit = { path = "../luajit" }
|
||||
sysexits = "0.9.0"
|
||||
tokio = { version = "1.45.1", features = ["rt", "time", "fs", "net", "process", "signal"] }
|
||||
|
||||
[dev-dependencies]
|
||||
luaify = { path = "../luaify" }
|
||||
tokio = { version = "1.45.1", features = ["full"] }
|
||||
tempfile = { version = "3.20.0", optional = true }
|
||||
thiserror = "2.0.12"
|
||||
tokio = { version = "1.45.1" }
|
||||
walkdir = { version = "2.5.0", optional = true }
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
// use flume::{Receiver, Sender};
|
||||
//! Channel library
|
||||
//!
|
||||
//! The `lb:chan` library provides primitives for asynchronous communication between tasks via
|
||||
//! message passing channels.
|
||||
//!
|
||||
//! ## Exports
|
||||
//!
|
||||
//! See [`lb_chanlib`] for items exported by this library.
|
||||
use luaffi::{cdef, metatype};
|
||||
|
||||
#[cdef]
|
||||
pub struct lb_libchannel;
|
||||
/// Items exported by the `lb:chan` library.
|
||||
///
|
||||
/// This library can be acquired by calling
|
||||
/// [`require("lb:chan")`](https://www.lua.org/manual/5.1/manual.html#pdf-require) in Lua.
|
||||
///
|
||||
/// ```lua
|
||||
/// local chan = require("lb:chan");
|
||||
/// ```
|
||||
#[cdef(module = "lb:chan")]
|
||||
pub struct lb_chanlib;
|
||||
|
||||
#[metatype]
|
||||
impl lb_libchannel {
|
||||
impl lb_chanlib {
|
||||
#[new]
|
||||
extern "Lua-C" fn new() -> Self {
|
||||
Self
|
||||
@@ -1,32 +1,428 @@
|
||||
//! The `lb:fs` module provides utilities for interacting with the file system asynchronously.
|
||||
//! # Filesystem library
|
||||
//!
|
||||
//! See [`lb_libfs`] for items exported by this module.
|
||||
//! The `lb:fs` library provides synchronous and asynchronous utilities for interacting with the
|
||||
//! filesystem.
|
||||
//!
|
||||
//! ## Asynchronous by default
|
||||
//!
|
||||
//! Filesystem operations are blocking by nature; to provide asynchronicity, luby performs blocking
|
||||
//! operations in a background thread pool by default. Synchronous complements to all asynchronous
|
||||
//! functions are always provided.
|
||||
//!
|
||||
//! ## Exports
|
||||
//!
|
||||
//! See [`lb_fslib`] for items exported by this library.
|
||||
use derive_more::From;
|
||||
use luaffi::{cdef, metatype};
|
||||
use std::io;
|
||||
use tokio::fs;
|
||||
use std::{path::PathBuf, time::SystemTime};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Items exported by the `lb:fs` module.
|
||||
/// Errors that can be thrown by this library.
|
||||
///
|
||||
/// This module can be obtained by calling `require` in Lua.
|
||||
/// Functions which return this error will **throw** in Lua. The error message can be caught by
|
||||
/// using [`pcall(f, ...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pcall).
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// I/O error.
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
/// Walk directory error.
|
||||
#[error("{0}")]
|
||||
Walk(#[from] walkdir::Error),
|
||||
/// Glob pattern error.
|
||||
#[error("{0}")]
|
||||
Glob(#[from] globset::Error),
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Items exported by the `lb:fs` library.
|
||||
///
|
||||
/// This library can be acquired by calling `require` in Lua.
|
||||
///
|
||||
/// ```lua
|
||||
/// local fs = require("lb:fs");
|
||||
/// ```
|
||||
#[cdef]
|
||||
pub struct lb_libfs;
|
||||
#[cdef(module = "lb:fs")]
|
||||
pub struct lb_fslib;
|
||||
|
||||
#[metatype]
|
||||
impl lb_libfs {
|
||||
impl lb_fslib {
|
||||
#[new]
|
||||
extern "Lua-C" fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub extern "Lua" fn read(&self, path: string) -> string {
|
||||
self.__read(path)
|
||||
pub async extern "Lua-C" fn read(&self, path: &str) -> Result<Vec<u8>> {
|
||||
Ok(tokio::fs::read(path).await?)
|
||||
}
|
||||
|
||||
async extern "Lua-C" fn __read(&self, path: &str) -> io::Result<Vec<u8>> {
|
||||
fs::read(path).await
|
||||
pub extern "Lua-C" fn read_sync(&self, path: &str) -> Result<Vec<u8>> {
|
||||
Ok(std::fs::read(path)?)
|
||||
}
|
||||
|
||||
pub async extern "Lua-C" fn write(&self, path: &str, contents: &[u8]) -> Result<()> {
|
||||
Ok(tokio::fs::write(path, contents).await?)
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn write_sync(&self, path: &str, contents: &[u8]) -> Result<()> {
|
||||
Ok(std::fs::write(path, contents)?)
|
||||
}
|
||||
|
||||
pub async extern "Lua-C" fn read_dir(&self, path: &str) -> Result<lb_read_dir> {
|
||||
Ok(tokio::fs::read_dir(path).await?.into())
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn read_dir_sync(&self, path: &str) -> Result<lb_read_dir_sync> {
|
||||
Ok(std::fs::read_dir(path)?.into())
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn walk_dir(&self, path: &str) -> lb_walk_dir {
|
||||
walkdir::WalkDir::new(path).into_iter().into()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn glob(&self, pattern: &str) -> Result<lb_glob_dir> {
|
||||
self.glob_dir(".", pattern)
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn glob_dir(&self, path: &str, pattern: &str) -> Result<lb_glob_dir> {
|
||||
let prefix = PathBuf::from(path);
|
||||
let iter = walkdir::WalkDir::new(path).min_depth(1).into_iter();
|
||||
let matcher = globset::GlobSet::builder()
|
||||
.add(globset::Glob::new(pattern)?)
|
||||
.build()?;
|
||||
|
||||
Ok(lb_glob_dir {
|
||||
iter,
|
||||
matcher,
|
||||
prefix,
|
||||
})
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn temp_dir(&self) -> Result<lb_temp_dir> {
|
||||
Ok(tempfile::tempdir()?.into())
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn temp_dir_in(&self, path: &str) -> Result<lb_temp_dir> {
|
||||
Ok(tempfile::tempdir_in(path)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over the entries in a directory.
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_read_dir(#[opaque] tokio::fs::ReadDir);
|
||||
|
||||
#[metatype]
|
||||
impl lb_read_dir {
|
||||
#[call]
|
||||
pub async extern "Lua-C" fn next(&mut self) -> Result<Option<lb_dir_entry>> {
|
||||
Ok(self.0.next_entry().await?.map(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
/// Synchronous version of [`lb_read_dir`].
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_read_dir_sync(#[opaque] std::fs::ReadDir);
|
||||
|
||||
#[metatype]
|
||||
impl lb_read_dir_sync {
|
||||
#[call]
|
||||
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_dir_entry_sync>> {
|
||||
Ok(self.0.next().transpose()?.map(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
/// Entry inside of a directory on the filesystem.
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_dir_entry(#[opaque] tokio::fs::DirEntry);
|
||||
|
||||
#[metatype]
|
||||
impl lb_dir_entry {
|
||||
pub extern "Lua-C" fn path(&self) -> String {
|
||||
self.0.path().to_string_lossy().into()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn name(&self) -> String {
|
||||
self.0.file_name().to_string_lossy().into()
|
||||
}
|
||||
|
||||
pub async extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> {
|
||||
Ok(self.0.file_type().await?.into())
|
||||
}
|
||||
|
||||
pub async extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
|
||||
Ok(self.0.metadata().await?.into())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub extern "Lua-C" fn ino(&self) -> u64 {
|
||||
self.0.ino()
|
||||
}
|
||||
|
||||
#[tostring]
|
||||
pub extern "Lua" fn tostring(&self) -> String {
|
||||
self.path()
|
||||
}
|
||||
}
|
||||
|
||||
/// Synchronous version of [`lb_dir_entry`].
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_dir_entry_sync(#[opaque] std::fs::DirEntry);
|
||||
|
||||
#[metatype]
|
||||
impl lb_dir_entry_sync {
|
||||
pub extern "Lua-C" fn path(&self) -> String {
|
||||
self.0.path().to_string_lossy().into()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn name(&self) -> String {
|
||||
self.0.file_name().to_string_lossy().into()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn r#type(&self) -> Result<lb_file_type> {
|
||||
Ok(self.0.file_type()?.into())
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
|
||||
Ok(self.0.metadata()?.into())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub extern "Lua-C" fn ino(&self) -> u64 {
|
||||
use std::os::unix::fs::DirEntryExt;
|
||||
self.0.ino()
|
||||
}
|
||||
|
||||
#[tostring]
|
||||
pub extern "Lua" fn tostring(&self) -> String {
|
||||
self.path()
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure representing the type of a file with accessors for each file type.
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_file_type(#[opaque] std::fs::FileType);
|
||||
|
||||
#[metatype]
|
||||
impl lb_file_type {
|
||||
pub extern "Lua-C" fn is_dir(&self) -> bool {
|
||||
self.0.is_dir()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn is_file(&self) -> bool {
|
||||
self.0.is_file()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn is_symlink(&self) -> bool {
|
||||
self.0.is_file()
|
||||
}
|
||||
|
||||
#[tostring]
|
||||
pub extern "Lua-C" fn tostring(&self) -> String {
|
||||
if self.0.is_file() {
|
||||
"file"
|
||||
} else if self.0.is_dir() {
|
||||
"dir"
|
||||
} else if self.0.is_symlink() {
|
||||
"symlink"
|
||||
} else {
|
||||
"other"
|
||||
}
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata information about a file.
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_file_meta(#[opaque] std::fs::Metadata);
|
||||
|
||||
#[metatype]
|
||||
impl lb_file_meta {
|
||||
pub extern "Lua-C" fn is_dir(&self) -> bool {
|
||||
self.0.is_dir()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn is_file(&self) -> bool {
|
||||
self.0.is_file()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn is_symlink(&self) -> bool {
|
||||
self.0.is_file()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn r#type(&self) -> lb_file_type {
|
||||
self.0.file_type().into()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn size(&self) -> u64 {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn perms(&self) -> lb_file_perms {
|
||||
self.0.permissions().into()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn created(&self) -> Result<f64> {
|
||||
Ok(self
|
||||
.0
|
||||
.created()?
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map(|dur| dur.as_secs_f64())
|
||||
.unwrap_or(0.))
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn modified(&self) -> Result<f64> {
|
||||
Ok(self
|
||||
.0
|
||||
.modified()?
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map(|dur| dur.as_secs_f64())
|
||||
.unwrap_or(0.))
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn accessed(&self) -> Result<f64> {
|
||||
Ok(self
|
||||
.0
|
||||
.accessed()?
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map(|dur| dur.as_secs_f64())
|
||||
.unwrap_or(0.))
|
||||
}
|
||||
|
||||
#[tostring]
|
||||
pub extern "Lua-C" fn tostring(&self) -> String {
|
||||
let ty = self.0.file_type();
|
||||
if ty.is_file() {
|
||||
format!("file {}", self.0.len())
|
||||
} else if ty.is_dir() {
|
||||
"dir".into()
|
||||
} else if ty.is_symlink() {
|
||||
"symlink".into()
|
||||
} else {
|
||||
"other".into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of the various permissions on a file.
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_file_perms(#[opaque] std::fs::Permissions);
|
||||
|
||||
#[metatype]
|
||||
impl lb_file_perms {
|
||||
pub extern "Lua-C" fn readonly(&self) -> bool {
|
||||
self.0.readonly()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn set_readonly(&mut self, readonly: bool) {
|
||||
self.0.set_readonly(readonly);
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator for recursively descending into a directory.
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_walk_dir(#[opaque] walkdir::IntoIter);
|
||||
|
||||
#[metatype]
|
||||
impl lb_walk_dir {
|
||||
#[call]
|
||||
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_walk_dir_entry>> {
|
||||
Ok(self.0.next().transpose()?.map(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
/// Entry inside of a directory on the filesystem obtained from [`lb_walk_dir`].
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_walk_dir_entry(#[opaque] walkdir::DirEntry);
|
||||
|
||||
#[metatype]
|
||||
impl lb_walk_dir_entry {
|
||||
pub extern "Lua-C" fn path(&self) -> String {
|
||||
self.0.path().to_string_lossy().into()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn name(&self) -> String {
|
||||
self.0.file_name().to_string_lossy().into()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn r#type(&self) -> lb_file_type {
|
||||
self.0.file_type().into()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn metadata(&self) -> Result<lb_file_meta> {
|
||||
Ok(self.0.metadata()?.into())
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn is_symlink(&self) -> bool {
|
||||
self.0.path_is_symlink()
|
||||
}
|
||||
|
||||
pub extern "Lua-C" fn depth(&self) -> u32 {
|
||||
self.0.depth() as u32
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub extern "Lua-C" fn ino(&self) -> u64 {
|
||||
use walkdir::DirEntryExt;
|
||||
self.0.ino()
|
||||
}
|
||||
|
||||
#[tostring]
|
||||
pub extern "Lua" fn tostring(&self) -> String {
|
||||
self.path()
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator that yields paths from the filesystem that match a particular pattern.
|
||||
#[derive(Debug)]
|
||||
#[cdef]
|
||||
pub struct lb_glob_dir {
|
||||
#[opaque]
|
||||
iter: walkdir::IntoIter,
|
||||
#[opaque]
|
||||
matcher: globset::GlobSet,
|
||||
#[opaque]
|
||||
prefix: PathBuf,
|
||||
}
|
||||
|
||||
#[metatype]
|
||||
impl lb_glob_dir {
|
||||
#[call]
|
||||
pub extern "Lua-C" fn next(&mut self) -> Result<Option<lb_walk_dir_entry>> {
|
||||
while let Some(res) = self.iter.next() {
|
||||
let entry = res?;
|
||||
let path = entry.path().strip_prefix(&self.prefix).unwrap();
|
||||
if self.matcher.is_match(path) {
|
||||
return Ok(Some(entry.into()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
/// Directory in the filesystem that is automatically deleted when it is garbage-collected.
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_temp_dir(#[opaque] tempfile::TempDir);
|
||||
|
||||
#[metatype]
|
||||
impl lb_temp_dir {
|
||||
pub extern "Lua-C" fn path(&self) -> String {
|
||||
self.0.path().to_string_lossy().into()
|
||||
}
|
||||
|
||||
#[tostring]
|
||||
pub extern "Lua" fn tostring(&self) -> String {
|
||||
self.path()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
pub mod channel;
|
||||
pub mod fs;
|
||||
pub mod net;
|
||||
//! luby standard library
|
||||
#![warn(missing_docs)]
|
||||
pub mod runtime;
|
||||
|
||||
#[cfg(feature = "task")]
|
||||
pub mod chan;
|
||||
#[cfg(feature = "fs")]
|
||||
pub mod fs;
|
||||
#[cfg(feature = "net")]
|
||||
pub mod net;
|
||||
#[cfg(feature = "task")]
|
||||
pub mod task;
|
||||
|
||||
@@ -1,27 +1,52 @@
|
||||
//! The `lb:net` module provides an asynchronous network API for creating TCP or UDP servers and
|
||||
//! Networking library
|
||||
//!
|
||||
//! The `lb:net` library provides an asynchronous network API for creating TCP or UDP servers and
|
||||
//! clients.
|
||||
//!
|
||||
//! See [`lb_libnet`] for items exported by this module.
|
||||
//! ## Exports
|
||||
//!
|
||||
//! See [`lb_netlib`] for items exported by this library.
|
||||
use derive_more::{From, FromStr};
|
||||
use luaffi::{cdef, metatype};
|
||||
use std::{
|
||||
io,
|
||||
net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
time::Duration,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
||||
|
||||
/// Items exported by the `lb:net` module.
|
||||
/// Errors that can be thrown by this library.
|
||||
///
|
||||
/// This module can be obtained by calling `require` in Lua.
|
||||
/// Functions which return this error will **throw** in Lua. The error message can be caught by
|
||||
/// using [`pcall(f, ...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pcall).
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
/// I/O error.
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
/// IP or socket address syntax error.
|
||||
#[error("{0}")]
|
||||
InvalidAddr(#[from] AddrParseError),
|
||||
/// Socket was already converted and cannot be used anymore.
|
||||
#[error("socket was already converted")]
|
||||
SocketConsumed,
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Items exported by the `lb:net` library.
|
||||
///
|
||||
/// This library can be acquired by calling
|
||||
/// [`require("lb:net")`](https://www.lua.org/manual/5.1/manual.html#pdf-require) in Lua.
|
||||
///
|
||||
/// ```lua
|
||||
/// local net = require("lb:net");
|
||||
/// ```
|
||||
#[cdef]
|
||||
pub struct lb_libnet;
|
||||
#[cdef(module = "lb:net")]
|
||||
pub struct lb_netlib;
|
||||
|
||||
#[metatype]
|
||||
impl lb_libnet {
|
||||
impl lb_netlib {
|
||||
#[new]
|
||||
extern "Lua-C" fn new() -> Self {
|
||||
Self
|
||||
@@ -57,22 +82,18 @@ impl lb_libnet {
|
||||
/// If `s` is an [`lb_ipaddr`], a copy of that value is returned. If `s` is an
|
||||
/// [`lb_socketaddr`], the IP address part of the socket address is returned. Otherwise, parses
|
||||
/// `s` as an IP address string. Both IPv4 or IPv6 addresses are supported.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Throws if `s` cannot be parsed as an IP address.
|
||||
pub extern "Lua" fn ipaddr(&self, s: any) -> lb_ipaddr {
|
||||
if __istype(__ct.lb_ipaddr, s) {
|
||||
__new(__ct.lb_ipaddr, s) // copy constructor
|
||||
} else if __istype(__ct.lb_socketaddr, s) {
|
||||
pub extern "Lua" fn ipaddr(&self, addr: any) -> Result<lb_ipaddr> {
|
||||
if __istype(__ct.lb_ipaddr, addr) {
|
||||
__new(__ct.lb_ipaddr, addr) // copy constructor
|
||||
} else if __istype(__ct.lb_socketaddr, addr) {
|
||||
s.ip()
|
||||
} else {
|
||||
self.__parse_ipaddr(s)
|
||||
self.__parse_ipaddr(addr)
|
||||
}
|
||||
}
|
||||
|
||||
extern "Lua-C" fn __parse_ipaddr(&self, s: &str) -> Result<lb_ipaddr, AddrParseError> {
|
||||
s.parse()
|
||||
extern "Lua-C" fn __parse_ipaddr(&self, addr: &str) -> Result<lb_ipaddr> {
|
||||
Ok(addr.parse()?)
|
||||
}
|
||||
|
||||
/// Creates an [`lb_socketaddr`] from the given input.
|
||||
@@ -84,64 +105,75 @@ impl lb_libnet {
|
||||
/// socket address string. Both IPv4 and IPv6 addresses are supported.
|
||||
///
|
||||
/// If `port` is not specified, `0` is used as the default.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Throws if `s` cannot be parsed as an IP or socket address.
|
||||
pub extern "Lua" fn socketaddr(&self, s: any, port: any) -> lb_socketaddr {
|
||||
pub extern "Lua" fn socketaddr(&self, addr: any, port: any) -> Result<lb_socketaddr> {
|
||||
if port != () {
|
||||
self.__new_socketaddr(self.ipaddr(s), port)
|
||||
self.__new_skaddr(self.ipaddr(addr), port)
|
||||
} else {
|
||||
if __istype(__ct.lb_socketaddr, s) {
|
||||
__new(__ct.lb_socketaddr, s) // copy constructor
|
||||
} else if __istype(__ct.lb_ipaddr, s) {
|
||||
self.__new_socketaddr(s, 0) // default port 0
|
||||
if __istype(__ct.lb_socketaddr, addr) {
|
||||
__new(__ct.lb_socketaddr, addr) // copy constructor
|
||||
} else if __istype(__ct.lb_ipaddr, addr) {
|
||||
self.__new_skaddr(addr, 0) // default port 0
|
||||
} else {
|
||||
self.__parse_socketaddr(s)
|
||||
self.__parse_skaddr(addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "Lua-C" fn __new_socketaddr(&self, ip: &lb_ipaddr, port: u16) -> lb_socketaddr {
|
||||
extern "Lua-C" fn __new_skaddr(&self, ip: &lb_ipaddr, port: u16) -> lb_socketaddr {
|
||||
SocketAddr::new(ip.0, port).into()
|
||||
}
|
||||
|
||||
extern "Lua-C" fn __parse_socketaddr(&self, s: &str) -> Result<lb_socketaddr, AddrParseError> {
|
||||
s.parse()
|
||||
extern "Lua-C" fn __parse_skaddr(&self, addr: &str) -> Result<lb_socketaddr> {
|
||||
Ok(if let Ok(addr) = addr.parse() {
|
||||
SocketAddr::new(addr, 0).into() // default port 0
|
||||
} else {
|
||||
addr.parse::<SocketAddr>()?.into()
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new TCP socket configured for IPv4.
|
||||
///
|
||||
/// See [`TcpSocket::new_v4`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Throws if an error was encountered during the socket creation.
|
||||
pub extern "Lua" fn tcp_v4(&self) -> lb_tcpsocket {
|
||||
self.__new_tcp_v4()
|
||||
pub extern "Lua-C" fn tcp(&self) -> Result<lb_tcpsocket> {
|
||||
Ok(Some(TcpSocket::new_v4()?).into())
|
||||
}
|
||||
|
||||
/// Creates a new TCP socket configured for IPv6.
|
||||
///
|
||||
/// See [`TcpSocket::new_v6`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Throws if an error was encountered during the socket creation.
|
||||
pub extern "Lua" fn tcp_v6(&self) -> lb_tcpsocket {
|
||||
self.__new_tcp_v6()
|
||||
pub extern "Lua-C" fn tcp6(&self) -> Result<lb_tcpsocket> {
|
||||
Ok(Some(TcpSocket::new_v6()?).into())
|
||||
}
|
||||
|
||||
extern "Lua-C" fn __new_tcp_v4(&self) -> io::Result<lb_tcpsocket> {
|
||||
TcpSocket::new_v4().map(lb_tcpsocket)
|
||||
pub async extern "Lua" fn bind_tcp(&self, addr: any, port: any) -> Result<lb_tcpsocket> {
|
||||
let addr = self.socketaddr(addr, port);
|
||||
let socket;
|
||||
if addr.ip().is_v6() {
|
||||
socket = self.tcp6();
|
||||
} else {
|
||||
socket = self.tcp();
|
||||
}
|
||||
socket.bind(addr);
|
||||
socket
|
||||
}
|
||||
|
||||
extern "Lua-C" fn __new_tcp_v6(&self) -> io::Result<lb_tcpsocket> {
|
||||
TcpSocket::new_v6().map(lb_tcpsocket)
|
||||
pub async extern "Lua" fn connect_tcp(&self, addr: any, port: any) -> Result<lb_tcpstream> {
|
||||
let addr = self.socketaddr(addr, port);
|
||||
let socket;
|
||||
if addr.ip().is_v6() {
|
||||
socket = self.tcp6();
|
||||
} else {
|
||||
socket = self.tcp();
|
||||
}
|
||||
socket.connect(addr)
|
||||
}
|
||||
|
||||
pub async extern "Lua" fn listen_tcp(&self, addr: any, port: any) -> Result<lb_tcplistener> {
|
||||
self.bind_tcp(addr, port).listen(1024)
|
||||
}
|
||||
}
|
||||
|
||||
/// An IP address, either IPv4 or IPv6.
|
||||
/// IP address, either IPv4 or IPv6.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@@ -266,7 +298,7 @@ impl lb_ipaddr {
|
||||
}
|
||||
}
|
||||
|
||||
/// A socket address, which is an IP address with a port number.
|
||||
/// Socket address, which is an IP address with a port number.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, From, FromStr)]
|
||||
#[cdef]
|
||||
pub struct lb_socketaddr(#[opaque] SocketAddr);
|
||||
@@ -280,14 +312,14 @@ impl lb_socketaddr {
|
||||
|
||||
/// Sets the IP part of this address.
|
||||
///
|
||||
/// This function accepts the same arguments as [`ipaddr`](lb_libnet::ipaddr).
|
||||
pub extern "Lua" fn set_ip(&mut self, s: any) -> &mut Self {
|
||||
if __istype(__ct.lb_ipaddr, s) {
|
||||
self.__set_ip(s);
|
||||
} else if __istype(__ct.lb_socketaddr, s) {
|
||||
self.__set_ip(s.ip());
|
||||
/// This function accepts the same arguments as [`ipaddr`](lb_netlib::ipaddr).
|
||||
pub extern "Lua" fn set_ip(&mut self, addr: any) -> &mut Self {
|
||||
if __istype(__ct.lb_ipaddr, addr) {
|
||||
self.__set_ip(addr);
|
||||
} else if __istype(__ct.lb_socketaddr, addr) {
|
||||
self.__set_ip(addr.ip());
|
||||
} else {
|
||||
self.__set_ip_parse(s);
|
||||
self.__set_ip_parse(addr);
|
||||
}
|
||||
self
|
||||
}
|
||||
@@ -296,8 +328,8 @@ impl lb_socketaddr {
|
||||
self.0.set_ip(ip.0);
|
||||
}
|
||||
|
||||
extern "Lua-C" fn __set_ip_parse(&mut self, s: &str) -> Result<(), AddrParseError> {
|
||||
s.parse().map(|ip| self.0.set_ip(ip))
|
||||
extern "Lua-C" fn __set_ip_parse(&mut self, addr: &str) -> Result<()> {
|
||||
Ok(self.0.set_ip(addr.parse()?))
|
||||
}
|
||||
|
||||
/// Returns the port part of this address.
|
||||
@@ -306,7 +338,7 @@ impl lb_socketaddr {
|
||||
}
|
||||
|
||||
/// Sets the port part of this address.
|
||||
pub extern "Lua" fn set_port(&mut self, port: number) -> &mut Self {
|
||||
pub extern "Lua" fn set_port(&mut self, port: integer) -> &mut Self {
|
||||
self.__set_port(port);
|
||||
self
|
||||
}
|
||||
@@ -322,14 +354,127 @@ impl lb_socketaddr {
|
||||
}
|
||||
}
|
||||
|
||||
/// A TCP socket which has not yet been converted to a [`lb_tcpstream`] or [`lb_tcplistener`].
|
||||
/// TCP socket which has not yet been converted to an [`lb_tcpstream`] or [`lb_tcplistener`].
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_tcpsocket(#[opaque] TcpSocket);
|
||||
pub struct lb_tcpsocket(#[opaque] Option<TcpSocket>);
|
||||
|
||||
#[metatype]
|
||||
impl lb_tcpsocket {}
|
||||
impl lb_tcpsocket {
|
||||
fn socket(&self) -> Result<&TcpSocket> {
|
||||
self.0.as_ref().ok_or(Error::SocketConsumed)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::keepalive`].
|
||||
pub extern "Lua-C" fn keepalive(&self) -> Result<bool> {
|
||||
Ok(self.socket()?.keepalive()?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::set_keepalive`].
|
||||
pub extern "Lua-C" fn set_keepalive(&self, enabled: bool) -> Result<()> {
|
||||
Ok(self.socket()?.set_keepalive(enabled)?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::reuseaddr`].
|
||||
pub extern "Lua-C" fn reuseaddr(&self) -> Result<bool> {
|
||||
Ok(self.socket()?.reuseaddr()?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::set_reuseaddr`].
|
||||
pub extern "Lua-C" fn set_reuseaddr(&self, enabled: bool) -> Result<()> {
|
||||
Ok(self.socket()?.set_reuseaddr(enabled)?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::reuseport`].
|
||||
pub extern "Lua-C" fn reuseport(&self) -> Result<bool> {
|
||||
Ok(self.socket()?.reuseport()?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::set_reuseport`].
|
||||
pub extern "Lua-C" fn set_reuseport(&self, enabled: bool) -> Result<()> {
|
||||
Ok(self.socket()?.set_reuseport(enabled)?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::send_buffer_size`].
|
||||
pub extern "Lua-C" fn sendbuf(&self) -> Result<u32> {
|
||||
Ok(self.socket()?.send_buffer_size()?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::set_send_buffer_size`].
|
||||
pub extern "Lua-C" fn set_sendbuf(&self, size: u32) -> Result<()> {
|
||||
Ok(self.socket()?.set_send_buffer_size(size)?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::recv_buffer_size`].
|
||||
pub extern "Lua-C" fn recvbuf(&self) -> Result<u32> {
|
||||
Ok(self.socket()?.recv_buffer_size()?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::set_recv_buffer_size`].
|
||||
pub extern "Lua-C" fn set_recvbuf(&self, size: u32) -> Result<()> {
|
||||
Ok(self.socket()?.set_recv_buffer_size(size)?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::linger`].
|
||||
pub extern "Lua-C" fn linger(&self) -> Result<f64> {
|
||||
Ok(self
|
||||
.socket()?
|
||||
.linger()?
|
||||
.map(|n| n.as_secs_f64())
|
||||
.unwrap_or(0.))
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::set_linger`].
|
||||
pub extern "Lua-C" fn set_linger(&self, secs: f64) -> Result<()> {
|
||||
Ok(self
|
||||
.socket()?
|
||||
.set_linger((secs != 0.).then_some(Duration::from_secs_f64(secs)))?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::nodelay`].
|
||||
pub extern "Lua-C" fn nodelay(&self) -> Result<bool> {
|
||||
Ok(self.socket()?.nodelay()?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::set_nodelay`].
|
||||
pub extern "Lua-C" fn set_nodelay(&self, enabled: bool) -> Result<()> {
|
||||
Ok(self.socket()?.set_nodelay(enabled)?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::tos`].
|
||||
pub extern "Lua-C" fn tos(&self) -> Result<u32> {
|
||||
Ok(self.socket()?.tos()?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::set_tos`].
|
||||
pub extern "Lua-C" fn set_tos(&self, tos: u32) -> Result<()> {
|
||||
Ok(self.socket()?.set_tos(tos)?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::local_addr`].
|
||||
pub extern "Lua-C" fn local_addr(&self) -> Result<lb_socketaddr> {
|
||||
Ok(self.socket()?.local_addr()?.into())
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::bind`].
|
||||
pub extern "Lua-C" fn bind(&self, addr: &lb_socketaddr) -> Result<()> {
|
||||
Ok(self.socket()?.bind(addr.0)?)
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::connect`].
|
||||
pub async extern "Lua-C" fn connect(&mut self, addr: &lb_socketaddr) -> Result<lb_tcpstream> {
|
||||
let socket = self.0.take().ok_or(Error::SocketConsumed)?;
|
||||
Ok(socket.connect(addr.0).await?.into())
|
||||
}
|
||||
|
||||
/// See [`TcpSocket::listen`].
|
||||
pub extern "Lua-C" fn listen(&mut self, backlog: u32) -> Result<lb_tcplistener> {
|
||||
let socket = self.0.take().ok_or(Error::SocketConsumed)?;
|
||||
Ok(socket.listen(backlog)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// TCP connection between a local and a remote socket.
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_tcpstream(#[opaque] TcpStream);
|
||||
@@ -337,6 +482,7 @@ pub struct lb_tcpstream(#[opaque] TcpStream);
|
||||
#[metatype]
|
||||
impl lb_tcpstream {}
|
||||
|
||||
/// TCP socket server, listening for connections.
|
||||
#[derive(Debug, From)]
|
||||
#[cdef]
|
||||
pub struct lb_tcplistener(#[opaque] TcpListener);
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
local task = require("lb:task")
|
||||
|
||||
function spawn(f, ...)
|
||||
return task:spawn(f, ...)
|
||||
end
|
||||
@@ -1,85 +1,115 @@
|
||||
use crate::{channel::lb_libchannel, fs::lb_libfs, net::lb_libnet, task::lb_libtask};
|
||||
#![doc(hidden)]
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use luaffi::{Registry, Type};
|
||||
use luaffi::{Module, Registry};
|
||||
use luajit::{Chunk, State};
|
||||
use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
use tokio::{
|
||||
task::{JoinHandle, LocalSet, futures::TaskLocalFuture, spawn_local},
|
||||
task_local,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub type ErrorFn = dyn Fn(&luajit::Error);
|
||||
|
||||
pub struct Builder {
|
||||
registry: Registry,
|
||||
report_err: Rc<ErrorFn>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new() -> Self {
|
||||
let mut registry = Registry::new();
|
||||
|
||||
registry
|
||||
.preload::<lb_libtask>("lb:task")
|
||||
.preload::<lb_libchannel>("lb:channel")
|
||||
.preload::<lb_libfs>("lb:fs")
|
||||
.preload::<lb_libnet>("lb:net");
|
||||
|
||||
Self { registry }
|
||||
Self {
|
||||
registry: Registry::new(),
|
||||
report_err: Rc::new(|err| match err.trace() {
|
||||
Some(trace) => eprintln!("unhandled lua error: {err}\n{trace}"),
|
||||
None => eprintln!("unhandled lua error: {err}"),
|
||||
}),
|
||||
}
|
||||
|
||||
pub fn module<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
||||
self.registry.preload::<T>(name);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn registry(&self) -> &Registry {
|
||||
&self.registry
|
||||
}
|
||||
|
||||
pub fn unhandled_error(&mut self, handler: impl Fn(&luajit::Error) + 'static) -> &mut Self {
|
||||
self.report_err = Rc::new(handler);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn module<T: Module>(&mut self) -> &mut Self {
|
||||
self.registry.preload::<T>();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> luajit::Result<Runtime> {
|
||||
Ok(Runtime {
|
||||
cx: Context {
|
||||
state: {
|
||||
let mut s = State::new()?;
|
||||
let mut chunk = Chunk::new(self.registry.done());
|
||||
chunk.extend(include_bytes!("./runtime.lua"));
|
||||
s.eval(chunk.path("[luby]"), 0, 0)?;
|
||||
s.eval(Chunk::new(self.registry.build()).path("[luby]"), 0, 0)?;
|
||||
s
|
||||
},
|
||||
report_err: self.report_err.clone(),
|
||||
},
|
||||
tasks: LocalSet::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deref, DerefMut)]
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct Runtime {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
state: State,
|
||||
cx: Context,
|
||||
tasks: LocalSet,
|
||||
}
|
||||
|
||||
task_local! {
|
||||
static STATE: State;
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
pub fn spawn<T: 'static>(
|
||||
&self,
|
||||
f: impl AsyncFnOnce(&mut State) -> T + 'static,
|
||||
f: impl AsyncFnOnce(&mut Context) -> T + 'static,
|
||||
) -> JoinHandle<T> {
|
||||
self.tasks
|
||||
.spawn_local(async move { f(&mut STATE.with(|s| s.new_thread())).await })
|
||||
.spawn_local(async move { f(&mut CURRENT.with(|s| s.new_thread())).await })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn<T: 'static>(f: impl AsyncFnOnce(&mut State) -> T + 'static) -> JoinHandle<T> {
|
||||
spawn_local(async move { f(&mut STATE.with(|s| s.new_thread())).await })
|
||||
}
|
||||
|
||||
impl IntoFuture for Runtime {
|
||||
type Output = ();
|
||||
type IntoFuture = TaskLocalFuture<State, LocalSet>;
|
||||
type IntoFuture = TaskLocalFuture<Context, LocalSet>;
|
||||
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
STATE.scope(self.state, self.tasks)
|
||||
CURRENT.scope(self.cx, self.tasks)
|
||||
}
|
||||
}
|
||||
|
||||
task_local! {
|
||||
static CURRENT: Context;
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct Context {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
state: State,
|
||||
report_err: Rc<ErrorFn>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new_thread(&self) -> Self {
|
||||
Self {
|
||||
state: self.state.new_thread(),
|
||||
report_err: self.report_err.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report_error(&self, err: &luajit::Error) {
|
||||
(self.report_err)(&err);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn<T: 'static>(f: impl AsyncFnOnce(&mut Context) -> T + 'static) -> JoinHandle<T> {
|
||||
// SAFETY: `new_thread` must be called inside `spawn_local` because this free-standing spawn
|
||||
// function may be called via ffi from lua, and it is not safe to access the lua state within
|
||||
// ffi calls.
|
||||
spawn_local(async move { f(&mut CURRENT.with(|s| s.new_thread())).await })
|
||||
}
|
||||
|
||||
10
crates/lb/src/task.lua
Normal file
10
crates/lb/src/task.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
-- include task functions in the global scope
|
||||
local task = require("lb:task")
|
||||
|
||||
function sleep(ms)
|
||||
return task:sleep(ms)
|
||||
end
|
||||
|
||||
function spawn(f, ...)
|
||||
return task:spawn(f, ...)
|
||||
end
|
||||
@@ -1,46 +1,103 @@
|
||||
//! Task library
|
||||
//!
|
||||
//! The `lb:task` library primitives for asynchronous communication between tasks via message
|
||||
//! passing channels.
|
||||
//!
|
||||
//! ## Exports
|
||||
//!
|
||||
//! See [`lb_tasklib`] for items exported by this library.
|
||||
use crate::runtime::spawn;
|
||||
use luaffi::{cdef, metatype};
|
||||
use std::{ffi::c_int, process};
|
||||
use tokio::task::JoinHandle;
|
||||
use luajit::{LUA_MULTRET, Type};
|
||||
use std::{ffi::c_int, time::Duration};
|
||||
use tokio::{task::JoinHandle, time::sleep};
|
||||
|
||||
#[cdef]
|
||||
pub struct lb_libtask;
|
||||
/// Items exported by the `lb:task` library.
|
||||
///
|
||||
/// This library can be acquired by calling
|
||||
/// [`require("lb:task")`](https://www.lua.org/manual/5.1/manual.html#pdf-require) in Lua.
|
||||
///
|
||||
/// ```lua
|
||||
/// local task = require("lb:task");
|
||||
/// ```
|
||||
#[cdef(module = "lb:task")]
|
||||
pub struct lb_tasklib;
|
||||
|
||||
#[metatype]
|
||||
impl lb_libtask {
|
||||
#[include("task.lua")]
|
||||
impl lb_tasklib {
|
||||
#[new]
|
||||
extern "Lua-C" fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub extern "Lua" fn spawn(self, f: function, ...) {
|
||||
// pack the function and its arguments into a table and pass its ref to rust
|
||||
pub async extern "Lua-C" fn sleep(&self, ms: f64) {
|
||||
sleep(Duration::from_secs_f64(ms / 1000.)).await;
|
||||
}
|
||||
|
||||
pub extern "Lua" fn spawn(&self, f: function, ...) -> lb_task {
|
||||
// pack the function and its arguments into a table and pass its ref to rust.
|
||||
//
|
||||
// this table is used from rust-side to call the function with its args, and it's also
|
||||
// reused to store its return values that the task handle can return when awaited. the ref
|
||||
// is owned by the task handle and unref'ed when it's gc'ed.
|
||||
self.__spawn(__ref(__tpack(f, variadic!())))
|
||||
}
|
||||
|
||||
extern "Lua-C" fn __spawn(&self, key: c_int) -> lb_task {
|
||||
let handle = spawn(async move |s| {
|
||||
// SAFETY: key is always unique, created by __ref above
|
||||
let arg = unsafe { s.new_ref_unchecked(key) };
|
||||
let handle = spawn(async move |cx| {
|
||||
// SAFETY: key is always unique, created by __ref above.
|
||||
let arg = unsafe { cx.new_ref_unchecked(key) };
|
||||
let mut s = cx.guard();
|
||||
s.resize(0);
|
||||
s.push(arg);
|
||||
let narg = s.unpack(1, 1, None) - 1;
|
||||
println!("{s:?}");
|
||||
if let Err(_err) = s.call_async(narg, 0).await {
|
||||
process::exit(1)
|
||||
s.push(&arg);
|
||||
let narg = s.unpack(1, 1, None) - 1; // unpack the function and its args from the table
|
||||
debug_assert!(s.slot(2).type_of() == Type::Function);
|
||||
match s.call_async(narg, LUA_MULTRET).await {
|
||||
Ok(nret) => {
|
||||
s.pack(1, nret); // pack the return values back into the table
|
||||
}
|
||||
println!("{s:?}");
|
||||
Err(err) => {
|
||||
drop(s);
|
||||
cx.report_error(&err);
|
||||
}
|
||||
}
|
||||
let _ = arg.into_raw(); // the original ref is owned by the task handle and unref'ed there
|
||||
});
|
||||
|
||||
lb_task { handle }
|
||||
lb_task {
|
||||
handle: Some(handle),
|
||||
__ref: key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle for an asynchronous task created by [`spawn`](lb_tasklib::spawn).
|
||||
#[cdef]
|
||||
pub struct lb_task {
|
||||
#[opaque]
|
||||
handle: JoinHandle<()>,
|
||||
handle: Option<JoinHandle<()>>,
|
||||
__ref: c_int,
|
||||
}
|
||||
|
||||
#[metatype]
|
||||
impl lb_task {}
|
||||
impl lb_task {
|
||||
pub async extern "Lua" fn r#await(&self) -> many {
|
||||
self.__await();
|
||||
let ret = __registry[self.__ref];
|
||||
__tunpack(ret, 1, ret.n)
|
||||
}
|
||||
|
||||
async extern "Lua-C" fn __await(&mut self) {
|
||||
if let Some(handle) = self.handle.take() {
|
||||
handle
|
||||
.await
|
||||
.unwrap_or_else(|err| panic!("task handler panicked: {err}"));
|
||||
}
|
||||
}
|
||||
|
||||
#[gc]
|
||||
extern "Lua" fn gc(&self) {
|
||||
__unref(self.__ref);
|
||||
}
|
||||
}
|
||||
|
||||
17
crates/lb/tests/fs.lua
Normal file
17
crates/lb/tests/fs.lua
Normal file
@@ -0,0 +1,17 @@
|
||||
local ok, fs = pcall(require, "lb:fs")
|
||||
if not ok then return end
|
||||
|
||||
describe("temp files", function()
|
||||
test("temp_dir cleans itself", function()
|
||||
local path
|
||||
do
|
||||
local dir = fs:temp_dir()
|
||||
path = dir:path()
|
||||
assert(path and path ~= "")
|
||||
fs:write(path .. "/test.txt", "test file")
|
||||
assert(fs:read(path .. "/test.txt") == "test file")
|
||||
end
|
||||
collectgarbage()
|
||||
assert(not pcall(fs.read, fs, path .. "/test.txt"))
|
||||
end)
|
||||
end)
|
||||
17
crates/lb/tests/net.lua
Normal file
17
crates/lb/tests/net.lua
Normal file
@@ -0,0 +1,17 @@
|
||||
local ok, net = pcall(require, "lb:net")
|
||||
if not ok then return end
|
||||
|
||||
describe("tcp", function()
|
||||
describe("socket", function()
|
||||
test("bind", function()
|
||||
local socket = net:bind_tcp("127.0.0.1")
|
||||
|
||||
-- binds to the correct port
|
||||
assert(tostring(socket:local_addr():ip()) == "127.0.0.1")
|
||||
assert(socket:local_addr():port() ~= 0)
|
||||
|
||||
-- should not be able to rebind socket
|
||||
assert(not pcall(socket.bind, socket, net:socketaddr("127.0.0.1")))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
@@ -1,32 +0,0 @@
|
||||
use lb::runtime;
|
||||
use luaify::luaify;
|
||||
use luajit::{Chunk, LoadMode};
|
||||
use tokio::test;
|
||||
|
||||
async fn run_lua(s: &'static str) {
|
||||
let rt = runtime::Builder::new().build().unwrap();
|
||||
let task = rt.spawn(async move |state| {
|
||||
println!("executing test chunk: {s}");
|
||||
|
||||
state
|
||||
.load(Chunk::new(s).mode(LoadMode::TEXT))
|
||||
.unwrap_or_else(|err| panic!("{err}"));
|
||||
|
||||
state
|
||||
.call_async(0, 0)
|
||||
.await
|
||||
.unwrap_or_else(|err| panic!("{err}"));
|
||||
});
|
||||
|
||||
rt.await;
|
||||
task.await.unwrap_or_else(|err| panic!("{err}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn ipaddr() {
|
||||
run_lua(luaify!({
|
||||
let net = require("lb:net");
|
||||
print(net.ipaddr("127.0.0.1"));
|
||||
}))
|
||||
.await
|
||||
}
|
||||
103
crates/lb/tests/task.lua
Normal file
103
crates/lb/tests/task.lua
Normal file
@@ -0,0 +1,103 @@
|
||||
local ok, task = pcall(require, "lb:task")
|
||||
if not ok then return end
|
||||
|
||||
describe("spawn", function()
|
||||
test("callback receives args", function()
|
||||
spawn(function(...)
|
||||
assert(select("#", ...) == 0)
|
||||
end):await()
|
||||
|
||||
spawn(function(...)
|
||||
assert(select("#", ...) == 1)
|
||||
assert((...) == nil)
|
||||
end, nil):await()
|
||||
|
||||
spawn(function(...)
|
||||
assert(select("#", ...) == 4)
|
||||
local args = table.pack(...)
|
||||
assert(args[1] == 1 and args[2] == 2 and args[3] == nil and args[4] == 3)
|
||||
end, 1, 2, nil, 3):await()
|
||||
end)
|
||||
|
||||
test("await returns callback results", function()
|
||||
local res = table.pack(spawn(function()
|
||||
-- no returns
|
||||
end):await())
|
||||
assert(res.n == 0)
|
||||
|
||||
local res = table.pack(spawn(function()
|
||||
return nil
|
||||
end):await())
|
||||
assert(res.n == 1 and res[1] == nil)
|
||||
|
||||
local res = table.pack(spawn(function()
|
||||
return 1, 2, nil, 3
|
||||
end):await())
|
||||
assert(res.n == 4 and res[1] == 1 and res[2] == 2 and res[3] == nil and res[4] == 3)
|
||||
end)
|
||||
|
||||
test("callback args and results", function()
|
||||
local res = table.pack(spawn(function(...)
|
||||
assert(select("#", ...) == 5)
|
||||
local args = table.pack(...)
|
||||
assert(args[1] == 5 and args[2] == 4 and args[3] == nil and args[4] == 3, args[5] == nil)
|
||||
return 1, 3, nil
|
||||
end, 5, 4, nil, 3, nil):await())
|
||||
assert(res.n == 3 and res[1] == 1 and res[2] == 3 and res[3] == nil)
|
||||
end)
|
||||
|
||||
test("order is consistent", function()
|
||||
-- all tasks spawned in one batch should be resumed in the spawn order
|
||||
local tasks, nums = {}, {}
|
||||
for i = 1, 10 do
|
||||
table.insert(
|
||||
tasks,
|
||||
spawn(function()
|
||||
table.insert(nums, i)
|
||||
end)
|
||||
)
|
||||
end
|
||||
for i = 10, 1, -1 do
|
||||
tasks[i]:await()
|
||||
end
|
||||
assert(#nums == 10)
|
||||
for i = 1, 10 do
|
||||
assert(nums[i] == i)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("sleep", function()
|
||||
test("sleep is asynchronous", function()
|
||||
local value
|
||||
spawn(function()
|
||||
value = "value"
|
||||
end)
|
||||
assert(value == nil)
|
||||
task:sleep(100) -- implicit await: if it's synchronous, value wouldn't change
|
||||
assert(value == "value")
|
||||
end)
|
||||
end)
|
||||
|
||||
describe("task", function()
|
||||
test("properly unrefs arg and ret table", function()
|
||||
local registry = debug.getregistry()
|
||||
local ref, t
|
||||
local cb = function()
|
||||
return "my ret"
|
||||
end
|
||||
do
|
||||
local task = spawn(cb, "my arg")
|
||||
ref = task.__ref
|
||||
t = registry[ref]
|
||||
assert(type(t) == "table")
|
||||
assert(t[1] == cb)
|
||||
assert(t[2] == "my arg")
|
||||
task:await()
|
||||
assert(registry[task.__ref] == t)
|
||||
assert(t[1] == "my ret")
|
||||
end
|
||||
collectgarbage()
|
||||
assert(registry[ref] ~= t) -- if task unref'ed it, it should be either nil or the next freelist number
|
||||
end)
|
||||
end)
|
||||
@@ -1,35 +0,0 @@
|
||||
use lb::runtime;
|
||||
use luaify::luaify;
|
||||
use luajit::{Chunk, LoadMode};
|
||||
use tokio::test;
|
||||
|
||||
async fn run_lua(s: &'static str) {
|
||||
let rt = runtime::Builder::new().build().unwrap();
|
||||
let task = rt.spawn(async move |state| {
|
||||
println!("executing test chunk: {s}");
|
||||
|
||||
state
|
||||
.load(Chunk::new(s).mode(LoadMode::TEXT))
|
||||
.unwrap_or_else(|err| panic!("{err}"));
|
||||
|
||||
state
|
||||
.call_async(0, 0)
|
||||
.await
|
||||
.unwrap_or_else(|err| panic!("{err}"));
|
||||
});
|
||||
|
||||
rt.await;
|
||||
task.await.unwrap_or_else(|err| panic!("{err}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn task_test() {
|
||||
run_lua(luaify!({
|
||||
let thing = spawn(|| {
|
||||
print("spawn callback!!!!!!!!!!!!!");
|
||||
});
|
||||
print("thing is", thing);
|
||||
//
|
||||
}))
|
||||
.await
|
||||
}
|
||||
@@ -1,11 +1,19 @@
|
||||
[package]
|
||||
name = "luaffi"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[features]
|
||||
option_ref_abi = []
|
||||
option_string_abi = []
|
||||
|
||||
[dependencies]
|
||||
bstr = "1.12.0"
|
||||
luaffi_impl = { version = "0.1.0", path = "../luaffi_impl" }
|
||||
luaify = { version = "0.1.0", path = "../luaify" }
|
||||
luaffi_impl = { path = "../luaffi_impl" }
|
||||
luaify = { path = "../luaify" }
|
||||
rustc-hash = "2.1.1"
|
||||
simdutf8 = "0.1.5"
|
||||
|
||||
@@ -177,6 +177,15 @@ unsafe impl<F: Future<Output: IntoFfi> + 'static> IntoFfi for lua_future<F> {
|
||||
self
|
||||
}
|
||||
|
||||
fn require_owned() -> bool {
|
||||
// future always requires full ownership of itself even if it's a "temporary", because we
|
||||
// must yield a full cdata to the runtime not a cdata containing a pointer to the future. if
|
||||
// this is set to false, postlude might receive a reference lua_future cdata instead of a
|
||||
// full lua_future cdata, and the runtime might incorrectly read the pointer value as
|
||||
// lua_future itself (it does not dereference it).
|
||||
true
|
||||
}
|
||||
|
||||
fn postlude(ret: &str) -> impl Display {
|
||||
// When returning a future from Rust to Lua, yield it immediately to the runtime which will
|
||||
// poll it to completion in the background, then take the fulfilled value once the thread
|
||||
|
||||
@@ -9,12 +9,13 @@ use std::{
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod stub_types {
|
||||
pub struct any;
|
||||
pub struct many;
|
||||
pub struct nil;
|
||||
pub struct boolean;
|
||||
pub type boolean = bool;
|
||||
pub struct lightuserdata;
|
||||
pub struct number;
|
||||
pub struct integer;
|
||||
pub struct string;
|
||||
pub type string = String;
|
||||
pub struct table;
|
||||
pub struct function;
|
||||
pub struct userdata;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
---@diagnostic disable
|
||||
local LUA_REFNIL = -1 -- lib_aux.c
|
||||
local FREELIST_REF = 0
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@ use std::{
|
||||
mem,
|
||||
};
|
||||
|
||||
pub mod future;
|
||||
pub mod string;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[path = "./internal.rs"]
|
||||
pub mod __internal;
|
||||
pub mod future;
|
||||
pub mod option;
|
||||
pub mod result;
|
||||
pub mod string;
|
||||
|
||||
// Dummy function to ensure that strings passed to Rust via wrapper objects will not be
|
||||
// garbage-collected until the end of the function (used in string.rs when string marshalling is
|
||||
@@ -38,7 +38,7 @@ const CACHE_LIBS: &[(&str, &str)] = &[
|
||||
("package", "package"),
|
||||
("debug", "debug"),
|
||||
("jit", "jit"),
|
||||
// require
|
||||
// requires
|
||||
("bit", r#"require("bit")"#),
|
||||
("ffi", r#"require("ffi")"#),
|
||||
("__tnew", r#"require("table.new")"#),
|
||||
@@ -47,7 +47,7 @@ const CACHE_LIBS: &[(&str, &str)] = &[
|
||||
|
||||
// https://www.lua.org/manual/5.1/manual.html#5.1
|
||||
const CACHE_LOCALS: &[(&str, &str)] = &[
|
||||
// base
|
||||
// baselib
|
||||
("assert", "assert"),
|
||||
("error", "error"),
|
||||
("type", "type"),
|
||||
@@ -68,7 +68,8 @@ const CACHE_LOCALS: &[(&str, &str)] = &[
|
||||
("tonumber", "tonumber"),
|
||||
("tostring", "tostring"),
|
||||
("require", "require"),
|
||||
// table
|
||||
("__yield", "coroutine.yield"), // (used in future.rs)
|
||||
// tablib
|
||||
("__tconcat", "table.concat"),
|
||||
("__tinsert", "table.insert"),
|
||||
("__tmaxn", "table.maxn"),
|
||||
@@ -76,23 +77,21 @@ const CACHE_LOCALS: &[(&str, &str)] = &[
|
||||
("__tsort", "table.sort"),
|
||||
("__tpack", "table.pack"),
|
||||
("__tunpack", "table.unpack"),
|
||||
// string
|
||||
// strlib
|
||||
("__slen", "string.len"),
|
||||
("__sprintf", "string.format"),
|
||||
("__ssub", "string.sub"),
|
||||
("__sgsub", "string.gsub"),
|
||||
("__sgmatch", "string.gmatch"),
|
||||
("__sdump", "string.dump"),
|
||||
// math (used in luaify! macro)
|
||||
// mathlib (used in luaify! macro)
|
||||
("__fmod", "math.fmod"),
|
||||
// coroutine (used in future.rs)
|
||||
("__yield", "coroutine.yield"),
|
||||
// package
|
||||
// loadlib
|
||||
("__preload", "package.preload"),
|
||||
// debug
|
||||
// dblib
|
||||
("__traceback", "debug.traceback"),
|
||||
("__registry", "debug.getregistry()"), // (used in lib.lua)
|
||||
// ffi
|
||||
// ffilib
|
||||
("__C", "ffi.C"),
|
||||
("__ct", "{}"),
|
||||
("__cdef", "ffi.cdef"),
|
||||
@@ -105,7 +104,7 @@ const CACHE_LOCALS: &[(&str, &str)] = &[
|
||||
("__sizeof", "ffi.sizeof"),
|
||||
("__alignof", "ffi.alignof"),
|
||||
("__intern", "ffi.string"), // (used in string.rs)
|
||||
// bit (used in luaify! macro)
|
||||
// bitlib (used in luaify! macro)
|
||||
("__bnot", "bit.bnot"),
|
||||
("__band", "bit.band"),
|
||||
("__bor", "bit.bor"),
|
||||
@@ -159,19 +158,19 @@ impl Registry {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn preload<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
||||
pub fn preload<T: Module>(&mut self) -> &mut Self {
|
||||
assert!(T::ty() != TypeType::Void, "cannot declare void type");
|
||||
self.include::<T>();
|
||||
let ct = T::name();
|
||||
let name = <T as Module>::name();
|
||||
let ct = <T as Type>::name();
|
||||
writeln!(
|
||||
self.lua,
|
||||
r#"__preload["{name}"] = function(...) return __ct.{ct}(...); end;"#,
|
||||
)
|
||||
.unwrap();
|
||||
self
|
||||
self.include::<T>()
|
||||
}
|
||||
|
||||
pub fn done(&self) -> String {
|
||||
pub fn build(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
@@ -241,6 +240,10 @@ impl<'r> TypeBuilder<'r> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Module: Type {
|
||||
fn name() -> impl Display;
|
||||
}
|
||||
|
||||
pub unsafe trait Cdef: Type {
|
||||
fn build(b: &mut CdefBuilder);
|
||||
}
|
||||
@@ -321,6 +324,7 @@ pub struct MetatypeBuilder<'r> {
|
||||
ct: String,
|
||||
cdef: String,
|
||||
lua: String,
|
||||
lua_includes: Vec<&'static str>,
|
||||
}
|
||||
|
||||
impl<'r> MetatypeBuilder<'r> {
|
||||
@@ -330,6 +334,7 @@ impl<'r> MetatypeBuilder<'r> {
|
||||
ct: T::Target::name().to_string(),
|
||||
cdef: String::new(),
|
||||
lua: r#"do local __mt, __idx = {}, {}; __mt.__index = __idx; "#.into(),
|
||||
lua_includes: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,35 +343,40 @@ impl<'r> MetatypeBuilder<'r> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn include_lua(&mut self, lua: &'static str) -> &mut Self {
|
||||
self.lua_includes.push(lua);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn index(
|
||||
&mut self,
|
||||
name: impl Display,
|
||||
f: impl FnOnce(&mut MetatypeMethodBuilder),
|
||||
f: impl FnOnce(&mut MetatypeFunctionBuilder),
|
||||
) -> &mut Self {
|
||||
write!(self.lua, "__idx.{name} = ").unwrap();
|
||||
f(&mut MetatypeMethodBuilder::new(self));
|
||||
write!(self.lua, "; ").unwrap();
|
||||
f(&mut MetatypeFunctionBuilder::new(self));
|
||||
writeln!(self.lua, ";").unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn index_raw(&mut self, name: impl Display, value: impl Display) -> &mut Self {
|
||||
write!(self.lua, "__idx.{name} = {value}; ").unwrap();
|
||||
writeln!(self.lua, "__idx.{name} = {value};").unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn metatable(
|
||||
&mut self,
|
||||
name: impl Display,
|
||||
f: impl FnOnce(&mut MetatypeMethodBuilder),
|
||||
f: impl FnOnce(&mut MetatypeFunctionBuilder),
|
||||
) -> &mut Self {
|
||||
write!(self.lua, "__mt.__{name} = ").unwrap();
|
||||
f(&mut MetatypeMethodBuilder::new(self));
|
||||
write!(self.lua, "; ").unwrap();
|
||||
f(&mut MetatypeFunctionBuilder::new(self));
|
||||
writeln!(self.lua, ";").unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn metatable_raw(&mut self, name: impl Display, value: impl Display) -> &mut Self {
|
||||
write!(self.lua, "__mt.__{name} = {value}; ").unwrap();
|
||||
writeln!(self.lua, "__mt.__{name} = {value};").unwrap();
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -378,12 +388,15 @@ impl<'r> Drop for MetatypeBuilder<'r> {
|
||||
ct,
|
||||
cdef,
|
||||
lua,
|
||||
..
|
||||
lua_includes: lua_postlude,
|
||||
} = self;
|
||||
|
||||
registry.cdef.push_str(cdef);
|
||||
registry.lua.push_str(lua);
|
||||
writeln!(registry.lua, r#"__metatype(__ct.{ct}, __mt); end;"#).unwrap();
|
||||
for lua in lua_postlude {
|
||||
writeln!(registry.lua, r#"do {lua} end;"#).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +430,10 @@ pub unsafe trait IntoFfi: Sized {
|
||||
}
|
||||
}
|
||||
|
||||
fn require_owned() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn postlude(_ret: &str) -> impl Display {
|
||||
""
|
||||
}
|
||||
@@ -425,16 +442,16 @@ pub unsafe trait IntoFfi: Sized {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MetatypeMethodBuilder<'r, 'm> {
|
||||
pub struct MetatypeFunctionBuilder<'r, 'm> {
|
||||
metatype: &'m mut MetatypeBuilder<'r>,
|
||||
lparams: String, // parameters to the lua function
|
||||
cparams: String, // parameters to the lua function
|
||||
cargs: String, // arguments to the C call
|
||||
prelude: String, // function body prelude
|
||||
postlude: String, // function body postlude
|
||||
lparams: String, // lua function parameters
|
||||
cparams: String, // C function parameters
|
||||
cargs: String, // C call arguments
|
||||
prelude: String, // lua function body prelude
|
||||
postlude: String, // lua function body postlude
|
||||
}
|
||||
|
||||
impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
||||
impl<'r, 'm> MetatypeFunctionBuilder<'r, 'm> {
|
||||
pub fn new(metatype: &'m mut MetatypeBuilder<'r>) -> Self {
|
||||
Self {
|
||||
metatype,
|
||||
@@ -481,12 +498,17 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn param_str(&mut self, name: impl Display) -> &mut Self {
|
||||
// fast-path for &str and &[u8]-like parameters
|
||||
pub fn param_str(
|
||||
&mut self,
|
||||
name: impl Display,
|
||||
allow_nil: bool,
|
||||
check_utf8: bool,
|
||||
) -> &mut Self {
|
||||
// fast-path for &[u8] and &str-like parameters
|
||||
//
|
||||
// this passes one lua `string` argument as two C `const uint8_t *ptr` and `uintptr_t len`
|
||||
// arguments, bypassing the slower generic `&[u8]: FromFfi` path which constructs a
|
||||
// temporary cdata to pass the string and its length in one argument
|
||||
// temporary cdata to pass the string and its length in one argument.
|
||||
let Self {
|
||||
lparams,
|
||||
cparams,
|
||||
@@ -495,22 +517,29 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
||||
..
|
||||
} = self;
|
||||
|
||||
let param_ptr = <*const u8>::cdecl("ptr");
|
||||
let param_len = usize::cdecl("len");
|
||||
let param_ptr = <*const u8>::cdecl(&name);
|
||||
let param_len = usize::cdecl(format!("{name}_len"));
|
||||
|
||||
(!lparams.is_empty()).then(|| lparams.push_str(", "));
|
||||
(!cparams.is_empty()).then(|| cparams.push_str(", "));
|
||||
(!cargs.is_empty()).then(|| cargs.push_str(", "));
|
||||
|
||||
write!(lparams, "{name}").unwrap();
|
||||
write!(cparams, "{param_ptr}, {param_len}",).unwrap();
|
||||
write!(cparams, "{param_ptr}, {param_len}").unwrap();
|
||||
write!(cargs, "{name}, __{name}_len").unwrap();
|
||||
write!(prelude, "local __{name}_len = 0; ").unwrap();
|
||||
write!(
|
||||
prelude,
|
||||
r#"if {name} ~= nil then assert(type({name}) == "string", "string expected in argument '{name}', got " .. type({name})); __{name}_len = #{name}; end; "#
|
||||
)
|
||||
.unwrap();
|
||||
write!(prelude, "local __{name}_len = 0; if {name} ~= nil then ").unwrap();
|
||||
write!(prelude, r#"assert(type({name}) == "string", "string expected in argument '{name}', got " .. type({name})); "#).unwrap();
|
||||
write!(prelude, r#"__{name}_len = #{name}; "#).unwrap();
|
||||
|
||||
if check_utf8 {
|
||||
write!(prelude, r#"assert(__C.{IS_UTF8_FN}({name}, __{name}_len), "argument '{name}' must be a valid utf-8 string"); "#).unwrap();
|
||||
}
|
||||
|
||||
if !allow_nil {
|
||||
write!(prelude, r#"else return error("string expected in argument '{name}', got " .. type({name})); "#).unwrap();
|
||||
}
|
||||
|
||||
write!(prelude, r#"end; "#).unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
@@ -545,21 +574,21 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
||||
if T::Into::ty() == TypeType::Void {
|
||||
write!(lua, "__C.{func}({cargs}); {postlude}end").unwrap();
|
||||
} else {
|
||||
let check = T::postlude("__res");
|
||||
write!(lua, "local __res = __C.{func}({cargs}); ").unwrap();
|
||||
write!(lua, "{check}{postlude}return __res; end").unwrap();
|
||||
let check = T::postlude("__ret");
|
||||
write!(lua, "local __ret = __C.{func}({cargs}); ").unwrap();
|
||||
write!(lua, "{check}{postlude}return __ret; end").unwrap();
|
||||
}
|
||||
|
||||
writeln!(cdef, "{};", T::Into::cdecl(display!("{func}({cparams})"))).unwrap();
|
||||
}
|
||||
FfiReturnConvention::ByOutParam => {
|
||||
let ct = T::Into::name();
|
||||
let check = T::postlude("__res");
|
||||
write!(lua, "local __res = __new(__ct.{ct}); __C.{func}(__res").unwrap();
|
||||
let check = T::postlude("__out");
|
||||
write!(lua, "local __out = __new(__ct.{ct}); __C.{func}(__out").unwrap();
|
||||
if !cargs.is_empty() {
|
||||
write!(lua, ", {cargs}").unwrap();
|
||||
}
|
||||
write!(lua, "); {check}{postlude}return __res; end").unwrap();
|
||||
write!(lua, "); {check}{postlude}return __out; end").unwrap();
|
||||
write!(cdef, "void {func}({}", <*mut T::Into>::cdecl("out")).unwrap();
|
||||
if !cparams.is_empty() {
|
||||
write!(cdef, ", {cparams}").unwrap();
|
||||
@@ -844,7 +873,9 @@ macro_rules! impl_ptr_intoabi {
|
||||
|
||||
impl_ptr_intoabi!(*const T);
|
||||
impl_ptr_intoabi!(*mut T);
|
||||
#[cfg(feature = "option_ref_abi")] // disabled because it conflicts with the generic Option<T> impl
|
||||
impl_ptr_intoabi!(Option<&'static T>);
|
||||
#[cfg(feature = "option_ref_abi")]
|
||||
impl_ptr_intoabi!(Option<&'static mut T>);
|
||||
|
||||
//
|
||||
|
||||
84
crates/luaffi/src/option.rs
Normal file
84
crates/luaffi/src/option.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use crate::{
|
||||
__internal::{disp, display},
|
||||
Cdef, CdefBuilder, IntoFfi, KEEP_FN, Type, TypeBuilder, TypeType,
|
||||
};
|
||||
use std::{ffi::c_int, fmt::Display};
|
||||
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum lua_option<T> {
|
||||
None, // __tag = 0
|
||||
Some(T), // __tag = 1
|
||||
}
|
||||
|
||||
unsafe impl<T: Type> Type for lua_option<T> {
|
||||
fn name() -> impl Display {
|
||||
display!("option__{}", T::name())
|
||||
}
|
||||
|
||||
fn ty() -> TypeType {
|
||||
TypeType::Aggregate
|
||||
}
|
||||
|
||||
fn cdecl(name: impl Display) -> impl Display {
|
||||
display!("struct {} {name}", Self::name())
|
||||
}
|
||||
|
||||
fn build(b: &mut TypeBuilder) {
|
||||
b.cdef::<Self>();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Type> Cdef for lua_option<T> {
|
||||
fn build(b: &mut CdefBuilder) {
|
||||
b.field::<c_int>("__tag");
|
||||
(T::ty() != TypeType::Void).then(|| b.field::<T>("__value"));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: IntoFfi> IntoFfi for Option<T> {
|
||||
type Into = lua_option<T::Into>;
|
||||
|
||||
fn convert(self) -> Self::Into {
|
||||
match self {
|
||||
Some(value) => lua_option::Some(T::convert(value)),
|
||||
None => lua_option::None,
|
||||
}
|
||||
}
|
||||
|
||||
fn require_owned() -> bool {
|
||||
// lua_option is only used to transmit information about whether we have a value or not and
|
||||
// is forgotten immediately after use, so there is no need for an owned option
|
||||
false
|
||||
}
|
||||
|
||||
fn postlude(ret: &str) -> impl Display {
|
||||
disp(move |f| {
|
||||
write!(f, "if {ret}.__tag ~= 0 then ")?;
|
||||
match T::Into::ty() {
|
||||
TypeType::Void => write!(f, "{ret} = nil; ")?, // for void options, we don't have a __value
|
||||
TypeType::Primitive => {
|
||||
// can always copy primitives to stack
|
||||
write!(f, "{ret} = {ret}.__value; {}", T::postlude(ret))?;
|
||||
}
|
||||
TypeType::Aggregate => {
|
||||
let ct = T::Into::name();
|
||||
if T::require_owned() {
|
||||
// inner value requires ownership; copy it into its own cdata and forget
|
||||
// option.
|
||||
write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); ")?;
|
||||
write!(f, "{}", T::postlude(ret))?;
|
||||
} else {
|
||||
// inner value is a "temporary" like an option itself and doesn't require
|
||||
// full ownership of itself. we just need to keep the option object alive
|
||||
// until its postlude completes.
|
||||
write!(f, "local {ret}_keep = {ret}; {ret} = {ret}.__value; ")?;
|
||||
write!(f, "do {}end; ", T::postlude(ret))?;
|
||||
write!(f, "__C.{KEEP_FN}({ret}_keep); ")?; // keep original option alive
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(f, "else {ret} = nil; end; ")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
__internal::{disp, display},
|
||||
Cdef, CdefBuilder, IntoFfi, Type, TypeBuilder, TypeType,
|
||||
Cdef, CdefBuilder, IntoFfi, KEEP_FN, Type, TypeBuilder, TypeType,
|
||||
string::{DROP_BUFFER_FN, lua_buffer},
|
||||
};
|
||||
use std::{ffi::c_int, fmt::Display};
|
||||
@@ -49,22 +49,44 @@ unsafe impl<T: IntoFfi, E: Display> IntoFfi for Result<T, E> {
|
||||
}
|
||||
}
|
||||
|
||||
fn require_owned() -> bool {
|
||||
// lua_result is only used to transmit information about whether an operation succeeded or
|
||||
// not and is forgotten immediately after use, so there is no need for an owned result
|
||||
false
|
||||
}
|
||||
|
||||
fn postlude(ret: &str) -> impl Display {
|
||||
disp(move |f| {
|
||||
let ct = T::Into::name();
|
||||
write!(f, "if {ret}.__tag ~= 0 then ")?;
|
||||
match T::Into::ty() {
|
||||
TypeType::Void => write!(f, "{ret} = nil; "), // for void results, we don't have a __value
|
||||
TypeType::Primitive => write!(f, "{ret} = {ret}.__value; "),
|
||||
TypeType::Aggregate => write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); "),
|
||||
}?;
|
||||
TypeType::Void => write!(f, "{ret} = nil; ")?, // for void results, we don't have a __value
|
||||
TypeType::Primitive => {
|
||||
// can always copy primitives to stack
|
||||
write!(f, "{ret} = {ret}.__value; {}", T::postlude(ret))?;
|
||||
}
|
||||
TypeType::Aggregate => {
|
||||
let ct = T::Into::name();
|
||||
if T::require_owned() {
|
||||
// inner value requires ownership; copy it into its own cdata and forget
|
||||
// result.
|
||||
write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); ")?;
|
||||
write!(f, "{}", T::postlude(ret))?;
|
||||
} else {
|
||||
// inner value is a "temporary" like a result itself and doesn't require
|
||||
// full ownership of itself. we just need to keep the result object alive
|
||||
// until its postlude completes.
|
||||
write!(f, "local {ret}_keep = {ret}; {ret} = {ret}.__value; ")?;
|
||||
write!(f, "do {}end; ", T::postlude(ret))?;
|
||||
write!(f, "__C.{KEEP_FN}({ret}_keep); ")?; // keep original result alive
|
||||
}
|
||||
}
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"else \
|
||||
local __{ret}_msg = __intern({ret}.__err.__ptr, {ret}.__err.__len); \
|
||||
local {ret}_err = __intern({ret}.__err.__ptr, {ret}.__err.__len); \
|
||||
__C.{DROP_BUFFER_FN}({ret}.__err); \
|
||||
return error(__{ret}_msg); \
|
||||
return error({ret}_err); \
|
||||
end; "
|
||||
)
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
};
|
||||
use bstr::{BStr, BString};
|
||||
use luaffi_impl::{cdef, metatype};
|
||||
use std::{fmt::Display, mem::ManuallyDrop, ptr, slice};
|
||||
use std::{fmt::Display, mem::ManuallyDrop, slice};
|
||||
|
||||
pub(crate) const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
||||
pub(crate) const DROP_BUFFER_FN: &str = "luaffi_drop_buffer";
|
||||
@@ -42,6 +42,7 @@ impl lua_buf {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "option_string_abi")]
|
||||
pub(crate) fn null() -> Self {
|
||||
Self {
|
||||
__ptr: ptr::null(),
|
||||
@@ -71,6 +72,7 @@ impl lua_buffer {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "option_string_abi")]
|
||||
pub(crate) fn null() -> Self {
|
||||
Self {
|
||||
__ptr: ptr::null_mut(),
|
||||
@@ -154,8 +156,14 @@ unsafe impl IntoFfi for &'static [u8] {
|
||||
lua_buf::new(self)
|
||||
}
|
||||
|
||||
fn require_owned() -> bool {
|
||||
// lua_buf is only used to have its contents interned then forgotten immediately; no need
|
||||
// for ownership of it
|
||||
false
|
||||
}
|
||||
|
||||
fn postlude(ret: &str) -> impl Display {
|
||||
display!("{ret} = __intern({ret}.__ptr, {ret}.__len)")
|
||||
display!("{ret} = __intern({ret}.__ptr, {ret}.__len); ")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,9 +174,15 @@ unsafe impl IntoFfi for Vec<u8> {
|
||||
lua_buffer::new(self)
|
||||
}
|
||||
|
||||
fn require_owned() -> bool {
|
||||
// lua_buffer is only used to have its contents interned then forgotten immediately; no need
|
||||
// for ownership of it
|
||||
false
|
||||
}
|
||||
|
||||
fn postlude(ret: &str) -> impl Display {
|
||||
display!(
|
||||
"do local __{ret} = {ret}; {ret} = __intern({ret}.__ptr, {ret}.__len); __C.{DROP_BUFFER_FN}(__{ret}); end; "
|
||||
"local {ret}_buf = {ret}; {ret} = __intern({ret}.__ptr, {ret}.__len); __C.{DROP_BUFFER_FN}({ret}_buf); "
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -216,6 +230,13 @@ impl_into_via!(&'static str, &'static BStr);
|
||||
impl_into_via!(BString, Vec<u8>);
|
||||
impl_into_via!(String, BString);
|
||||
|
||||
// `Option<String>: From/IntoFfi` isn't implemented because it conflicts with the generic
|
||||
// `Option<T>: From/IntoFfi` impl and rust doesn't have specialisation yet (and probably not anytime
|
||||
// soon). this is fine for now because we have specialisation for string-like parameters implemented
|
||||
// in the #[metatype] macro already, and string returns wrapped in `Option<T>` isn't much additional
|
||||
// overhead.
|
||||
#[cfg(feature = "option_string_abi")]
|
||||
mod impl_option_string {
|
||||
macro_rules! impl_optional_from {
|
||||
($ty:ty) => {
|
||||
unsafe impl<'s> FromFfi for Option<$ty> {
|
||||
@@ -269,3 +290,4 @@ impl_optional_into!(&'static str, lua_buf::null());
|
||||
impl_optional_into!(Vec<u8>, lua_buffer::null());
|
||||
impl_optional_into!(BString, lua_buffer::null());
|
||||
impl_optional_into!(String, lua_buffer::null());
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
[package]
|
||||
name = "luaffi_impl"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
@@ -5,18 +5,26 @@ use quote::{format_ident, quote, quote_spanned};
|
||||
use syn::{ext::IdentExt, spanned::Spanned, *};
|
||||
|
||||
#[derive(Debug, FromMeta)]
|
||||
pub struct Args {}
|
||||
pub struct Args {
|
||||
module: Option<String>,
|
||||
}
|
||||
|
||||
pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
||||
let (name, impl_type, impl_cdef) = match item {
|
||||
pub fn transform(args: Args, mut item: Item) -> Result<TokenStream> {
|
||||
let (name, impl_type, impl_module, impl_cdef) = match item {
|
||||
Item::Struct(ref mut str) => (
|
||||
str.ident.clone(),
|
||||
generate_type(&str.ident)?,
|
||||
args.module
|
||||
.map(|name| generate_module(&name, &str.ident))
|
||||
.transpose()?,
|
||||
generate_cdef_structure(str)?,
|
||||
),
|
||||
Item::Enum(ref mut enu) => (
|
||||
enu.ident.clone(),
|
||||
generate_type(&enu.ident)?,
|
||||
args.module
|
||||
.map(|name| generate_module(&name, &enu.ident))
|
||||
.transpose()?,
|
||||
generate_cdef_enum(enu)?,
|
||||
),
|
||||
_ => syn_error!(item, "expected struct or enum"),
|
||||
@@ -24,7 +32,7 @@ pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
||||
|
||||
let mod_name = format_ident!("__{name}_cdef");
|
||||
|
||||
Ok(quote!(
|
||||
Ok(quote_spanned!(name.span() =>
|
||||
#[repr(C)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#item
|
||||
@@ -35,6 +43,7 @@ pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
||||
mod #mod_name {
|
||||
use super::*;
|
||||
#impl_type
|
||||
#impl_module
|
||||
#impl_cdef
|
||||
}
|
||||
))
|
||||
@@ -47,9 +56,7 @@ fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
||||
|
||||
Ok(quote_spanned!(span =>
|
||||
unsafe impl #ffi::Type for #ty {
|
||||
fn name() -> impl ::std::fmt::Display {
|
||||
#name
|
||||
}
|
||||
fn name() -> impl ::std::fmt::Display { #name }
|
||||
|
||||
fn ty() -> #ffi::TypeType {
|
||||
#ffi::TypeType::Aggregate
|
||||
@@ -72,6 +79,16 @@ fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
||||
))
|
||||
}
|
||||
|
||||
fn generate_module(name: &str, ty: &Ident) -> Result<TokenStream> {
|
||||
let ffi = ffi_crate();
|
||||
|
||||
Ok(quote_spanned!(ty.span() =>
|
||||
impl #ffi::Module for #ty {
|
||||
fn name() -> impl ::std::fmt::Display { #name }
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
|
||||
syn_assert!(
|
||||
str.generics.params.is_empty(),
|
||||
@@ -155,13 +172,12 @@ fn parse_cfield_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
|
||||
let mut parsed = CFieldAttrs::default();
|
||||
let mut i = 0;
|
||||
while let Some(attr) = attrs.get(i) {
|
||||
if let Some(name) = attr.path().get_ident() {
|
||||
if name == "opaque" {
|
||||
let path = attr.path();
|
||||
if path.is_ident("opaque") {
|
||||
parsed.opaque = true;
|
||||
attrs.remove(i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use darling::{FromMeta, ast::NestedMeta};
|
||||
use proc_macro::TokenStream as TokenStream1;
|
||||
use quote::ToTokens;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
mod cdef;
|
||||
mod metatype;
|
||||
@@ -17,8 +16,10 @@ pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn metatype(_args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
||||
metatype::transform(parse_macro_input!(input))
|
||||
pub fn metatype(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
||||
NestedMeta::parse_meta_list(args.into())
|
||||
.and_then(|meta| metatype::Args::from_list(&meta).map_err(Into::into))
|
||||
.and_then(|args| metatype::transform(args, syn::parse(input)?))
|
||||
.unwrap_or_else(|err| err.into_compile_error().into_token_stream())
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
use crate::utils::{
|
||||
ffi_crate, is_primitivelike, is_unit, pat_ident, syn_assert, syn_error, ty_name,
|
||||
StringLike, ffi_crate, is_optionlike, is_primitivelike, is_stringlike, is_unit, pat_ident,
|
||||
syn_assert, syn_error, ty_name,
|
||||
};
|
||||
use darling::FromMeta;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{ToTokens, format_ident, quote, quote_spanned};
|
||||
use std::{collections::HashSet, fmt, iter};
|
||||
use syn::{ext::IdentExt, spanned::Spanned, *};
|
||||
|
||||
pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
|
||||
#[derive(Debug, FromMeta)]
|
||||
pub struct Args {}
|
||||
|
||||
pub fn transform(args: Args, mut imp: ItemImpl) -> Result<TokenStream> {
|
||||
syn_assert!(
|
||||
imp.generics.params.is_empty(),
|
||||
imp.generics,
|
||||
"cannot be generic (not yet implemented)"
|
||||
);
|
||||
|
||||
let impls = generate_impls(&mut imp)?;
|
||||
let impls = generate_impls(&args, &mut imp)?;
|
||||
let mod_name = format_ident!("__{}_metatype", ty_name(&imp.self_ty)?);
|
||||
|
||||
Ok(quote!(
|
||||
Ok(quote_spanned!(imp.self_ty.span() =>
|
||||
#imp
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -29,15 +34,50 @@ pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
|
||||
))
|
||||
}
|
||||
|
||||
fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
struct Registry {
|
||||
ty: Ident,
|
||||
shims: Vec<ImplItemFn>,
|
||||
build: Vec<TokenStream>,
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
fn new(ty: Ident) -> Self {
|
||||
Self {
|
||||
ty,
|
||||
shims: vec![],
|
||||
build: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_impls(_args: &Args, imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
let ffi = ffi_crate();
|
||||
let ty = imp.self_ty.clone();
|
||||
let ty_name = ty_name(&ty)?;
|
||||
let mut ffi_funcs = FfiRegistry::new(ty_name.clone());
|
||||
let mut lua_funcs = LuaRegistry::new(ty_name.clone());
|
||||
let mut registry = Registry::new(ty_name.clone());
|
||||
let mut mms = HashSet::new();
|
||||
let mut lua_drop = None;
|
||||
|
||||
// process lua includes
|
||||
imp.attrs.retain(|attr| {
|
||||
if attr.path().is_ident("include") {
|
||||
if let Ok(path) = attr.parse_args::<LitStr>() {
|
||||
registry.build.push(quote_spanned!(path.span() =>
|
||||
b.include_lua(::std::include_str!(#path));
|
||||
));
|
||||
return false;
|
||||
} else if let Ok(chunk) = attr.parse_args::<Block>() {
|
||||
registry.build.push(quote_spanned!(chunk.span() =>
|
||||
b.include_lua(#ffi::__internal::luaify_chunk!(#chunk));
|
||||
));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
// process extern "Lua-C" ffi functions
|
||||
for func in get_ffi_functions(imp)? {
|
||||
if let Some(mm) = func.attrs.metamethod {
|
||||
syn_assert!(
|
||||
@@ -47,9 +87,10 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
);
|
||||
}
|
||||
|
||||
add_ffi_function(&mut ffi_funcs, &func)?;
|
||||
add_ffi_function(&mut registry, &func)?;
|
||||
}
|
||||
|
||||
// process extern "Lua" lua functions
|
||||
for func in get_lua_functions(imp)? {
|
||||
if let Some(mm) = func.attrs.metamethod {
|
||||
syn_assert!(
|
||||
@@ -59,38 +100,35 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
);
|
||||
}
|
||||
|
||||
if func.attrs.metamethod == Some(Metamethod::Gc) {
|
||||
if let Some(Metamethod::Gc) = func.attrs.metamethod {
|
||||
lua_drop = Some(func);
|
||||
} else {
|
||||
add_lua_function(&mut lua_funcs, &func)?;
|
||||
add_lua_function(&mut registry, &func)?;
|
||||
}
|
||||
}
|
||||
|
||||
// if no #[new] metamethod is defined, inject fallback new
|
||||
if !mms.contains(&Metamethod::New) {
|
||||
inject_fallback_new(&mut lua_funcs)?;
|
||||
inject_fallback_new(&mut registry)?;
|
||||
}
|
||||
|
||||
inject_merged_drop(&mut ffi_funcs, lua_drop.as_ref())?;
|
||||
// inject __gc/drop
|
||||
inject_merged_drop(&mut registry, lua_drop.as_ref())?;
|
||||
|
||||
let ffi_shims = &ffi_funcs.shims;
|
||||
let ffi_build = &ffi_funcs.build;
|
||||
let lua_build = &lua_funcs.build;
|
||||
let ffi_exports = generate_ffi_exports(&ffi_funcs)?;
|
||||
let shims = ®istry.shims;
|
||||
let build = ®istry.build;
|
||||
let exports = generate_ffi_exports(®istry)?;
|
||||
|
||||
Ok(quote! {
|
||||
impl #ty { #(#ffi_shims)* }
|
||||
Ok(quote_spanned!(ty.span() =>
|
||||
impl #ty { #(#shims)* }
|
||||
|
||||
unsafe impl #ffi::Metatype for #ty {
|
||||
type Target = Self;
|
||||
|
||||
fn build(b: &mut #ffi::MetatypeBuilder) {
|
||||
#(#ffi_build)*
|
||||
#(#lua_build)*
|
||||
}
|
||||
fn build(b: &mut #ffi::MetatypeBuilder) { #(#build)* }
|
||||
}
|
||||
|
||||
#ffi_exports
|
||||
})
|
||||
#exports
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@@ -201,9 +239,15 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
&& let Some(ref abi) = abi.name
|
||||
&& abi.value() == "Lua-C"
|
||||
{
|
||||
syn_assert!(
|
||||
func.sig.generics.params.len() == 0,
|
||||
func.sig.generics,
|
||||
"cannot be generic"
|
||||
);
|
||||
|
||||
func.sig.abi = None;
|
||||
|
||||
let params = func
|
||||
let params: Vec<_> = func
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
@@ -221,8 +265,28 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||
ReturnType::Type(_, ref ty) => (**ty).clone(),
|
||||
};
|
||||
|
||||
for param in params.iter() {
|
||||
// double underscores are reserved for generated glue code
|
||||
syn_assert!(
|
||||
!pat_ident(¶m.pat)?.to_string().starts_with("__"),
|
||||
param.pat,
|
||||
"parameter names should not start with `__`"
|
||||
);
|
||||
|
||||
// lifetime should be determined by the caller (lua)
|
||||
if let Type::Reference(ref ty) = *param.ty {
|
||||
syn_assert!(
|
||||
ty.lifetime.is_none(),
|
||||
ty.lifetime,
|
||||
"lifetime should be determined by the caller"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let attrs = parse_ffi_function_attrs(&mut func.attrs)?;
|
||||
attrs.metamethod.map(|mm| document_metamethod(func, mm));
|
||||
func.sig.asyncness.is_some().then(|| document_async(func));
|
||||
document_ffi_function(func);
|
||||
|
||||
funcs.push(FfiFunction {
|
||||
name: func.sig.ident.clone(),
|
||||
@@ -242,7 +306,7 @@ fn parse_ffi_function_attrs(attrs: &mut Vec<Attribute>) -> Result<FfiFunctionAtt
|
||||
let mut i = 0;
|
||||
while let Some(attr) = attrs.get(i) {
|
||||
if let Some(name) = attr.path().get_ident()
|
||||
&& let Ok(method) = Metamethod::try_from(name)
|
||||
&& let Ok(method) = Metamethod::try_from(&name.unraw())
|
||||
{
|
||||
match method {
|
||||
Metamethod::Gc => syn_error!(attr, "implement `Drop` instead"),
|
||||
@@ -259,14 +323,26 @@ fn parse_ffi_function_attrs(attrs: &mut Vec<Attribute>) -> Result<FfiFunctionAtt
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum FfiParameterType {
|
||||
Default,
|
||||
StringLike(StringLike),
|
||||
OptionStringLike(StringLike),
|
||||
}
|
||||
|
||||
fn get_ffi_param_type(_ty: &Type) -> FfiParameterType {
|
||||
fn get_ffi_param_type(ty: &Type) -> FfiParameterType {
|
||||
if let Some(str) = is_stringlike(ty) {
|
||||
FfiParameterType::StringLike(str)
|
||||
} else if let Some(arg) = is_optionlike(ty)
|
||||
&& let Some(str) = is_stringlike(arg)
|
||||
{
|
||||
FfiParameterType::OptionStringLike(str)
|
||||
} else {
|
||||
FfiParameterType::Default
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum FfiReturnType {
|
||||
Void,
|
||||
ByValue,
|
||||
@@ -296,23 +372,7 @@ fn get_ffi_ret_type(ty: &Type) -> FfiReturnType {
|
||||
}
|
||||
}
|
||||
|
||||
struct FfiRegistry {
|
||||
ty: Ident,
|
||||
shims: Vec<ImplItemFn>,
|
||||
build: Vec<TokenStream>,
|
||||
}
|
||||
|
||||
impl FfiRegistry {
|
||||
fn new(ty: Ident) -> Self {
|
||||
Self {
|
||||
ty,
|
||||
shims: vec![],
|
||||
build: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_ffi_function(registry: &mut FfiRegistry, func: &FfiFunction) -> Result<()> {
|
||||
fn add_ffi_function(registry: &mut Registry, func: &FfiFunction) -> Result<()> {
|
||||
let ffi = ffi_crate();
|
||||
let ty = ®istry.ty;
|
||||
let func_name = &func.name;
|
||||
@@ -364,6 +424,41 @@ fn add_ffi_function(registry: &mut FfiRegistry, func: &FfiFunction) -> Result<()
|
||||
b.param::<#func_param>(#name);
|
||||
));
|
||||
}
|
||||
ty @ (FfiParameterType::StringLike(str) | FfiParameterType::OptionStringLike(str)) => {
|
||||
let shim_param_len = format_ident!("arg{i}_len");
|
||||
shim_params.push(quote_spanned!(func_param.span() =>
|
||||
#shim_param: ::std::option::Option<&::std::primitive::u8>,
|
||||
#shim_param_len: ::std::primitive::usize
|
||||
));
|
||||
let allow_nil = matches!(ty, FfiParameterType::OptionStringLike(_));
|
||||
let check_utf8 = matches!(str, StringLike::Str);
|
||||
let mut func_arg = quote_spanned!(func_param.span() =>
|
||||
#shim_param.map(|s| ::std::slice::from_raw_parts(s, #shim_param_len))
|
||||
);
|
||||
func_arg = match str {
|
||||
StringLike::SliceU8 => func_arg,
|
||||
StringLike::BStr => {
|
||||
quote_spanned!(func_param.span() => #func_arg.map(::bstr::BStr::new))
|
||||
}
|
||||
StringLike::Str => {
|
||||
quote_spanned!(func_param.span() => #func_arg.map(|s| {
|
||||
::std::debug_assert!(::std::str::from_utf8(s).is_ok());
|
||||
::std::str::from_utf8_unchecked(s)
|
||||
}))
|
||||
}
|
||||
};
|
||||
if !allow_nil {
|
||||
func_arg = quote_spanned!(func_param.span() => {
|
||||
let arg = #func_arg;
|
||||
::std::debug_assert!(arg.is_some());
|
||||
arg.unwrap_unchecked()
|
||||
});
|
||||
}
|
||||
func_args.push(func_arg);
|
||||
build.push(quote_spanned!(param.pat.span() =>
|
||||
b.param_str(#name, #allow_nil, #check_utf8);
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,8 +487,12 @@ fn add_ffi_function(registry: &mut FfiRegistry, func: &FfiFunction) -> Result<()
|
||||
<#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByOutParam
|
||||
));
|
||||
|
||||
shim_params.insert(0, quote!(out: *mut #shim_ret));
|
||||
(shim_body, shim_ret) = (quote!(::std::ptr::write(out, #shim_body)), quote!(()));
|
||||
shim_params.insert(0, quote_spanned!(func_ret.span() => out: *mut #shim_ret));
|
||||
|
||||
(shim_body, shim_ret) = (
|
||||
quote_spanned!(func_ret.span() => ::std::ptr::write(out, #shim_body)),
|
||||
quote_spanned!(func_ret.span() => ()),
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -417,19 +516,23 @@ fn add_ffi_function(registry: &mut FfiRegistry, func: &FfiFunction) -> Result<()
|
||||
));
|
||||
|
||||
registry.build.push(match func.attrs.metamethod {
|
||||
Some(ref mm) => quote!(b.metatable(#mm, |b| { #(#build)* });),
|
||||
None => quote!(b.index(#lua_name, |b| { #(#build)* });),
|
||||
Some(ref mm) => quote_spanned!(func_name.span() =>
|
||||
b.metatable(#mm, |b| { #(#build)* });
|
||||
),
|
||||
None => quote_spanned!(func_name.span() =>
|
||||
b.index(#lua_name, |b| { #(#build)* });
|
||||
),
|
||||
});
|
||||
|
||||
registry.shims.push(parse_quote_spanned!(func_name.span() =>
|
||||
#[unsafe(export_name = #c_name)]
|
||||
unsafe extern "C" fn #shim_name(#(#shim_params),*) -> #shim_ret { #shim_body }
|
||||
unsafe extern "C" fn #shim_name(#(#shim_params),*) -> #shim_ret { unsafe { #shim_body } }
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_ffi_exports(registry: &FfiRegistry) -> Result<TokenStream> {
|
||||
fn generate_ffi_exports(registry: &Registry) -> Result<TokenStream> {
|
||||
let ty = ®istry.ty;
|
||||
let names = registry.shims.iter().map(|f| &f.sig.ident);
|
||||
|
||||
@@ -464,6 +567,18 @@ fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
||||
&& let Some(ref abi) = abi.name
|
||||
&& abi.value() == "Lua"
|
||||
{
|
||||
syn_assert!(
|
||||
func.sig.generics.params.len() == 0,
|
||||
func.sig.generics,
|
||||
"cannot be generic"
|
||||
);
|
||||
|
||||
syn_assert!(
|
||||
func.sig.constness.is_none(),
|
||||
func.sig.constness,
|
||||
"cannot be const"
|
||||
);
|
||||
|
||||
let mut params: Vec<_> = func
|
||||
.sig
|
||||
.inputs
|
||||
@@ -484,6 +599,8 @@ fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
||||
|
||||
let attrs = parse_lua_function_attrs(&mut func.attrs)?;
|
||||
attrs.metamethod.map(|mm| document_metamethod(func, mm));
|
||||
func.sig.asyncness.is_some().then(|| document_async(func));
|
||||
document_lua_function(func);
|
||||
|
||||
funcs.push(LuaFunction {
|
||||
name: func.sig.ident.clone(),
|
||||
@@ -506,9 +623,10 @@ fn stub_lua_function(func: &mut ImplItemFn) -> Result<()> {
|
||||
// documentation generation
|
||||
func.sig.abi = None;
|
||||
func.attrs.push(parse_quote!(#[allow(unused)]));
|
||||
func.block = parse_quote!({
|
||||
func.block.stmts.clear();
|
||||
func.block.stmts.push(parse_quote!(
|
||||
::std::unreachable!("cannot call lua function from rust");
|
||||
});
|
||||
));
|
||||
|
||||
let inputs = &mut func.sig.inputs;
|
||||
let output = &mut func.sig.output;
|
||||
@@ -544,6 +662,7 @@ fn stub_lua_type(ty: &Type) -> Result<Type> {
|
||||
} else {
|
||||
match ty_name(ty)?.to_string().as_str() {
|
||||
"any" => quote_spanned!(span => any),
|
||||
"many" => quote_spanned!(span => many),
|
||||
"nil" => quote_spanned!(span => nil),
|
||||
"boolean" => quote_spanned!(span => boolean),
|
||||
"lightuserdata" => quote_spanned!(span => lightuserdata),
|
||||
@@ -567,7 +686,7 @@ fn parse_lua_function_attrs(attrs: &mut Vec<Attribute>) -> Result<LuaFunctionAtt
|
||||
let mut i = 0;
|
||||
while let Some(attr) = attrs.get(i) {
|
||||
if let Some(name) = attr.path().get_ident()
|
||||
&& let Ok(method) = Metamethod::try_from(name)
|
||||
&& let Ok(method) = Metamethod::try_from(&name.unraw())
|
||||
{
|
||||
match method {
|
||||
Metamethod::New => syn_error!(attr, r#"cannot be applied to a lua function"#),
|
||||
@@ -584,63 +703,27 @@ fn parse_lua_function_attrs(attrs: &mut Vec<Attribute>) -> Result<LuaFunctionAtt
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
struct LuaRegistry {
|
||||
ty: Ident,
|
||||
build: Vec<TokenStream>,
|
||||
}
|
||||
|
||||
impl LuaRegistry {
|
||||
fn new(ty: Ident) -> Self {
|
||||
Self { ty, build: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
fn add_lua_function(registry: &mut LuaRegistry, func: &LuaFunction) -> Result<()> {
|
||||
fn add_lua_function(registry: &mut Registry, func: &LuaFunction) -> Result<()> {
|
||||
let ffi = ffi_crate();
|
||||
let luaify = quote!(#ffi::__internal::luaify!);
|
||||
let name = func.name.unraw().to_string();
|
||||
let func_name = &func.name;
|
||||
let params = &func.params;
|
||||
let body = &func.body;
|
||||
let name = func_name.unraw().to_string();
|
||||
|
||||
registry.build.push(match func.attrs.metamethod {
|
||||
Some(ref mm) => quote!(b.metatable_raw(#mm, #luaify(|#(#params),*| #body));),
|
||||
None => quote!(b.index_raw(#name, #luaify(|#(#params),*| #body));),
|
||||
Some(ref mm) => quote_spanned!(func_name.span() =>
|
||||
b.metatable_raw(#mm, #luaify(|#(#params),*| #body));
|
||||
),
|
||||
None => quote_spanned!(func_name.span() =>
|
||||
b.index_raw(#name, #luaify(|#(#params),*| #body));
|
||||
),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn document_metamethod(func: &mut ImplItemFn, method: Metamethod) {
|
||||
let s = match method {
|
||||
Metamethod::Eq => "This is a metamethod which is called by the `==` operator.".into(),
|
||||
Metamethod::Len => "This is a metamethod which is called by the `#` operator.".into(),
|
||||
Metamethod::Lt => "This is a metamethod which is called by the `<` operator.".into(),
|
||||
Metamethod::Le => "This is a metamethod which is called by the `<=` operator.".into(),
|
||||
Metamethod::Concat => "This is a metamethod which is called by the `..` operator.".into(),
|
||||
Metamethod::Add => "This is a metamethod which is called by the `+` operator.".into(),
|
||||
Metamethod::Sub => "This is a metamethod which is called by the `-` operator.".into(),
|
||||
Metamethod::Mul => "This is a metamethod which is called by the `*` operator.".into(),
|
||||
Metamethod::Div => "This is a metamethod which is called by the `/` operator.".into(),
|
||||
Metamethod::Mod => "This is a metamethod which is called by the `%` operator.".into(),
|
||||
Metamethod::Pow => "This is a metamethod which is called by the `^` operator.".into(),
|
||||
Metamethod::Unm => "This is a metamethod which is called by the `-` operator.".into(),
|
||||
Metamethod::ToString => {
|
||||
"This is a metamethod which can be called by the `tostring` built-in function.".into()
|
||||
}
|
||||
Metamethod::Pairs => {
|
||||
"This is a metamethod which can be called by the `pairs` built-in function.".into()
|
||||
}
|
||||
Metamethod::Ipairs => {
|
||||
"This is a metamethod which can be called by the `ipairs` built-in function.".into()
|
||||
}
|
||||
_ => format!("This is a metamethod and cannot be called directly."),
|
||||
};
|
||||
|
||||
func.attrs.push(parse_quote!(#[doc = ""]));
|
||||
func.attrs.push(parse_quote!(#[doc = #s]));
|
||||
}
|
||||
|
||||
fn inject_fallback_new(registry: &mut LuaRegistry) -> Result<()> {
|
||||
fn inject_fallback_new(registry: &mut Registry) -> Result<()> {
|
||||
let ty = ®istry.ty;
|
||||
let lua = format!(
|
||||
r#"function() error("type '{}' has no constructor"); end"#,
|
||||
@@ -654,7 +737,7 @@ fn inject_fallback_new(registry: &mut LuaRegistry) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn inject_merged_drop(registry: &mut FfiRegistry, lua: Option<&LuaFunction>) -> Result<()> {
|
||||
fn inject_merged_drop(registry: &mut Registry, lua: Option<&LuaFunction>) -> Result<()> {
|
||||
let ffi = ffi_crate();
|
||||
let luaify = quote!(#ffi::__internal::luaify!);
|
||||
let ty = ®istry.ty;
|
||||
@@ -669,11 +752,17 @@ fn inject_merged_drop(registry: &mut FfiRegistry, lua: Option<&LuaFunction>) ->
|
||||
"finaliser must take exactly one parameter"
|
||||
);
|
||||
|
||||
match lua.params[0] {
|
||||
// should be `self: cdata` PatType
|
||||
Pat::Type(ref ty) => {
|
||||
syn_assert!(
|
||||
pat_ident(&lua.params[0])? == "self",
|
||||
pat_ident(&ty.pat)? == "self",
|
||||
lua.params[0],
|
||||
"finaliser parameter must be `self`"
|
||||
);
|
||||
}
|
||||
_ => syn_error!(lua.params[0], "finaliser parameter must be `self`"),
|
||||
}
|
||||
|
||||
let params = &lua.params;
|
||||
let body = &lua.body;
|
||||
@@ -711,3 +800,58 @@ fn inject_merged_drop(registry: &mut FfiRegistry, lua: Option<&LuaFunction>) ->
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn document_ffi_function(func: &mut ImplItemFn) {
|
||||
func.attrs.insert(0, parse_quote!(#[doc =
|
||||
r#"<span class="stab" title="This function is implemented in Rust and called via FFI." style="float: right; background: #fff5d6; font-weight: 500; margin-left: 3px; padding-left: 5px; padding-right: 5px;">FFI</span>"#
|
||||
]));
|
||||
}
|
||||
|
||||
fn document_lua_function(func: &mut ImplItemFn) {
|
||||
func.attrs.insert(0, parse_quote!(#[doc =
|
||||
r#"<span class="stab" title="This function is implemented in Lua." style="float: right; background: #ebf5ff; font-weight: 500; margin-left: 3px; padding-left: 5px; padding-right: 5px;">Lua</span>"#
|
||||
]));
|
||||
}
|
||||
|
||||
fn document_async(func: &mut ImplItemFn) {
|
||||
func.attrs.insert(0, parse_quote!(#[doc =
|
||||
r#"<span class="stab" title="This function is asynchronous." style="float: right; background: #ebf5ff; margin-left: 3px; padding-left: 5px; padding-right: 5px;">Async</span>"#
|
||||
]));
|
||||
}
|
||||
|
||||
fn document_metamethod(func: &mut ImplItemFn, method: Metamethod) {
|
||||
let s = match method {
|
||||
Metamethod::Eq => "This is a metamethod which is called by the `==` operator.",
|
||||
Metamethod::Len => "This is a metamethod which is called by the `#` operator.",
|
||||
Metamethod::Lt => "This is a metamethod which is called by the `<` operator.",
|
||||
Metamethod::Le => "This is a metamethod which is called by the `<=` operator.",
|
||||
Metamethod::Concat => "This is a metamethod which is called by the `..` operator.",
|
||||
Metamethod::Call => {
|
||||
"This is a metamethod which can be called by calling `(...)` on the value directly."
|
||||
}
|
||||
Metamethod::Add => "This is a metamethod which is called by the `+` operator.",
|
||||
Metamethod::Sub => "This is a metamethod which is called by the `-` operator.",
|
||||
Metamethod::Mul => "This is a metamethod which is called by the `*` operator.",
|
||||
Metamethod::Div => "This is a metamethod which is called by the `/` operator.",
|
||||
Metamethod::Mod => "This is a metamethod which is called by the `%` operator.",
|
||||
Metamethod::Pow => "This is a metamethod which is called by the `^` operator.",
|
||||
Metamethod::Unm => "This is a metamethod which is called by the `-` operator.",
|
||||
Metamethod::ToString => {
|
||||
"This is a metamethod which is called by the [`tostring(...)`](https://www.lua.org/manual/5.1/manual.html#pdf-tostring) built-in function."
|
||||
}
|
||||
Metamethod::Pairs => {
|
||||
"This is a metamethod which is called by the [`pairs(...)`](https://www.lua.org/manual/5.1/manual.html#pdf-pairs) built-in function."
|
||||
}
|
||||
Metamethod::Ipairs => {
|
||||
"This is a metamethod which is called by the [`ipairs(...)`](https://www.lua.org/manual/5.1/manual.html#pdf-ipairs) built-in function."
|
||||
}
|
||||
_ => "This is a metamethod and cannot be called directly.",
|
||||
};
|
||||
|
||||
func.attrs.insert(0, parse_quote!(#[doc =
|
||||
r#"<span class="stab" title="This function is a metamethod." style="float: right; background: #ebf5ff; margin-left: 3px; padding-left: 5px; padding-right: 5px;">Metamethod</span>"#
|
||||
]));
|
||||
|
||||
func.attrs.push(parse_quote!(#[doc = ""]));
|
||||
func.attrs.push(parse_quote!(#[doc = #s]));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::env;
|
||||
use syn::{spanned::Spanned, *};
|
||||
use syn::{ext::IdentExt, spanned::Spanned, *};
|
||||
|
||||
macro_rules! syn_error {
|
||||
($src:expr, $($fmt:expr),+) => {{
|
||||
@@ -57,13 +57,13 @@ pub fn is_unit(ty: &Type) -> bool {
|
||||
|
||||
pub fn is_primitivelike(ty: &Type) -> bool {
|
||||
match ty {
|
||||
Type::Tuple(tuple) if tuple.elems.is_empty() => true, // unit type
|
||||
Type::Reference(_) | Type::Ptr(_) => true,
|
||||
Type::Paren(paren) => is_primitivelike(&paren.elem),
|
||||
Type::Tuple(tuple) if tuple.elems.is_empty() => return true, // unit type
|
||||
Type::Reference(_) | Type::Ptr(_) => return true,
|
||||
Type::Paren(paren) => return is_primitivelike(&paren.elem),
|
||||
Type::Path(path) => {
|
||||
if let Some(name) = path.path.get_ident() {
|
||||
matches!(
|
||||
format!("{name}").as_str(),
|
||||
return matches!(
|
||||
name.unraw().to_string().as_str(),
|
||||
"bool"
|
||||
| "u8"
|
||||
| "u16"
|
||||
@@ -94,11 +94,72 @@ pub fn is_primitivelike(ty: &Type) -> bool {
|
||||
| "c_size_t"
|
||||
| "c_ssize_t"
|
||||
| "c_ptrdiff_t"
|
||||
)
|
||||
} else {
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum StringLike {
|
||||
SliceU8,
|
||||
Str,
|
||||
BStr,
|
||||
}
|
||||
_ => false,
|
||||
|
||||
pub fn is_stringlike(ty: &Type) -> Option<StringLike> {
|
||||
if let Type::Reference(ty) = ty
|
||||
&& ty.mutability.is_none()
|
||||
&& ty.lifetime.is_none()
|
||||
{
|
||||
Some(match *ty.elem {
|
||||
Type::Slice(ref slice) => {
|
||||
// match &[u8]
|
||||
if let Type::Path(ref path) = *slice.elem
|
||||
&& let Some(name) = path.path.get_ident()
|
||||
{
|
||||
match name.unraw().to_string().as_str() {
|
||||
"u8" => StringLike::SliceU8,
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Type::Path(ref path) => {
|
||||
// match &str or &BStr
|
||||
if let Some(name) = path.path.get_ident() {
|
||||
match name.unraw().to_string().as_str() {
|
||||
"str" => StringLike::Str,
|
||||
"BStr" => StringLike::BStr,
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => return None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_optionlike(ty: &Type) -> Option<&Type> {
|
||||
if let Type::Path(path) = ty
|
||||
&& path.path.leading_colon.is_none()
|
||||
&& path.path.segments.len() == 1
|
||||
&& let Some(segment) = path.path.segments.get(0)
|
||||
&& segment.ident == "Option"
|
||||
&& let PathArguments::AngleBracketed(ref angle) = segment.arguments
|
||||
&& angle.args.len() == 1
|
||||
&& let Some(GenericArgument::Type(ty)) = angle.args.get(0)
|
||||
{
|
||||
Some(ty)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
[package]
|
||||
name = "luaify"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
@@ -10,3 +14,6 @@ proc-macro = true
|
||||
proc-macro2 = "1.0.95"
|
||||
quote = "1.0.40"
|
||||
syn = { version = "2.0.103", features = ["full", "visit-mut"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.1"
|
||||
|
||||
@@ -14,6 +14,12 @@ pub fn generate(expr: &Expr) -> Result<TokenStream> {
|
||||
Ok(f.done())
|
||||
}
|
||||
|
||||
pub fn generate_chunk(block: &Block) -> Result<TokenStream> {
|
||||
let mut f = Formatter::default();
|
||||
generate_block(&mut f, &block, Context::stmt(false))?;
|
||||
Ok(f.done())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Formatter {
|
||||
buf: String,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::{generate::generate, transform::transform};
|
||||
use crate::{
|
||||
generate::{generate, generate_chunk},
|
||||
transform::{transform, transform_chunk},
|
||||
};
|
||||
use proc_macro::TokenStream as TokenStream1;
|
||||
use quote::ToTokens;
|
||||
use syn::parse_macro_input;
|
||||
@@ -16,3 +19,13 @@ pub fn luaify(input: TokenStream1) -> TokenStream1 {
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn luaify_chunk(input: TokenStream1) -> TokenStream1 {
|
||||
let mut block = parse_macro_input!(input);
|
||||
match transform_chunk(&mut block).and_then(|()| generate_chunk(&block)) {
|
||||
Ok(s) => s,
|
||||
Err(err) => err.into_compile_error().into_token_stream(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,12 @@ pub fn transform(expr: &mut Expr) -> Result<()> {
|
||||
visitor.result
|
||||
}
|
||||
|
||||
pub fn transform_chunk(block: &mut Block) -> Result<()> {
|
||||
let mut visitor = Visitor::new();
|
||||
visitor.visit_block_mut(block);
|
||||
visitor.result
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Visitor {
|
||||
result: Result<()>,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use luaify::luaify;
|
||||
use luaify::{luaify, luaify_chunk};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn raw_ident() {
|
||||
@@ -402,3 +403,19 @@ fn length() {
|
||||
r#"local a,b,c=#a,#b,#c;"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chunk() {
|
||||
assert_eq!(
|
||||
luaify_chunk!({
|
||||
if a == b {
|
||||
c()
|
||||
} else if b == c {
|
||||
a()
|
||||
} else {
|
||||
d()
|
||||
}
|
||||
}),
|
||||
"if a==b then c();elseif b==c then a();else d();end;"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
[package]
|
||||
name = "luajit-sys"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
[package]
|
||||
name = "luajit"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[features]
|
||||
runtime = ["luajit-sys/runtime"]
|
||||
@@ -10,6 +14,6 @@ unwind = ["luajit-sys/unwind"]
|
||||
[dependencies]
|
||||
bitflags = { version = "2.9.1", features = ["std"] }
|
||||
bstr = "1.12.0"
|
||||
luaffi = { version = "0.1.0", path = "../luaffi" }
|
||||
luajit-sys = { version = "0.1.0", path = "../luajit-sys" }
|
||||
luaffi = { path = "../luaffi" }
|
||||
luajit-sys = { path = "../luajit-sys" }
|
||||
thiserror = "2.0.12"
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::{
|
||||
ffi::{CStr, CString, NulError},
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
mem::ManuallyDrop,
|
||||
ops::{Deref, DerefMut},
|
||||
os::raw::{c_char, c_int, c_void},
|
||||
pin::Pin,
|
||||
@@ -25,6 +26,21 @@ pub fn version() -> &'static str {
|
||||
LUAJIT_VERSION.to_str().unwrap()
|
||||
}
|
||||
|
||||
/// LuaJIT copyright string.
|
||||
pub fn copyright() -> &'static str {
|
||||
LUAJIT_COPYRIGHT.to_str().unwrap()
|
||||
}
|
||||
|
||||
/// LuaJIT URL string.
|
||||
pub fn url() -> &'static str {
|
||||
LUAJIT_URL.to_str().unwrap()
|
||||
}
|
||||
|
||||
// reexport constants
|
||||
pub use luajit_sys::{
|
||||
LUA_ENVIRONINDEX, LUA_GLOBALSINDEX, LUA_MULTRET, LUA_NOREF, LUA_REFNIL, LUA_REGISTRYINDEX,
|
||||
};
|
||||
|
||||
/// Lua error.
|
||||
#[derive(Debug, Error)]
|
||||
#[non_exhaustive]
|
||||
@@ -467,6 +483,19 @@ pub struct Ref {
|
||||
key: c_int,
|
||||
}
|
||||
|
||||
impl Ref {
|
||||
/// Consumes this ref and returns the original key used to create the ref.
|
||||
///
|
||||
/// This key can be used to index into the registry table ([`LUA_REGISTRYINDEX`]) to retrieve
|
||||
/// the referenced value. The key can be converted back into a ref using
|
||||
/// [`State::new_ref_unchecked`].
|
||||
pub fn into_raw(self) -> c_int {
|
||||
let Self { ref mut state, key } = *ManuallyDrop::new(self);
|
||||
unsafe { ptr::drop_in_place(state) }
|
||||
key
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Ref {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: luaL_unref is guaranteed to not fail
|
||||
@@ -861,7 +890,7 @@ impl Stack {
|
||||
/// array-part, these values are **not** cleared. The number of values popped is returned, which
|
||||
/// is always equal to `n`.
|
||||
///
|
||||
/// This method does not invoke any metamethods.
|
||||
/// This method does not invoke any metamethods. The table is not popped from the stack.
|
||||
///
|
||||
/// Equivalent to `table.pack(...)`.
|
||||
///
|
||||
@@ -897,7 +926,8 @@ impl Stack {
|
||||
/// pushed at the top of the stack in ascending order. If `i > j`, then nothing is pushed.
|
||||
/// Otherwise, `j - i + 1` values are pushed, and the number of values pushed is returned.
|
||||
///
|
||||
/// This method does not invoke any metamethods.
|
||||
/// This method does not invoke any metamethods. The table is not popped from the stack or
|
||||
/// altered in any way.
|
||||
///
|
||||
/// Equivalent to `table.unpack(list, i, j)`.
|
||||
///
|
||||
@@ -1333,8 +1363,7 @@ impl<'s> DerefMut for StackGuard<'s> {
|
||||
|
||||
impl<'s> Drop for StackGuard<'s> {
|
||||
fn drop(&mut self) {
|
||||
#[cfg(debug_assertions)]
|
||||
if self.check_overpop {
|
||||
if cfg!(debug_assertions) && self.check_overpop {
|
||||
let new_size = self.stack.size();
|
||||
assert!(
|
||||
self.size <= new_size,
|
||||
|
||||
18
src/lib.rs
18
src/lib.rs
@@ -1,2 +1,20 @@
|
||||
#[cfg(feature = "task")]
|
||||
pub use lb::chan;
|
||||
#[cfg(feature = "fs")]
|
||||
pub use lb::fs;
|
||||
#[cfg(feature = "net")]
|
||||
pub use lb::net;
|
||||
#[cfg(feature = "task")]
|
||||
pub use lb::task;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn open(#[allow(unused)] rt: &mut lb::runtime::Builder) {
|
||||
#[cfg(feature = "task")]
|
||||
rt.module::<task::lb_tasklib>();
|
||||
#[cfg(feature = "task")]
|
||||
rt.module::<chan::lb_chanlib>();
|
||||
#[cfg(feature = "fs")]
|
||||
rt.module::<fs::lb_fslib>();
|
||||
#[cfg(feature = "net")]
|
||||
rt.module::<net::lb_netlib>();
|
||||
}
|
||||
|
||||
172
src/main.rs
172
src/main.rs
@@ -1,7 +1,7 @@
|
||||
use clap::Parser;
|
||||
use mimalloc::MiMalloc;
|
||||
use owo_colors::OwoColorize;
|
||||
use std::{backtrace::Backtrace, fmt::Display, net::SocketAddr, num::NonZero, panic, thread};
|
||||
use std::{backtrace::Backtrace, fmt::Display, num::NonZero, panic, process, thread};
|
||||
use sysexits::ExitCode;
|
||||
|
||||
#[global_allocator]
|
||||
@@ -20,21 +20,42 @@ fn panic_cb(panic: &panic::PanicHookInfo) {
|
||||
};
|
||||
|
||||
eprint!(
|
||||
"{}:\n{trace}",
|
||||
"{}\n{trace}",
|
||||
format_args!(
|
||||
"thread '{}' panicked at {location}: {msg}",
|
||||
thread::current().name().unwrap_or("<unnamed>")
|
||||
)
|
||||
.red()
|
||||
.bold()
|
||||
);
|
||||
|
||||
eprintln!(
|
||||
"{}",
|
||||
"This is a bug in luby. Please kindly report this at https://git.lua.re/luaneko/luby."
|
||||
format_args!(
|
||||
"luby should never panic. Please kindly report this bug at {}.",
|
||||
env!("CARGO_PKG_REPOSITORY")
|
||||
)
|
||||
.yellow()
|
||||
.bold()
|
||||
);
|
||||
}
|
||||
|
||||
fn error_cb(err: &luajit::Error) {
|
||||
match err.trace() {
|
||||
Some(trace) => eprintln!("{}\n{trace}", err.red().bold()),
|
||||
None => eprintln!("{}", err.red().bold()),
|
||||
}
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
fn unwrap_exit<T, E: Display>(code: ExitCode) -> impl FnOnce(E) -> T {
|
||||
move |err| {
|
||||
eprintln!("{}", err.red().bold());
|
||||
code.exit()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
/// Paths to scripts to execute.
|
||||
@@ -45,7 +66,7 @@ struct Args {
|
||||
#[clap(long, short = 'e', value_name = "CHUNK")]
|
||||
eval: Vec<String>,
|
||||
|
||||
/// Libraries to require on startup.
|
||||
/// Libraries to require.
|
||||
#[clap(long, short = 'l', value_name = "NAME")]
|
||||
lib: Vec<String>,
|
||||
|
||||
@@ -54,29 +75,56 @@ struct Args {
|
||||
log: tracing::Level,
|
||||
|
||||
/// LuaJIT control commands.
|
||||
#[clap(long, short = 'j', value_name = "CMD=FLAGS")]
|
||||
#[clap(long, short = 'j', help_heading = "Runtime", value_name = "CMD=FLAGS")]
|
||||
jit: Vec<String>,
|
||||
|
||||
/// Number of tokio worker threads.
|
||||
#[clap(long, value_name = "THREADS", default_value_t = Self::threads())]
|
||||
/// Number of worker threads.
|
||||
#[clap(
|
||||
long,
|
||||
short = 'T',
|
||||
help_heading = "Runtime",
|
||||
value_name = "THREADS",
|
||||
default_value_t = Self::threads()
|
||||
)]
|
||||
threads: NonZero<usize>,
|
||||
|
||||
/// Number of tokio blocking threads.
|
||||
#[clap(long, value_name = "THREADS", default_value_t = Self::blocking_threads())]
|
||||
/// Number of blocking threads.
|
||||
#[clap(
|
||||
long,
|
||||
help_heading = "Runtime",
|
||||
value_name = "THREADS",
|
||||
default_value_t = Self::blocking_threads()
|
||||
)]
|
||||
blocking_threads: NonZero<usize>,
|
||||
|
||||
/// Enable tokio-console integration.
|
||||
#[clap(long)]
|
||||
#[cfg(feature = "tokio-console")]
|
||||
#[clap(long, help_heading = "Debugging")]
|
||||
enable_console: bool,
|
||||
|
||||
/// tokio-console publish address.
|
||||
#[cfg(feature = "tokio-console")]
|
||||
#[clap(
|
||||
long,
|
||||
help_heading = "Debugging",
|
||||
value_name = "ADDRESS",
|
||||
default_value = "127.0.0.1:6669",
|
||||
requires = "enable_console"
|
||||
)]
|
||||
console_addr: SocketAddr,
|
||||
console_addr: std::net::SocketAddr,
|
||||
|
||||
/// Dump internal data.
|
||||
#[clap(
|
||||
long,
|
||||
help_heading = "Debugging",
|
||||
value_name = "DATA",
|
||||
value_parser = ["cdef"]
|
||||
)]
|
||||
dump: Vec<String>,
|
||||
|
||||
/// Print version.
|
||||
#[clap(long, short = 'V')]
|
||||
version: bool,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
@@ -89,17 +137,14 @@ impl Args {
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_err<T, E: Display>(code: ExitCode) -> impl FnOnce(E) -> T {
|
||||
move |err| {
|
||||
eprintln!("{}", err.red());
|
||||
code.exit()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), ExitCode> {
|
||||
panic::set_hook(Box::new(panic_cb));
|
||||
|
||||
let args = Args::parse();
|
||||
if args.version {
|
||||
return Ok(print_version());
|
||||
}
|
||||
|
||||
init_logger(&args);
|
||||
|
||||
let tokio = init_tokio(&args);
|
||||
@@ -112,9 +157,21 @@ fn main() -> Result<(), ExitCode> {
|
||||
})
|
||||
}
|
||||
|
||||
fn print_version() {
|
||||
println!("luby {}", env!("VERGEN_GIT_DESCRIBE"));
|
||||
println!("{}\n", env!("CARGO_PKG_HOMEPAGE"));
|
||||
println!("Compiled with {} -- {}", luajit::version(), luajit::url());
|
||||
println!(
|
||||
"Compiled with rustc {} on {} for {}",
|
||||
env!("VERGEN_RUSTC_SEMVER"),
|
||||
env!("VERGEN_RUSTC_HOST_TRIPLE"),
|
||||
env!("VERGEN_CARGO_TARGET_TRIPLE"),
|
||||
);
|
||||
}
|
||||
|
||||
fn init_logger(args: &Args) {
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::{Layer, util::*};
|
||||
use tracing_subscriber::util::*;
|
||||
|
||||
let log = tracing_subscriber::fmt()
|
||||
.compact()
|
||||
@@ -128,61 +185,75 @@ fn init_logger(args: &Args) {
|
||||
.with_target(false)
|
||||
.finish();
|
||||
|
||||
if args.enable_console {
|
||||
#[cfg(feature = "tokio-console")]
|
||||
{
|
||||
use tracing_subscriber::Layer;
|
||||
console_subscriber::ConsoleLayer::builder()
|
||||
.with_default_env()
|
||||
.server_addr(args.console_addr)
|
||||
.spawn()
|
||||
.with_subscriber(log)
|
||||
.init()
|
||||
} else {
|
||||
log.init()
|
||||
.init();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tokio-console"))]
|
||||
log.init();
|
||||
}
|
||||
|
||||
fn init_tokio(args: &Args) -> tokio::runtime::Runtime {
|
||||
let mut rt = match args.threads.get() {
|
||||
match args.threads.get() {
|
||||
1 => tokio::runtime::Builder::new_current_thread(),
|
||||
n => {
|
||||
let mut rt = tokio::runtime::Builder::new_multi_thread();
|
||||
rt.worker_threads(n - 1);
|
||||
rt
|
||||
}
|
||||
};
|
||||
|
||||
rt.enable_all()
|
||||
}
|
||||
.enable_all()
|
||||
.thread_name("luby")
|
||||
.max_blocking_threads(args.blocking_threads.get())
|
||||
.build()
|
||||
.unwrap_or_else(exit_err(ExitCode::OsErr))
|
||||
.unwrap_or_else(unwrap_exit(ExitCode::OsErr))
|
||||
}
|
||||
|
||||
fn init_lua(args: &Args) -> lb::runtime::Runtime {
|
||||
let rt = lb::runtime::Builder::new();
|
||||
let mut rt = rt.build().unwrap_or_else(exit_err(ExitCode::Software));
|
||||
let mut rt = {
|
||||
let mut rt = lb::runtime::Builder::new();
|
||||
luby::open(&mut rt);
|
||||
|
||||
if args.dump.iter().find(|s| *s == "cdef").is_some() {
|
||||
print!("{}", rt.registry()); // for cdef debugging
|
||||
}
|
||||
|
||||
rt.unhandled_error(error_cb).build().unwrap()
|
||||
};
|
||||
|
||||
for arg in args.jit.iter() {
|
||||
let mut s = rt.guard();
|
||||
if let Some((cmd, flags)) = parse_jitlib_cmd(arg)
|
||||
let res = if let Some((cmd, flags)) = parse_jitlib_cmd(arg)
|
||||
&& let Ok(_) = s.require(format!("jit.{cmd}"), 1)
|
||||
{
|
||||
(s.push("start"), s.get(-2), s.push(flags));
|
||||
s.call(1, 0)
|
||||
s.call(1, 0) // require("jit.{cmd}").start(flags)
|
||||
} else {
|
||||
s.require("jit", 1).unwrap();
|
||||
match arg.as_str() {
|
||||
cmd @ ("on" | "off" | "flush") => {
|
||||
(s.push(cmd), s.get(-2));
|
||||
s.call(0, 0)
|
||||
s.call(0, 0) // require("jit").[on/off/flush]()
|
||||
}
|
||||
arg => {
|
||||
flags => {
|
||||
(s.push("opt"), s.get(-2));
|
||||
(s.push("start"), s.get(-2), s.push(arg));
|
||||
s.call(1, 0)
|
||||
(s.push("start"), s.get(-2), s.push(flags));
|
||||
s.call(1, 0) // require("jit").opt.start(flags)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = res {
|
||||
drop(s);
|
||||
rt.report_error(&err);
|
||||
}
|
||||
.unwrap_or_else(exit_err(ExitCode::Usage));
|
||||
}
|
||||
|
||||
rt
|
||||
@@ -190,34 +261,27 @@ fn init_lua(args: &Args) -> lb::runtime::Runtime {
|
||||
|
||||
fn parse_jitlib_cmd(s: &str) -> Option<(&str, &str)> {
|
||||
match s {
|
||||
"p" => Some(("p", "Flspv10")),
|
||||
"v" => Some(("v", "-")),
|
||||
"dump" => Some(("dump", "tirs")),
|
||||
"p" => Some(("p", "Flspv10")), // default -jp flags
|
||||
"v" => Some(("v", "-")), // default -jv flags
|
||||
"dump" => Some(("dump", "tirs")), // default -jdump flags
|
||||
_ => s.split_once('='),
|
||||
}
|
||||
}
|
||||
|
||||
async fn main_async(args: Args, state: &mut luajit::State) -> Result<(), ExitCode> {
|
||||
async fn main_async(args: Args, cx: &mut lb::runtime::Context) -> Result<(), ExitCode> {
|
||||
for ref path in args.path {
|
||||
let mut s = state.guard();
|
||||
let chunk = match std::fs::read(path) {
|
||||
Ok(chunk) => chunk,
|
||||
Err(err) => {
|
||||
eprintln!("{}", format_args!("{path}: {err}").red());
|
||||
ExitCode::NoInput.exit();
|
||||
eprintln!("{}", format_args!("{path}: {err}").red().bold());
|
||||
return Err(ExitCode::NoInput);
|
||||
}
|
||||
};
|
||||
|
||||
s.load(&luajit::Chunk::new(chunk).path(path))
|
||||
.unwrap_or_else(exit_err(ExitCode::NoInput));
|
||||
|
||||
if let Err(err) = s.call_async(0, 0).await {
|
||||
match err.trace() {
|
||||
Some(trace) => eprintln!("{}\n{trace}", err.red()), // runtime error
|
||||
None => eprintln!("{}", err.red()),
|
||||
}
|
||||
|
||||
ExitCode::DataErr.exit();
|
||||
if let Err(ref err) = cx.load(&luajit::Chunk::new(chunk).path(path)) {
|
||||
cx.report_error(err);
|
||||
} else if let Err(ref err) = cx.call_async(0, 0).await {
|
||||
cx.report_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
test.lua
6
test.lua
@@ -1,6 +0,0 @@
|
||||
local ffi = require("ffi")
|
||||
local lb = ffi.new("struct lb_core")
|
||||
|
||||
print(lb)
|
||||
|
||||
lb.spawn("")
|
||||
97
tests/main.lua
Normal file
97
tests/main.lua
Normal file
@@ -0,0 +1,97 @@
|
||||
if (...) ~= nil and (...).type == "group" then return end -- prevent recursive main call
|
||||
local fs = require("lb:fs")
|
||||
local global = _G
|
||||
local colors = {
|
||||
reset = "\x1b[0m",
|
||||
pass = "\x1b[32;1m",
|
||||
fail = "\x1b[31;1m",
|
||||
}
|
||||
|
||||
local function color(name, s)
|
||||
return colors[name] .. s .. colors.reset
|
||||
end
|
||||
|
||||
local function create_test(name, f, group)
|
||||
local test = { type = "test", name = name or "", group = group, state = "pending", f = f }
|
||||
local fenv = setmetatable({}, { __index = global })
|
||||
setfenv(f, fenv)
|
||||
return test
|
||||
end
|
||||
|
||||
local function create_group(name, f, parent)
|
||||
local group = { type = "group", name = name or "", parent = parent, items = {} }
|
||||
local fenv = setmetatable({
|
||||
describe = function(name, f)
|
||||
local item = create_group(name, f, group)
|
||||
table.insert(group.items, item)
|
||||
return item
|
||||
end,
|
||||
|
||||
test = function(name, f)
|
||||
local item = create_test(name, f, group)
|
||||
table.insert(group.items, item)
|
||||
return item
|
||||
end,
|
||||
}, { __index = global })
|
||||
|
||||
setfenv(f, fenv)
|
||||
f(group)
|
||||
return group
|
||||
end
|
||||
|
||||
local function name_test(test)
|
||||
local name = test.name
|
||||
local group = test.group
|
||||
while group ~= nil do
|
||||
if group.name ~= "" then name = group.name .. " › " .. name end
|
||||
group = group.parent
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
local function trace(msg)
|
||||
return color("fail", msg) .. debug.traceback("", 2):sub(("\nstack traceback:"):len() + 1)
|
||||
end
|
||||
|
||||
local function run_test(test)
|
||||
local ok, res = xpcall(test.f, trace, test)
|
||||
if ok then
|
||||
test.state = "pass"
|
||||
print("", color("pass", "PASS") .. " " .. name_test(test))
|
||||
else
|
||||
test.state = "fail"
|
||||
print("", color("fail", "FAIL") .. " " .. name_test(test) .. "\n")
|
||||
print(res .. "\n")
|
||||
end
|
||||
return test
|
||||
end
|
||||
|
||||
local function start(cx, item)
|
||||
if item.type == "test" then
|
||||
table.insert(cx.tasks, spawn(run_test, item))
|
||||
elseif item.type == "group" then
|
||||
for _, item in ipairs(item.items) do
|
||||
start(cx, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function run(item)
|
||||
local cx = { tasks = {} }
|
||||
local pass = true
|
||||
start(cx, item)
|
||||
for _, task in ipairs(cx.tasks) do
|
||||
if task:await().state ~= "pass" then pass = false end
|
||||
end
|
||||
if pass then return 0 end -- report status to cargo
|
||||
return 1
|
||||
end
|
||||
|
||||
return run(create_group("", function()
|
||||
for entry in fs:glob("{tests,crates/*/tests}/**/*.lua") do
|
||||
local path = entry:path():sub(3)
|
||||
local f, err = loadfile(path)
|
||||
if not f then error(err) end
|
||||
describe(path, f)
|
||||
end
|
||||
end))
|
||||
44
tests/main.rs
Normal file
44
tests/main.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use luajit::Chunk;
|
||||
use owo_colors::OwoColorize;
|
||||
use std::{
|
||||
fs,
|
||||
process::{self, ExitCode},
|
||||
};
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let tokio = tokio::runtime::Runtime::new().unwrap();
|
||||
let lua = {
|
||||
let mut rt = lb::runtime::Builder::new();
|
||||
luby::open(&mut rt);
|
||||
rt.unhandled_error(error_cb).build().unwrap()
|
||||
};
|
||||
|
||||
let path = "tests/main.lua";
|
||||
let main = lua.spawn(async move |s| {
|
||||
if let Err(ref err) = s.load(Chunk::new(fs::read(path).unwrap()).path(path)) {
|
||||
s.report_error(err);
|
||||
} else if let Err(ref err) = s.call_async(0, 1).await {
|
||||
s.report_error(err);
|
||||
}
|
||||
|
||||
if s.slot(1).integer().unwrap_or(1) == 0 {
|
||||
ExitCode::SUCCESS
|
||||
} else {
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
});
|
||||
|
||||
tokio.block_on(async move {
|
||||
lua.await;
|
||||
main.await.unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
fn error_cb(err: &luajit::Error) {
|
||||
match err.trace() {
|
||||
Some(trace) => eprintln!("{}\n{trace}", err.red().bold()),
|
||||
None => eprintln!("{}", err.red().bold()),
|
||||
}
|
||||
|
||||
process::exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user