Start process of implementing Lua to frame connection.
This commit is contained in:
parent
1fbcfefe1e
commit
1b14973b1d
3
.cargo/config.toml
Normal file
3
.cargo/config.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[unstable]
|
||||
# Remove once Rust makes this the default.
|
||||
wasm_c_abi="spec"
|
546
Cargo.lock
generated
546
Cargo.lock
generated
@ -4,9 +4,9 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
@ -17,6 +17,37 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aliasable"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||
|
||||
[[package]]
|
||||
name = "anymap2"
|
||||
version = "0.13.0"
|
||||
@ -25,15 +56,15 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
version = "0.3.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
@ -53,6 +84,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bitmaps"
|
||||
version = "2.1.0"
|
||||
@ -70,24 +107,27 @@ checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.5.0"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -105,6 +145,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
@ -119,18 +165,18 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.0"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
|
||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -142,9 +188,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
|
||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@ -152,44 +198,44 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
|
||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
|
||||
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
|
||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
|
||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.29"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -204,10 +250,34 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.11"
|
||||
name = "gc-arena"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
checksum = "3cd70cf88a32937834aae9614ff2569b5d9467fa0c42c5d7762fd94a8de88266"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"gc-arena-derive",
|
||||
"hashbrown",
|
||||
"sptr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gc-arena-derive"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c612a69f5557a11046b77a7408d2836fe77077f842171cd211c5ef504bd3cddd"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.73",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@ -218,9 +288,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.0"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
|
||||
|
||||
[[package]]
|
||||
name = "gloo"
|
||||
@ -251,7 +321,7 @@ dependencies = [
|
||||
"gloo-dialogs 0.2.0",
|
||||
"gloo-events 0.2.0",
|
||||
"gloo-file 0.3.0",
|
||||
"gloo-history 0.2.1",
|
||||
"gloo-history 0.2.2",
|
||||
"gloo-net 0.4.0",
|
||||
"gloo-render 0.2.0",
|
||||
"gloo-storage 0.3.0",
|
||||
@ -368,15 +438,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gloo-history"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4022e82f5f9e03cb1251b13c0a967e0600e97aa179c617f6519bac40640160"
|
||||
checksum = "903f432be5ba34427eac5e16048ef65604a82061fe93789f2212afc73d8617d6"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"gloo-events 0.2.0",
|
||||
"gloo-utils 0.2.0",
|
||||
"serde",
|
||||
"serde-wasm-bindgen 0.6.1",
|
||||
"serde-wasm-bindgen 0.6.5",
|
||||
"serde_urlencoded",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
@ -566,26 +636,36 @@ dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.2"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.3"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.11"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
|
||||
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -608,9 +688,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "implicit-clone"
|
||||
version = "0.4.7"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16c5448a864f9abf124ef8bf2a3cc37eb9fd99fe2e804a8b3235d7357bca2c25"
|
||||
checksum = "f8a9aa791c7b5a71b636b7a68207fdebf171ddfc593d9c8506ec4cbc527b6a84"
|
||||
dependencies = [
|
||||
"implicit-clone-derive",
|
||||
"indexmap",
|
||||
@ -623,24 +703,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9311685eb9a34808bbb0608ad2fcab9ae216266beca5848613e95553ac914e3b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.1.0"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
@ -653,31 +751,55 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.150"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "minicrossterm"
|
||||
version = "0.28.1"
|
||||
source = "git+https://git.blastmud.org/blasthavers/minicrossterm.git?rev=494f89daef41162fbd89d5266e261018ed5ff6dc#494f89daef41162fbd89d5266e261018ed5ff6dc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
@ -690,50 +812,90 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.1"
|
||||
version = "0.36.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
|
||||
checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros"
|
||||
version = "0.18.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67"
|
||||
dependencies = [
|
||||
"aliasable",
|
||||
"ouroboros_macro",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros_macro"
|
||||
version = "0.18.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"itertools 0.12.1",
|
||||
"proc-macro2",
|
||||
"proc-macro2-diagnostics",
|
||||
"quote",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "piccolo"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "003bf52de285e1ff1adcbc6572588db3849988ea660a2d55af3a2ffbc81f597f"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
"anyhow",
|
||||
"gc-arena",
|
||||
"hashbrown",
|
||||
"rand",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.3"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
|
||||
checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.3"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
@ -753,13 +915,22 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.15"
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -798,13 +969,26 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2-diagnostics"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.73",
|
||||
"version_check",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prokio"
|
||||
version = "0.1.0"
|
||||
@ -824,18 +1008,42 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xoshiro"
|
||||
@ -848,27 +1056,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.15"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.192"
|
||||
version = "1.0.206"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
|
||||
checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -886,9 +1094,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.6.1"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17ba92964781421b6cef36bf0d7da26d201e96d84e1b10e7ae6ed416e516906d"
|
||||
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
@ -897,22 +1105,23 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.192"
|
||||
version = "1.0.206"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
|
||||
checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.108"
|
||||
version = "1.0.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||
checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
@ -948,6 +1157,18 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sptr"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@ -960,9 +1181,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.39"
|
||||
version = "2.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
|
||||
checksum = "837a7e8026c6ce912ff01cefbe8cafc2f8010ac49682e2a3d9decc3bce1ecaaf"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -970,30 +1191,41 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.50"
|
||||
name = "synstructure"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||
checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.50"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.34.0"
|
||||
version = "1.39.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
|
||||
checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"pin-project-lite",
|
||||
@ -1001,9 +1233,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.14"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
|
||||
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
@ -1012,9 +1244,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.5"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
@ -1046,7 +1278,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1071,10 +1303,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
name = "unicode-segmentation"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
@ -1103,15 +1347,15 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.38"
|
||||
version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02"
|
||||
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@ -1137,7 +1381,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -1160,13 +1404,38 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.19"
|
||||
version = "0.5.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b"
|
||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "worldwideportal"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"im",
|
||||
"itertools 0.13.0",
|
||||
"minicrossterm",
|
||||
"nom",
|
||||
"ouroboros",
|
||||
"piccolo",
|
||||
"thiserror",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"yew",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "yew"
|
||||
version = "0.21.0"
|
||||
@ -1204,15 +1473,26 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-test"
|
||||
version = "0.1.0"
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"im",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"yew",
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.73",
|
||||
]
|
||||
|
@ -7,6 +7,15 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
im = "15.1.0"
|
||||
itertools = "0.13.0"
|
||||
nom = "7.1.3"
|
||||
piccolo = "0.3.3"
|
||||
unicode-segmentation = "1.11.0"
|
||||
unicode-width = "0.1.13"
|
||||
wasm-bindgen = "0.2.92"
|
||||
web-sys = "0.3.69"
|
||||
yew = { version = "0.21.0", features = ["csr"] }
|
||||
minicrossterm = { git = "https://git.blastmud.org/blasthavers/minicrossterm.git", rev = "494f89daef41162fbd89d5266e261018ed5ff6dc" }
|
||||
thiserror = "1.0.63"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
ouroboros = "0.18.4"
|
||||
|
57
src/command_handler.rs
Normal file
57
src/command_handler.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use itertools::join;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use yew::Callback;
|
||||
|
||||
use crate::{
|
||||
echo_to_term_frame, lua_state::LuaState, parsing::parse_commands, RegisteredTermFrameLens,
|
||||
TermFrame,
|
||||
};
|
||||
|
||||
fn reentrant_command_handler(
|
||||
lua_state: &mut LuaState,
|
||||
frames: &RegisteredTermFrameLens,
|
||||
term_frame: &TermFrame,
|
||||
command_in: &str,
|
||||
) {
|
||||
web_sys::console::log_1(&"Inside command handler".into());
|
||||
echo_to_term_frame(frames, term_frame, "Hello World!\n");
|
||||
for command in parse_commands(command_in).commands {
|
||||
match command.split_out_command() {
|
||||
None => (),
|
||||
Some((cmd, rest)) => {
|
||||
if cmd == "##" {
|
||||
match lua_state.execute(&join(rest.arguments.iter(), " ")) {
|
||||
Ok(msg) => echo_to_term_frame(frames, term_frame, &msg).unwrap_or(()),
|
||||
Err(msg) => echo_to_term_frame(frames, term_frame, &msg).unwrap_or(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn command_handler(
|
||||
lua_state: &RefCell<LuaState>,
|
||||
frames: &RegisteredTermFrameLens,
|
||||
term_frame: &TermFrame,
|
||||
command_in: String,
|
||||
) {
|
||||
match lua_state.try_borrow_mut() {
|
||||
Err(_) => echo_to_term_frame(
|
||||
frames,
|
||||
term_frame,
|
||||
"Attempt to re-enter command handler during processing.\n",
|
||||
)
|
||||
.unwrap_or(()), // Ignore error handling error.
|
||||
Ok(mut lua_state_m) => {
|
||||
reentrant_command_handler(&mut lua_state_m, frames, term_frame, &command_in)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_command_handler_callback(
|
||||
lua_state: Rc<RefCell<LuaState>>,
|
||||
frames: RegisteredTermFrameLens,
|
||||
) -> Callback<(TermFrame, String), ()> {
|
||||
Callback::from(move |(term, cmd)| command_handler(&lua_state, &frames, &term, cmd))
|
||||
}
|
2
src/lineengine.rs
Normal file
2
src/lineengine.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod history;
|
||||
pub mod line;
|
10
src/lineengine/README.md
Normal file
10
src/lineengine/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
The code is this directory is a hacked and cut back version of
|
||||
https://github.com/zyansheep/rustyline-async
|
||||
|
||||
In particular, we only use the history and line state machine part
|
||||
of it, not the intended async interface of that crate (which is
|
||||
not a good fit for our needs, and uses standard input).
|
||||
|
||||
rustyline-async was released into the public domain under
|
||||
the [Unlicense](https://unlicense.org/). Thank you to the original
|
||||
authors!
|
46
src/lineengine/history.rs
Normal file
46
src/lineengine/history.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
pub struct History {
|
||||
pub entries: VecDeque<String>,
|
||||
pub max_size: usize,
|
||||
current_position: Option<usize>,
|
||||
}
|
||||
impl Default for History {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
entries: Default::default(),
|
||||
max_size: 1000,
|
||||
current_position: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl History {
|
||||
// Find next history that matches a given string from an index
|
||||
pub fn search_next(&mut self, _current: &str) -> Option<&str> {
|
||||
if let Some(index) = &mut self.current_position {
|
||||
if *index < self.entries.len() - 1 {
|
||||
*index += 1;
|
||||
}
|
||||
Some(&self.entries[*index])
|
||||
} else if !self.entries.is_empty() {
|
||||
self.current_position = Some(0);
|
||||
Some(&self.entries[0])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// Find previous history item that matches a given string from an index
|
||||
pub fn search_previous(&mut self, _current: &str) -> Option<&str> {
|
||||
if let Some(index) = &mut self.current_position {
|
||||
if *index == 0 {
|
||||
self.current_position = None;
|
||||
return Some("");
|
||||
}
|
||||
*index -= 1;
|
||||
Some(&self.entries[*index])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
509
src/lineengine/line.rs
Normal file
509
src/lineengine/line.rs
Normal file
@ -0,0 +1,509 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use minicrossterm::{
|
||||
cursor,
|
||||
event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers, TerminalState},
|
||||
terminal::{Clear, ClearType::*},
|
||||
QueueableCommand,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::lineengine::history::History;
|
||||
|
||||
/// Error returned from [`readline()`][Readline::readline]. Such errors
|
||||
/// generally require specific procedures to recover from.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ReadlineError {
|
||||
/// An internal I/O error occurred
|
||||
#[error(transparent)]
|
||||
IO(#[from] io::Error),
|
||||
|
||||
/// `readline()` was called after the [`SharedWriter`] was dropped and
|
||||
/// everything written to the `SharedWriter` was already output
|
||||
#[error("line writers closed")]
|
||||
Closed,
|
||||
}
|
||||
|
||||
/// Events emitted by [`Readline::readline()`]
|
||||
#[derive(Debug)]
|
||||
pub enum ReadlineEvent {
|
||||
/// The user entered a line of text
|
||||
Line(String),
|
||||
/// The user pressed Ctrl-D
|
||||
Eof,
|
||||
/// The user pressed Ctrl-C
|
||||
Interrupted,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LineState {
|
||||
// Unicode Line
|
||||
line: String,
|
||||
// Index of grapheme in line
|
||||
line_cursor_grapheme: usize,
|
||||
// Column of grapheme in line
|
||||
current_column: u16,
|
||||
|
||||
cluster_buffer: String, // buffer for holding partial grapheme clusters as they come in
|
||||
|
||||
prompt: String,
|
||||
pub should_print_line_on_enter: bool, // After pressing enter, should we print the line just submitted?
|
||||
pub should_print_line_on_control_c: bool, // After pressing control_c should we print the line just cancelled?
|
||||
|
||||
last_line_length: usize,
|
||||
last_line_completed: bool,
|
||||
|
||||
term_size: (u16, u16),
|
||||
|
||||
pub history: History,
|
||||
}
|
||||
|
||||
impl LineState {
|
||||
pub fn new(prompt: String, term_size: (u16, u16)) -> Self {
|
||||
let current_column = prompt.len() as u16;
|
||||
Self {
|
||||
prompt,
|
||||
last_line_completed: true,
|
||||
term_size,
|
||||
current_column,
|
||||
should_print_line_on_enter: true,
|
||||
should_print_line_on_control_c: true,
|
||||
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
fn line_height(&self, pos: u16) -> u16 {
|
||||
pos / self.term_size.0 // Gets the number of lines wrapped
|
||||
}
|
||||
/// Move from a position on the line to the start
|
||||
fn move_to_beginning(&self, term: &mut impl Write, from: u16) -> io::Result<()> {
|
||||
let move_up = self.line_height(from.saturating_sub(1));
|
||||
term.queue(cursor::MoveToColumn(0))?;
|
||||
if move_up != 0 {
|
||||
term.queue(cursor::MoveUp(move_up))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
/// Move from the start of the line to some position
|
||||
fn move_from_beginning(&self, term: &mut impl Write, to: u16) -> io::Result<()> {
|
||||
let line_height = self.line_height(to.saturating_sub(1));
|
||||
let line_remaining_len = to % self.term_size.0; // Get the remaining length
|
||||
if line_height != 0 {
|
||||
term.queue(cursor::MoveDown(line_height))?;
|
||||
}
|
||||
term.queue(cursor::MoveRight(line_remaining_len))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Move cursor by one unicode grapheme either left (negative) or right (positive)
|
||||
fn move_cursor(&mut self, change: isize) -> io::Result<()> {
|
||||
if change > 0 {
|
||||
let count = self.line.graphemes(true).count();
|
||||
self.line_cursor_grapheme =
|
||||
usize::min(self.line_cursor_grapheme + change as usize, count);
|
||||
} else {
|
||||
self.line_cursor_grapheme =
|
||||
self.line_cursor_grapheme.saturating_sub((-change) as usize);
|
||||
}
|
||||
let (pos, str) = self.current_grapheme().unwrap_or((0, ""));
|
||||
let pos = pos + str.len();
|
||||
self.current_column =
|
||||
(self.prompt.len() + UnicodeWidthStr::width(&self.line[0..pos])) as u16;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn current_grapheme(&self) -> Option<(usize, &str)> {
|
||||
self.line
|
||||
.grapheme_indices(true)
|
||||
.take(self.line_cursor_grapheme)
|
||||
.last()
|
||||
}
|
||||
fn next_grapheme(&self) -> Option<(usize, &str)> {
|
||||
let total = self.line.grapheme_indices(true).count();
|
||||
if self.line_cursor_grapheme == total {
|
||||
return None;
|
||||
}
|
||||
self.line
|
||||
.grapheme_indices(true)
|
||||
.take(self.line_cursor_grapheme + 1)
|
||||
.last()
|
||||
}
|
||||
fn reset_cursor(&self, term: &mut impl Write) -> io::Result<()> {
|
||||
self.move_to_beginning(term, self.current_column)
|
||||
}
|
||||
fn set_cursor(&self, term: &mut impl Write) -> io::Result<()> {
|
||||
self.move_from_beginning(term, self.current_column)
|
||||
}
|
||||
/// Clear current line
|
||||
pub fn clear(&self, term: &mut impl Write) -> io::Result<()> {
|
||||
self.move_to_beginning(term, self.current_column)?;
|
||||
term.queue(Clear(FromCursorDown))?;
|
||||
Ok(())
|
||||
}
|
||||
/// Render line
|
||||
pub fn render(&self, term: &mut impl Write) -> io::Result<()> {
|
||||
write!(term, "{}{}", self.prompt, self.line)?;
|
||||
let line_len = self.prompt.len() + UnicodeWidthStr::width(&self.line[..]);
|
||||
self.move_to_beginning(term, line_len as u16)?;
|
||||
self.move_from_beginning(term, self.current_column)?;
|
||||
Ok(())
|
||||
}
|
||||
/// Clear line and render
|
||||
pub fn clear_and_render(&self, term: &mut impl Write) -> io::Result<()> {
|
||||
self.clear(term)?;
|
||||
self.render(term)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn print_data(&mut self, data: &[u8], term: &mut impl Write) -> Result<(), ReadlineError> {
|
||||
self.clear(term)?;
|
||||
|
||||
// If last written data was not newline, restore the cursor
|
||||
if !self.last_line_completed {
|
||||
term.queue(cursor::MoveUp(1))?
|
||||
.queue(cursor::MoveToColumn(0))?
|
||||
.queue(cursor::MoveRight(self.last_line_length as u16))?;
|
||||
}
|
||||
|
||||
// Write data in a way that newlines also act as carriage returns
|
||||
for line in data.split_inclusive(|b| *b == b'\n') {
|
||||
term.write_all(line)?;
|
||||
term.queue(cursor::MoveToColumn(0))?;
|
||||
}
|
||||
|
||||
self.last_line_completed = data.ends_with(b"\n"); // Set whether data ends with newline
|
||||
|
||||
// If data does not end with newline, save the cursor and write newline for prompt
|
||||
// Usually data does end in newline due to the buffering of SharedWriter, but sometimes it may not (i.e. if .flush() is called)
|
||||
if !self.last_line_completed {
|
||||
self.last_line_length += data.len();
|
||||
// Make sure that last_line_length wraps around when doing multiple writes
|
||||
if self.last_line_length >= self.term_size.0 as usize {
|
||||
self.last_line_length %= self.term_size.0 as usize;
|
||||
writeln!(term)?;
|
||||
}
|
||||
writeln!(term)?; // Move to beginning of line and make new line
|
||||
} else {
|
||||
self.last_line_length = 0;
|
||||
}
|
||||
|
||||
term.queue(cursor::MoveToColumn(0))?;
|
||||
|
||||
self.render(term)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn print(&mut self, string: &str, term: &mut impl Write) -> Result<(), ReadlineError> {
|
||||
self.print_data(string.as_bytes(), term)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn update_prompt(
|
||||
&mut self,
|
||||
prompt: &str,
|
||||
term: &mut impl Write,
|
||||
) -> Result<(), ReadlineError> {
|
||||
self.clear(term)?;
|
||||
self.prompt.clear();
|
||||
self.prompt.push_str(prompt);
|
||||
// recalculates column
|
||||
self.move_cursor(0)?;
|
||||
self.render(term)?;
|
||||
term.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn handle_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
term: &mut impl Write,
|
||||
) -> Result<Option<ReadlineEvent>, ReadlineError> {
|
||||
match event {
|
||||
// Control Keys
|
||||
Event::Key(KeyEvent {
|
||||
code,
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
kind: KeyEventKind::Press,
|
||||
..
|
||||
}) => match code {
|
||||
// End of transmission (CTRL-D)
|
||||
KeyCode::Char('d') => {
|
||||
writeln!(term)?;
|
||||
self.clear(term)?;
|
||||
return Ok(Some(ReadlineEvent::Eof));
|
||||
}
|
||||
// End of text (CTRL-C)
|
||||
KeyCode::Char('c') => {
|
||||
if self.should_print_line_on_control_c {
|
||||
self.print(&format!("{}{}", self.prompt, self.line), term)?;
|
||||
}
|
||||
|
||||
self.line.clear();
|
||||
self.move_cursor(-10000)?;
|
||||
self.clear_and_render(term)?;
|
||||
return Ok(Some(ReadlineEvent::Interrupted));
|
||||
}
|
||||
// Clear all
|
||||
KeyCode::Char('l') => {
|
||||
term.queue(Clear(All))?.queue(cursor::MoveTo(0, 0))?;
|
||||
self.clear_and_render(term)?;
|
||||
}
|
||||
// Clear to start
|
||||
KeyCode::Char('u') => {
|
||||
if let Some((pos, str)) = self.current_grapheme() {
|
||||
let pos = pos + str.len();
|
||||
self.line.drain(0..pos);
|
||||
self.move_cursor(-100000)?;
|
||||
self.clear_and_render(term)?;
|
||||
}
|
||||
}
|
||||
// Clear last word
|
||||
KeyCode::Char('w') => {
|
||||
let count = self.line.graphemes(true).count();
|
||||
let skip_count = count - self.line_cursor_grapheme;
|
||||
let start = self
|
||||
.line
|
||||
.grapheme_indices(true)
|
||||
.rev()
|
||||
.skip(skip_count)
|
||||
.skip_while(|(_, str)| *str == " ")
|
||||
.find_map(|(pos, str)| if str == " " { Some(pos + 1) } else { None })
|
||||
.unwrap_or(0);
|
||||
let end = self
|
||||
.line
|
||||
.grapheme_indices(true)
|
||||
.nth(self.line_cursor_grapheme)
|
||||
.map(|(end, _)| end);
|
||||
let change = start as isize - self.line_cursor_grapheme as isize;
|
||||
self.move_cursor(change)?;
|
||||
if let Some(end) = end {
|
||||
self.line.drain(start..end);
|
||||
} else {
|
||||
self.line.drain(start..);
|
||||
}
|
||||
self.clear_and_render(term)?;
|
||||
}
|
||||
// Move to beginning
|
||||
#[cfg(feature = "emacs")]
|
||||
KeyCode::Char('a') => {
|
||||
self.reset_cursor(term)?;
|
||||
self.move_cursor(-100000)?;
|
||||
self.set_cursor(term)?;
|
||||
}
|
||||
// Move to end
|
||||
#[cfg(feature = "emacs")]
|
||||
KeyCode::Char('e') => {
|
||||
self.reset_cursor(term)?;
|
||||
self.move_cursor(100000)?;
|
||||
self.set_cursor(term)?;
|
||||
}
|
||||
// Move cursor left to previous word
|
||||
KeyCode::Left => {
|
||||
self.reset_cursor(term)?;
|
||||
let count = self.line.graphemes(true).count();
|
||||
let skip_count = count - self.line_cursor_grapheme;
|
||||
if let Some((pos, _)) = self
|
||||
.line
|
||||
.grapheme_indices(true)
|
||||
.rev()
|
||||
.skip(skip_count)
|
||||
.skip_while(|(_, str)| *str == " ")
|
||||
.find(|(_, str)| *str == " ")
|
||||
{
|
||||
let change = pos as isize - self.line_cursor_grapheme as isize;
|
||||
self.move_cursor(change + 1)?;
|
||||
} else {
|
||||
self.move_cursor(-100000)?
|
||||
}
|
||||
self.set_cursor(term)?;
|
||||
}
|
||||
// Move cursor right to next word
|
||||
KeyCode::Right => {
|
||||
self.reset_cursor(term)?;
|
||||
if let Some((pos, _)) = self
|
||||
.line
|
||||
.grapheme_indices(true)
|
||||
.skip(self.line_cursor_grapheme)
|
||||
.skip_while(|(_, c)| *c == " ")
|
||||
.find(|(_, c)| *c == " ")
|
||||
{
|
||||
let change = pos as isize - self.line_cursor_grapheme as isize;
|
||||
self.move_cursor(change)?;
|
||||
} else {
|
||||
self.move_cursor(10000)?;
|
||||
};
|
||||
self.set_cursor(term)?;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
// Other Modifiers (None, Shift, Control+Alt)
|
||||
// All other modifiers must be considered because the match expression cannot match
|
||||
// combined KeyModifiers. Control+Alt is used to reach certain special symbols on a lot
|
||||
// of international keyboard layouts.
|
||||
Event::Key(KeyEvent {
|
||||
code,
|
||||
modifiers: _,
|
||||
kind: KeyEventKind::Press,
|
||||
..
|
||||
}) => match code {
|
||||
KeyCode::Enter => {
|
||||
// Print line so you can see what commands you've typed
|
||||
if self.should_print_line_on_enter {
|
||||
self.print(&format!("{}{}\n", self.prompt, self.line), term)?;
|
||||
}
|
||||
|
||||
// Take line
|
||||
let line = std::mem::take(&mut self.line);
|
||||
|
||||
// Render new line from beginning
|
||||
self.move_cursor(-100000)?;
|
||||
self.clear_and_render(term)?;
|
||||
|
||||
// Return line
|
||||
return Ok(Some(ReadlineEvent::Line(line)));
|
||||
}
|
||||
// Delete character from line
|
||||
KeyCode::Backspace => {
|
||||
if let Some((pos, str)) = self.current_grapheme() {
|
||||
self.clear(term)?;
|
||||
|
||||
let len = pos + str.len();
|
||||
self.line.replace_range(pos..len, "");
|
||||
self.move_cursor(-1)?;
|
||||
|
||||
self.render(term)?;
|
||||
}
|
||||
}
|
||||
KeyCode::Delete => {
|
||||
if let Some((pos, str)) = self.next_grapheme() {
|
||||
self.clear(term)?;
|
||||
|
||||
let len = pos + str.len();
|
||||
self.line.replace_range(pos..len, "");
|
||||
|
||||
self.render(term)?;
|
||||
}
|
||||
}
|
||||
KeyCode::Left => {
|
||||
self.reset_cursor(term)?;
|
||||
self.move_cursor(-1)?;
|
||||
self.set_cursor(term)?;
|
||||
}
|
||||
KeyCode::Right => {
|
||||
self.reset_cursor(term)?;
|
||||
self.move_cursor(1)?;
|
||||
self.set_cursor(term)?;
|
||||
}
|
||||
KeyCode::Home => {
|
||||
self.reset_cursor(term)?;
|
||||
self.move_cursor(-100000)?;
|
||||
self.set_cursor(term)?;
|
||||
}
|
||||
KeyCode::End => {
|
||||
self.reset_cursor(term)?;
|
||||
self.move_cursor(100000)?;
|
||||
self.set_cursor(term)?;
|
||||
}
|
||||
KeyCode::Up => {
|
||||
// search for next history item, replace line if found.
|
||||
if let Some(line) = self.history.search_next(&self.line) {
|
||||
self.line.clear();
|
||||
self.line += line;
|
||||
self.clear(term)?;
|
||||
self.move_cursor(100000)?;
|
||||
self.render(term)?;
|
||||
}
|
||||
}
|
||||
KeyCode::Down => {
|
||||
// search for next history item, replace line if found.
|
||||
if let Some(line) = self.history.search_previous(&self.line) {
|
||||
self.line.clear();
|
||||
self.line += line;
|
||||
self.clear(term)?;
|
||||
self.move_cursor(100000)?;
|
||||
self.render(term)?;
|
||||
}
|
||||
}
|
||||
// Add character to line and output
|
||||
KeyCode::Char(c) => {
|
||||
self.clear(term)?;
|
||||
let prev_len = self.cluster_buffer.graphemes(true).count();
|
||||
self.cluster_buffer.push(c);
|
||||
let new_len = self.cluster_buffer.graphemes(true).count();
|
||||
|
||||
let (g_pos, g_str) = self.current_grapheme().unwrap_or((0, ""));
|
||||
let pos = g_pos + g_str.len();
|
||||
|
||||
self.line.insert(pos, c);
|
||||
|
||||
if prev_len != new_len {
|
||||
self.move_cursor(1)?;
|
||||
if prev_len > 0 {
|
||||
if let Some((pos, str)) =
|
||||
self.cluster_buffer.grapheme_indices(true).next()
|
||||
{
|
||||
let len = str.len();
|
||||
self.cluster_buffer.replace_range(pos..len, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
self.render(term)?;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Event::Resize(x, y) => {
|
||||
self.term_size = (x, y);
|
||||
self.clear_and_render(term)?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WritetermFunction {
|
||||
f: Box<dyn FnMut(&[u8])>,
|
||||
}
|
||||
|
||||
pub struct Readline {
|
||||
term: TerminalState,
|
||||
line_state: LineState,
|
||||
write_term: WritetermFunction,
|
||||
}
|
||||
|
||||
impl Write for WritetermFunction {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
(self.f)(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Readline {
|
||||
pub fn new(prompt: String, write_fn: Box<dyn FnMut(&[u8])>, term_size: (u16, u16)) -> Self {
|
||||
Self {
|
||||
term: TerminalState::new(),
|
||||
line_state: LineState::new(prompt, term_size),
|
||||
write_term: WritetermFunction { f: write_fn },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_resize(&mut self, term_size: (u16, u16)) -> Result<(), ReadlineError> {
|
||||
self.line_state.handle_event(
|
||||
Event::Resize(term_size.0, term_size.1),
|
||||
&mut self.write_term,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn readline(&mut self, input: &[u8]) -> Result<Vec<ReadlineEvent>, ReadlineError> {
|
||||
let evs = self.term.events_for_input(input)?;
|
||||
let mut results: Vec<ReadlineEvent> = Vec::new();
|
||||
for ev in evs {
|
||||
results.extend(self.line_state.handle_event(ev, &mut self.write_term)?);
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
}
|
1
src/lineengine/rustyline-async
Submodule
1
src/lineengine/rustyline-async
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit d5894639257d1f0c881366b0cd7152f023fdcc76
|
55
src/lua_state.rs
Normal file
55
src/lua_state.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use piccolo::{Callback, Closure, Context, Executor, IntoValue, Lua, StashedExecutor, Table};
|
||||
|
||||
use crate::{echo_to_term_frame, RegisteredTermFrameLens, TermFrame};
|
||||
use std::str;
|
||||
|
||||
pub struct LuaState {
|
||||
pub interp: Lua,
|
||||
pub exec: StashedExecutor,
|
||||
}
|
||||
|
||||
impl LuaState {
|
||||
pub fn setup(frames: RegisteredTermFrameLens) -> Result<LuaState, String> {
|
||||
let mut interp = Lua::core();
|
||||
let exec: StashedExecutor = interp.enter(|ctx| {
|
||||
let cmd_table = Table::new(&ctx);
|
||||
cmd_table
|
||||
.set(
|
||||
ctx,
|
||||
ctx.intern_static(b"echo_frame"),
|
||||
echo_frame(ctx, frames),
|
||||
)
|
||||
.map_err(|_| "Can't add command")?;
|
||||
ctx.set_global(ctx.intern_static(b"commands").into_value(ctx), cmd_table)
|
||||
.map(|_| ())
|
||||
.map_err(|_| "Can't set commands key".to_owned())?;
|
||||
Ok::<StashedExecutor, String>(ctx.stash(Executor::new(ctx)))
|
||||
})?;
|
||||
|
||||
Ok(LuaState { interp, exec })
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, command: &str) -> Result<String, String> {
|
||||
self.interp
|
||||
.try_enter(|ctx| {
|
||||
let closure = Closure::load(ctx, None, format!("return ({})", command).as_bytes())
|
||||
.or_else(|_| Closure::load(ctx, None, command.as_bytes()))?;
|
||||
ctx.fetch(&self.exec).restart(ctx, closure.into(), ());
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| format!("{}", err))?;
|
||||
Ok("Blah".to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
fn echo_frame(ctx: Context, frames: RegisteredTermFrameLens) -> Callback {
|
||||
Callback::from_fn(&ctx, move |ctx, _ex, mut stack| {
|
||||
let frame_no: u64 = stack.consume(ctx)?;
|
||||
let message: piccolo::String = stack.consume(ctx)?;
|
||||
let message_str = str::from_utf8(message.as_bytes())
|
||||
.map_err(|_| "Expected message to echo to be UTF-8.".into_value(ctx))?;
|
||||
echo_to_term_frame(&frames, &TermFrame(frame_no), message_str)
|
||||
.map_err(|m| m.into_value(ctx))?;
|
||||
Ok(piccolo::CallbackReturn::Return)
|
||||
})
|
||||
}
|
43
src/main.rs
43
src/main.rs
@ -1,30 +1,49 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use yew::prelude::*;
|
||||
|
||||
pub mod command_handler;
|
||||
pub mod lineengine;
|
||||
pub mod lua_state;
|
||||
pub mod parsing;
|
||||
pub mod term_view;
|
||||
use crate::command_handler::*;
|
||||
use crate::lua_state::LuaState;
|
||||
use crate::term_view::*;
|
||||
|
||||
#[derive(Properties)]
|
||||
struct GlobalState {
|
||||
frame_registry: RegisteredTermFrames,
|
||||
lua_engine: LuaState,
|
||||
}
|
||||
|
||||
// Used only for yew. Always equal since we don't want it to
|
||||
// actually look into GlobalState, changes there should never
|
||||
// cause a re-render.
|
||||
impl PartialEq for GlobalState {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[function_component(App)]
|
||||
fn app() -> Html {
|
||||
let frames_handle: UseStateHandle<Rc<RegisteredTermFrames>> =
|
||||
use_state(|| RegisteredTermFrames::new().into());
|
||||
let frames = RegisteredTermFrameLens {
|
||||
get: (*frames_handle).clone(),
|
||||
set: Callback::from(move |s| frames_handle.set(s)),
|
||||
};
|
||||
let global = use_mut_ref(|| Global {
|
||||
frame_registry: RegisteredTermFrames::new().into(),
|
||||
lua_engine: LuaState::setup(frames.clone()).expect("Can create interpreter"),
|
||||
});
|
||||
|
||||
html! {
|
||||
<div class="vpane toplevel">
|
||||
<TermView terminal={TermFrame(0)} frames={frames.clone()}/>
|
||||
<TermView terminal={TermFrame(1)} frames={frames.clone()}/>
|
||||
<div class="hpane">
|
||||
<TermView terminal={TermFrame(2)} frames={frames.clone()}/>
|
||||
<TermView terminal={TermFrame(3)} {frames}/>
|
||||
</div>
|
||||
<TermView terminal={TermFrame(0)} frames={frames.clone()} handler={command_handler.clone()}/>
|
||||
<TermView terminal={TermFrame(1)} frames={frames.clone()} handler={command_handler.clone()}/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
yew::Renderer::<App>::new().render();
|
||||
}
|
||||
|
231
src/parsing.rs
Normal file
231
src/parsing.rs
Normal file
@ -0,0 +1,231 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use nom::{
|
||||
branch::alt,
|
||||
character::complete::{anychar, char, none_of},
|
||||
combinator::{eof, map, recognize, value},
|
||||
multi::{many0_count, separated_list0},
|
||||
sequence::{preceded, separated_pair},
|
||||
IResult,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct ParseResult<'l> {
|
||||
pub commands: Vec<ParsedCommand<'l>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct ParsedCommand<'l> {
|
||||
pub arguments: VecDeque<&'l str>,
|
||||
}
|
||||
|
||||
impl ParsedCommand<'_> {
|
||||
pub fn split_out_command(&self) -> Option<(&str, Self)> {
|
||||
let mut tmp_arguments = self.arguments.clone();
|
||||
loop {
|
||||
match tmp_arguments.pop_front() {
|
||||
None => return None,
|
||||
Some(head) => {
|
||||
let trimmed_cmd = head.trim();
|
||||
if !trimmed_cmd.is_empty() {
|
||||
return Some((
|
||||
trimmed_cmd,
|
||||
Self {
|
||||
arguments: tmp_arguments,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_string(input: &str) -> IResult<&str, ()> {
|
||||
value(
|
||||
(),
|
||||
many0_count(alt((
|
||||
value((), preceded(char('\\'), anychar)),
|
||||
value((), none_of("\"")),
|
||||
))),
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_parenthetical(input: &str) -> IResult<&str, ()> {
|
||||
value(
|
||||
(),
|
||||
many0_count(alt((
|
||||
value((), preceded(char('\\'), anychar)),
|
||||
value(
|
||||
(),
|
||||
separated_pair(
|
||||
char('{'),
|
||||
parse_parenthetical,
|
||||
alt((value((), eof), value((), char('}')))),
|
||||
),
|
||||
),
|
||||
value(
|
||||
(),
|
||||
separated_pair(
|
||||
char('"'),
|
||||
parse_string,
|
||||
alt((value((), eof), value((), char('"')))),
|
||||
),
|
||||
),
|
||||
value((), none_of("}")),
|
||||
))),
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_argument(input: &str) -> IResult<&str, ()> {
|
||||
value(
|
||||
(),
|
||||
many0_count(alt((
|
||||
value((), preceded(char('\\'), anychar)),
|
||||
value(
|
||||
(),
|
||||
separated_pair(
|
||||
char('{'),
|
||||
parse_parenthetical,
|
||||
alt((value((), eof), value((), char('}')))),
|
||||
),
|
||||
),
|
||||
value(
|
||||
(),
|
||||
separated_pair(
|
||||
char('"'),
|
||||
parse_string,
|
||||
alt((value((), eof), value((), char('"')))),
|
||||
),
|
||||
),
|
||||
value((), none_of(" ;")),
|
||||
))),
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn parse_command(input: &str) -> IResult<&str, ParsedCommand> {
|
||||
map(
|
||||
separated_list0(char(' '), recognize(parse_argument)),
|
||||
|arguments| ParsedCommand {
|
||||
arguments: arguments.into(),
|
||||
},
|
||||
)(input)
|
||||
}
|
||||
|
||||
pub fn parse_commands(input: &str) -> ParseResult {
|
||||
// Note that the core parser doesn't do things like skipping multiple whitespace,
|
||||
separated_list0(preceded(char(';'), many0_count(char(' '))), parse_command)(input)
|
||||
.map(|(_, commands)| ParseResult { commands })
|
||||
.unwrap_or_else(|_| ParseResult { commands: vec![] })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_parse_commands() {
|
||||
assert_eq!(
|
||||
parse_commands(""),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec![""]
|
||||
}]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("north"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["north"]
|
||||
}]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("north "),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
// This is deliberate, ensures we can reconstruct the input.
|
||||
arguments: vec!["north", ""]
|
||||
}]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("#blah {x = 1 + 2; y = 3}; #home"),
|
||||
ParseResult {
|
||||
commands: vec![
|
||||
ParsedCommand {
|
||||
arguments: vec!["#blah", "{x = 1 + 2; y = 3}"]
|
||||
},
|
||||
ParsedCommand {
|
||||
arguments: vec!["#home"]
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("#blah {x = 1 + 2"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "{x = 1 + 2"]
|
||||
},]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("#blah {x = 1} {y = 1}"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "{x = 1}", "{y = 1}"]
|
||||
}]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("#blah \"hello\" \"world\""),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "\"hello\"", "\"world\""]
|
||||
}]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("#blah {x = \"}\"} {y = 1}"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "{x = \"}\"}", "{y = 1}"]
|
||||
}]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("#blah {x = \"}\"; a = \"{\"; y = {}; z = 1;} { q = 5 };"),
|
||||
ParseResult {
|
||||
commands: vec![
|
||||
ParsedCommand {
|
||||
arguments: vec![
|
||||
"#blah",
|
||||
"{x = \"}\"; a = \"{\"; y = {}; z = 1;}",
|
||||
"{ q = 5 }"
|
||||
]
|
||||
},
|
||||
ParsedCommand {
|
||||
arguments: vec![""]
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("#blah {\\}\\}\\}} {y = 1}"),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "{\\}\\}\\}}", "{y = 1}"]
|
||||
}]
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
parse_commands("#blah \"This is a \\\"test\\\"\""),
|
||||
ParseResult {
|
||||
commands: vec![ParsedCommand {
|
||||
arguments: vec!["#blah", "\"This is a \\\"test\\\"\""]
|
||||
}]
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
131
src/term_view.rs
131
src/term_view.rs
@ -1,23 +1,57 @@
|
||||
use std::rc::Rc;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
use im::hashmap::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::{Element, Node};
|
||||
use yew::prelude::*;
|
||||
|
||||
use crate::lineengine::line::{Readline, ReadlineEvent};
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
#[derive(PartialEq)]
|
||||
pub type Terminal;
|
||||
// Sadly, can't do type parameters with wasm_bindgen.
|
||||
pub type IEventString;
|
||||
pub type IEventDims;
|
||||
pub type IDisposable;
|
||||
|
||||
#[wasm_bindgen(constructor)]
|
||||
fn new() -> Terminal;
|
||||
#[wasm_bindgen(method)]
|
||||
fn open(this: &Terminal, element: &Element);
|
||||
#[wasm_bindgen(method)]
|
||||
fn write(this: &Terminal, data: &str);
|
||||
pub fn write(this: &Terminal, data: &str);
|
||||
// Todo: Can we do this with interfaces somehow?
|
||||
#[wasm_bindgen(method)]
|
||||
fn loadAddon(this: &Terminal, addon: &FitAddon);
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
fn onData(this: &Terminal, listener: &Closure<dyn FnMut(String)>) -> IDisposable;
|
||||
#[wasm_bindgen(method)]
|
||||
fn onResize(this: &Terminal, listener: &Closure<dyn FnMut(Dims)>) -> IDisposable;
|
||||
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn rows(this: &Terminal) -> u16;
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn cols(this: &Terminal) -> u16;
|
||||
|
||||
pub type Dims;
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn rows(this: &Dims) -> u16;
|
||||
#[wasm_bindgen(method, getter)]
|
||||
fn cols(this: &Dims) -> u16;
|
||||
|
||||
#[wasm_bindgen(method, setter)]
|
||||
fn set_listener(this: &IEventString, handler: &Closure<dyn FnMut(String)>) -> IDisposable;
|
||||
#[wasm_bindgen(method, setter)]
|
||||
fn set_listener(this: &IEventDims, handler: &Closure<dyn FnMut(Dims)>) -> IDisposable;
|
||||
#[wasm_bindgen(method)]
|
||||
fn dispose(this: &IDisposable);
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub type FitAddon;
|
||||
}
|
||||
@ -33,30 +67,40 @@ extern "C" {
|
||||
#[derive(Eq, Ord, Hash, PartialEq, PartialOrd, Clone)]
|
||||
pub struct TermFrame(pub u64);
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
#[derive(Properties)]
|
||||
pub struct TermFrameData {
|
||||
pub id: TermFrame,
|
||||
pub term: Terminal,
|
||||
pub fit: FitAddon,
|
||||
pub node: Node,
|
||||
pub readline: RefCell<Readline>,
|
||||
pub retained_closures: RefCell<Option<(Closure<dyn FnMut(String)>, Closure<dyn FnMut(Dims)>)>>,
|
||||
}
|
||||
|
||||
impl PartialEq for TermFrameData {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// Only the ID matters, the rest are just reference data.
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
pub type RegisteredTermFrames = HashMap<TermFrame, Rc<TermFrameData>>;
|
||||
#[derive(Properties, PartialEq, Clone)]
|
||||
pub struct RegisteredTermFrameLens {
|
||||
pub get: Rc<RegisteredTermFrames>,
|
||||
pub get: Callback<(), Rc<RegisteredTermFrames>>,
|
||||
pub set: Callback<Rc<RegisteredTermFrames>, ()>,
|
||||
}
|
||||
|
||||
fn get_or_make_term_frame(
|
||||
frame: &TermFrame,
|
||||
frames: &RegisteredTermFrameLens,
|
||||
handler: &Callback<(TermFrame, String), ()>,
|
||||
) -> Rc<TermFrameData> {
|
||||
if let Some(tfd) = frames.get.get(frame) {
|
||||
if let Some(tfd) = frames.get.emit(()).get(frame) {
|
||||
return tfd.clone();
|
||||
}
|
||||
|
||||
let mut new_frames: RegisteredTermFrames = (*frames.get).clone();
|
||||
let mut new_frames: RegisteredTermFrames = (*frames.get.emit(())).clone();
|
||||
let term = Terminal::new();
|
||||
let fit = FitAddon::new();
|
||||
let element = web_sys::window()
|
||||
@ -66,17 +110,69 @@ fn get_or_make_term_frame(
|
||||
element.set_class_name("hterminal");
|
||||
term.open(&element);
|
||||
term.loadAddon(&fit);
|
||||
fit.fit();
|
||||
for i in 0..100 {
|
||||
term.write(&format!("{} Hello world\r\n", i));
|
||||
}
|
||||
let term_for_readline: Terminal = Terminal { obj: term.clone() };
|
||||
let initial_size = (term.cols(), term.rows());
|
||||
let new_data: Rc<TermFrameData> = TermFrameData {
|
||||
id: frame.clone(),
|
||||
term,
|
||||
term: Terminal { obj: term.clone() },
|
||||
fit,
|
||||
node: element.into(),
|
||||
readline: Readline::new(
|
||||
"".to_owned(),
|
||||
Box::new(move |dat| {
|
||||
term_for_readline
|
||||
.write(std::str::from_utf8(dat).expect("readline tried to emit invalid UTF-8"))
|
||||
}),
|
||||
initial_size,
|
||||
)
|
||||
.into(),
|
||||
retained_closures: RefCell::new(None),
|
||||
}
|
||||
.into();
|
||||
|
||||
let data_for_resize: Weak<TermFrameData> = Rc::downgrade(&new_data);
|
||||
let resize_closure = Closure::new(move |dims: Dims| match Weak::upgrade(&data_for_resize) {
|
||||
None => {}
|
||||
Some(r) => match r.readline.try_borrow_mut() {
|
||||
Err(_) => {}
|
||||
Ok(mut v) => v.handle_resize((dims.cols(), dims.rows())).unwrap_or(()),
|
||||
},
|
||||
});
|
||||
term.onResize(&resize_closure);
|
||||
|
||||
let cloned_handler = (*handler).clone();
|
||||
let emit_frame = frame.clone();
|
||||
let data_for_on_data: Weak<TermFrameData> = Rc::downgrade(&new_data);
|
||||
|
||||
let data_closure = Closure::new(move |d: String| match Weak::upgrade(&data_for_on_data) {
|
||||
None => {}
|
||||
Some(r) => match r.readline.try_borrow_mut() {
|
||||
Err(_) => {}
|
||||
Ok(mut v) => {
|
||||
for ev in v.readline(d.as_bytes()).expect("Readline failed") {
|
||||
match ev {
|
||||
ReadlineEvent::Line(l) => cloned_handler.emit((emit_frame.clone(), l)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
term.onData(&data_closure);
|
||||
new_data
|
||||
.retained_closures
|
||||
.replace(Some((data_closure, resize_closure)));
|
||||
|
||||
new_frames.insert(frame.clone(), new_data.clone());
|
||||
web_sys::console::log_2(
|
||||
&"Setting frames to have length: ".into(),
|
||||
&new_frames.iter().count().into(),
|
||||
);
|
||||
frames.set.emit(new_frames.into());
|
||||
new_data
|
||||
}
|
||||
@ -85,13 +181,32 @@ fn get_or_make_term_frame(
|
||||
pub struct TermViewProps {
|
||||
pub terminal: TermFrame,
|
||||
pub frames: RegisteredTermFrameLens,
|
||||
pub handler: Callback<(TermFrame, String), ()>,
|
||||
}
|
||||
|
||||
#[function_component(TermView)]
|
||||
pub fn term_view(props: &TermViewProps) -> Html {
|
||||
let term = get_or_make_term_frame(&props.terminal, &props.frames);
|
||||
let term = get_or_make_term_frame(&props.terminal, &props.frames, &props.handler);
|
||||
term.fit.fit();
|
||||
html! {
|
||||
{Html::VRef(term.node.clone())}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn echo_to_term_frame(
|
||||
frames: &RegisteredTermFrameLens,
|
||||
frame_id: &TermFrame,
|
||||
message: &str,
|
||||
) -> Result<(), &'static str> {
|
||||
let frame_val = frames.get.emit(());
|
||||
let frame: &TermFrameData = frame_val.get(frame_id).ok_or_else(|| {
|
||||
web_sys::console::log_3(
|
||||
&"Attempt to echo to frame that doesn't exist.".into(),
|
||||
&frame_id.0.into(),
|
||||
&frame_val.iter().count().into(),
|
||||
);
|
||||
"Attempt to echo to frame that doesn't exist."
|
||||
})?;
|
||||
frame.term.write(message);
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user