Compare commits
2 Commits
3997e6c45f
...
d8356ecb6c
| Author | SHA1 | Date | |
|---|---|---|---|
| d8356ecb6c | |||
| 2b180d9a95 |
338
Cargo.lock
generated
338
Cargo.lock
generated
@@ -56,6 +56,15 @@ dependencies = [
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
@@ -65,6 +74,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.71"
|
||||
@@ -318,6 +336,12 @@ version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
@@ -380,9 +404,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.12.1"
|
||||
version = "3.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8"
|
||||
checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -601,17 +625,33 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "d3270"
|
||||
name = "d3270-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitflags 2.2.1",
|
||||
"serde",
|
||||
"serde_cbor",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "d3270d"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.0",
|
||||
"bytes",
|
||||
"d3270-common",
|
||||
"futures",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tide",
|
||||
"tide-websockets",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"tracing-fmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -705,6 +745,21 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.28"
|
||||
@@ -712,6 +767,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -720,6 +776,17 @@ version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.28"
|
||||
@@ -770,10 +837,13 @@ version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite 0.2.9",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
@@ -834,10 +904,13 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@@ -990,7 +1063,7 @@ version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.3.1",
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
@@ -1003,9 +1076,9 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.61"
|
||||
version = "0.3.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
|
||||
checksum = "68c16e1bfd491478ab155fd8b4896b86f9ede344949b641e61501e07c2b8b4d5"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -1020,10 +1093,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.143"
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
@@ -1051,6 +1130,21 @@ dependencies = [
|
||||
"value-bag",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
@@ -1088,6 +1182,16 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
@@ -1100,6 +1204,15 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "owning_ref"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.1.0"
|
||||
@@ -1198,6 +1311,10 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qt3270"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
@@ -1278,6 +1395,38 @@ dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.7.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
||||
|
||||
[[package]]
|
||||
name = "route-recognizer"
|
||||
version = "0.2.0"
|
||||
@@ -1343,16 +1492,6 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_cbor"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
|
||||
dependencies = [
|
||||
"half",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.162"
|
||||
@@ -1485,6 +1624,15 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "0.6.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
|
||||
dependencies = [
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.9"
|
||||
@@ -1495,6 +1643,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "standback"
|
||||
version = "0.2.17"
|
||||
@@ -1726,14 +1880,15 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.28.0"
|
||||
version = "1.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f"
|
||||
checksum = "0aa32867d44e6f2ce3385e89dceb990188b8bb0fb25b0cf576647a6f98ac5105"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"pin-project-lite 0.2.9",
|
||||
"signal-hook-registry",
|
||||
"tokio-macros",
|
||||
@@ -1751,6 +1906,101 @@ dependencies = [
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite 0.2.9",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite 0.2.9",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"pin-project-lite 0.2.9",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-fmt"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "880547feb88739526e322a366be2411c41c797f0dabcddfe99a3216e5a664f71"
|
||||
dependencies = [
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "192ca16595cdd0661ce319e8eede9c975f227cdaabc4faaefdc256f43d852e45"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
"matchers",
|
||||
"owning_ref",
|
||||
"regex",
|
||||
"smallvec",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
version = "0.13.0"
|
||||
@@ -1832,6 +2082,12 @@ version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.0.0-alpha.9"
|
||||
@@ -1878,9 +2134,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.84"
|
||||
version = "0.2.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
|
||||
checksum = "5b6cb788c4e39112fbe1822277ef6fb3c55cd86b95cb3d3c4c1c9597e4ac74b4"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"serde",
|
||||
@@ -1890,24 +2146,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.84"
|
||||
version = "0.2.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
|
||||
checksum = "35e522ed4105a9d626d885b35d62501b30d9666283a5c8be12c14a8bdafe7822"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.15",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.34"
|
||||
version = "0.4.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
|
||||
checksum = "083abe15c5d88556b77bdf7aef403625be9e327ad37c62c4e4129af740168163"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
@@ -1917,9 +2173,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.84"
|
||||
version = "0.2.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
|
||||
checksum = "358a79a0cb89d21db8120cbfb91392335913e4890665b1a7981d9e956903b434"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1927,28 +2183,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.84"
|
||||
version = "0.2.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
||||
checksum = "4783ce29f09b9d93134d41297aded3a712b7b979e9c6f28c32cb88c973a94869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.15",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.84"
|
||||
version = "0.2.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||
checksum = "a901d592cafaa4d711bc324edfaff879ac700b19c3dfd60058d2b445be2691eb"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.61"
|
||||
version = "0.3.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
|
||||
checksum = "16b5f940c7edfdc6d12126d98c9ef4d1b3d470011c47c76a6581df47ad9ba721"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "d3270"
|
||||
name = "d3270-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.28.0", features = ["rt", "macros", "sync", "process"] }
|
||||
tide = "0.16.0"
|
||||
tide-websockets = "0.4.0"
|
||||
serde = { version = "1.0.162", features = ["derive"]}
|
||||
serde_json = "1.0.96"
|
||||
serde_cbor = "0.11.2"
|
||||
#serde_cbor = "0.11.2"
|
||||
anyhow = "1.0.71"
|
||||
bitflags = "2.2.1"
|
||||
bitflags = "2.2.1"
|
||||
|
||||
# deps for bins
|
||||
#structopt = "0.3.26"
|
||||
@@ -87,18 +87,22 @@ pub enum InitializeIndication {
|
||||
Models(Vec<Model>),
|
||||
/// Change in the state of the Operator Information Area
|
||||
Oia(Oia),
|
||||
/// List of supported proxies
|
||||
Proxies(Vec<Proxy>),
|
||||
/// Set of supported prefixes
|
||||
Prefixes{value: String},
|
||||
/// List of supported proxies
|
||||
Proxies(Vec<Proxy>),
|
||||
/// Screen dimensions/characteristics changed
|
||||
ScreenMode(ScreenMode),
|
||||
/// Setting changed
|
||||
Setting(Setting),
|
||||
/// Scroll thumb position
|
||||
Thumb(Thumb),
|
||||
/// Indicates build-time TLS config
|
||||
TlsHello(TlsHello),
|
||||
/// TLS state changed
|
||||
Tls(Tls),
|
||||
/// Trace file
|
||||
TraceFile(TraceFile),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
@@ -1,5 +1,5 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::b3270::types::GraphicRendition;
|
||||
use crate::b3270::types::{Color, GraphicRendition};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||
#[serde(rename_all="kebab-case")]
|
||||
@@ -69,7 +69,7 @@ pub enum ConnectionState {
|
||||
ConnectedETn3270e,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
|
||||
#[serde(rename_all="kebab-case")]
|
||||
pub struct Erase {
|
||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
||||
@@ -147,18 +147,54 @@ pub enum OiaField {
|
||||
value: bool,
|
||||
},
|
||||
/// Screen trace count
|
||||
Screentrace {
|
||||
ScreenTrace {
|
||||
value: String,
|
||||
},
|
||||
/// Host command timer (minutes:seconds)
|
||||
Script {
|
||||
value: String,
|
||||
},
|
||||
Timing {
|
||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
||||
value: Option<String>,
|
||||
},
|
||||
Typeahead {
|
||||
value: bool,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialOrd, PartialEq, Hash)]
|
||||
pub enum OiaFieldName {
|
||||
Compose,
|
||||
Insert,
|
||||
Lock,
|
||||
Lu,
|
||||
NotUndera,
|
||||
PrinterSession,
|
||||
ReverseInput,
|
||||
ScreenTrace,
|
||||
Script,
|
||||
Timing,
|
||||
Typeahead,
|
||||
}
|
||||
|
||||
impl OiaField {
|
||||
pub fn field_name(&self) -> OiaFieldName {
|
||||
match self {
|
||||
OiaField::Compose {..} => OiaFieldName::Compose,
|
||||
OiaField::Insert {..} => OiaFieldName::Insert,
|
||||
OiaField::Lock {..} => OiaFieldName::Lock,
|
||||
OiaField::Lu {..} => OiaFieldName::Lu,
|
||||
OiaField::NotUndera {..} => OiaFieldName::NotUndera,
|
||||
OiaField::PrinterSession {..} => OiaFieldName::PrinterSession,
|
||||
OiaField::ReverseInput {..} => OiaFieldName::ReverseInput,
|
||||
OiaField::ScreenTrace {..} => OiaFieldName::ScreenTrace,
|
||||
OiaField::Script {..} => OiaFieldName::Script,
|
||||
OiaField::Timing {..} => OiaFieldName::Timing,
|
||||
OiaField::Typeahead {..} => OiaFieldName::Typeahead,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
pub struct Proxy {
|
||||
pub name: String,
|
||||
@@ -211,7 +247,7 @@ pub struct ConnectAttempt {
|
||||
pub port: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)]
|
||||
// TODO: change this to an enum
|
||||
pub struct Cursor {
|
||||
pub enabled: bool,
|
||||
@@ -342,27 +378,6 @@ pub struct Scroll {
|
||||
pub bg: Option<Color>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||
#[serde(rename="camelCase")]
|
||||
pub enum Color {
|
||||
NeutralBlack,
|
||||
Blue,
|
||||
Red,
|
||||
Pink,
|
||||
Green,
|
||||
Turquoise,
|
||||
Yellow,
|
||||
NeutralWhite,
|
||||
Black,
|
||||
DeepBlue,
|
||||
Orange,
|
||||
Purple,
|
||||
PaleGreen,
|
||||
PaleTurquoise,
|
||||
Gray,
|
||||
White,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
#[serde(rename="kebab-case")]
|
||||
pub struct Stats {
|
||||
@@ -412,3 +427,13 @@ pub struct UiError {
|
||||
#[serde(default, skip_serializing_if="Option::is_none")]
|
||||
pub column: Option<usize>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
pub fn connection_state_serializes_as_expected() {
|
||||
assert_eq!(serde_json::to_string(&ConnectionState::ConnectedETn3270e).unwrap(),r#""connected-e-tn3270e""#);
|
||||
assert_eq!(serde_json::to_string(&ConnectionState::ConnectedESscp).unwrap(),r#""connected-e-sscp""#);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// {"run":{"actions":[{"action":"Connect","args":["10.24.74.37:3270"]}]}}
|
||||
// {"run":{"actions":"Key(a)"}}
|
||||
// Operations
|
||||
#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
|
||||
#[serde(rename_all="kebab-case")]
|
||||
@@ -17,6 +17,7 @@ bitflags! {
|
||||
const PRIVATE_USE = 0x080;
|
||||
const NO_COPY = 0x100;
|
||||
const WRAP = 0x200;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,4 +114,115 @@ mod test {
|
||||
fn from_str_1() {
|
||||
assert_eq!(GraphicRendition::from_str("underline,blink"), Ok(GraphicRendition::BLINK | GraphicRendition::UNDERLINE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||
#[serde(rename="camelCase")]
|
||||
#[repr(u8)]
|
||||
pub enum Color {
|
||||
NeutralBlack,
|
||||
Blue,
|
||||
Red,
|
||||
Pink,
|
||||
Green,
|
||||
Turquoise,
|
||||
Yellow,
|
||||
NeutralWhite,
|
||||
Black,
|
||||
DeepBlue,
|
||||
Orange,
|
||||
Purple,
|
||||
PaleGreen,
|
||||
PaleTurquoise,
|
||||
Gray,
|
||||
White,
|
||||
}
|
||||
|
||||
impl From<Color> for u8 {
|
||||
fn from(value: Color) -> Self {
|
||||
use Color::*;
|
||||
match value {
|
||||
NeutralBlack => 0,
|
||||
Blue => 1,
|
||||
Red => 2,
|
||||
Pink => 3,
|
||||
Green => 4,
|
||||
Turquoise => 5,
|
||||
Yellow => 6,
|
||||
NeutralWhite => 7,
|
||||
Black => 8,
|
||||
DeepBlue => 9,
|
||||
Orange => 10,
|
||||
Purple => 11,
|
||||
PaleGreen => 12,
|
||||
PaleTurquoise => 13,
|
||||
Gray => 14,
|
||||
White => 15,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Color {
|
||||
fn from(value: u8) -> Self {
|
||||
use Color::*;
|
||||
match value & 0xF {
|
||||
0 => NeutralBlack,
|
||||
1 => Blue,
|
||||
2 => Red,
|
||||
3 => Pink,
|
||||
4 => Green,
|
||||
5 => Turquoise,
|
||||
6 => Yellow,
|
||||
7 => NeutralWhite,
|
||||
8 => Black,
|
||||
9 => DeepBlue,
|
||||
10 => Orange,
|
||||
11 => Purple,
|
||||
12 => PaleGreen,
|
||||
13 => PaleTurquoise,
|
||||
14 => Gray,
|
||||
15 => White,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PackedAttr {
|
||||
fn c_gr(self) -> GraphicRendition;
|
||||
fn c_fg(self) -> Color;
|
||||
fn c_bg(self) -> Color;
|
||||
fn c_setgr(self, gr: GraphicRendition) -> Self;
|
||||
fn c_setfg(self, fg: Color) -> Self;
|
||||
fn c_setbg(self, bg: Color) -> Self;
|
||||
fn c_pack(fg: Color, bg: Color, gr: GraphicRendition) -> Self;
|
||||
}
|
||||
|
||||
impl PackedAttr for u32 {
|
||||
fn c_gr(self) -> GraphicRendition {
|
||||
GraphicRendition::from_bits_truncate((self & 0xFFFF) as u16)
|
||||
}
|
||||
|
||||
fn c_fg(self) -> Color {
|
||||
((self >> 16 & 0xF) as u8).into()
|
||||
}
|
||||
|
||||
fn c_bg(self) -> Color {
|
||||
((self >> 20 & 0xF) as u8).into()
|
||||
}
|
||||
|
||||
fn c_setgr(self, gr: GraphicRendition) -> Self {
|
||||
self & !0xFFFF | gr.bits() as u32
|
||||
}
|
||||
|
||||
fn c_setfg(self, fg: Color) -> Self {
|
||||
self & !0xF0000 | (u8::from(fg) as u32) << 16
|
||||
}
|
||||
|
||||
fn c_setbg(self, bg: Color) -> Self {
|
||||
self & !0xF0000 | (u8::from(bg) as u32) << 20
|
||||
}
|
||||
|
||||
fn c_pack(fg: Color, bg: Color, gr: GraphicRendition) -> Self {
|
||||
0.c_setfg(fg).c_setbg(bg).c_setgr(gr)
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
pub mod b3270;
|
||||
pub mod tracker;
|
||||
|
||||
pub mod executor;
|
||||
0
d3270-common/src/proto.rs
Normal file
0
d3270-common/src/proto.rs
Normal file
331
d3270-common/src/tracker.rs
Normal file
331
d3270-common/src/tracker.rs
Normal file
@@ -0,0 +1,331 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::b3270::indication::{Change, Connection, ConnectionState, CountOrText, Cursor, Erase, Oia, OiaFieldName, Row, RunResult, Screen, ScreenMode, Scroll, Setting, TerminalName, Thumb, Tls, TraceFile};
|
||||
use crate::b3270::{Indication, InitializeIndication};
|
||||
use crate::b3270::types::{Color, GraphicRendition, PackedAttr};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct CharCell {
|
||||
pub ch: char,
|
||||
pub attr: u32,
|
||||
}
|
||||
pub struct Tracker {
|
||||
screen: Vec<Vec<CharCell>>,
|
||||
oia: HashMap<OiaFieldName, Oia>,
|
||||
screen_mode: ScreenMode,
|
||||
erase: Erase,
|
||||
thumb: Thumb,
|
||||
settings: HashMap<String, Setting>,
|
||||
|
||||
// These are not init indications, but need to be tracked anyways
|
||||
cursor: Cursor,
|
||||
connection: Connection,
|
||||
formatted: bool,
|
||||
terminal_name: Option<TerminalName>,
|
||||
trace_file: Option<String>,
|
||||
tls: Option<Tls>,
|
||||
|
||||
// These never change, but need to be represented in an initialize message
|
||||
static_init: Vec<InitializeIndication>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Disposition {
|
||||
// Deliver this indication to every connected client
|
||||
Broadcast,
|
||||
// Ignore this message
|
||||
Drop,
|
||||
// Send this message to one particular client
|
||||
Direct(String),
|
||||
}
|
||||
|
||||
impl Tracker {
|
||||
pub fn handle_indication(&mut self, indication: &mut Indication) -> Disposition {
|
||||
match indication {
|
||||
Indication::Bell { .. }
|
||||
| Indication::ConnectAttempt(_)
|
||||
| Indication::Flipped { .. }
|
||||
| Indication::Font { .. }
|
||||
| Indication::Icon { .. }
|
||||
| Indication::Popup(_)
|
||||
| Indication::Stats(_)
|
||||
| Indication::WindowTitle { .. }
|
||||
|
||||
=> (),
|
||||
Indication::Connection(conn) => {
|
||||
self.connection = conn.clone();
|
||||
}
|
||||
Indication::Erase(erase) => {
|
||||
self.erase.logical_cols = erase.logical_cols.or(self.erase.logical_cols);
|
||||
self.erase.logical_rows = erase.logical_rows.or(self.erase.logical_rows);
|
||||
self.erase.fg = erase.fg.or(self.erase.fg);
|
||||
self.erase.bg = erase.bg.or(self.erase.bg);
|
||||
|
||||
let rows = self.erase.logical_rows.unwrap_or(self.screen_mode.rows) as usize;
|
||||
let cols = self.erase.logical_cols.unwrap_or(self.screen_mode.cols) as usize;
|
||||
|
||||
self.screen = vec![
|
||||
vec![CharCell{
|
||||
attr: u32::c_pack(
|
||||
erase.fg.unwrap_or(Color::NeutralBlack),
|
||||
erase.bg.unwrap_or(Color::Blue),
|
||||
GraphicRendition::empty(),
|
||||
),
|
||||
ch: ' ',
|
||||
};cols]
|
||||
; rows
|
||||
]
|
||||
}
|
||||
Indication::Formatted { state } => {self.formatted = *state; }
|
||||
|
||||
Indication::Initialize(init) => {
|
||||
let mut static_init = Vec::with_capacity(init.len());
|
||||
for indicator in init.clone() {
|
||||
match indicator {
|
||||
InitializeIndication::CodePages(_) |
|
||||
InitializeIndication::Hello(_) |
|
||||
InitializeIndication::Models(_) |
|
||||
InitializeIndication::Prefixes { .. } |
|
||||
InitializeIndication::Proxies(_) |
|
||||
InitializeIndication::TlsHello(_) |
|
||||
InitializeIndication::Tls(_) |
|
||||
InitializeIndication::TraceFile(_) =>
|
||||
static_init.push(indicator),
|
||||
|
||||
// The rest are passed through to normal processing.
|
||||
InitializeIndication::Thumb(thumb) => {
|
||||
self.handle_indication(&mut Indication::Thumb(thumb));
|
||||
},
|
||||
InitializeIndication::Setting(setting) => {
|
||||
self.handle_indication(&mut Indication::Setting(setting));
|
||||
}
|
||||
InitializeIndication::ScreenMode(mode) => {
|
||||
self.handle_indication(&mut Indication::ScreenMode(mode));
|
||||
},
|
||||
InitializeIndication::Oia(oia) => {
|
||||
self.handle_indication(&mut Indication::Oia(oia));
|
||||
}
|
||||
InitializeIndication::Erase(erase) => {
|
||||
self.handle_indication(&mut Indication::Erase(erase));
|
||||
}
|
||||
InitializeIndication::Connection(conn) => {
|
||||
self.handle_indication(&mut Indication::Connection(conn));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Indication::Oia(oia) => {
|
||||
self.oia.insert(oia.field.field_name(), oia.clone());
|
||||
}
|
||||
Indication::Screen(screen) => {
|
||||
if let Some(cursor) = screen.cursor {
|
||||
self.cursor = cursor;
|
||||
}
|
||||
for row in screen.rows.iter() {
|
||||
let row_idx = row.row as usize - 1;
|
||||
for change in row.changes.iter() {
|
||||
let col_idx = change.column as usize - 1;
|
||||
// update screen contents
|
||||
let cols = self.screen[row_idx].iter_mut().skip(col_idx);
|
||||
match change.change {
|
||||
CountOrText::Count(n) => {
|
||||
cols.take(n).for_each(|cell| {
|
||||
let mut attr = cell.attr;
|
||||
if let Some(fg) = change.fg {
|
||||
attr = attr.c_setfg(fg);
|
||||
}
|
||||
if let Some(bg) = change.bg {
|
||||
attr = attr.c_setbg(bg);
|
||||
}
|
||||
if let Some(gr) = change.gr {
|
||||
attr = attr.c_setgr(gr);
|
||||
}
|
||||
cell.attr = attr;
|
||||
})
|
||||
}
|
||||
CountOrText::Text(ref text) => {
|
||||
cols.zip(text.chars()).for_each(|(cell, ch)| {
|
||||
let mut attr = cell.attr;
|
||||
if let Some(fg) = change.fg {
|
||||
attr = attr.c_setfg(fg);
|
||||
}
|
||||
if let Some(bg) = change.bg {
|
||||
attr = attr.c_setbg(bg);
|
||||
}
|
||||
if let Some(gr) = change.gr {
|
||||
attr = attr.c_setgr(gr);
|
||||
}
|
||||
cell.attr = attr;
|
||||
cell.ch = ch;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Indication::ScreenMode(mode) => {
|
||||
self.screen_mode = *mode;
|
||||
self.handle_indication(&mut Indication::Erase(Erase{
|
||||
logical_rows: Some(self.screen_mode.rows),
|
||||
logical_cols: Some(self.screen_mode.cols),
|
||||
fg: None,
|
||||
bg: None,
|
||||
}));
|
||||
}
|
||||
Indication::Scroll(Scroll{ fg, bg }) => {
|
||||
let fg = fg.or(self.erase.fg).unwrap_or(Color::Blue);
|
||||
let bg = bg.or(self.erase.bg).unwrap_or(Color::NeutralBlack);
|
||||
let mut row = self.screen.remove(0);
|
||||
row.fill(CharCell{
|
||||
attr: u32::c_pack(fg, bg, GraphicRendition::empty()),
|
||||
ch: ' ',
|
||||
});
|
||||
self.screen.push(row);
|
||||
}
|
||||
Indication::Setting(setting) => {
|
||||
self.settings.insert(setting.name.clone(), setting.clone());
|
||||
}
|
||||
Indication::TerminalName(term) => {
|
||||
self.terminal_name = Some(term.clone());
|
||||
}
|
||||
Indication::Thumb(thumb) => {
|
||||
self.thumb = thumb.clone();
|
||||
}
|
||||
Indication::TraceFile(TraceFile{name}) => {
|
||||
self.trace_file = name.clone();
|
||||
}
|
||||
Indication::Tls(tls) => {
|
||||
self.tls = Some(tls.clone());
|
||||
}
|
||||
|
||||
// These need direction
|
||||
Indication::UiError(_) => {} // we can assume that this came from the last sent command
|
||||
Indication::Passthru(_) => {} // dunno how to handle this one
|
||||
Indication::FileTransfer(_) => {}
|
||||
Indication::RunResult(RunResult{r_tag, ..}) => {
|
||||
if let Some(dest) = r_tag {
|
||||
return Disposition::Direct(dest.clone());
|
||||
} else {
|
||||
return Disposition::Drop;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return Disposition::Broadcast
|
||||
}
|
||||
|
||||
pub fn get_init_indication(&self) -> Vec<Indication> {
|
||||
let mut contents = self.static_init.clone();
|
||||
contents.push(InitializeIndication::ScreenMode(self.screen_mode));
|
||||
contents.push(InitializeIndication::Erase(self.erase));
|
||||
contents.push(InitializeIndication::Thumb(self.thumb));
|
||||
|
||||
contents.extend(self.oia.values()
|
||||
.cloned()
|
||||
.map(InitializeIndication::Oia));
|
||||
contents.extend(self.settings.values()
|
||||
.cloned()
|
||||
.map(InitializeIndication::Setting));
|
||||
contents.extend(self.tls.clone().map(InitializeIndication::Tls));
|
||||
|
||||
// Construct a screen snapshot
|
||||
let mut result = vec![
|
||||
Indication::Initialize(contents),
|
||||
Indication::Connection(self.connection.clone()),
|
||||
Indication::Screen(self.screen_snapshot()),
|
||||
Indication::Formatted {state: self.formatted},
|
||||
];
|
||||
if let Some(terminal_name) = self.terminal_name.clone() {
|
||||
result.push(Indication::TerminalName(terminal_name));
|
||||
}
|
||||
if let Some(trace_file) = self.trace_file.clone() {
|
||||
result.push(Indication::TraceFile(TraceFile {
|
||||
name: Some(trace_file),
|
||||
}))
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn format_row(mut row: &[CharCell]) -> Vec<Change> {
|
||||
let mut result = vec![];
|
||||
let mut column = 1;
|
||||
while !row.is_empty() {
|
||||
let cur_gr = row[0].attr;
|
||||
|
||||
let split_pt = row.iter().take_while(|ch| ch.attr == cur_gr).count();
|
||||
let (first, rest) = row.split_at(split_pt);
|
||||
row = rest;
|
||||
let content = first.iter().map(|cell| cell.ch).collect();
|
||||
result.push(Change{
|
||||
column,
|
||||
fg: Some(cur_gr.c_fg()),
|
||||
bg: Some(cur_gr.c_bg()),
|
||||
gr: Some(cur_gr.c_gr()),
|
||||
change: CountOrText::Text(content),
|
||||
});
|
||||
column += first.len() as u8;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn screen_snapshot(&self) -> Screen {
|
||||
Screen{
|
||||
cursor: Some(self.cursor),
|
||||
rows: self.screen.iter()
|
||||
.map(Vec::as_slice).map(Self::format_row)
|
||||
.enumerate()
|
||||
.map(|(row_id, changes)| Row{
|
||||
row: row_id as u8 - 1,
|
||||
changes,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Tracker {
|
||||
fn default() -> Self {
|
||||
let ret = Self {
|
||||
screen: vec![],
|
||||
oia: Default::default(),
|
||||
screen_mode: ScreenMode {
|
||||
cols: 80,
|
||||
rows: 43,
|
||||
color: true,
|
||||
model: 4,
|
||||
extended: true,
|
||||
oversize: false,
|
||||
},
|
||||
erase: Erase {
|
||||
logical_rows: None,
|
||||
logical_cols: None,
|
||||
fg: None,
|
||||
bg: None,
|
||||
},
|
||||
thumb: Thumb {
|
||||
top: 0.0,
|
||||
shown: 0.0,
|
||||
saved: 0,
|
||||
screen: 0,
|
||||
back: 0,
|
||||
},
|
||||
settings: Default::default(),
|
||||
cursor: Cursor {
|
||||
enabled: false,
|
||||
row: None,
|
||||
column: None
|
||||
},
|
||||
connection: Connection {
|
||||
state: ConnectionState::NotConnected,
|
||||
host: None,
|
||||
cause: None,
|
||||
},
|
||||
formatted: false,
|
||||
terminal_name: None,
|
||||
trace_file: None,
|
||||
tls: None,
|
||||
static_init: vec![],
|
||||
};
|
||||
ret
|
||||
}
|
||||
}
|
||||
22
d3270d/Cargo.toml
Normal file
22
d3270d/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "d3270d"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.162", features = ["derive"]}
|
||||
serde_json = "1.0.96"
|
||||
anyhow = "1.0.71"
|
||||
tokio = { version = "1.28.0", features = ["rt", "macros", "sync", "process", "rt-multi-thread", "bytes", "io-util"] }
|
||||
tide = "0.16.0"
|
||||
tide-websockets = "0.4.0"
|
||||
d3270-common = {path = "../d3270-common"}
|
||||
bytes = "1.4.0"
|
||||
tracing = "0.1.37"
|
||||
tracing-fmt = "0.1.1"
|
||||
futures = "0.3.28"
|
||||
tokio-stream = { version = "0.1.14", features = ["sync"] }
|
||||
rand = "0.8.5"
|
||||
base64 = "0.21.0"
|
||||
263
d3270d/src/connection.rs
Normal file
263
d3270d/src/connection.rs
Normal file
@@ -0,0 +1,263 @@
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use anyhow::anyhow;
|
||||
use base64::Engine;
|
||||
use base64::engine::general_purpose::STANDARD as B64_STANDARD;
|
||||
use bytes::Buf;
|
||||
use futures::{FutureExt, Stream, StreamExt, TryFutureExt};
|
||||
use futures::future::BoxFuture;
|
||||
use rand::RngCore;
|
||||
use tokio::io::{BufReader, AsyncBufReadExt, Lines, AsyncWrite};
|
||||
use tokio::process::{Child, ChildStdout};
|
||||
use tokio::sync::{mpsc, oneshot, broadcast};
|
||||
use tokio_stream::wrappers::{BroadcastStream, errors::BroadcastStreamRecvError};
|
||||
use tracing::{error, info, warn};
|
||||
use d3270_common::b3270::{Indication, Operation, operation};
|
||||
use d3270_common::b3270::indication::RunResult;
|
||||
use d3270_common::b3270::operation::Action;
|
||||
use d3270_common::tracker::{Disposition, Tracker};
|
||||
|
||||
pub struct B3270 {
|
||||
tracker: Tracker, //
|
||||
child: Child, //
|
||||
comm: mpsc::Receiver<B3270Request>, //
|
||||
ind_chan: broadcast::Sender<Indication>, //
|
||||
child_reader: Lines<BufReader<ChildStdout>>, //
|
||||
|
||||
write_buf: VecDeque<u8>,
|
||||
action_response_map: HashMap<String, oneshot::Sender<RunResult>>, //
|
||||
}
|
||||
|
||||
pub enum B3270Request {
|
||||
Action(Vec<Action>, oneshot::Sender<RunResult>),
|
||||
Resync(oneshot::Sender<(Vec<Indication>, broadcast::Receiver<Indication>)>),
|
||||
}
|
||||
|
||||
enum HandleReceiveState {
|
||||
Steady(BroadcastStream<Indication>),
|
||||
Wait(oneshot::Receiver<(Vec<Indication>, broadcast::Receiver<Indication>)>),
|
||||
Resume(std::vec::IntoIter<Indication>, broadcast::Receiver<Indication>),
|
||||
TryRestart(BoxFuture<'static, Result<(), ()>>, oneshot::Receiver<(Vec<Indication>, broadcast::Receiver<Indication>)>),
|
||||
}
|
||||
pub struct Handle {
|
||||
sender: mpsc::Sender<B3270Request>,
|
||||
receiver: Option<HandleReceiveState>,
|
||||
}
|
||||
|
||||
impl Stream for Handle {
|
||||
type Item = Indication;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
// iter tracks whether any progress has been made
|
||||
|
||||
loop {
|
||||
match self.receiver.take() {
|
||||
Some(HandleReceiveState::TryRestart(mut fut, receiver)) => {
|
||||
if fut.poll_unpin(cx).is_pending() {
|
||||
self.receiver = Some(HandleReceiveState::TryRestart(fut, receiver));
|
||||
return Poll::Pending;
|
||||
}
|
||||
// The option is only there to solve a lifetime issue, so this unwrap is safe
|
||||
self.receiver = Some(HandleReceiveState::Wait(receiver));
|
||||
}
|
||||
Some(HandleReceiveState::Wait(mut rcvr)) => {
|
||||
match rcvr.poll_unpin(cx) {
|
||||
Poll::Ready(Ok((inds, rcvr))) => {
|
||||
// reverse the indicators so that they can be popped.
|
||||
self.receiver = Some(HandleReceiveState::Resume(inds.into_iter(), rcvr));
|
||||
}
|
||||
Poll::Ready(Err(error)) => {
|
||||
warn!(%error, "unable to reconnect to b3270 server");
|
||||
return Poll::Ready(None)
|
||||
}
|
||||
Poll::Pending => {
|
||||
self.receiver = Some(HandleReceiveState::Wait(rcvr));
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(HandleReceiveState::Resume(mut inds, rcvr)) => {
|
||||
match inds.next() {
|
||||
Some(next) => {
|
||||
self.receiver = Some(HandleReceiveState::Resume(inds, rcvr));
|
||||
return Poll::Ready(Some(next));
|
||||
}
|
||||
None => {
|
||||
self.receiver = Some(HandleReceiveState::Steady(BroadcastStream::new(rcvr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(HandleReceiveState::Steady(mut rcvr)) => {
|
||||
match rcvr.poll_next_unpin(cx) {
|
||||
Poll::Ready(Some(Ok(msg))) => {
|
||||
self.receiver = Some(HandleReceiveState::Steady(rcvr));
|
||||
return Poll::Ready(Some(msg))
|
||||
}
|
||||
Poll::Ready(Some(Err(BroadcastStreamRecvError::Lagged(_)))) => {
|
||||
warn!("Dropped messages from b3270 server; starting resync");
|
||||
let (os_snd, os_rcv) = oneshot::channel();
|
||||
let fut = self.sender.clone().reserve_owned()
|
||||
.map_ok(move |permit| {
|
||||
permit.send(B3270Request::Resync(os_snd));
|
||||
})
|
||||
.map_err(|_| ())
|
||||
.boxed();
|
||||
self.receiver = Some(HandleReceiveState::TryRestart(fut, os_rcv));
|
||||
}
|
||||
Poll::Ready(None) => {
|
||||
warn!("Failed to receive from b3270 server");
|
||||
return Poll::Ready(None)
|
||||
},
|
||||
Poll::Pending => return Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
None => {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl B3270 {
|
||||
pub fn spawn(mut child: Child) -> (tokio::task::JoinHandle<anyhow::Error>, mpsc::Sender<B3270Request>) {
|
||||
let (subproc_snd, subproc_rcv) = mpsc::channel(10);
|
||||
let child_reader = child.stdout.take().expect("Should always be given a child that has stdout captured");
|
||||
let child_reader = BufReader::new(child_reader).lines();
|
||||
// A single connect can result in a flurry of messages, so we need a big buffer
|
||||
let (ind_chan, _) = broadcast::channel(100);
|
||||
let proc = B3270 {
|
||||
child,
|
||||
child_reader,
|
||||
tracker: Tracker::default(),
|
||||
comm: subproc_rcv,
|
||||
ind_chan,
|
||||
write_buf: VecDeque::new(),
|
||||
action_response_map: Default::default(),
|
||||
};
|
||||
(tokio::task::spawn(proc), subproc_snd)
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for B3270 {
|
||||
type Output = anyhow::Error;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
// try to read data from the child
|
||||
let mut indications = vec![];
|
||||
// handle new indications first, so that new subscribers get the results in the sync state.
|
||||
while let Poll::Ready(buf) = Pin::new(&mut self.child_reader).poll_next_line(cx) {
|
||||
match buf {
|
||||
Ok(Some(line)) => match serde_json::from_str(&line) {
|
||||
Ok(ind) => {
|
||||
indications.push(ind)
|
||||
},
|
||||
Err(error) => {
|
||||
warn!(%error, msg=line, "Failed to parse indication");
|
||||
}
|
||||
},
|
||||
// EOF on stdin; this is a big problem
|
||||
Ok(None) => return Poll::Ready(anyhow!("Child exited unexpectedly")),
|
||||
Err(err) => return Poll::Ready(anyhow!(err).context("Failed to read from child")),
|
||||
}
|
||||
}
|
||||
|
||||
for mut ind in indications {
|
||||
match self.tracker.handle_indication(&mut ind) {
|
||||
Disposition::Broadcast => {
|
||||
// It's OK to drop these, as anybody who cares will resync
|
||||
self.ind_chan.send(ind).ok();
|
||||
}
|
||||
Disposition::Drop => {
|
||||
// do nothing
|
||||
}
|
||||
Disposition::Direct(dst) => {
|
||||
// TODO: handle this once we have a map of destinations.
|
||||
if let Indication::RunResult(run_res) = ind {
|
||||
if let Some(dest) = self.action_response_map.remove(&dst) {
|
||||
// If this fails, whoever sent the request must not have cared.
|
||||
dest.send(run_res).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check if the server has exited; if so, no sense looking at new connections
|
||||
match self.child.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
info!(%status, "b3270 process exited");
|
||||
return Poll::Ready(anyhow!("b3270 process exited"));
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(error) => {
|
||||
warn!(%error, "Failed to check status of b3270");
|
||||
// TODO: should we end now?
|
||||
}
|
||||
}
|
||||
|
||||
// Only now do we handle connection requests. This way new connections
|
||||
// cache the sync state in case we have multiple requests for it at once
|
||||
let mut sync_state = None;
|
||||
while let Poll::Ready(cmd) = self.comm.poll_recv(cx) {
|
||||
match cmd {
|
||||
None => {},
|
||||
Some(B3270Request::Resync(sender)) => {
|
||||
if sync_state.is_none() {
|
||||
sync_state = Some(self.tracker.get_init_indication());
|
||||
}
|
||||
// it's OK for this to fail; we just don't get a new client
|
||||
sender.send((sync_state.clone().unwrap(), self.ind_chan.subscribe())).ok();
|
||||
}
|
||||
Some(B3270Request::Action(actions, response_chan)) => {
|
||||
let tag = 'find_tag: loop {
|
||||
let tag = rand::thread_rng().next_u64().to_le_bytes();
|
||||
let tag = B64_STANDARD.encode(tag);
|
||||
if !self.action_response_map.contains_key(&tag) {
|
||||
break 'find_tag tag;
|
||||
}
|
||||
};
|
||||
let op = Operation::Run(operation::Run {
|
||||
r_tag: Some(tag.clone()),
|
||||
type_: Some("keymap".to_owned()),
|
||||
actions,
|
||||
});
|
||||
let result = serde_json::to_writer(
|
||||
&mut self.write_buf,
|
||||
&op
|
||||
);
|
||||
match result {
|
||||
Ok(()) => {
|
||||
self.write_buf.push_back(b'\n');
|
||||
self.action_response_map.insert(tag, response_chan);
|
||||
},
|
||||
Err(error) => error!(?op, %error, "Failed to serialize op"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, check if there's anything to be written
|
||||
'write: while !self.write_buf.is_empty() {
|
||||
let myself = &mut *self;
|
||||
let chunk = myself.write_buf.chunk();
|
||||
let stdin = Pin::new(myself.child.stdin.as_mut().expect("Should always have child stdin"));
|
||||
match stdin.poll_write(cx, chunk) {
|
||||
Poll::Pending | Poll::Ready(Ok(0)) => {
|
||||
break 'write;
|
||||
}
|
||||
Poll::Ready(Ok(n)) => {
|
||||
myself.write_buf.advance(n);
|
||||
}
|
||||
Poll::Ready(Err(error)) => {
|
||||
warn!(%error, "Failed to write to b3270");
|
||||
}
|
||||
}
|
||||
}
|
||||
// We only complete when the child dies, which we catch above
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
55
d3270d/src/main.rs
Normal file
55
d3270d/src/main.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use std::ffi::OsString;
|
||||
use std::process::Stdio;
|
||||
use std::str::FromStr;
|
||||
use anyhow::anyhow;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let mut subprocess_args = vec![
|
||||
OsString::from_str("-json").unwrap(),
|
||||
];
|
||||
let mut args_iter = std::env::args_os().peekable();
|
||||
let mut connect_str = None;
|
||||
while let Some(arg) = args_iter.next() {
|
||||
// we default to one of the ignored args
|
||||
match arg.to_str().unwrap_or("-json") {
|
||||
"-json" | "-xml" | "-indent" | "--" |
|
||||
"-scriptportonce" | "-nowrapperdoc" |
|
||||
"-socket" | "-v" | "--version" => {}
|
||||
"-scriptport" | "-httpd" => {
|
||||
args_iter.next();
|
||||
}
|
||||
"-connect" => {
|
||||
connect_str = args_iter.next()
|
||||
.ok_or_else(|| anyhow!("Arg required for -connect"))
|
||||
.and_then(|arg| arg.into_string().map_err(|_| anyhow!("Invalid connect string")))
|
||||
.map(Some)?;
|
||||
}
|
||||
"-e" => {
|
||||
'skip: while let Some(arg) = args_iter.peek() {
|
||||
if arg.to_str().unwrap_or("").starts_with("-") {
|
||||
break 'skip;
|
||||
}
|
||||
args_iter.next();
|
||||
}
|
||||
}
|
||||
_ => subprocess_args.push(arg),
|
||||
}
|
||||
}
|
||||
|
||||
let _connect_str = connect_str.ok_or_else(||anyhow!("No connect string given"))?;
|
||||
|
||||
let subproc = tokio::process::Command::new("b3270")
|
||||
.args(&subprocess_args)
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let (_server, _server_req) = connection::B3270::spawn(subproc);
|
||||
// TODO: make connection before starting listeners
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub mod connection;
|
||||
8
qt3270/Cargo.toml
Normal file
8
qt3270/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "qt3270"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
3
qt3270/src/main.rs
Normal file
3
qt3270/src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
eprintln!("Hello from d3270c");
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
|
||||
pub struct Tracker {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user