Compare commits
54 Commits
94e1cf2eb0
...
v0.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
a81271c0a8
|
|||
|
7ec60f0e6e
|
|||
|
82726ebb5d
|
|||
|
86bfc7ad34
|
|||
|
6cdf186b61
|
|||
|
98100d02fa
|
|||
|
681dd332ab
|
|||
|
af3dbe60cb
|
|||
|
a07f62ecc5
|
|||
|
e71d618d10
|
|||
|
3ac7df004d
|
|||
|
7e8a655186
|
|||
|
3ba568dc90
|
|||
|
a295107bac
|
|||
|
e0898c22a0
|
|||
|
69ac13ea47
|
|||
|
2352cb0225
|
|||
|
cbf786206d
|
|||
|
9ba9762185
|
|||
|
ee64b22510
|
|||
|
8c47987a45
|
|||
|
122ef04b16
|
|||
|
1aa9b4aa02
|
|||
|
9d5bcc5ef2
|
|||
|
3dd375b071
|
|||
|
f8e7b8ae62
|
|||
|
45db380466
|
|||
|
cadf0a8551
|
|||
|
7548c68c7c
|
|||
|
91e1f33b6c
|
|||
|
a9a067f5a9
|
|||
|
2352ba66d4
|
|||
|
c39106b790
|
|||
|
173149f2f7
|
|||
|
e801ee468b
|
|||
|
7c5ea599c8
|
|||
|
ba6cb10a1a
|
|||
|
300b4539bd
|
|||
|
9f76dff977
|
|||
|
fae1c6e162
|
|||
|
68f6e48043
|
|||
|
066b4e7564
|
|||
|
c9026123e6
|
|||
|
c3fb3407c4
|
|||
|
884acd71e1
|
|||
|
e08ff38803
|
|||
|
40478fb7de
|
|||
|
0fd59f6874
|
|||
|
0667f79ff5
|
|||
|
5be3f2970c
|
|||
|
728ee58e0d
|
|||
|
1b8c461d7e
|
|||
|
ab14e5f28d
|
|||
|
06d3eebaa1
|
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[build]
|
||||||
|
rustflags = ["--cfg", "tokio_unstable"]
|
||||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
|||||||
[submodule "luajit"]
|
[submodule "luajit"]
|
||||||
path = crates/luajit/src
|
path = crates/luajit-sys/src
|
||||||
url = https://github.com/LuaJIT/LuaJIT.git
|
url = https://github.com/LuaJIT/LuaJIT.git
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||||
"runtime.version": "LuaJIT",
|
"runtime.version": "LuaJIT",
|
||||||
"diagnostics.disable": ["redefined-local"]
|
"diagnostics.disable": ["redefined-local", "lowercase-global"]
|
||||||
}
|
}
|
||||||
|
|||||||
684
Cargo.lock
generated
684
Cargo.lock
generated
@@ -210,7 +210,7 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"itertools",
|
"itertools 0.13.0",
|
||||||
"log",
|
"log",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -250,12 +250,46 @@ version = "1.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
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]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.27"
|
version = "1.2.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -370,6 +404,15 @@ dependencies = [
|
|||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@@ -429,6 +472,79 @@ dependencies = [
|
|||||||
"syn",
|
"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"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -449,12 +565,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.12"
|
version = "0.3.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -473,6 +589,15 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
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]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -520,7 +645,19 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
@@ -529,6 +666,19 @@ version = "0.31.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
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]]
|
[[package]]
|
||||||
name = "glob"
|
name = "glob"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -692,12 +842,119 @@ dependencies = [
|
|||||||
"tracing",
|
"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]]
|
[[package]]
|
||||||
name = "ident_case"
|
name = "ident_case"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
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]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.9.3"
|
version = "1.9.3"
|
||||||
@@ -733,12 +990,31 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
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]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -746,13 +1022,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lb_core"
|
name = "lb"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"camino",
|
||||||
|
"derive_more",
|
||||||
"luaffi",
|
"luaffi",
|
||||||
|
"luaify",
|
||||||
"luajit",
|
"luajit",
|
||||||
"owo-colors",
|
"sysexits",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -761,6 +1041,18 @@ version = "0.2.174"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
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]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
version = "0.8.8"
|
version = "0.8.8"
|
||||||
@@ -781,12 +1073,30 @@ dependencies = [
|
|||||||
"libc",
|
"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]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litemap"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.13"
|
version = "0.4.13"
|
||||||
@@ -805,19 +1115,18 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "luaffi"
|
name = "luaffi"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bstr",
|
"bstr",
|
||||||
"luaffi_impl",
|
"luaffi_impl",
|
||||||
"luaify",
|
"luaify",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"simdutf8",
|
"simdutf8",
|
||||||
"static_assertions",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "luaffi_impl"
|
name = "luaffi_impl"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -827,7 +1136,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "luaify"
|
name = "luaify"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -836,31 +1145,39 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "luajit"
|
name = "luajit"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"bstr",
|
"bstr",
|
||||||
"cc",
|
|
||||||
"luaffi",
|
"luaffi",
|
||||||
|
"luajit-sys",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "luajit-sys"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"bindgen",
|
||||||
|
"cc",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "luby"
|
name = "luby"
|
||||||
version = "0.1.0"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"lb_core",
|
"lb",
|
||||||
"luaffi",
|
|
||||||
"luajit",
|
"luajit",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
|
"sysexits",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"vergen-git2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -921,7 +1238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -945,6 +1262,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-conv"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -954,6 +1277,15 @@ dependencies = [
|
|||||||
"autocfg",
|
"autocfg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
@@ -983,9 +1315,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "owo-colors"
|
name = "owo-colors"
|
||||||
version = "4.2.1"
|
version = "4.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec"
|
checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
@@ -1048,6 +1380,27 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
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]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.21"
|
version = "0.2.21"
|
||||||
@@ -1059,9 +1412,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.2.34"
|
version = "0.2.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55"
|
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn",
|
"syn",
|
||||||
@@ -1093,7 +1446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools",
|
"itertools 0.14.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
@@ -1117,6 +1470,12 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r-efi"
|
||||||
|
version = "5.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@@ -1144,7 +1503,7 @@ version = "0.6.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.16",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1212,6 +1571,15 @@ version = "2.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
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]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -1243,6 +1611,15 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@@ -1328,10 +1705,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "stable_deref_trait"
|
||||||
version = "1.1.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
@@ -1341,9 +1718,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.103"
|
version = "2.0.104"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -1356,6 +1733,23 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
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]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.12"
|
version = "2.0.12"
|
||||||
@@ -1385,6 +1779,49 @@ dependencies = [
|
|||||||
"cfg-if",
|
"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]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.45.1"
|
version = "1.45.1"
|
||||||
@@ -1588,6 +2025,35 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
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]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@@ -1600,6 +2066,53 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
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]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -1615,6 +2128,15 @@ version = "0.11.1+wasi-snapshot-preview1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
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]]
|
[[package]]
|
||||||
name = "which"
|
name = "which"
|
||||||
version = "8.0.0"
|
version = "8.0.0"
|
||||||
@@ -1666,6 +2188,15 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.53.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -1800,6 +2331,45 @@ version = "0.0.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
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 = "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]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.26"
|
version = "0.8.26"
|
||||||
@@ -1819,3 +2389,57 @@ dependencies = [
|
|||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"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",
|
||||||
|
]
|
||||||
|
|||||||
35
Cargo.toml
35
Cargo.toml
@@ -1,25 +1,46 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
resolver = "3"
|
||||||
members = [
|
members = [
|
||||||
"crates/lb_core",
|
"crates/lb",
|
||||||
"crates/luaffi",
|
"crates/luaffi",
|
||||||
"crates/luaffi_impl",
|
"crates/luaffi_impl",
|
||||||
"crates/luaify",
|
"crates/luaify",
|
||||||
"crates/luajit",
|
"crates/luajit",
|
||||||
|
"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]
|
[package]
|
||||||
name = "luby"
|
name = "luby"
|
||||||
version = "0.1.0"
|
version.workspace = true
|
||||||
edition = "2024"
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[profile]
|
||||||
|
dev.panic = "abort"
|
||||||
|
release.panic = "abort"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.40", features = ["derive"] }
|
clap = { version = "4.5.40", features = ["derive", "env"] }
|
||||||
console-subscriber = "0.4.1"
|
console-subscriber = "0.4.1"
|
||||||
lb_core = { version = "0.1.0", path = "crates/lb_core" }
|
lb = { path = "crates/lb" }
|
||||||
luaffi = { version = "0.1.0", path = "crates/luaffi" }
|
luajit = { path = "crates/luajit", features = ["runtime"] }
|
||||||
luajit = { version = "0.1.0", path = "crates/luajit", features = ["runtime"] }
|
|
||||||
mimalloc = "0.1.47"
|
mimalloc = "0.1.47"
|
||||||
owo-colors = "4.2.1"
|
owo-colors = "4.2.1"
|
||||||
|
sysexits = "0.9.0"
|
||||||
tokio = { version = "1.45.1", features = ["full", "tracing"] }
|
tokio = { version = "1.45.1", features = ["full", "tracing"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = "0.3.19"
|
tracing-subscriber = "0.3.19"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
vergen-git2 = { version = "1.0.7", features = ["cargo", "rustc"] }
|
||||||
|
|||||||
10
bacon.toml
Normal file
10
bacon.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[jobs.test]
|
||||||
|
command = ["cargo", "test", "--workspace"]
|
||||||
|
need_stdout = true
|
||||||
|
|
||||||
|
[jobs.doc]
|
||||||
|
command = ["cargo", "doc", "--workspace"]
|
||||||
|
|
||||||
|
[jobs.doc-open]
|
||||||
|
command = ["cargo", "doc", "--workspace", "--open"]
|
||||||
|
on_success = "back"
|
||||||
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();
|
||||||
|
}
|
||||||
21
crates/lb/Cargo.toml
Normal file
21
crates/lb/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "lb"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
camino = "1.1.10"
|
||||||
|
derive_more = { version = "2.0.1", features = ["full"] }
|
||||||
|
luaffi = { path = "../luaffi" }
|
||||||
|
luajit = { path = "../luajit" }
|
||||||
|
sysexits = "0.9.0"
|
||||||
|
tokio = { version = "1.45.1", features = ["rt", "time", "fs", "net", "process", "signal", "tracing"] }
|
||||||
|
tracing = "0.1.41"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
luaify = { path = "../luaify" }
|
||||||
|
tokio = { version = "1.45.1", features = ["full"] }
|
||||||
64
crates/lb/src/channel.rs
Normal file
64
crates/lb/src/channel.rs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// use flume::{Receiver, Sender};
|
||||||
|
use luaffi::{cdef, metatype};
|
||||||
|
|
||||||
|
#[cdef]
|
||||||
|
pub struct lb_libchannel;
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_libchannel {
|
||||||
|
#[new]
|
||||||
|
extern "Lua-C" fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "Lua" fn unbounded(self) {
|
||||||
|
let (send, recv) = (__new(__ct.lb_sender), __new(__ct.lb_receiver));
|
||||||
|
self.__unbounded(send, recv);
|
||||||
|
(send, recv)
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "Lua" fn bounded(self, cap: number) {
|
||||||
|
assert(cap >= 0, "channel capacity must be nonnegative");
|
||||||
|
let (send, recv) = (__new(__ct.lb_sender), __new(__ct.lb_receiver));
|
||||||
|
self.__bounded(cap, send, recv);
|
||||||
|
(send, recv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// extern "Lua-C" fn __unbounded(&self, s: *mut lb_sender, r: *mut lb_receiver) {
|
||||||
|
// let (send, recv) = flume::unbounded();
|
||||||
|
// unsafe {
|
||||||
|
// ptr::write(s, lb_sender { send });
|
||||||
|
// ptr::write(r, lb_receiver { recv });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// extern "Lua-C" fn __bounded(&self, cap: usize, s: *mut lb_sender, r: *mut lb_receiver) {
|
||||||
|
// let (send, recv) = flume::bounded(cap);
|
||||||
|
// unsafe {
|
||||||
|
// ptr::write(s, lb_sender { send });
|
||||||
|
// ptr::write(r, lb_receiver { recv });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[cdef]
|
||||||
|
// pub struct lb_sender {
|
||||||
|
// #[opaque]
|
||||||
|
// send: Sender<c_int>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[metatype]
|
||||||
|
// impl lb_sender {
|
||||||
|
// extern "Lua" fn send(self, value: _) {
|
||||||
|
// let key = __ref(value);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[cdef]
|
||||||
|
// pub struct lb_receiver {
|
||||||
|
// #[opaque]
|
||||||
|
// recv: Receiver<c_int>,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[metatype]
|
||||||
|
// impl lb_receiver {}
|
||||||
34
crates/lb/src/fs.rs
Normal file
34
crates/lb/src/fs.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//! The `lb:fs` module provides utilities for interacting with the file system asynchronously.
|
||||||
|
//!
|
||||||
|
//! # Exports
|
||||||
|
//!
|
||||||
|
//! See [`lb_libfs`] for items exported by this module.
|
||||||
|
use luaffi::{cdef, metatype};
|
||||||
|
use std::io;
|
||||||
|
use tokio::fs;
|
||||||
|
|
||||||
|
/// Items exported by the `lb:fs` module.
|
||||||
|
///
|
||||||
|
/// This module can be obtained by calling `require` in Lua.
|
||||||
|
///
|
||||||
|
/// ```lua
|
||||||
|
/// local fs = require("lb:fs");
|
||||||
|
/// ```
|
||||||
|
#[cdef]
|
||||||
|
pub struct lb_libfs;
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_libfs {
|
||||||
|
#[new]
|
||||||
|
extern "Lua-C" fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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) -> io::Result<Vec<u8>> {
|
||||||
|
std::fs::read(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
5
crates/lb/src/lib.rs
Normal file
5
crates/lb/src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod channel;
|
||||||
|
pub mod fs;
|
||||||
|
pub mod net;
|
||||||
|
pub mod runtime;
|
||||||
|
pub mod task;
|
||||||
339
crates/lb/src/net.rs
Normal file
339
crates/lb/src/net.rs
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
//! The `lb:net` module provides an asynchronous network API for creating TCP or UDP servers and
|
||||||
|
//! clients.
|
||||||
|
//!
|
||||||
|
//! # Exports
|
||||||
|
//!
|
||||||
|
//! See [`lb_libnet`] for items exported by this module.
|
||||||
|
use derive_more::{From, FromStr};
|
||||||
|
use luaffi::{cdef, metatype};
|
||||||
|
use std::{
|
||||||
|
io,
|
||||||
|
net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||||
|
};
|
||||||
|
use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
||||||
|
|
||||||
|
/// Items exported by the `lb:net` module.
|
||||||
|
///
|
||||||
|
/// This module can be obtained by calling `require` in Lua.
|
||||||
|
///
|
||||||
|
/// ```lua
|
||||||
|
/// local net = require("lb:net");
|
||||||
|
/// ```
|
||||||
|
#[cdef]
|
||||||
|
pub struct lb_libnet;
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_libnet {
|
||||||
|
#[new]
|
||||||
|
extern "Lua-C" fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv4Addr::LOCALHOST`].
|
||||||
|
pub extern "Lua-C" fn localhost_v4(&self) -> lb_ipaddr {
|
||||||
|
lb_ipaddr(Ipv4Addr::LOCALHOST.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv6Addr::LOCALHOST`].
|
||||||
|
pub extern "Lua-C" fn localhost_v6(&self) -> lb_ipaddr {
|
||||||
|
lb_ipaddr(Ipv6Addr::LOCALHOST.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv4Addr::UNSPECIFIED`].
|
||||||
|
pub extern "Lua-C" fn unspecified_v4(&self) -> lb_ipaddr {
|
||||||
|
lb_ipaddr(Ipv4Addr::UNSPECIFIED.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv6Addr::UNSPECIFIED`].
|
||||||
|
pub extern "Lua-C" fn unspecified_v6(&self) -> lb_ipaddr {
|
||||||
|
lb_ipaddr(Ipv6Addr::UNSPECIFIED.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv4Addr::BROADCAST`].
|
||||||
|
pub extern "Lua-C" fn broadcast_v4(&self) -> lb_ipaddr {
|
||||||
|
lb_ipaddr(Ipv4Addr::BROADCAST.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [`lb_ipaddr`] from the given input.
|
||||||
|
///
|
||||||
|
/// 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) {
|
||||||
|
s.ip()
|
||||||
|
} else {
|
||||||
|
self.__parse_ipaddr(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "Lua-C" fn __parse_ipaddr(&self, s: &str) -> Result<lb_ipaddr, AddrParseError> {
|
||||||
|
s.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an [`lb_socketaddr`] from the given input.
|
||||||
|
///
|
||||||
|
/// A socket address is an IP address with a port number.
|
||||||
|
///
|
||||||
|
/// If `s` is an [`lb_socketaddr`], a copy of that value is returned. If `s` is an
|
||||||
|
/// [`lb_ipaddr`], a socket address with that IP address is returned. Otherwise, parses `s` as a
|
||||||
|
/// 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 {
|
||||||
|
if port != () {
|
||||||
|
self.__new_socketaddr(self.ipaddr(s), 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
|
||||||
|
} else {
|
||||||
|
self.__parse_socketaddr(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "Lua-C" fn __new_socketaddr(&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()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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-C" fn tcp_v4(&self) -> io::Result<lb_tcpsocket> {
|
||||||
|
TcpSocket::new_v4().map(lb_tcpsocket)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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-C" fn tcp_v6(&self) -> io::Result<lb_tcpsocket> {
|
||||||
|
TcpSocket::new_v6().map(lb_tcpsocket)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An IP address, either IPv4 or IPv6.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// This example creates an [`lb_ipaddr`] by parsing an IP address string.
|
||||||
|
///
|
||||||
|
/// ```lua
|
||||||
|
/// local net = require("lb:net");
|
||||||
|
/// local addr = net:ipaddr("127.0.0.1"); -- ipv4 loopback address
|
||||||
|
///
|
||||||
|
/// assert(addr:is_v4());
|
||||||
|
/// assert(addr:is_loopback());
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, From, FromStr)]
|
||||||
|
#[cdef]
|
||||||
|
pub struct lb_ipaddr(#[opaque] IpAddr);
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_ipaddr {
|
||||||
|
/// See [`IpAddr::is_unspecified`].
|
||||||
|
pub extern "Lua-C" fn is_unspecified(&self) -> bool {
|
||||||
|
self.0.is_unspecified()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`IpAddr::is_loopback`].
|
||||||
|
pub extern "Lua-C" fn is_loopback(&self) -> bool {
|
||||||
|
self.0.is_loopback()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`IpAddr::is_multicast`].
|
||||||
|
pub extern "Lua-C" fn is_multicast(&self) -> bool {
|
||||||
|
self.0.is_multicast()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the string `"v4"` if this is an IPv4 address or `"v6"` if this is an IPv6 address.
|
||||||
|
pub extern "Lua" fn family(&self) -> string {
|
||||||
|
if self.is_v6() { "v6" } else { "v4" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this is an IPv4 address.
|
||||||
|
pub extern "Lua-C" fn is_v4(&self) -> bool {
|
||||||
|
self.0.is_ipv4()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv4Addr::is_private`].
|
||||||
|
pub extern "Lua-C" fn is_v4_private(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
IpAddr::V4(v4) => v4.is_private(),
|
||||||
|
IpAddr::V6(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv4Addr::is_link_local`].
|
||||||
|
pub extern "Lua-C" fn is_v4_link_local(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
IpAddr::V4(v4) => v4.is_link_local(),
|
||||||
|
IpAddr::V6(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv4Addr::is_broadcast`].
|
||||||
|
pub extern "Lua-C" fn is_v4_broadcast(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
IpAddr::V4(v4) => v4.is_broadcast(),
|
||||||
|
IpAddr::V6(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv4Addr::is_documentation`].
|
||||||
|
pub extern "Lua-C" fn is_v4_documentation(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
IpAddr::V4(v4) => v4.is_documentation(),
|
||||||
|
IpAddr::V6(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if this is an IPv6 address.
|
||||||
|
pub extern "Lua-C" fn is_v6(&self) -> bool {
|
||||||
|
self.0.is_ipv6()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv6Addr::is_unique_local`].
|
||||||
|
pub extern "Lua-C" fn is_v6_unique_local(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
IpAddr::V4(_) => false,
|
||||||
|
IpAddr::V6(v6) => v6.is_unique_local(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv6Addr::is_unicast_link_local`].
|
||||||
|
pub extern "Lua-C" fn is_v6_unicast_link_local(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
IpAddr::V4(_) => false,
|
||||||
|
IpAddr::V6(v6) => v6.is_unicast_link_local(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv4Addr::to_ipv6_compatible`].
|
||||||
|
pub extern "Lua-C" fn to_v6_compat(&self) -> Self {
|
||||||
|
match self.0 {
|
||||||
|
IpAddr::V4(v4) => Self(v4.to_ipv6_compatible().into()),
|
||||||
|
IpAddr::V6(_) => *self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Ipv4Addr::to_ipv6_mapped`].
|
||||||
|
pub extern "Lua-C" fn to_v6_mapped(&self) -> Self {
|
||||||
|
match self.0 {
|
||||||
|
IpAddr::V4(v4) => Self(v4.to_ipv6_mapped().into()),
|
||||||
|
IpAddr::V6(_) => *self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`IpAddr::to_canonical`].
|
||||||
|
pub extern "Lua-C" fn canonical(&self) -> Self {
|
||||||
|
self.0.to_canonical().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the string representation of this address.
|
||||||
|
#[tostring]
|
||||||
|
pub extern "Lua-C" fn tostring(&self) -> String {
|
||||||
|
self.0.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A 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);
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_socketaddr {
|
||||||
|
/// Returns the IP part of this address.
|
||||||
|
pub extern "Lua-C" fn ip(&self) -> lb_ipaddr {
|
||||||
|
self.0.ip().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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());
|
||||||
|
} else {
|
||||||
|
self.__set_ip_parse(s);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "Lua-C" fn __set_ip(&mut self, ip: &lb_ipaddr) {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the port part of this address.
|
||||||
|
pub extern "Lua-C" fn port(&self) -> u16 {
|
||||||
|
self.0.port()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the port part of this address.
|
||||||
|
pub extern "Lua" fn set_port(&mut self, port: number) -> &mut Self {
|
||||||
|
self.__set_port(port);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "Lua-C" fn __set_port(&mut self, port: u16) {
|
||||||
|
self.0.set_port(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the string representation of this address.
|
||||||
|
#[tostring]
|
||||||
|
pub extern "Lua-C" fn tostring(&self) -> String {
|
||||||
|
self.0.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A TCP socket which has not yet been converted to a [`lb_tcpstream`] or [`lb_tcplistener`].
|
||||||
|
#[derive(Debug, From)]
|
||||||
|
#[cdef]
|
||||||
|
pub struct lb_tcpsocket(#[opaque] TcpSocket);
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_tcpsocket {}
|
||||||
|
|
||||||
|
#[derive(Debug, From)]
|
||||||
|
#[cdef]
|
||||||
|
pub struct lb_tcpstream(#[opaque] TcpStream);
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_tcpstream {}
|
||||||
|
|
||||||
|
#[derive(Debug, From)]
|
||||||
|
#[cdef]
|
||||||
|
pub struct lb_tcplistener(#[opaque] TcpListener);
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_tcplistener {}
|
||||||
5
crates/lb/src/runtime.lua
Normal file
5
crates/lb/src/runtime.lua
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
local task = require("lb:task")
|
||||||
|
|
||||||
|
function spawn(f, ...)
|
||||||
|
return task:spawn(f, ...)
|
||||||
|
end
|
||||||
85
crates/lb/src/runtime.rs
Normal file
85
crates/lb/src/runtime.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
use crate::{channel::lb_libchannel, fs::lb_libfs, net::lb_libnet, task::lb_libtask};
|
||||||
|
use derive_more::{Deref, DerefMut};
|
||||||
|
use luaffi::{Registry, Type};
|
||||||
|
use luajit::{Chunk, State};
|
||||||
|
use std::fmt::Display;
|
||||||
|
use tokio::{
|
||||||
|
task::{JoinHandle, LocalSet, futures::TaskLocalFuture, spawn_local},
|
||||||
|
task_local,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Builder {
|
||||||
|
registry: Registry,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
|
||||||
|
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 build(&self) -> luajit::Result<Runtime> {
|
||||||
|
Ok(Runtime {
|
||||||
|
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
|
||||||
|
},
|
||||||
|
tasks: LocalSet::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deref, DerefMut)]
|
||||||
|
pub struct Runtime {
|
||||||
|
#[deref]
|
||||||
|
#[deref_mut]
|
||||||
|
state: State,
|
||||||
|
tasks: LocalSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
task_local! {
|
||||||
|
static STATE: State;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Runtime {
|
||||||
|
pub fn spawn<T: 'static>(
|
||||||
|
&self,
|
||||||
|
f: impl AsyncFnOnce(&mut State) -> T + 'static,
|
||||||
|
) -> JoinHandle<T> {
|
||||||
|
self.tasks
|
||||||
|
.spawn_local(async move { f(&mut STATE.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>;
|
||||||
|
|
||||||
|
fn into_future(self) -> Self::IntoFuture {
|
||||||
|
STATE.scope(self.state, self.tasks)
|
||||||
|
}
|
||||||
|
}
|
||||||
46
crates/lb/src/task.rs
Normal file
46
crates/lb/src/task.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
use crate::runtime::spawn;
|
||||||
|
use luaffi::{cdef, metatype};
|
||||||
|
use std::{ffi::c_int, process};
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
|
#[cdef]
|
||||||
|
pub struct lb_libtask;
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_libtask {
|
||||||
|
#[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
|
||||||
|
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) };
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
println!("{s:?}");
|
||||||
|
});
|
||||||
|
|
||||||
|
lb_task { handle }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cdef]
|
||||||
|
pub struct lb_task {
|
||||||
|
#[opaque]
|
||||||
|
handle: JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[metatype]
|
||||||
|
impl lb_task {}
|
||||||
32
crates/lb/tests/net.rs
Normal file
32
crates/lb/tests/net.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
35
crates/lb/tests/task.rs
Normal file
35
crates/lb/tests/task.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
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,10 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "lb_core"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
luaffi = { version = "0.1.0", path = "../luaffi" }
|
|
||||||
luajit = { version = "0.1.0", path = "../luajit" }
|
|
||||||
owo-colors = "4.2.1"
|
|
||||||
tokio = { version = "1.45.1", features = ["full"] }
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
use luaffi::{cdef, metatype};
|
|
||||||
use luajit::State;
|
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
use std::{cell::RefCell, fmt, process};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct GlobalState(State);
|
|
||||||
|
|
||||||
impl GlobalState {
|
|
||||||
thread_local! {
|
|
||||||
static STATE: RefCell<Option<GlobalState>> = RefCell::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(state: State) -> Option<State> {
|
|
||||||
Self::STATE.with_borrow_mut(|s| s.replace(Self(state)).map(|s| s.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_current<T>(f: impl FnOnce(&State) -> T) -> T {
|
|
||||||
Self::STATE.with_borrow(|s| f(&s.as_ref().expect("lua state not initialised").0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_current_mut<T>(f: impl FnOnce(&mut State) -> T) -> T {
|
|
||||||
Self::STATE.with_borrow_mut(|s| f(&mut s.as_mut().expect("lua state not initialised").0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_thread() -> State {
|
|
||||||
Self::with_current(|s| s.new_thread())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uncaught_error(err: luajit::Error) {
|
|
||||||
let mut err = PrettyError::from(err);
|
|
||||||
if let Some(task) = tokio::task::try_id() {
|
|
||||||
err.prepend(format_args!("uncaught error in task {task}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
eprintln!("{err}");
|
|
||||||
process::abort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PrettyError {
|
|
||||||
msg: String,
|
|
||||||
trace: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrettyError {
|
|
||||||
pub fn new(msg: impl fmt::Display) -> Self {
|
|
||||||
Self {
|
|
||||||
msg: format!("{msg}"),
|
|
||||||
trace: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_trace(mut self, trace: impl fmt::Display) -> Self {
|
|
||||||
self.trace = Some(format!("{trace}"));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepend(&mut self, msg: impl fmt::Display) -> &mut Self {
|
|
||||||
if self.msg.is_empty() {
|
|
||||||
self.msg = format!("{msg}");
|
|
||||||
} else {
|
|
||||||
self.msg = format!("{msg}:\n{}", self.msg);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<luajit::Error> for PrettyError {
|
|
||||||
fn from(value: luajit::Error) -> Self {
|
|
||||||
match value {
|
|
||||||
luajit::Error::Resume { msg, trace } => Self::new(msg).with_trace(trace),
|
|
||||||
err => Self::new(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for PrettyError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self.trace {
|
|
||||||
Some(ref trace) => write!(f, "{}\n{trace}", self.msg.red()),
|
|
||||||
None => write!(f, "{}", self.msg.red()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_core;
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_core {}
|
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "luaffi"
|
name = "luaffi"
|
||||||
version = "0.1.0"
|
version.workspace = true
|
||||||
edition = "2024"
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bstr = "1.12.0"
|
bstr = "1.12.0"
|
||||||
luaffi_impl = { version = "0.1.0", path = "../luaffi_impl" }
|
luaffi_impl = { path = "../luaffi_impl" }
|
||||||
luaify = { version = "0.1.0", path = "../luaify" }
|
luaify = { path = "../luaify" }
|
||||||
rustc-hash = "2.1.1"
|
rustc-hash = "2.1.1"
|
||||||
simdutf8 = "0.1.5"
|
simdutf8 = "0.1.5"
|
||||||
static_assertions = "1.1.0"
|
|
||||||
|
|||||||
@@ -1,32 +1,49 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
__internal::{display, type_id},
|
__internal::{display, type_id},
|
||||||
CDef, CDefBuilder, Metatype, MetatypeBuilder, ToFfi, Type, TypeBuilder,
|
Cdef, CdefBuilder, FfiReturnConvention, IntoFfi, Metatype, MetatypeBuilder, Type, TypeBuilder,
|
||||||
|
TypeType, UnsafeExternCFn,
|
||||||
};
|
};
|
||||||
use luaify::luaify;
|
use luaify::luaify;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
|
marker::PhantomPinned,
|
||||||
mem,
|
mem,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
ptr,
|
ptr,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// guardrail bytes prepended to all lua_future values in the header part to hopefully try to prevent
|
||||||
|
// misinterpreting other kinds of cdata as a lua_future if the lua user code yields a cdata that is
|
||||||
|
// not a lua_future for some odd reason.
|
||||||
|
type Signature = u64;
|
||||||
|
const SIGNATURE: Signature = Signature::from_ne_bytes(*b"\x00lb_poll");
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub struct lua_future<F: Future<Output: ToFfi>> {
|
pub struct lua_future<F: Future<Output: IntoFfi>> {
|
||||||
//
|
//
|
||||||
// SAFETY: .poll MUST be the first field. It is only to be called by the Rust async runtime to
|
// SAFETY: LuaJIT guarantees that cdata payloads, which are GC-managed, are never relocated
|
||||||
// advance the future without knowing its type (see `lua_pollable` below).
|
// (i.e. pinned). We can safely assume that we are pinned and poll the future inside this
|
||||||
|
// wrapper. We use this to our advantage by storing the future value directly inside the cdata
|
||||||
|
// payload instead of boxing the future and introducing indirection.
|
||||||
|
//
|
||||||
|
// https://github.com/LuaJIT/LuaJIT/issues/1167#issuecomment-1968047229
|
||||||
|
//
|
||||||
|
// .sig and .poll fields MUST come first. .poll is only to be called by the Rust async runtime
|
||||||
|
// to advance the future without knowing its type (see `lua_pollable` below).
|
||||||
//
|
//
|
||||||
// This assumes that the crate containing the async runtime and the crate containing the future
|
// This assumes that the crate containing the async runtime and the crate containing the future
|
||||||
// type are ABI-compatible (compiled by the same compiler with the same target into the same binary).
|
// type are ABI-compatible (compiled by the same compiler with the same target into the same
|
||||||
// This is always the case for luby because all modules are statically linked into one binary.
|
// binary). This is always the case for luby because all modules are statically linked into one
|
||||||
|
// binary.
|
||||||
//
|
//
|
||||||
// .poll and .state are opaque to Lua itself.
|
// only .take and .drop are visible to Lua itself; other fields are opaque.
|
||||||
//
|
//
|
||||||
poll: fn(&mut Self, cx: &mut Context) -> Poll<()>,
|
sig: Signature,
|
||||||
|
poll: fn(Pin<&mut Self>, cx: &mut Context) -> Poll<()>,
|
||||||
state: State<F>,
|
state: State<F>,
|
||||||
take: unsafe extern "C" fn(&mut Self) -> <F::Output as ToFfi>::To,
|
take: unsafe extern "C" fn(&mut Self) -> <F::Output as IntoFfi>::Into,
|
||||||
drop: unsafe extern "C" fn(&mut Self),
|
drop: unsafe extern "C" fn(&mut Self),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,15 +51,17 @@ pub struct lua_future<F: Future<Output: ToFfi>> {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub struct lua_pollable {
|
pub struct lua_pollable {
|
||||||
//
|
//
|
||||||
// SAFETY: The only way to obtain a reference to a `lua_pollable` is by returning a `lua_future<T>`
|
// SAFETY: The only way to obtain a reference to a `lua_pollable` is by returning a
|
||||||
// from Rust to Lua, which LuaJIT boxes into cdata and `coroutine.yield`'s back to Rust, then
|
// `lua_future<T>` from Rust to Lua, which LuaJIT boxes into cdata and `coroutine.yield`'s back
|
||||||
// casting the yielded pointer value to `*mut lua_pollable`.
|
// to Rust, then casting the yielded pointer value to `*mut lua_pollable`.
|
||||||
//
|
//
|
||||||
// This is the type-erased "header" part of a `lua_future<T>` which allows the async runtime to
|
// This is the type-erased "header" part of a `lua_future<T>` which allows the async runtime to
|
||||||
// poll the future without knowing its concrete type (essentially dynamic dispatch). It has the
|
// poll the future without knowing its concrete type (essentially dynamic dispatch). It has the
|
||||||
// same layout as `lua_future<T>` without the state part.
|
// same layout as `lua_future<T>` without the state part.
|
||||||
//
|
//
|
||||||
poll: fn(&mut Self, cx: &mut Context) -> Poll<()>,
|
sig: Signature,
|
||||||
|
poll: fn(Pin<&mut Self>, cx: &mut Context) -> Poll<()>,
|
||||||
|
_phantom: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State<F: Future> {
|
enum State<F: Future> {
|
||||||
@@ -51,9 +70,10 @@ enum State<F: Future> {
|
|||||||
Complete,
|
Complete,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Future<Output: ToFfi>> lua_future<F> {
|
impl<F: Future<Output: IntoFfi>> lua_future<F> {
|
||||||
pub fn new(fut: F) -> Self {
|
pub fn new(fut: F) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
sig: SIGNATURE,
|
||||||
poll: Self::poll,
|
poll: Self::poll,
|
||||||
state: State::Pending(fut),
|
state: State::Pending(fut),
|
||||||
take: Self::take,
|
take: Self::take,
|
||||||
@@ -61,39 +81,29 @@ impl<F: Future<Output: ToFfi>> lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll(&mut self, cx: &mut Context) -> Poll<()> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
|
||||||
//
|
// SAFETY: we do not ever move the future here, so this is safe
|
||||||
// SAFETY: LuaJIT guarantees that cdata payloads, which are GC-managed, are never
|
let this = unsafe { Pin::into_inner_unchecked(self) };
|
||||||
// relocated (i.e. pinned). We can safely assume that we are pinned and poll the future.
|
match this.state {
|
||||||
//
|
|
||||||
// We use this to our advantage by storing the future value directly inside the cdata
|
|
||||||
// payload instead of boxing the future and introducing indirection.
|
|
||||||
//
|
|
||||||
// https://github.com/LuaJIT/LuaJIT/issues/1167#issuecomment-1968047229
|
|
||||||
//
|
|
||||||
match self.state {
|
|
||||||
State::Pending(ref mut fut) => match unsafe { Pin::new_unchecked(fut) }.poll(cx) {
|
State::Pending(ref mut fut) => match unsafe { Pin::new_unchecked(fut) }.poll(cx) {
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
Poll::Ready(value) => Poll::Ready(self.state = State::Fulfilled(value)),
|
Poll::Ready(value) => Poll::Ready(this.state = State::Fulfilled(value)), // drop the future in-place
|
||||||
},
|
},
|
||||||
State::Fulfilled(_) => Poll::Ready(()),
|
State::Fulfilled(_) => Poll::Ready(()),
|
||||||
State::Complete => unreachable!("lua_future::poll() called on completed future"),
|
State::Complete => unreachable!("lua_future::poll() called on a completed future"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn take(&mut self) -> <F::Output as ToFfi>::To {
|
unsafe extern "C" fn take(&mut self) -> <F::Output as IntoFfi>::Into {
|
||||||
// `fut:__take()` returns the fulfilled value by-value because it is the lowest common
|
// `fut:__take()` returns the fulfilled value by-value (not by out-param) because if we
|
||||||
// denominator for supported return conventions (all `ToFfi` impls support return by-value;
|
// preallocate a cdata for the out-param and the thread for some reason gets dropped and
|
||||||
// primitives e.g. don't support return by out-param because they get boxed in cdata).
|
// never resumed, the GC could call the destructor on an uninitialised cdata.
|
||||||
//
|
|
||||||
// Plus, if we preallocate a cdata for out-param and the thread for some reason gets dropped
|
|
||||||
// and never resumed, GC could call the destructor on an uninitialised cdata.
|
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Fulfilled(_) => match mem::replace(&mut self.state, State::Complete) {
|
State::Fulfilled(_) => match mem::replace(&mut self.state, State::Complete) {
|
||||||
State::Fulfilled(value) => value.convert(),
|
State::Fulfilled(value) => value.convert(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
State::Pending(_) => panic!("lua_future::take() called on pending future"),
|
State::Pending(_) => panic!("lua_future::take() called on a pending future"),
|
||||||
State::Complete => panic!("lua_future::take() called twice"),
|
State::Complete => panic!("lua_future::take() called twice"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,22 +113,35 @@ impl<F: Future<Output: ToFfi>> lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl lua_pollable {
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
// TODO: signature check can currently read out-of-bounds if lua code for some reason yields
|
||||||
|
// a cdata of size less than 8 bytes that is not a lua_future. there is no easy way to fix
|
||||||
|
// afaik this because there is no way to find the size of a cdata payload using the C API.
|
||||||
|
self.sig == SIGNATURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Future for lua_pollable {
|
impl Future for lua_pollable {
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
// SAFETY: see comment above in `lua_future::poll()`
|
assert!(self.is_valid(), "invalid lua_pollable value");
|
||||||
(self.poll)(Pin::into_inner(self), cx)
|
(self.poll)(self, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: ToFfi> + 'static> Type for lua_future<F> {
|
unsafe impl<F: Future<Output: IntoFfi> + 'static> Type for lua_future<F> {
|
||||||
fn name() -> impl Display {
|
fn name() -> impl Display {
|
||||||
display!("future__{:x}", type_id::<F>())
|
display!("future__{:x}", type_id::<F>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ty() -> TypeType {
|
||||||
|
TypeType::Aggregate
|
||||||
|
}
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
display!("struct future__{:x} {name}", type_id::<F>())
|
display!("struct {} {name}", Self::name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(s: &mut TypeBuilder) {
|
fn build(s: &mut TypeBuilder) {
|
||||||
@@ -126,15 +149,15 @@ unsafe impl<F: Future<Output: ToFfi> + 'static> Type for lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: ToFfi> + 'static> CDef for lua_future<F> {
|
unsafe impl<F: Future<Output: IntoFfi> + 'static> Cdef for lua_future<F> {
|
||||||
fn build(s: &mut CDefBuilder) {
|
fn build(s: &mut CdefBuilder) {
|
||||||
s.field_opaque(mem::offset_of!(Self, take))
|
s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state
|
||||||
.field::<unsafe extern "C" fn(*mut Self) -> <F::Output as ToFfi>::To>("__take")
|
.field::<UnsafeExternCFn<(&mut Self,), <F::Output as IntoFfi>::Into>>("__take")
|
||||||
.field::<unsafe extern "C" fn(*mut Self)>("__drop");
|
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: ToFfi> + 'static> Metatype for lua_future<F> {
|
unsafe impl<F: Future<Output: IntoFfi> + 'static> Metatype for lua_future<F> {
|
||||||
type Target = Self;
|
type Target = Self;
|
||||||
|
|
||||||
fn build(s: &mut MetatypeBuilder) {
|
fn build(s: &mut MetatypeBuilder) {
|
||||||
@@ -142,30 +165,45 @@ unsafe impl<F: Future<Output: ToFfi> + 'static> Metatype for lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: ToFfi> + 'static> ToFfi for lua_future<F> {
|
unsafe impl<F: Future<Output: IntoFfi> + 'static> IntoFfi for lua_future<F> {
|
||||||
type To = lua_future<F>;
|
type Into = lua_future<F>;
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
fn convention() -> FfiReturnConvention {
|
||||||
|
// futures are always returned by-value due to rust type inference limitations
|
||||||
|
FfiReturnConvention::ByValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Into {
|
||||||
self
|
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 {
|
fn postlude(ret: &str) -> impl Display {
|
||||||
// When returning a future from Rust to Lua, yield it immediately to the runtime which will
|
// 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
|
// poll it to completion in the background, then take the fulfilled value once the thread
|
||||||
// gets resumed. Lua user code should never to worry about awaiting futures.
|
// gets resumed. Lua user code should never to worry about awaiting futures.
|
||||||
//
|
//
|
||||||
// Once the current thread gets resumed and we take the future's fulfilled value, we clear
|
// Once the current thread gets resumed and we take the future's fulfilled value, we clear
|
||||||
// the finaliser on the future and forget it (there is nothing to call drop on).
|
// the finaliser on the future and forget it (there is nothing to drop once the value is
|
||||||
|
// taken).
|
||||||
//
|
//
|
||||||
// `coroutine.yield` is cached as `yield` and `ffi.gc` as `gc` in locals (see lib.rs)
|
// `coroutine.yield` is cached as `__yield` and `ffi.gc` as `__gc` in locals (see lib.rs)
|
||||||
display!(
|
display!(
|
||||||
"yield({ret}); {ret} = gc({ret}, nil):__take(); {}",
|
"__yield({ret}); {ret} = __gc({ret}, nil):__take(); {}",
|
||||||
<F::Output as ToFfi>::postlude(ret)
|
<F::Output as IntoFfi>::postlude(ret)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: IntoFuture<Output: ToFfi>> From<F> for lua_future<F::IntoFuture> {
|
impl<F: IntoFuture<Output: IntoFfi>> From<F> for lua_future<F::IntoFuture> {
|
||||||
fn from(value: F) -> Self {
|
fn from(value: F) -> Self {
|
||||||
Self::new(value.into_future())
|
Self::new(value.into_future())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,50 @@
|
|||||||
pub use luaify::*;
|
pub use luaify::*;
|
||||||
use rustc_hash::FxHasher;
|
use rustc_hash::FxHasher;
|
||||||
pub use static_assertions::*;
|
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub mod stub_types {
|
||||||
|
pub struct any;
|
||||||
|
pub struct nil;
|
||||||
|
pub struct boolean;
|
||||||
|
pub struct lightuserdata;
|
||||||
|
pub struct number;
|
||||||
|
pub struct integer;
|
||||||
|
pub struct string;
|
||||||
|
pub struct table;
|
||||||
|
pub struct function;
|
||||||
|
pub struct userdata;
|
||||||
|
pub struct thread;
|
||||||
|
pub struct cdata;
|
||||||
|
pub struct variadic;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn type_id<T: 'static>() -> u64 {
|
pub fn type_id<T: 'static>() -> u64 {
|
||||||
let mut hash = FxHasher::default();
|
let mut hash = FxHasher::default();
|
||||||
TypeId::of::<T>().hash(&mut hash);
|
TypeId::of::<T>().hash(&mut hash);
|
||||||
hash.finish()
|
hash.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! export {
|
||||||
|
($($fn:expr),+ $(,)?) => {
|
||||||
|
// this ensures ffi function symbol exports are actually present in the resulting binary,
|
||||||
|
// otherwise they may get dead code-eliminated before it reaches the linker
|
||||||
|
#[used]
|
||||||
|
static __FFI_EXPORTS: &[fn()] = unsafe {
|
||||||
|
&[$(::std::mem::transmute($fn as *const ())),*]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! display {
|
macro_rules! display {
|
||||||
($($fmt:expr),+) => {{ crate::__internal::disp(move |f| write!(f, $($fmt),+)) }};
|
($($fmt:expr),+) => {{ crate::__internal::disp(move |f| write!(f, $($fmt),+)) }};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use display;
|
pub(crate) use {display, export};
|
||||||
|
|
||||||
pub fn disp(f: impl Fn(&mut Formatter) -> fmt::Result) -> impl Display {
|
pub fn disp(f: impl Fn(&mut Formatter) -> fmt::Result) -> impl Display {
|
||||||
struct Disp<F: Fn(&mut Formatter) -> fmt::Result>(F);
|
struct Disp<F: Fn(&mut Formatter) -> fmt::Result>(F);
|
||||||
|
|||||||
24
crates/luaffi/src/lib.lua
Normal file
24
crates/luaffi/src/lib.lua
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
local LUA_REFNIL = -1 -- lib_aux.c
|
||||||
|
local FREELIST_REF = 0
|
||||||
|
|
||||||
|
local function __ref(value, t)
|
||||||
|
if value == nil then return LUA_REFNIL end
|
||||||
|
if t == nil then t = __registry end
|
||||||
|
local ref = t[FREELIST_REF]
|
||||||
|
if ref ~= nil and ref ~= 0 then
|
||||||
|
t[FREELIST_REF] = t[ref]
|
||||||
|
else
|
||||||
|
ref = #t + 1
|
||||||
|
end
|
||||||
|
t[ref] = value
|
||||||
|
return ref
|
||||||
|
end
|
||||||
|
|
||||||
|
local function __unref(ref, t)
|
||||||
|
if ref < 0 then return nil end
|
||||||
|
if t == nil then t = __registry end
|
||||||
|
local value = t[ref]
|
||||||
|
t[ref] = t[FREELIST_REF]
|
||||||
|
t[FREELIST_REF] = ref
|
||||||
|
return value
|
||||||
|
end
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,80 +0,0 @@
|
|||||||
use crate::{CDef, CDefBuilder, FromFfi, ToFfi, Type, TypeBuilder, display};
|
|
||||||
use std::{ffi::c_int, fmt::Display, ptr};
|
|
||||||
|
|
||||||
#[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 cdecl(name: impl Display) -> impl Display {
|
|
||||||
display!("struct option__{} {name}", T::name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build(b: &mut TypeBuilder) {
|
|
||||||
b.include::<T>().cdef::<Self>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Type> CDef for lua_option<T> {
|
|
||||||
fn build(b: &mut CDefBuilder) {
|
|
||||||
b.field::<c_int>("__tag").field::<T>("__value");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: FromFfi> FromFfi for Option<T> {
|
|
||||||
type From = *mut Self::FromValue; // pass by-ref
|
|
||||||
type FromValue = lua_option<T::FromValue>;
|
|
||||||
|
|
||||||
const ARG_KEEPALIVE: bool = T::ARG_KEEPALIVE;
|
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
|
||||||
let ct = Self::FromValue::name();
|
|
||||||
display!(
|
|
||||||
"if {arg} == nil then {arg} = {ct}(); else {}{arg} = {ct}(1, {arg}); end; ",
|
|
||||||
T::prelude(arg)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
|
||||||
debug_assert!(
|
|
||||||
!from.is_null(),
|
|
||||||
"Option<T>::convert() called on a null lua_option<T>"
|
|
||||||
);
|
|
||||||
|
|
||||||
Self::convert_value(unsafe { ptr::replace(from, lua_option::None) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
|
||||||
match from {
|
|
||||||
lua_option::Some(value) => Some(T::convert_value(value)),
|
|
||||||
lua_option::None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: ToFfi> ToFfi for Option<T> {
|
|
||||||
type To = lua_option<T::To>;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
|
||||||
match self {
|
|
||||||
Some(value) => lua_option::Some(value.convert()),
|
|
||||||
None => lua_option::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
// if we don't have a value, return nil. otherwise copy out the inner value immediately,
|
|
||||||
// forget the option cdata, then call postlude on the inner value.
|
|
||||||
display!(
|
|
||||||
"if {ret}.__tag == 0 then {ret} = nil; else {ret} = {ret}.__value; {}end; ",
|
|
||||||
T::postlude(ret)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
94
crates/luaffi/src/result.rs
Normal file
94
crates/luaffi/src/result.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
use crate::{
|
||||||
|
__internal::{disp, display},
|
||||||
|
Cdef, CdefBuilder, IntoFfi, KEEP_FN, Type, TypeBuilder, TypeType,
|
||||||
|
string::{DROP_BUFFER_FN, lua_buffer},
|
||||||
|
};
|
||||||
|
use std::{ffi::c_int, fmt::Display};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum lua_result<T> {
|
||||||
|
Err(lua_buffer), // __tag = 0
|
||||||
|
Ok(T), // __tag = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: Type> Type for lua_result<T> {
|
||||||
|
fn name() -> impl Display {
|
||||||
|
display!("result__{}", 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_result<T> {
|
||||||
|
fn build(b: &mut CdefBuilder) {
|
||||||
|
b.field::<c_int>("__tag").inner_union(|b| {
|
||||||
|
(T::ty() != TypeType::Void).then(|| b.field::<T>("__value"));
|
||||||
|
b.field::<lua_buffer>("__err");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: IntoFfi, E: Display> IntoFfi for Result<T, E> {
|
||||||
|
type Into = lua_result<T::Into>;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Into {
|
||||||
|
match self {
|
||||||
|
Ok(value) => lua_result::Ok(T::convert(value)),
|
||||||
|
Err(err) => lua_result::Err(lua_buffer::new(err.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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| {
|
||||||
|
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 => {
|
||||||
|
// 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" 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} = {ret}; {ret} = {ret}.__value; ")?;
|
||||||
|
write!(f, "do {}end; ", T::postlude(ret))?;
|
||||||
|
write!(f, "__C.{KEEP_FN}(__{ret}); ")?; // keep original result alive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"else \
|
||||||
|
local {ret}_err = __intern({ret}.__err.__ptr, {ret}.__err.__len); \
|
||||||
|
__C.{DROP_BUFFER_FN}({ret}.__err); \
|
||||||
|
return error({ret}_err); \
|
||||||
|
end; "
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,89 +1,283 @@
|
|||||||
use crate::{__internal::disp, FromFfi, IS_UTF8_FN, Type};
|
use crate::{
|
||||||
|
__internal::{disp, display, export},
|
||||||
|
FromFfi, IntoFfi,
|
||||||
|
};
|
||||||
|
use bstr::{BStr, BString};
|
||||||
use luaffi_impl::{cdef, metatype};
|
use luaffi_impl::{cdef, metatype};
|
||||||
use std::{fmt, ptr, slice};
|
use std::{fmt::Display, mem::ManuallyDrop, ptr, slice};
|
||||||
|
|
||||||
|
pub(crate) const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
||||||
|
pub(crate) const DROP_BUFFER_FN: &str = "luaffi_drop_buffer";
|
||||||
|
|
||||||
|
#[unsafe(export_name = "luaffi_is_utf8")]
|
||||||
|
unsafe extern "C" fn __is_utf8(ptr: *const u8, len: usize) -> bool {
|
||||||
|
debug_assert!(!ptr.is_null());
|
||||||
|
simdutf8::basic::from_utf8(unsafe { slice::from_raw_parts(ptr, len) }).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(export_name = "luaffi_drop_buffer")]
|
||||||
|
unsafe extern "C" fn __drop_buffer(buf: *mut lua_buffer) {
|
||||||
|
debug_assert!(!buf.is_null());
|
||||||
|
debug_assert!(!unsafe { (*buf).__ptr.is_null() });
|
||||||
|
drop(unsafe { Vec::from_raw_parts((*buf).__ptr, (*buf).__len, (*buf).__cap) })
|
||||||
|
}
|
||||||
|
|
||||||
|
export![__is_utf8, __drop_buffer];
|
||||||
|
|
||||||
#[cdef]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cdef]
|
||||||
pub struct lua_buf {
|
pub struct lua_buf {
|
||||||
__ptr: *mut u8,
|
__ptr: *const u8,
|
||||||
__len: usize,
|
__len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[metatype]
|
#[metatype]
|
||||||
impl lua_buf {}
|
impl lua_buf {
|
||||||
|
// this takes a slice and decomposes it into its raw parts. caller should ensure the result is
|
||||||
|
// used only as long as the original buffer is still alive.
|
||||||
|
pub(crate) fn new(s: &[u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
__ptr: s.as_ptr(),
|
||||||
|
__len: s.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl FromFfi for *const [u8] {
|
pub(crate) fn null() -> Self {
|
||||||
type From = *const Self::FromValue; // pass by-ref
|
Self {
|
||||||
type FromValue = lua_buf;
|
__ptr: ptr::null(),
|
||||||
|
__len: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ARG_KEEPALIVE: bool = true;
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cdef]
|
||||||
|
pub struct lua_buffer {
|
||||||
|
__ptr: *mut u8,
|
||||||
|
__len: usize,
|
||||||
|
__cap: usize,
|
||||||
|
}
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl fmt::Display {
|
#[metatype]
|
||||||
|
impl lua_buffer {
|
||||||
|
// this takes ownership of the Vec and decomposes it into its raw parts. the result must be
|
||||||
|
// dropped by `__drop_buffer` (see [`DROP_BUFFER_FN`]).
|
||||||
|
pub(crate) fn new(s: impl Into<Vec<u8>>) -> Self {
|
||||||
|
let s = s.into();
|
||||||
|
Self {
|
||||||
|
__cap: s.capacity(),
|
||||||
|
__len: s.len(),
|
||||||
|
__ptr: ManuallyDrop::new(s).as_mut_ptr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn null() -> Self {
|
||||||
|
Self {
|
||||||
|
__ptr: ptr::null_mut(),
|
||||||
|
__len: 0,
|
||||||
|
__cap: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<'s> FromFfi for &'s [u8] {
|
||||||
|
type From = Option<&'s lua_buf>;
|
||||||
|
|
||||||
|
fn require_keepalive() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prelude(arg: &str) -> impl Display {
|
||||||
// this converts string arguments to a `lua_buf` with a pointer to the string and its length
|
// this converts string arguments to a `lua_buf` with a pointer to the string and its length
|
||||||
disp(move |f| {
|
disp(move |f| {
|
||||||
let ct = lua_buf::name();
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
r#"if {arg} ~= nil then assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
||||||
)?;
|
)?;
|
||||||
write!(f, "{arg} = {ct}({arg}, #{arg}); end; ")
|
// SAFETY: the lua_buf is only valid for as long as the string is alive. we've ensured
|
||||||
|
// that it is alive for at least the duration of the ffi call via `require_keepalive()`.
|
||||||
|
write!(f, "{arg} = __new(__ct.lua_buf, {arg}, #{arg}); end; ")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
if from.is_null() {
|
// SAFETY: we already checked that the string is nonnull from the lua side
|
||||||
ptr::slice_from_raw_parts(ptr::null(), 0)
|
debug_assert!(from.is_some());
|
||||||
} else {
|
let from = unsafe { from.unwrap_unchecked() };
|
||||||
// SAFETY: this is safe because lua_buf is copyable
|
debug_assert!(!from.__ptr.is_null());
|
||||||
unsafe { Self::convert_value(*from) }
|
unsafe { slice::from_raw_parts(from.__ptr, from.__len) }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
|
||||||
ptr::slice_from_raw_parts(from.__ptr, from.__len)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FromFfi for &str {
|
unsafe impl<'s> FromFfi for &'s str {
|
||||||
type From = *const Self::FromValue; // pass by-ref
|
type From = Option<&'s lua_buf>;
|
||||||
type FromValue = lua_buf;
|
|
||||||
|
|
||||||
const ARG_KEEPALIVE: bool = true;
|
fn require_keepalive() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl fmt::Display {
|
fn prelude(arg: &str) -> impl Display {
|
||||||
|
// this converts string arguments to a `lua_buf` with a pointer to the string and its length
|
||||||
|
// and ensures that the string is valid utf8
|
||||||
disp(move |f| {
|
disp(move |f| {
|
||||||
let ct = lua_buf::name();
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
||||||
)?;
|
)?;
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
r#"assert(C.{IS_UTF8_FN}({arg}, #{arg}), "argument '{arg}' must be a valid utf8 string"); "#
|
r#"assert(__C.{IS_UTF8_FN}({arg}, #{arg}), "argument '{arg}' must be a valid utf-8 string"); "#
|
||||||
)?;
|
)?;
|
||||||
write!(f, "{arg} = {ct}({arg}, #{arg}); ")
|
write!(f, "{arg} = __new(__ct.lua_buf, {arg}, #{arg}); ")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
|
// SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side
|
||||||
|
debug_assert!(from.is_some());
|
||||||
|
let from = unsafe { from.unwrap_unchecked() };
|
||||||
|
debug_assert!(!from.__ptr.is_null());
|
||||||
|
let from = unsafe { slice::from_raw_parts(from.__ptr, from.__len) };
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!from.is_null(),
|
std::str::from_utf8(from).is_ok(),
|
||||||
"<&str>::convert() called on a null lua_buf"
|
|
||||||
);
|
|
||||||
|
|
||||||
// SAFETY: this is safe because lua_buf is copyable
|
|
||||||
unsafe { Self::convert_value(*from) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
|
||||||
let s = unsafe { slice::from_raw_parts(from.__ptr, from.__len) };
|
|
||||||
|
|
||||||
debug_assert!(
|
|
||||||
std::str::from_utf8(s).is_ok(),
|
|
||||||
"<&str>::convert() called on an invalid utf8 string when it was checked to be valid"
|
"<&str>::convert() called on an invalid utf8 string when it was checked to be valid"
|
||||||
);
|
);
|
||||||
|
unsafe { std::str::from_utf8_unchecked(from) }
|
||||||
// SAFETY: we already checked that the string is valid utf8 from the lua side
|
|
||||||
unsafe { std::str::from_utf8_unchecked(s) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl IntoFfi for &'static [u8] {
|
||||||
|
type Into = lua_buf;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Into {
|
||||||
|
// SAFETY: the slice is 'static so the resulting lua_buf is always valid
|
||||||
|
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); ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl IntoFfi for Vec<u8> {
|
||||||
|
type Into = lua_buffer;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Into {
|
||||||
|
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!(
|
||||||
|
"local {ret}_buf = {ret}; {ret} = __intern({ret}.__ptr, {ret}.__len); __C.{DROP_BUFFER_FN}({ret}_buf); "
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_from_via {
|
||||||
|
($ty:ty, $via:ty) => {
|
||||||
|
unsafe impl<'s> FromFfi for $ty {
|
||||||
|
type From = <$via as FromFfi>::From;
|
||||||
|
|
||||||
|
fn require_keepalive() -> bool {
|
||||||
|
<$via as FromFfi>::require_keepalive()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prelude(arg: &str) -> impl Display {
|
||||||
|
<$via as FromFfi>::prelude(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert(from: Self::From) -> Self {
|
||||||
|
<$via as FromFfi>::convert(from).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from_via!(&'s BStr, &'s [u8]);
|
||||||
|
|
||||||
|
macro_rules! impl_into_via {
|
||||||
|
($ty:ty, $via:ty) => {
|
||||||
|
unsafe impl IntoFfi for $ty {
|
||||||
|
type Into = <$via as IntoFfi>::Into;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Into {
|
||||||
|
<$via as IntoFfi>::convert(self.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn postlude(ret: &str) -> impl Display {
|
||||||
|
<$via as IntoFfi>::postlude(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_into_via!(&'static BStr, &'static [u8]);
|
||||||
|
impl_into_via!(&'static str, &'static BStr);
|
||||||
|
impl_into_via!(BString, Vec<u8>);
|
||||||
|
impl_into_via!(String, BString);
|
||||||
|
|
||||||
|
macro_rules! impl_optional_from {
|
||||||
|
($ty:ty) => {
|
||||||
|
unsafe impl<'s> FromFfi for Option<$ty> {
|
||||||
|
type From = <$ty as FromFfi>::From;
|
||||||
|
|
||||||
|
fn require_keepalive() -> bool {
|
||||||
|
<$ty as FromFfi>::require_keepalive()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prelude(arg: &str) -> impl Display {
|
||||||
|
// just pass a null pointer if argument is nil
|
||||||
|
display!(
|
||||||
|
"if {arg} ~= nil then {}end; ",
|
||||||
|
<$ty as FromFfi>::prelude(arg)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert(from: Self::From) -> Self {
|
||||||
|
from.map(|s| <$ty as FromFfi>::convert(Some(s)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_optional_from!(&'s [u8]);
|
||||||
|
impl_optional_from!(&'s BStr);
|
||||||
|
impl_optional_from!(&'s str);
|
||||||
|
|
||||||
|
macro_rules! impl_optional_into {
|
||||||
|
($ty:ty, $null:expr) => {
|
||||||
|
unsafe impl IntoFfi for Option<$ty> {
|
||||||
|
type Into = <$ty as IntoFfi>::Into;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::Into {
|
||||||
|
self.map_or($null, <$ty as IntoFfi>::convert)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn postlude(ret: &str) -> impl Display {
|
||||||
|
display!(
|
||||||
|
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
|
||||||
|
<$ty as IntoFfi>::postlude(ret)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_optional_into!(&'static [u8], lua_buf::null());
|
||||||
|
impl_optional_into!(&'static BStr, lua_buf::null());
|
||||||
|
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]
|
[package]
|
||||||
name = "luaffi_impl"
|
name = "luaffi_impl"
|
||||||
version = "0.1.0"
|
version.workspace = true
|
||||||
edition = "2024"
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
@@ -10,4 +14,4 @@ proc-macro = true
|
|||||||
darling = "0.20.11"
|
darling = "0.20.11"
|
||||||
proc-macro2 = "1.0.95"
|
proc-macro2 = "1.0.95"
|
||||||
quote = "1.0.40"
|
quote = "1.0.40"
|
||||||
syn = { version = "2.0.103", features = ["full", "visit-mut"] }
|
syn = { version = "2.0.103", features = ["full"] }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::utils::{ffi_crate, syn_assert, syn_error};
|
use crate::utils::{ffi_crate, syn_assert, syn_error};
|
||||||
use darling::FromMeta;
|
use darling::FromMeta;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote, quote_spanned};
|
||||||
use syn::{spanned::*, *};
|
use syn::{ext::IdentExt, spanned::Spanned, *};
|
||||||
|
|
||||||
#[derive(Debug, FromMeta)]
|
#[derive(Debug, FromMeta)]
|
||||||
pub struct Args {}
|
pub struct Args {}
|
||||||
@@ -22,44 +22,54 @@ pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
|||||||
_ => syn_error!(item, "expected struct or enum"),
|
_ => syn_error!(item, "expected struct or enum"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mod_name = format_ident!("__cdef__{name}");
|
let mod_name = format_ident!("__{name}_cdef");
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote_spanned!(name.span() =>
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#item
|
#item
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(unused, non_snake_case)]
|
#[allow(unused, non_snake_case)]
|
||||||
|
/// Automatically generated by luaffi.
|
||||||
mod #mod_name {
|
mod #mod_name {
|
||||||
use super::*;
|
use super::*;
|
||||||
#impl_type
|
#impl_type
|
||||||
#impl_cdef
|
#impl_cdef
|
||||||
}
|
}
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
let fmt = quote!(::std::format!);
|
let span = ty.span();
|
||||||
let name_fmt = LitStr::new(&format!("{ty}"), ty.span());
|
let name = LitStr::new(&ty.unraw().to_string(), span);
|
||||||
let cdecl_fmt = LitStr::new(&format!("struct {ty} {{name}}"), ty.span());
|
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote_spanned!(span =>
|
||||||
unsafe impl #ffi::Type for #ty {
|
unsafe impl #ffi::Type for #ty {
|
||||||
fn name() -> ::std::string::String {
|
fn name() -> impl ::std::fmt::Display {
|
||||||
#fmt(#name_fmt)
|
#name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cdecl(name: impl ::std::fmt::Display) -> ::std::string::String {
|
fn ty() -> #ffi::TypeType {
|
||||||
#fmt(#cdecl_fmt)
|
#ffi::TypeType::Aggregate
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cdecl(name: impl ::std::fmt::Display) -> impl ::std::fmt::Display {
|
||||||
|
::std::format!("struct {} {name}", #name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(b: &mut #ffi::TypeBuilder) {
|
fn build(b: &mut #ffi::TypeBuilder) {
|
||||||
b.cdef::<Self>().metatype::<Self>();
|
b.cdef::<Self>().metatype::<Self>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
// SAFETY: we can always implement `IntoFfi` because it transfers ownership from Rust to Lua
|
||||||
|
unsafe impl #ffi::IntoFfi for #ty {
|
||||||
|
type Into = Self;
|
||||||
|
fn convert(self) -> Self::Into { self }
|
||||||
|
}
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
|
fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
|
||||||
@@ -71,13 +81,14 @@ fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
|
|||||||
|
|
||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
let ty = &str.ident;
|
let ty = &str.ident;
|
||||||
let build = generate_build_cdef(&to_cfields(&mut str.fields)?)?;
|
let span = ty.span();
|
||||||
|
let build = generate_cdef_build(&get_cfields(&mut str.fields)?)?;
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote_spanned!(span =>
|
||||||
unsafe impl #ffi::CDef for #ty {
|
unsafe impl #ffi::Cdef for #ty {
|
||||||
fn build(b: &mut #ffi::CDefBuilder) { #build }
|
fn build(b: &mut #ffi::CdefBuilder) { #build }
|
||||||
}
|
}
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
|
fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
|
||||||
@@ -89,22 +100,24 @@ fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
|
|||||||
|
|
||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
let ty = &enu.ident;
|
let ty = &enu.ident;
|
||||||
|
let span = ty.span();
|
||||||
let build = enu
|
let build = enu
|
||||||
.variants
|
.variants
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|variant| {
|
.map(|variant| {
|
||||||
let build = generate_build_cdef(&to_cfields(&mut variant.fields)?)?;
|
let span = variant.span();
|
||||||
Ok(quote! { b.inner_struct(|b| { #build }); })
|
let build = generate_cdef_build(&get_cfields(&mut variant.fields)?)?;
|
||||||
|
Ok(quote_spanned!(span => b.inner_struct(|b| { #build })))
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote_spanned!(span =>
|
||||||
unsafe impl #ffi::CDef for #ty {
|
unsafe impl #ffi::Cdef for #ty {
|
||||||
fn build(b: &mut #ffi::CDefBuilder) {
|
fn build(b: &mut #ffi::CdefBuilder) {
|
||||||
b.field::<::std::ffi::c_int>("__tag").inner_union(|b| { #(#build)* });
|
b.field::<::std::ffi::c_int>("__tag").inner_union(|b| { #(#build;)* });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CField {
|
struct CField {
|
||||||
@@ -113,7 +126,12 @@ struct CField {
|
|||||||
attrs: CFieldAttrs,
|
attrs: CFieldAttrs,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
#[derive(Default)]
|
||||||
|
struct CFieldAttrs {
|
||||||
|
opaque: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
||||||
match fields {
|
match fields {
|
||||||
Fields::Named(fields) => fields.named.iter_mut(),
|
Fields::Named(fields) => fields.named.iter_mut(),
|
||||||
Fields::Unnamed(fields) => fields.unnamed.iter_mut(),
|
Fields::Unnamed(fields) => fields.unnamed.iter_mut(),
|
||||||
@@ -123,22 +141,17 @@ fn to_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
|||||||
.map(|(i, field)| {
|
.map(|(i, field)| {
|
||||||
Ok(CField {
|
Ok(CField {
|
||||||
name: match field.ident {
|
name: match field.ident {
|
||||||
Some(ref name) => format!("{name}"),
|
Some(ref name) => name.unraw().to_string(),
|
||||||
None => format!("__{i}"),
|
None => format!("__{i}"),
|
||||||
},
|
},
|
||||||
ty: field.ty.clone(),
|
ty: field.ty.clone(),
|
||||||
attrs: parse_attrs(&mut field.attrs)?,
|
attrs: parse_cfield_attrs(&mut field.attrs)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
fn parse_cfield_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
|
||||||
struct CFieldAttrs {
|
|
||||||
opaque: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
|
|
||||||
let mut parsed = CFieldAttrs::default();
|
let mut parsed = CFieldAttrs::default();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while let Some(attr) = attrs.get(i) {
|
while let Some(attr) = attrs.get(i) {
|
||||||
@@ -155,11 +168,11 @@ fn parse_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
|
|||||||
Ok(parsed)
|
Ok(parsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_build_cdef(fields: &[CField]) -> Result<TokenStream> {
|
fn generate_cdef_build(fields: &[CField]) -> Result<TokenStream> {
|
||||||
let mut body = vec![quote! {
|
let mut body = vec![quote!(
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
let mut align = 1;
|
let mut align = 1;
|
||||||
}];
|
)];
|
||||||
|
|
||||||
fn offset(i: usize) -> Ident {
|
fn offset(i: usize) -> Ident {
|
||||||
format_ident!("offset{i}")
|
format_ident!("offset{i}")
|
||||||
@@ -168,40 +181,41 @@ fn generate_build_cdef(fields: &[CField]) -> Result<TokenStream> {
|
|||||||
let max = quote!(::std::cmp::Ord::max);
|
let max = quote!(::std::cmp::Ord::max);
|
||||||
let size_of = quote!(::std::mem::size_of);
|
let size_of = quote!(::std::mem::size_of);
|
||||||
let align_of = quote!(::std::mem::align_of);
|
let align_of = quote!(::std::mem::align_of);
|
||||||
|
|
||||||
for (i, field) in fields.iter().enumerate() {
|
for (i, field) in fields.iter().enumerate() {
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
let offset = offset(i);
|
let offset = offset(i);
|
||||||
body.push(quote! {
|
body.push(quote_spanned!(ty.span() =>
|
||||||
// round up current offset to the alignment of field type for field offset
|
// round up current offset to the alignment of field type for field offset
|
||||||
offset = (offset + #align_of::<#ty>() - 1) & !(#align_of::<#ty>() - 1);
|
offset = (offset + #align_of::<#ty>() - 1) & !(#align_of::<#ty>() - 1);
|
||||||
align = #max(align, #align_of::<#ty>());
|
align = #max(align, #align_of::<#ty>());
|
||||||
let #offset = offset;
|
let #offset = offset;
|
||||||
offset += #size_of::<#ty>();
|
offset += #size_of::<#ty>();
|
||||||
});
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
body.push(quote! {
|
body.push(quote!(
|
||||||
// round up final offset to the total alignment of struct for struct size
|
// round up final offset to the total alignment of struct for struct size
|
||||||
let size = (offset + align - 1) & !(align - 1);
|
let size = (offset + align - 1) & !(align - 1);
|
||||||
});
|
));
|
||||||
|
|
||||||
let len = fields.len();
|
let len = fields.len();
|
||||||
for (i, field) in fields.iter().enumerate() {
|
for (i, field) in fields.iter().enumerate() {
|
||||||
let name = &field.name;
|
let name = &field.name;
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
if field.attrs.opaque {
|
body.push(if field.attrs.opaque {
|
||||||
body.push(if i == len - 1 {
|
if i == len - 1 {
|
||||||
let a = offset(i);
|
let a = offset(i);
|
||||||
quote! { b.field_opaque(size - #a); } // last field
|
quote_spanned!(ty.span() => b.field_opaque(size - #a);) // last field
|
||||||
} else {
|
} else {
|
||||||
let a = offset(i);
|
let a = offset(i);
|
||||||
let b = offset(i + 1);
|
let b = offset(i + 1);
|
||||||
quote! { b.field_opaque(#b - #a); }
|
quote_spanned!(ty.span() => b.field_opaque(#b - #a);)
|
||||||
});
|
}
|
||||||
} else {
|
} else {
|
||||||
body.push(quote! { b.field::<#ty>(#name); });
|
quote_spanned!(ty.span() => b.field::<#ty>(#name);)
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(quote! { #(#body)* })
|
Ok(quote!(#(#body)*))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn metatype(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
pub fn metatype(_args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
||||||
metatype::transform(parse_macro_input!(input))
|
metatype::transform(parse_macro_input!(input))
|
||||||
.unwrap_or_else(|err| err.into_compile_error().into_token_stream())
|
.unwrap_or_else(|err| err.into_compile_error().into_token_stream())
|
||||||
.into()
|
.into()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use syn::{spanned::*, *};
|
use syn::{spanned::Spanned, *};
|
||||||
|
|
||||||
macro_rules! syn_error {
|
macro_rules! syn_error {
|
||||||
($src:expr, $($fmt:expr),+) => {{
|
($src:expr, $($fmt:expr),+) => {{
|
||||||
@@ -10,7 +10,7 @@ macro_rules! syn_error {
|
|||||||
macro_rules! syn_assert {
|
macro_rules! syn_assert {
|
||||||
($cond:expr, $src:expr, $($fmt:expr),+) => {{
|
($cond:expr, $src:expr, $($fmt:expr),+) => {{
|
||||||
if !$cond {
|
if !$cond {
|
||||||
syn_error!($src, $($fmt),+);
|
crate::utils::syn_error!($src, $($fmt),+);
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@@ -34,11 +34,15 @@ pub fn ty_name(ty: &Type) -> Result<&Ident> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pat_ident(pat: &Pat) -> Result<&Ident> {
|
pub fn pat_ident(pat: &Pat) -> Result<Ident> {
|
||||||
match pat {
|
Ok(match pat {
|
||||||
Pat::Ident(ident) => Ok(&ident.ident),
|
Pat::Ident(ident) => match ident.subpat {
|
||||||
|
Some((_, ref subpat)) => syn_error!(subpat, "unexpected subpattern"),
|
||||||
|
None => ident.ident.clone(),
|
||||||
|
},
|
||||||
|
Pat::Wild(wild) => Ident::new("_", wild.span()),
|
||||||
_ => syn_error!(pat, "expected ident"),
|
_ => syn_error!(pat, "expected ident"),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_unit(ty: &Type) -> bool {
|
pub fn is_unit(ty: &Type) -> bool {
|
||||||
@@ -51,15 +55,15 @@ pub fn is_unit(ty: &Type) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_primitive(ty: &Type) -> bool {
|
pub fn is_primitivelike(ty: &Type) -> bool {
|
||||||
match ty {
|
match ty {
|
||||||
Type::Tuple(tuple) if tuple.elems.is_empty() => true, // unit type
|
Type::Tuple(tuple) if tuple.elems.is_empty() => return true, // unit type
|
||||||
Type::Reference(_) | Type::Ptr(_) => true,
|
Type::Reference(_) | Type::Ptr(_) => return true,
|
||||||
Type::Paren(paren) => is_primitive(&paren.elem),
|
Type::Paren(paren) => return is_primitivelike(&paren.elem),
|
||||||
Type::Path(path) => {
|
Type::Path(path) => {
|
||||||
if let Some(name) = path.path.get_ident() {
|
if let Some(name) = path.path.get_ident() {
|
||||||
matches!(
|
return matches!(
|
||||||
format!("{name}").as_str(),
|
name.to_string().as_str(),
|
||||||
"bool"
|
"bool"
|
||||||
| "u8"
|
| "u8"
|
||||||
| "u16"
|
| "u16"
|
||||||
@@ -90,11 +94,66 @@ pub fn is_primitive(ty: &Type) -> bool {
|
|||||||
| "c_size_t"
|
| "c_size_t"
|
||||||
| "c_ssize_t"
|
| "c_ssize_t"
|
||||||
| "c_ptrdiff_t"
|
| "c_ptrdiff_t"
|
||||||
)
|
);
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum StringLike {
|
||||||
|
SliceU8,
|
||||||
|
Str,
|
||||||
|
BStr,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_stringlike(ty: &Type) -> Option<StringLike> {
|
||||||
|
if let Type::Reference(ty) = ty
|
||||||
|
&& ty.mutability.is_none()
|
||||||
|
&& ty.lifetime.is_none()
|
||||||
|
{
|
||||||
|
match *ty.elem {
|
||||||
|
Type::Slice(ref slice) => {
|
||||||
|
// match &[u8]
|
||||||
|
if let Type::Path(ref path) = *slice.elem
|
||||||
|
&& let Some(name) = path.path.get_ident()
|
||||||
|
&& name == "u8"
|
||||||
|
{
|
||||||
|
return Some(StringLike::SliceU8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type::Path(ref path) => {
|
||||||
|
// match &str or &BStr
|
||||||
|
if let Some(name) = path.path.get_ident() {
|
||||||
|
match name.to_string().as_str() {
|
||||||
|
"str" => return Some(StringLike::Str),
|
||||||
|
"BStr" => return Some(StringLike::BStr),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
[package]
|
||||||
name = "luaify"
|
name = "luaify"
|
||||||
version = "0.1.0"
|
version.workspace = true
|
||||||
edition = "2024"
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|||||||
@@ -163,9 +163,9 @@ fn generate_expr_binary(f: &mut Formatter, bin: &ExprBinary, cx: Context) -> Res
|
|||||||
| BinOp::Shl(_)
|
| BinOp::Shl(_)
|
||||||
| BinOp::Shr(_)
|
| BinOp::Shr(_)
|
||||||
| BinOp::Eq(_)
|
| BinOp::Eq(_)
|
||||||
|
| BinOp::Ne(_)
|
||||||
| BinOp::Lt(_)
|
| BinOp::Lt(_)
|
||||||
| BinOp::Le(_)
|
| BinOp::Le(_)
|
||||||
| BinOp::Ne(_)
|
|
||||||
| BinOp::Ge(_)
|
| BinOp::Ge(_)
|
||||||
| BinOp::Gt(_)
|
| BinOp::Gt(_)
|
||||||
| BinOp::And(_)
|
| BinOp::And(_)
|
||||||
@@ -233,22 +233,22 @@ fn generate_expr_binary(f: &mut Formatter, bin: &ExprBinary, cx: Context) -> Res
|
|||||||
BinOp::MulAssign(_) => assign_bin_op!("*"),
|
BinOp::MulAssign(_) => assign_bin_op!("*"),
|
||||||
BinOp::Div(_) => bin_op!("/"),
|
BinOp::Div(_) => bin_op!("/"),
|
||||||
BinOp::DivAssign(_) => assign_bin_op!("/"),
|
BinOp::DivAssign(_) => assign_bin_op!("/"),
|
||||||
BinOp::Rem(_) => call_op!("math.fmod"),
|
BinOp::Rem(_) => call_op!("__fmod"),
|
||||||
BinOp::RemAssign(_) => assign_call_op!("math.fmod"),
|
BinOp::RemAssign(_) => assign_call_op!("__fmod"),
|
||||||
BinOp::BitAnd(_) => call_op!("band"),
|
BinOp::BitAnd(_) => call_op!("__band"),
|
||||||
BinOp::BitAndAssign(_) => assign_call_op!("band"),
|
BinOp::BitAndAssign(_) => assign_call_op!("__band"),
|
||||||
BinOp::BitOr(_) => call_op!("bor"),
|
BinOp::BitOr(_) => call_op!("__bor"),
|
||||||
BinOp::BitOrAssign(_) => assign_call_op!("bor"),
|
BinOp::BitOrAssign(_) => assign_call_op!("__bor"),
|
||||||
BinOp::BitXor(_) => call_op!("bxor"),
|
BinOp::BitXor(_) => call_op!("__bxor"),
|
||||||
BinOp::BitXorAssign(_) => assign_call_op!("bxor"),
|
BinOp::BitXorAssign(_) => assign_call_op!("__bxor"),
|
||||||
BinOp::Shl(_) => call_op!("lshift"),
|
BinOp::Shl(_) => call_op!("__blshift"),
|
||||||
BinOp::ShlAssign(_) => assign_call_op!("lshift"),
|
BinOp::ShlAssign(_) => assign_call_op!("__blshift"),
|
||||||
BinOp::Shr(_) => call_op!("arshift"),
|
BinOp::Shr(_) => call_op!("__barshift"),
|
||||||
BinOp::ShrAssign(_) => assign_call_op!("arshift"),
|
BinOp::ShrAssign(_) => assign_call_op!("__barshift"),
|
||||||
BinOp::Eq(_) => bin_op!("=="),
|
BinOp::Eq(_) => bin_op!("=="),
|
||||||
|
BinOp::Ne(_) => bin_op!("~="),
|
||||||
BinOp::Lt(_) => bin_op!("<"),
|
BinOp::Lt(_) => bin_op!("<"),
|
||||||
BinOp::Le(_) => bin_op!("<="),
|
BinOp::Le(_) => bin_op!("<="),
|
||||||
BinOp::Ne(_) => bin_op!("~="),
|
|
||||||
BinOp::Ge(_) => bin_op!(">="),
|
BinOp::Ge(_) => bin_op!(">="),
|
||||||
BinOp::Gt(_) => bin_op!(">"),
|
BinOp::Gt(_) => bin_op!(">"),
|
||||||
BinOp::And(_) => bin_op!("and"),
|
BinOp::And(_) => bin_op!("and"),
|
||||||
@@ -523,7 +523,7 @@ fn generate_expr_tuple(f: &mut Formatter, tuple: &ExprTuple, cx: Context) -> Res
|
|||||||
f.write("nil");
|
f.write("nil");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ if cx.is_multi_expr() => generate_punctuated_expr(f, &tuple.elems),
|
_ if cx.is_ret() || cx.is_multi_expr() => generate_punctuated_expr(f, &tuple.elems),
|
||||||
_ => syn_error!(tuple, "expected single-valued expression"),
|
_ => syn_error!(tuple, "expected single-valued expression"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -569,9 +569,10 @@ fn generate_expr_while(f: &mut Formatter, whil: &ExprWhile, cx: Context) -> Resu
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_punctuated_expr(f: &mut Formatter, exprs: &Punctuated<Expr, Token![,]>) -> Result<()> {
|
fn generate_punctuated_expr(f: &mut Formatter, exprs: &Punctuated<Expr, Token![,]>) -> Result<()> {
|
||||||
|
let len = exprs.len();
|
||||||
for (i, expr) in exprs.iter().enumerate() {
|
for (i, expr) in exprs.iter().enumerate() {
|
||||||
(i != 0).then(|| f.write(","));
|
(i != 0).then(|| f.write(","));
|
||||||
generate_expr(f, expr, Context::expr(false))?;
|
generate_expr(f, expr, Context::expr(i == len - 1))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -795,6 +796,8 @@ fn generate_receiver(f: &mut Formatter, recv: &Receiver) -> Result<()> {
|
|||||||
fn generate_macro(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
fn generate_macro(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
||||||
match format!("{}", mac.path.require_ident()?).as_str() {
|
match format!("{}", mac.path.require_ident()?).as_str() {
|
||||||
"concat" => generate_macro_concat(f, mac, cx),
|
"concat" => generate_macro_concat(f, mac, cx),
|
||||||
|
"len" => generate_macro_len(f, mac, cx),
|
||||||
|
"variadic" => generate_macro_variadic(f, mac, cx),
|
||||||
"embed" => generate_macro_embed(f, mac, cx),
|
"embed" => generate_macro_embed(f, mac, cx),
|
||||||
"raw" => generate_macro_raw(f, mac, cx),
|
"raw" => generate_macro_raw(f, mac, cx),
|
||||||
name => syn_error!(mac.path, "unknown macro '{name}'"),
|
name => syn_error!(mac.path, "unknown macro '{name}'"),
|
||||||
@@ -813,6 +816,24 @@ fn generate_macro_concat(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_macro_len(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
||||||
|
syn_assert!(cx.is_value(), mac, "len! must be in expression position");
|
||||||
|
cx.is_ret().then(|| f.write("return"));
|
||||||
|
f.write("#");
|
||||||
|
generate_expr(f, &mac.parse_body()?, Context::expr(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_macro_variadic(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
||||||
|
syn_assert!(
|
||||||
|
cx.is_multi_expr(),
|
||||||
|
mac,
|
||||||
|
"variadic! must be in multi-value expression position"
|
||||||
|
);
|
||||||
|
cx.is_ret().then(|| f.write("return"));
|
||||||
|
f.write("...");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_macro_embed(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
fn generate_macro_embed(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
||||||
syn_assert!(cx.is_value(), mac, "embed! must be in expression position");
|
syn_assert!(cx.is_value(), mac, "embed! must be in expression position");
|
||||||
cx.is_ret().then(|| f.write("return"));
|
cx.is_ret().then(|| f.write("return"));
|
||||||
@@ -871,9 +892,9 @@ fn generate_pat_ident(f: &mut Formatter, ident: &PatIdent, _cx: PatContext) -> R
|
|||||||
generate_ident(f, &ident.ident)
|
generate_ident(f, &ident.ident)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_pat_macro(f: &mut Formatter, mac: &PatMacro, _cx: PatContext) -> Result<()> {
|
fn generate_pat_macro(f: &mut Formatter, mac: &PatMacro, cx: PatContext) -> Result<()> {
|
||||||
assert_no_attrs!(mac);
|
assert_no_attrs!(mac);
|
||||||
generate_macro(f, &mac.mac, Context::expr(false))
|
generate_macro(f, &mac.mac, Context::expr(cx.is_multi()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_pat_tuple(f: &mut Formatter, tuple: &PatTuple, cx: PatContext) -> Result<()> {
|
fn generate_pat_tuple(f: &mut Formatter, tuple: &PatTuple, cx: PatContext) -> Result<()> {
|
||||||
@@ -900,9 +921,15 @@ fn generate_pat_wild(f: &mut Formatter, wild: &PatWild, _cx: PatContext) -> Resu
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_punctuated_pat(f: &mut Formatter, pats: &Punctuated<Pat, Token![,]>) -> Result<()> {
|
fn generate_punctuated_pat(f: &mut Formatter, pats: &Punctuated<Pat, Token![,]>) -> Result<()> {
|
||||||
|
let len = pats.len();
|
||||||
for (i, pat) in pats.iter().enumerate() {
|
for (i, pat) in pats.iter().enumerate() {
|
||||||
(i != 0).then(|| f.write(","));
|
(i != 0).then(|| f.write(","));
|
||||||
generate_pat(f, pat, PatContext::Single)?;
|
let cx = if i == len - 1 {
|
||||||
|
PatContext::Multi
|
||||||
|
} else {
|
||||||
|
PatContext::Single
|
||||||
|
};
|
||||||
|
generate_pat(f, pat, cx)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::utils::{LuaType, syn_error, unwrap_expr_ident, unwrap_pat_ident, wrap_expr_block};
|
use crate::utils::{LuaType, expr_ident, pat_ident, syn_error, wrap_expr_block};
|
||||||
use quote::format_ident;
|
use quote::format_ident;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use syn::{spanned::*, visit_mut::*, *};
|
use syn::{spanned::*, visit_mut::*, *};
|
||||||
@@ -73,7 +73,7 @@ impl Visitor {
|
|||||||
match input {
|
match input {
|
||||||
Pat::Ident(_) => {}
|
Pat::Ident(_) => {}
|
||||||
Pat::Type(typed) => {
|
Pat::Type(typed) => {
|
||||||
let ident = unwrap_pat_ident(&typed.pat)?;
|
let ident = pat_ident(&typed.pat)?;
|
||||||
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
||||||
match (&*ty).try_into()? {
|
match (&*ty).try_into()? {
|
||||||
LuaType::Any => {}
|
LuaType::Any => {}
|
||||||
@@ -112,7 +112,7 @@ impl Visitor {
|
|||||||
Some((Ident::new("self", recv.self_token.span()), ty))
|
Some((Ident::new("self", recv.self_token.span()), ty))
|
||||||
}
|
}
|
||||||
FnArg::Typed(typed) => {
|
FnArg::Typed(typed) => {
|
||||||
let ident = unwrap_pat_ident(&typed.pat)?;
|
let ident = pat_ident(&typed.pat)?;
|
||||||
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
||||||
Some((ident, ty))
|
Some((ident, ty))
|
||||||
}
|
}
|
||||||
@@ -149,16 +149,16 @@ impl Visitor {
|
|||||||
let mut prelude: Option<Stmt> = None;
|
let mut prelude: Option<Stmt> = None;
|
||||||
let ty: LuaType = (&*cast.ty).try_into()?;
|
let ty: LuaType = (&*cast.ty).try_into()?;
|
||||||
let ty_str = format!("{ty}");
|
let ty_str = format!("{ty}");
|
||||||
let (ident, msg) = match unwrap_expr_ident(&arg).ok() {
|
let (ident, msg) = match expr_ident(&arg) {
|
||||||
Some(ident) => (ident.clone(), format!("{ty} expected in '{ident}', got ")),
|
Ok(ident) => (ident.clone(), format!("{ty} expected in '{ident}', got ")),
|
||||||
None => {
|
Err(_) => {
|
||||||
let ident = Ident::new("_", arg.span());
|
let ident = Ident::new("_", arg.span());
|
||||||
prelude = Some(parse_quote! { let #ident = #arg; });
|
prelude = Some(parse_quote! { let #ident = #arg; });
|
||||||
(ident, format!("{ty} expected, got "))
|
(ident, format!("{ty} expected, got "))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let tmp = format_ident!("_{ident}");
|
let tmp = format_ident!("__{ident}");
|
||||||
let span = cast.span();
|
let span = cast.span();
|
||||||
*expr = match ty {
|
*expr = match ty {
|
||||||
LuaType::Any => parse_quote_spanned!(span => {}),
|
LuaType::Any => parse_quote_spanned!(span => {}),
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ pub fn wrap_expr_block(expr: &Expr) -> Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_expr_ident(expr: &Expr) -> Result<&Ident> {
|
pub fn expr_ident(expr: &Expr) -> Result<&Ident> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Path(path) => path.path.require_ident(),
|
Expr::Path(path) => path.path.require_ident(),
|
||||||
_ => syn_error!(expr, "expected ident"),
|
_ => syn_error!(expr, "expected ident"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_pat_ident(pat: &Pat) -> Result<Ident> {
|
pub fn pat_ident(pat: &Pat) -> Result<Ident> {
|
||||||
Ok(match pat {
|
Ok(match pat {
|
||||||
Pat::Ident(ident) => match ident.subpat {
|
Pat::Ident(ident) => match ident.subpat {
|
||||||
Some((_, ref subpat)) => syn_error!(subpat, "unexpected subpattern"),
|
Some((_, ref subpat)) => syn_error!(subpat, "unexpected subpattern"),
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ fn local_fn() {
|
|||||||
fn check(self: string, arg: number) {}
|
fn check(self: string, arg: number) {}
|
||||||
inner
|
inner
|
||||||
}),
|
}),
|
||||||
r#"function()local function check(self,arg)do if type(self)=="number"then self=tostring(self);else assert(type(self)=="string","string expected in \'self\', got "..type(self));end;end;do local _arg=arg;arg=tonumber(arg);assert(arg~=nil,"number expected in \'arg\', got "..type(_arg));end;end;return inner;end"#
|
r#"function()local function check(self,arg)do if type(self)=="number"then self=tostring(self);else assert(type(self)=="string","string expected in \'self\', got "..type(self));end;end;do local __arg=arg;arg=tonumber(arg);assert(arg~=nil,"number expected in \'arg\', got "..type(__arg));end;end;return inner;end"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ fn type_checks() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|s| { s as number }),
|
luaify!(|s| { s as number }),
|
||||||
r#"function(s)do local _s=s;s=tonumber(s);assert(s~=nil,"number expected in \'s\', got "..type(_s));end;end"#
|
r#"function(s)do local __s=s;s=tonumber(s);assert(s~=nil,"number expected in \'s\', got "..type(__s));end;end"#
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|s| { s as nil }),
|
luaify!(|s| { s as nil }),
|
||||||
@@ -279,16 +279,16 @@ fn ops() {
|
|||||||
assert_eq!(luaify!(|| a *= b), r#"function()a=a*b;end"#);
|
assert_eq!(luaify!(|| a *= b), r#"function()a=a*b;end"#);
|
||||||
assert_eq!(luaify!(|| a / b), r#"function()return a/b;end"#);
|
assert_eq!(luaify!(|| a / b), r#"function()return a/b;end"#);
|
||||||
assert_eq!(luaify!(|| a /= b), r#"function()a=a/b;end"#);
|
assert_eq!(luaify!(|| a /= b), r#"function()a=a/b;end"#);
|
||||||
assert_eq!(luaify!(|| a = b % c), r#"function()a=math.fmod(b,c);end"#);
|
assert_eq!(luaify!(|| a = b % c), r#"function()a=__fmod(b,c);end"#);
|
||||||
assert_eq!(luaify!(|| a = b << c), r#"function()a=lshift(b,c);end"#);
|
assert_eq!(luaify!(|| a = b << c), r#"function()a=__blshift(b,c);end"#);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| a <<= b << c),
|
luaify!(|| a <<= b << c),
|
||||||
r#"function()a=lshift(a,lshift(b,c));end"#
|
r#"function()a=__blshift(a,__blshift(b,c));end"#
|
||||||
);
|
);
|
||||||
assert_eq!(luaify!(|| a = b >> c), r#"function()a=arshift(b,c);end"#);
|
assert_eq!(luaify!(|| a = b >> c), r#"function()a=__barshift(b,c);end"#);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| a >>= b >> c),
|
luaify!(|| a >>= b >> c),
|
||||||
r#"function()a=arshift(a,arshift(b,c));end"#
|
r#"function()a=__barshift(a,__barshift(b,c));end"#
|
||||||
);
|
);
|
||||||
assert_eq!(luaify!(|| a && b), r#"function()return a and b;end"#);
|
assert_eq!(luaify!(|| a && b), r#"function()return a and b;end"#);
|
||||||
assert_eq!(luaify!(|| a || b), r#"function()return a or b;end"#);
|
assert_eq!(luaify!(|| a || b), r#"function()return a or b;end"#);
|
||||||
@@ -310,15 +310,15 @@ fn ops() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| -a || !--b && c >> d),
|
luaify!(|| -a || !--b && c >> d),
|
||||||
r#"function()return-a or not-(-b)and arshift(c,d);end"#
|
r#"function()return-a or not-(-b)and __barshift(c,d);end"#
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| -a || !(--b && c) >> d),
|
luaify!(|| -a || !(--b && c) >> d),
|
||||||
r#"function()return-a or arshift(not(-(-b)and c),d);end"#
|
r#"function()return-a or __barshift(not(-(-b)and c),d);end"#
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| a >> b << c >> d),
|
luaify!(|| a >> b << c >> d),
|
||||||
r#"function()return arshift(lshift(arshift(a,b),c),d);end"#
|
r#"function()return __barshift(__blshift(__barshift(a,b),c),d);end"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,3 +373,32 @@ fn ifs() {
|
|||||||
r#"function()if a==b then c();elseif b==c then return a();else d();end;end"#
|
r#"function()if a==b then c();elseif b==c then return a();else d();end;end"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn variadic() {
|
||||||
|
assert_eq!(luaify!(|a, b, variadic!()| {}), r#"function(a,b,...)end"#);
|
||||||
|
assert_eq!(
|
||||||
|
luaify!(|variadic!()| {
|
||||||
|
let (a, b) = variadic!();
|
||||||
|
}),
|
||||||
|
r#"function(...)local a,b=...;end"#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
luaify!(|a, variadic!()| {
|
||||||
|
let (a, b) = (a, b, c, variadic!());
|
||||||
|
func(a, b, (c, (d, variadic!())))
|
||||||
|
}),
|
||||||
|
r#"function(a,...)local a,b=a,b,c,...;return func(a,b,c,d,...);end"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn length() {
|
||||||
|
assert_eq!(luaify!(len!(a)), r#"#a"#);
|
||||||
|
assert_eq!(
|
||||||
|
luaify!({
|
||||||
|
let (a, b, c) = (len!(a), len!(b), len!(c));
|
||||||
|
}),
|
||||||
|
r#"local a,b,c=#a,#b,#c;"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
25
crates/luajit-sys/Cargo.toml
Normal file
25
crates/luajit-sys/Cargo.toml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "luajit-sys"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["jit", "ffi", "lua52"]
|
||||||
|
runtime = []
|
||||||
|
jit = []
|
||||||
|
ffi = []
|
||||||
|
unwind = []
|
||||||
|
bundled-alloc = []
|
||||||
|
lua52 = []
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
bindgen = "0.71.1"
|
||||||
|
cc = "1.2.26"
|
||||||
|
which = "8.0.0"
|
||||||
@@ -7,23 +7,28 @@ use std::{
|
|||||||
};
|
};
|
||||||
use which::which;
|
use which::which;
|
||||||
|
|
||||||
|
/// Unwraps a Result and panics in a way that prints a nicer error message to the user.
|
||||||
macro_rules! panic_err {
|
macro_rules! panic_err {
|
||||||
($value:expr) => { $value.unwrap_or_else(|err| panic!("{err}")) };
|
($value:expr) => {{ $value.unwrap_or_else(|err| panic!("{err}")) }};
|
||||||
($value:expr, $($arg:expr)+) => { $value.unwrap_or_else(|err| panic!("{}: {err}", format_args!($($arg),+))) };
|
($value:expr, $($arg:expr)+) => {{ $value.unwrap_or_else(|err| panic!("{}: {err}", format_args!($($arg),+))) }};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Formats arguments in a way that is intended to address environment variables.
|
||||||
macro_rules! env_name {
|
macro_rules! env_name {
|
||||||
($($arg:expr),+) => { format!($($arg),+).replace("-", "_").to_uppercase() };
|
($($arg:expr),+) => {{ format!($($arg),+).replace("-", "_").to_uppercase() }};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expands to the value of an environment variable.
|
||||||
macro_rules! env {
|
macro_rules! env {
|
||||||
($($arg:expr),+) => { panic_err!(env::var(env_name!($($arg),+))) };
|
($($arg:expr),+) => { panic_err!(env::var(env_name!($($arg),+))) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expands to the value of a cargo configuration environment variable.
|
||||||
macro_rules! cfg {
|
macro_rules! cfg {
|
||||||
($($arg:expr),+) => { env!("CARGO_CFG_{}", format_args!($($arg),+)) };
|
($($arg:expr),+) => { env!("CARGO_CFG_{}", format_args!($($arg),+)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether a cargo feature is enabled.
|
||||||
macro_rules! feature {
|
macro_rules! feature {
|
||||||
($($arg:expr),+) => { env::var_os(env_name!("CARGO_FEATURE_{}", format_args!($($arg),+))).is_some() };
|
($($arg:expr),+) => { env::var_os(env_name!("CARGO_FEATURE_{}", format_args!($($arg),+))).is_some() };
|
||||||
}
|
}
|
||||||
@@ -38,8 +43,9 @@ fn main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
panic_err!(write(out_path.join(".relver"), get_relver()));
|
panic_err!(write(out_path.join(".relver"), get_relver()));
|
||||||
println!("cargo::rerun-if-changed={}", src_path.display());
|
println!("cargo::rerun-if-changed={}", src_path.display()); // rerun if luajit source changed
|
||||||
|
|
||||||
|
// NOTE: we cannot build bindings without building luajit first (luajit generates some headers)
|
||||||
build_runtime(&out_path.join("src"));
|
build_runtime(&out_path.join("src"));
|
||||||
build_bindings(&out_path.join("src"));
|
build_bindings(&out_path.join("src"));
|
||||||
}
|
}
|
||||||
@@ -49,7 +55,7 @@ fn get_relver() -> String {
|
|||||||
Command::new("git")
|
Command::new("git")
|
||||||
.args(["show", "-s", "--format=%ct"])
|
.args(["show", "-s", "--format=%ct"])
|
||||||
.output(),
|
.output(),
|
||||||
"failed to obtain release version (is the Git command available?)"
|
"failed to obtain luajit release version (is the Git command available?)"
|
||||||
);
|
);
|
||||||
|
|
||||||
String::from_utf8_lossy(&out.stdout).into()
|
String::from_utf8_lossy(&out.stdout).into()
|
||||||
@@ -76,6 +82,7 @@ fn copy_recursive(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn find_make() -> Command {
|
fn find_make() -> Command {
|
||||||
|
// always use gnu make on bsds
|
||||||
let name = match parse_target(&env!("HOST")) {
|
let name = match parse_target(&env!("HOST")) {
|
||||||
(_, _, "freebsd" | "openbsd" | "netbsd" | "dragonfly", _) => "gmake",
|
(_, _, "freebsd" | "openbsd" | "netbsd" | "dragonfly", _) => "gmake",
|
||||||
_ => "make",
|
_ => "make",
|
||||||
@@ -100,61 +107,65 @@ fn parse_target(target: &str) -> (&str, &str, &str, Option<&str>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_runtime(src_path: &Path) {
|
fn build_runtime(src_path: &Path) {
|
||||||
let mut make = find_make();
|
let host = env!("HOST"); // host triplet
|
||||||
let opt_level = env!("OPT_LEVEL");
|
|
||||||
let debug = opt_level == "0";
|
|
||||||
|
|
||||||
let host = env!("HOST");
|
|
||||||
let host_cc = find_cc(&host).to_str().unwrap().to_owned();
|
let host_cc = find_cc(&host).to_str().unwrap().to_owned();
|
||||||
let host_ptr_width = 8 * mem::size_of::<usize>();
|
let host_ptr_width = 8 * mem::size_of::<usize>();
|
||||||
|
|
||||||
let target = env!("TARGET");
|
let target = env!("TARGET"); // target triplet
|
||||||
let target_cc = find_cc(&target).to_str().unwrap().to_owned();
|
let target_cc = find_cc(&target).to_str().unwrap().to_owned();
|
||||||
// let target_features: HashSet<&str> = HashSet::from_iter(cfg!("target_feature").split(","));
|
// let target_features: HashSet<&str> = HashSet::from_iter(cfg!("target_feature").split(","));
|
||||||
let target_ptr_width: usize = cfg!("target_pointer_width").parse().unwrap();
|
let target_ptr_width: usize = cfg!("target_pointer_width").parse().unwrap();
|
||||||
|
|
||||||
if target == host {
|
let mut make = find_make();
|
||||||
println!("--- begin compile for {target}:");
|
|
||||||
} else {
|
|
||||||
println!("--- begin cross-compile on {host} for {target}:");
|
|
||||||
}
|
|
||||||
|
|
||||||
make.current_dir(&src_path)
|
make.current_dir(src_path)
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.arg("-e")
|
.arg("-e")
|
||||||
.env("PATH", env!("PATH"))
|
.env("PATH", env!("PATH"))
|
||||||
.env("MAKEFLAGS", env!("CARGO_MAKEFLAGS"))
|
.env("MAKEFLAGS", env!("CARGO_MAKEFLAGS"))
|
||||||
.env("BUILDMODE", "static");
|
.env("BUILDMODE", "static");
|
||||||
|
|
||||||
let ccopt = vec![format!("-O{opt_level} -fomit-frame-pointer")];
|
let ccopt = vec![format!("-O{} -fomit-frame-pointer", env!("OPT_LEVEL"))]; // propagate opt_level
|
||||||
|
let mut ccdebug = vec![];
|
||||||
|
let mut xcflags = vec![];
|
||||||
|
|
||||||
let mut xcflags = vec![
|
if env!("OPT_LEVEL") == "0" {
|
||||||
"-DLUAJIT_ENABLE_LUA52COMPAT", // lua 5.2 compatibility
|
ccdebug.push("-g"); // generate debug information
|
||||||
"-DLUAJIT_USE_SYSMALLOC", // disable bundled allocator
|
|
||||||
"-DLUAJIT_UNWIND_EXTERNAL -funwind-tables", // use external frame unwinding
|
|
||||||
];
|
|
||||||
|
|
||||||
if debug {
|
|
||||||
make.env("CCDEBUG", "-g"); // generate debug information
|
|
||||||
xcflags.push("-DLUAJIT_USE_GDBJIT"); // gdb support
|
xcflags.push("-DLUAJIT_USE_GDBJIT"); // gdb support
|
||||||
xcflags.push("-DLUA_USE_APICHECK -DLUA_USE_ASSERT"); // enable assertions
|
xcflags.push("-DLUA_USE_APICHECK -DLUA_USE_ASSERT"); // enable assertions
|
||||||
}
|
}
|
||||||
|
|
||||||
if !feature!("enable-jit") {
|
xcflags.push(if feature!("unwind") {
|
||||||
|
"-DLUAJIT_UNWIND_EXTERNAL -funwind-tables" // external frame unwinding (C++ exceptions)
|
||||||
|
} else {
|
||||||
|
"-DLUAJIT_UNWIND_INTERNAL" // internal frame unwinding (setjmp/longjmp)
|
||||||
|
});
|
||||||
|
|
||||||
|
if !feature!("jit") {
|
||||||
xcflags.push("-DLUAJIT_DISABLE_JIT");
|
xcflags.push("-DLUAJIT_DISABLE_JIT");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !feature!("enable-ffi") {
|
if !feature!("ffi") {
|
||||||
xcflags.push("-DLUAJIT_DISABLE_FFI");
|
xcflags.push("-DLUAJIT_DISABLE_FFI");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !feature!("bundled-alloc") {
|
||||||
|
xcflags.push("-DLUAJIT_USE_SYSMALLOC"); // using system malloc disables the bundled allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
if feature!("lua52") {
|
||||||
|
xcflags.push("-DLUAJIT_ENABLE_LUA52COMPAT");
|
||||||
|
}
|
||||||
|
|
||||||
|
// host toolchain config
|
||||||
match (host_ptr_width, target_ptr_width) {
|
match (host_ptr_width, target_ptr_width) {
|
||||||
(64, 64) | (32, 32) => make.env("HOST_CC", &host_cc),
|
(64, 64) | (32, 32) => make.env("HOST_CC", &host_cc),
|
||||||
(64, 32) => make.env("HOST_CC", format!("{} -m32", host_cc)),
|
(64, 32) => make.env("HOST_CC", format!("{host_cc} -m32")),
|
||||||
(n, m) if n != m => panic!("cannot cross-compile on {n}-bit host for {m}-bit target"),
|
(n, m) if n != m => panic!("cannot cross-compile on {n}-bit host for {m}-bit target"),
|
||||||
(n, _) => panic!("unsupported {n}-bit architecture"),
|
(n, _) => panic!("unsupported {n}-bit architecture"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// target system config
|
||||||
match cfg!("target_os").as_str() {
|
match cfg!("target_os").as_str() {
|
||||||
"linux" | "android" => make.env("TARGET_SYS", "Linux"),
|
"linux" | "android" => make.env("TARGET_SYS", "Linux"),
|
||||||
"windows" => make.env("TARGET_SYS", "Windows"),
|
"windows" => make.env("TARGET_SYS", "Windows"),
|
||||||
@@ -173,6 +184,7 @@ fn build_runtime(src_path: &Path) {
|
|||||||
_ => make.env("TARGET_SYS", "Other"),
|
_ => make.env("TARGET_SYS", "Other"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// target toolchain config
|
||||||
if let Some(cross) = target_cc.strip_suffix("gcc") {
|
if let Some(cross) = target_cc.strip_suffix("gcc") {
|
||||||
make.env("CC", "gcc").env("CROSS", cross);
|
make.env("CC", "gcc").env("CROSS", cross);
|
||||||
} else if let Some(cross) = target_cc.strip_suffix("clang") {
|
} else if let Some(cross) = target_cc.strip_suffix("clang") {
|
||||||
@@ -183,16 +195,30 @@ fn build_runtime(src_path: &Path) {
|
|||||||
.env("CROSS", format!("{}/", path.parent().unwrap().display()));
|
.env("CROSS", format!("{}/", path.parent().unwrap().display()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(path) = env::var("RUSTC_LINKER").ok() {
|
// propagate linker config
|
||||||
|
if let Ok(path) = env::var("RUSTC_LINKER") {
|
||||||
make.env("TARGET_LD", panic_err!(which(path), "failed to find ld"));
|
make.env("TARGET_LD", panic_err!(which(path), "failed to find ld"));
|
||||||
}
|
}
|
||||||
|
|
||||||
make.env("CCOPT", ccopt.join(" "))
|
make.env("CCOPT", ccopt.join(" "))
|
||||||
|
.env("CCDEBUG", ccdebug.join(" "))
|
||||||
.env("XCFLAGS", xcflags.join(" "));
|
.env("XCFLAGS", xcflags.join(" "));
|
||||||
|
|
||||||
|
if target == host {
|
||||||
|
println!("--- begin compile for {target}:");
|
||||||
|
} else {
|
||||||
|
println!("--- begin cross-compile on {host} for {target}:");
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{make:?}");
|
||||||
let status = panic_err!(make.status(), "failed to execute make");
|
let status = panic_err!(make.status(), "failed to execute make");
|
||||||
(!status.success()).then(|| panic!("failed to compile luajit: {status}: {make:?}"));
|
(!status.success()).then(|| panic!("failed to compile luajit: {status}: {make:?}"));
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"cargo::rustc-env=LUAJIT_SYS_JITLIB={}",
|
||||||
|
src_path.join("jit").display(),
|
||||||
|
);
|
||||||
|
|
||||||
if feature!("runtime") {
|
if feature!("runtime") {
|
||||||
println!("cargo::rustc-link-search=native={}", src_path.display());
|
println!("cargo::rustc-link-search=native={}", src_path.display());
|
||||||
println!("cargo::rustc-link-lib=static=luajit");
|
println!("cargo::rustc-link-lib=static=luajit");
|
||||||
@@ -200,12 +226,22 @@ fn build_runtime(src_path: &Path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_bindings(src_path: &Path) {
|
fn build_bindings(src_path: &Path) {
|
||||||
|
let mut clang_args = vec![format!("--target={}", env!("TARGET"))];
|
||||||
|
let abi = if feature!("unwind") {
|
||||||
|
clang_args.push("-fexceptions".into());
|
||||||
|
bindgen::Abi::CUnwind
|
||||||
|
} else {
|
||||||
|
clang_args.push("-fno-exceptions".into());
|
||||||
|
bindgen::Abi::C
|
||||||
|
};
|
||||||
|
|
||||||
let mut bindgen = bindgen::builder()
|
let mut bindgen = bindgen::builder()
|
||||||
.clang_arg(format!("--target={}", env!("TARGET")))
|
.clang_args(clang_args)
|
||||||
.allowlist_item(r"^(lua|LUA).*_.+$")
|
.allowlist_item(r"^(lua|LUA).*_.+$")
|
||||||
.formatter(bindgen::Formatter::None)
|
.formatter(bindgen::Formatter::None)
|
||||||
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
|
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
|
||||||
.generate_cstr(true);
|
.generate_cstr(true)
|
||||||
|
.override_abi(abi, ".*");
|
||||||
|
|
||||||
for header in ["lua.h", "lualib.h", "lauxlib.h", "luaconf.h", "luajit.h"] {
|
for header in ["lua.h", "lualib.h", "lauxlib.h", "luaconf.h", "luajit.h"] {
|
||||||
bindgen = bindgen.header(src_path.join(header).to_str().unwrap());
|
bindgen = bindgen.header(src_path.join(header).to_str().unwrap());
|
||||||
@@ -213,5 +249,5 @@ fn build_bindings(src_path: &Path) {
|
|||||||
|
|
||||||
let path = src_path.join("bindgen.rs");
|
let path = src_path.join("bindgen.rs");
|
||||||
panic_err!(panic_err!(bindgen.generate(), "failed to generate bindings").write_to_file(&path));
|
panic_err!(panic_err!(bindgen.generate(), "failed to generate bindings").write_to_file(&path));
|
||||||
println!("cargo::rustc-env=LJ_BINDINGS={}", path.display());
|
println!("cargo::rustc-env=LUAJIT_SYS_BINDGEN={}", path.display());
|
||||||
}
|
}
|
||||||
138
crates/luajit-sys/lib.rs
Normal file
138
crates/luajit-sys/lib.rs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#![allow(nonstandard_style)]
|
||||||
|
use std::{ffi::*, ptr};
|
||||||
|
|
||||||
|
// #[cfg(all(panic = "abort", feature = "unwind"))]
|
||||||
|
// compile_error!(r#"feature "unwind" cannot be enabled if panic = "abort""#);
|
||||||
|
|
||||||
|
// #[cfg(all(panic = "unwind", not(feature = "unwind")))]
|
||||||
|
// compile_error!(r#"feature "unwind" must be enabled if panic = "unwind""#);
|
||||||
|
|
||||||
|
include!(env!("LUAJIT_SYS_BINDGEN"));
|
||||||
|
|
||||||
|
pub unsafe extern "C" fn luaJIT_openlibs(L: *mut lua_State) {
|
||||||
|
unsafe {
|
||||||
|
lua_getglobal(L, c"package".as_ptr());
|
||||||
|
lua_getfield(L, -1, c"preload".as_ptr());
|
||||||
|
lua_replace(L, -2);
|
||||||
|
macro_rules! load {
|
||||||
|
($n:literal, $f:literal) => {{
|
||||||
|
let n: &'static CStr = $n;
|
||||||
|
let f = include_bytes!(concat!(env!("LUAJIT_SYS_JITLIB"), "/", $f));
|
||||||
|
if luaL_loadbuffer(L, f.as_ptr().cast(), f.len(), n.as_ptr()) == 0 {
|
||||||
|
lua_setfield(L, -2, n.as_ptr());
|
||||||
|
} else {
|
||||||
|
lua_error(L);
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
load!(c"jit.vmdef", "vmdef.lua");
|
||||||
|
load!(c"jit.dis_x86", "dis_x86.lua");
|
||||||
|
load!(c"jit.dis_x64", "dis_x64.lua");
|
||||||
|
load!(c"jit.dis_arm", "dis_arm.lua");
|
||||||
|
load!(c"jit.dis_arm64", "dis_arm64.lua");
|
||||||
|
load!(c"jit.dis_arm64be", "dis_arm64be.lua");
|
||||||
|
load!(c"jit.dis_ppc", "dis_ppc.lua");
|
||||||
|
load!(c"jit.dis_mips", "dis_mips.lua");
|
||||||
|
load!(c"jit.dis_mipsel", "dis_mipsel.lua");
|
||||||
|
load!(c"jit.dis_mips64", "dis_mips64.lua");
|
||||||
|
load!(c"jit.dis_mips64el", "dis_mips64el.lua");
|
||||||
|
load!(c"jit.dis_mips64r6", "dis_mips64r6.lua");
|
||||||
|
load!(c"jit.dis_mips64r6el", "dis_mips64r6el.lua");
|
||||||
|
load!(c"jit.bc", "bc.lua");
|
||||||
|
load!(c"jit.bcsave", "bcsave.lua");
|
||||||
|
load!(c"jit.v", "v.lua");
|
||||||
|
load!(c"jit.p", "p.lua");
|
||||||
|
load!(c"jit.dump", "dump.lua");
|
||||||
|
load!(c"jit.zone", "zone.lua");
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// constants not exposed by lua.h
|
||||||
|
pub const LUA_TPROTO: c_int = LUA_TTHREAD + 1;
|
||||||
|
pub const LUA_TCDATA: c_int = LUA_TTHREAD + 2;
|
||||||
|
|
||||||
|
// macros not translated by bindgen
|
||||||
|
pub unsafe fn lua_upvalueindex(i: c_int) -> c_int {
|
||||||
|
LUA_GLOBALSINDEX - i
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
|
||||||
|
unsafe { lua_settop(L, -n - 1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_newtable(L: *mut lua_State) {
|
||||||
|
unsafe { lua_createtable(L, 0, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
|
||||||
|
unsafe { (lua_pushcfunction(L, f), lua_setglobal(L, n)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
|
||||||
|
unsafe { lua_pushcclosure(L, f, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_strlen(L: *mut lua_State, i: c_int) -> usize {
|
||||||
|
unsafe { lua_objlen(L, i) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
unsafe { (lua_type(L, n) == LUA_TFUNCTION) as c_int }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
unsafe { (lua_type(L, n) == LUA_TTABLE) as c_int }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
unsafe { (lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
unsafe { (lua_type(L, n) == LUA_TNIL) as c_int }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
unsafe { (lua_type(L, n) == LUA_TBOOLEAN) as c_int }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
unsafe { (lua_type(L, n) == LUA_TTHREAD) as c_int }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
unsafe { (lua_type(L, n) == LUA_TNONE) as c_int }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
|
||||||
|
unsafe { (lua_type(L, n) <= LUA_TNIL) as c_int }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: impl AsRef<[u8]>) {
|
||||||
|
unsafe { lua_pushlstring(L, s.as_ref().as_ptr().cast(), s.as_ref().len()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_setglobal(L: *mut lua_State, s: *const c_char) {
|
||||||
|
unsafe { lua_setfield(L, LUA_GLOBALSINDEX, s) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_getglobal(L: *mut lua_State, s: *const c_char) {
|
||||||
|
unsafe { lua_getfield(L, LUA_GLOBALSINDEX, s) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
|
||||||
|
unsafe { lua_tolstring(L, i, ptr::null_mut()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_open() -> *mut lua_State {
|
||||||
|
unsafe { luaL_newstate() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_getregistry(L: *mut lua_State) {
|
||||||
|
unsafe { lua_pushvalue(L, LUA_REGISTRYINDEX) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn lua_getgccount(L: *mut lua_State) -> c_int {
|
||||||
|
unsafe { lua_gc(L, LUA_GCCOUNT, 0) }
|
||||||
|
}
|
||||||
@@ -1,24 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "luajit"
|
name = "luajit"
|
||||||
version = "0.1.0"
|
version.workspace = true
|
||||||
edition = "2024"
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
[lib]
|
authors.workspace = true
|
||||||
path = "lib.rs"
|
homepage.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["enable-jit", "enable-ffi"]
|
runtime = ["luajit-sys/runtime"]
|
||||||
runtime = []
|
unwind = ["luajit-sys/unwind"]
|
||||||
enable-jit = []
|
|
||||||
enable-ffi = []
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = { version = "2.9.1", features = ["std"] }
|
bitflags = { version = "2.9.1", features = ["std"] }
|
||||||
bstr = "1.12.0"
|
bstr = "1.12.0"
|
||||||
luaffi = { version = "0.1.0", path = "../luaffi" }
|
luaffi = { path = "../luaffi" }
|
||||||
|
luajit-sys = { path = "../luajit-sys" }
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
bindgen = "0.71.1"
|
|
||||||
cc = "1.2.26"
|
|
||||||
which = "8.0.0"
|
|
||||||
|
|||||||
1138
crates/luajit/lib.rs
1138
crates/luajit/lib.rs
File diff suppressed because it is too large
Load Diff
1848
crates/luajit/src/lib.rs
Normal file
1848
crates/luajit/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
max_width = 100
|
||||||
2
src/lib.rs
Normal file
2
src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub use lb::fs;
|
||||||
|
pub use lb::net;
|
||||||
228
src/main.rs
228
src/main.rs
@@ -1,9 +1,8 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use lb_core::{GlobalState, PrettyError};
|
|
||||||
use mimalloc::MiMalloc;
|
use mimalloc::MiMalloc;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use std::{backtrace::Backtrace, net::SocketAddr, num::NonZero, panic, thread};
|
use std::{backtrace::Backtrace, fmt::Display, net::SocketAddr, num::NonZero, panic, thread};
|
||||||
use tokio::{runtime, task::LocalSet};
|
use sysexits::ExitCode;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: MiMalloc = MiMalloc;
|
static GLOBAL: MiMalloc = MiMalloc;
|
||||||
@@ -21,13 +20,21 @@ fn panic_cb(panic: &panic::PanicHookInfo) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
eprint!(
|
eprint!(
|
||||||
|
"{}:\n{trace}",
|
||||||
|
format_args!(
|
||||||
|
"thread '{}' panicked at {location}: {msg}",
|
||||||
|
thread::current().name().unwrap_or("<unnamed>")
|
||||||
|
)
|
||||||
|
.red()
|
||||||
|
);
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
"{}",
|
"{}",
|
||||||
PrettyError::new(msg)
|
format_args!(
|
||||||
.with_trace(trace)
|
"This is a bug in luby. Please kindly report this at {}.",
|
||||||
.prepend(format_args!(
|
env!("CARGO_PKG_REPOSITORY")
|
||||||
"thread '{}' panicked at {location}",
|
)
|
||||||
thread::current().name().unwrap_or("<unnamed>")
|
.yellow()
|
||||||
))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,35 +42,60 @@ fn panic_cb(panic: &panic::PanicHookInfo) {
|
|||||||
struct Args {
|
struct Args {
|
||||||
/// Paths to scripts to execute.
|
/// Paths to scripts to execute.
|
||||||
#[clap(value_name = "SCRIPTS")]
|
#[clap(value_name = "SCRIPTS")]
|
||||||
paths: Vec<String>,
|
path: Vec<String>,
|
||||||
|
|
||||||
/// Strings to execute.
|
/// Strings to execute.
|
||||||
#[clap(long, short = 'e', value_name = "CHUNK")]
|
#[clap(long, short = 'e', value_name = "CHUNK")]
|
||||||
evals: Vec<String>,
|
eval: Vec<String>,
|
||||||
|
|
||||||
/// Libraries to require on startup.
|
/// Libraries to require.
|
||||||
#[clap(long, short = 'l', value_name = "NAME")]
|
#[clap(long, short = 'l', value_name = "NAME")]
|
||||||
libs: Vec<String>,
|
lib: Vec<String>,
|
||||||
|
|
||||||
/// Console log level.
|
/// Console log level.
|
||||||
#[clap(long, value_name = "LEVEL", default_value = "debug")]
|
#[clap(long, value_name = "LEVEL", default_value = "debug")]
|
||||||
log_level: tracing::Level,
|
log: tracing::Level,
|
||||||
|
|
||||||
/// Number of runtime worker threads.
|
/// LuaJIT control commands.
|
||||||
#[clap(long, value_name = "THREADS", default_value_t = Self::threads())]
|
#[clap(long, short = 'j', help_heading = "Runtime", value_name = "CMD=FLAGS")]
|
||||||
|
jit: Vec<String>,
|
||||||
|
|
||||||
|
/// Number of worker threads.
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
short = 'T',
|
||||||
|
help_heading = "Runtime",
|
||||||
|
value_name = "THREADS",
|
||||||
|
default_value_t = Self::threads()
|
||||||
|
)]
|
||||||
threads: NonZero<usize>,
|
threads: NonZero<usize>,
|
||||||
|
|
||||||
/// Number of runtime blocking threads.
|
/// Number of blocking threads.
|
||||||
#[clap(long, value_name = "THREADS", default_value_t = Self::blocking_threads())]
|
#[clap(
|
||||||
|
long,
|
||||||
|
help_heading = "Runtime",
|
||||||
|
value_name = "THREADS",
|
||||||
|
default_value_t = Self::blocking_threads()
|
||||||
|
)]
|
||||||
blocking_threads: NonZero<usize>,
|
blocking_threads: NonZero<usize>,
|
||||||
|
|
||||||
/// Enable tokio-console integration.
|
/// Enable tokio-console integration.
|
||||||
#[clap(long)]
|
#[clap(long, help_heading = "Debugging")]
|
||||||
enable_console: bool,
|
enable_console: bool,
|
||||||
|
|
||||||
/// tokio-console publish address.
|
/// tokio-console publish address.
|
||||||
#[clap(long, value_name = "ADDRESS", default_value = "127.0.0.1:6669")]
|
#[clap(
|
||||||
|
long,
|
||||||
|
help_heading = "Debugging",
|
||||||
|
value_name = "ADDRESS",
|
||||||
|
default_value = "127.0.0.1:6669",
|
||||||
|
requires = "enable_console"
|
||||||
|
)]
|
||||||
console_addr: SocketAddr,
|
console_addr: SocketAddr,
|
||||||
|
|
||||||
|
/// Print version.
|
||||||
|
#[clap(long, short = 'V')]
|
||||||
|
version: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Args {
|
impl Args {
|
||||||
@@ -76,28 +108,54 @@ impl Args {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
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));
|
panic::set_hook(Box::new(panic_cb));
|
||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
init_logger(&args);
|
if args.version {
|
||||||
let runtime = init_runtime(&args);
|
return Ok(print_version());
|
||||||
GlobalState::set(init_vm(&args));
|
}
|
||||||
|
|
||||||
let main = LocalSet::new();
|
init_logger(&args);
|
||||||
main.spawn_local(run(args));
|
|
||||||
runtime.block_on(main);
|
let tokio = init_tokio(&args);
|
||||||
|
let lua = init_lua(&args);
|
||||||
|
let main = lua.spawn(async |s| main_async(args, s).await);
|
||||||
|
|
||||||
|
tokio.block_on(async {
|
||||||
|
lua.await;
|
||||||
|
main.await.unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
fn init_logger(args: &Args) {
|
||||||
use tracing::level_filters::LevelFilter;
|
use tracing::level_filters::LevelFilter;
|
||||||
use tracing_subscriber::{Layer, util::*};
|
use tracing_subscriber::{Layer, util::*};
|
||||||
|
|
||||||
let console = tracing_subscriber::fmt()
|
let log = tracing_subscriber::fmt()
|
||||||
.compact()
|
.compact()
|
||||||
.with_env_filter(
|
.with_env_filter(
|
||||||
tracing_subscriber::EnvFilter::builder()
|
tracing_subscriber::EnvFilter::builder()
|
||||||
.with_default_directive(LevelFilter::from(args.log_level).into())
|
.with_default_directive(LevelFilter::from(args.log).into())
|
||||||
.from_env_lossy(),
|
.from_env_lossy(),
|
||||||
)
|
)
|
||||||
.with_file(false)
|
.with_file(false)
|
||||||
@@ -110,59 +168,93 @@ fn init_logger(args: &Args) {
|
|||||||
.with_default_env()
|
.with_default_env()
|
||||||
.server_addr(args.console_addr)
|
.server_addr(args.console_addr)
|
||||||
.spawn()
|
.spawn()
|
||||||
.with_subscriber(console)
|
.with_subscriber(log)
|
||||||
.init()
|
.init()
|
||||||
} else {
|
} else {
|
||||||
console.init()
|
log.init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_runtime(args: &Args) -> runtime::Runtime {
|
fn init_tokio(args: &Args) -> tokio::runtime::Runtime {
|
||||||
if args.threads.get() == 1 {
|
let mut rt = match args.threads.get() {
|
||||||
runtime::Builder::new_current_thread()
|
1 => tokio::runtime::Builder::new_current_thread(),
|
||||||
} else {
|
n => {
|
||||||
runtime::Builder::new_multi_thread()
|
let mut rt = tokio::runtime::Builder::new_multi_thread();
|
||||||
|
rt.worker_threads(n - 1);
|
||||||
|
rt
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
rt.enable_all()
|
||||||
|
.thread_name("luby")
|
||||||
|
.max_blocking_threads(args.blocking_threads.get())
|
||||||
|
.build()
|
||||||
|
.unwrap_or_else(exit_err(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));
|
||||||
|
|
||||||
|
for arg in args.jit.iter() {
|
||||||
|
let mut s = rt.guard();
|
||||||
|
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)
|
||||||
|
} else {
|
||||||
|
s.require("jit", 1).unwrap();
|
||||||
|
match arg.as_str() {
|
||||||
|
cmd @ ("on" | "off" | "flush") => {
|
||||||
|
(s.push(cmd), s.get(-2));
|
||||||
|
s.call(0, 0)
|
||||||
|
}
|
||||||
|
arg => {
|
||||||
|
(s.push("opt"), s.get(-2));
|
||||||
|
(s.push("start"), s.get(-2), s.push(arg));
|
||||||
|
s.call(1, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.unwrap_or_else(exit_err(ExitCode::Usage));
|
||||||
}
|
}
|
||||||
.enable_all()
|
|
||||||
.thread_name("lb")
|
rt
|
||||||
.worker_threads(args.threads.get() - 1)
|
|
||||||
.max_blocking_threads(args.blocking_threads.get())
|
|
||||||
.build()
|
|
||||||
.unwrap_or_else(|err| panic!("failed to initialise runtime: {err}"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_vm(_args: &Args) -> luajit::State {
|
fn parse_jitlib_cmd(s: &str) -> Option<(&str, &str)> {
|
||||||
let mut state =
|
match s {
|
||||||
luajit::State::new().unwrap_or_else(|err| panic!("failed to initialise runtime: {err}"));
|
"p" => Some(("p", "Flspv10")),
|
||||||
|
"v" => Some(("v", "-")),
|
||||||
let mut registry = luaffi::Registry::new();
|
"dump" => Some(("dump", "tirs")),
|
||||||
registry.include::<lb_core::lb_core>();
|
_ => s.split_once('='),
|
||||||
|
}
|
||||||
println!("{registry}");
|
|
||||||
|
|
||||||
state
|
|
||||||
.load(Some("@[luby]"), registry.done(), luajit::LoadMode::TEXT)
|
|
||||||
.and_then(|()| state.call(0, 0))
|
|
||||||
.unwrap_or_else(|err| panic!("failed to load modules: {err}"));
|
|
||||||
|
|
||||||
state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(args: Args) {
|
async fn main_async(args: Args, state: &mut luajit::State) -> Result<(), ExitCode> {
|
||||||
let mut state = GlobalState::new_thread();
|
for ref path in args.path {
|
||||||
for ref path in args.paths {
|
let mut s = state.guard();
|
||||||
let chunk = match std::fs::read(path) {
|
let chunk = match std::fs::read(path) {
|
||||||
Ok(chunk) => chunk,
|
Ok(chunk) => chunk,
|
||||||
Err(err) => return eprintln!("{}", format!("{path}: {err}").red()),
|
Err(err) => {
|
||||||
|
eprintln!("{}", format_args!("{path}: {err}").red());
|
||||||
|
ExitCode::NoInput.exit();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = state.load(Some(format!("@{path}")), chunk, Default::default()) {
|
s.load(&luajit::Chunk::new(chunk).path(path))
|
||||||
return eprintln!("{}", err.red());
|
.unwrap_or_else(exit_err(ExitCode::NoInput));
|
||||||
}
|
|
||||||
|
|
||||||
state
|
if let Err(err) = s.call_async(0, 0).await {
|
||||||
.call_async(0)
|
match err.trace() {
|
||||||
.await
|
Some(trace) => eprintln!("{}\n{trace}", err.red()), // runtime error
|
||||||
.unwrap_or_else(GlobalState::uncaught_error);
|
None => eprintln!("{}", err.red()),
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitCode::DataErr.exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
7
stylua.toml
Normal file
7
stylua.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
syntax = "LuaJIT"
|
||||||
|
indent_type = "Spaces"
|
||||||
|
indent_width = 2
|
||||||
|
column_width = 100
|
||||||
|
quote_style = "ForceDouble"
|
||||||
|
call_parentheses = "NoSingleTable"
|
||||||
|
collapse_simple_statement = "ConditionalOnly"
|
||||||
Reference in New Issue
Block a user