瀏覽代碼

Added a crates for the frontend and backend of the console app.

Matthew Carr 1 年之前
父節點
當前提交
e3e74ef5d8

+ 609 - 23
Cargo.lock

@@ -30,9 +30,9 @@ dependencies = [
 
 [[package]]
 name = "aho-corasick"
-version = "0.7.19"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
 dependencies = [
  "memchr",
 ]
@@ -61,6 +61,12 @@ dependencies = [
  "backtrace",
 ]
 
+[[package]]
+name = "anymap2"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
+
 [[package]]
 name = "arc-swap"
 version = "1.5.1"
@@ -69,13 +75,13 @@ checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164"
 
 [[package]]
 name = "async-trait"
-version = "0.1.64"
+version = "0.1.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
+checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 1.0.100",
+ "syn 2.0.13",
 ]
 
 [[package]]
@@ -95,6 +101,55 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
+[[package]]
+name = "axum"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "mime",
+ "rustversion",
+ "tower-layer",
+ "tower-service",
+]
+
 [[package]]
 name = "backtrace"
 version = "0.3.66"
@@ -141,6 +196,15 @@ dependencies = [
  "smallvec",
 ]
 
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "bindgen"
 version = "0.63.0"
@@ -184,6 +248,12 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "boolinator"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
+
 [[package]]
 name = "btconfig"
 version = "0.1.0"
@@ -194,6 +264,34 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "btconsole"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "axum",
+ "btconsole-client",
+ "btlib",
+ "bytes",
+ "ctrlc",
+ "env_logger",
+ "futures",
+ "hyper",
+ "log",
+ "regex",
+ "tokio",
+ "tower",
+ "tower-http",
+ "yew",
+]
+
+[[package]]
+name = "btconsole-client"
+version = "0.1.0"
+dependencies = [
+ "yew",
+]
+
 [[package]]
 name = "btfproto"
 version = "0.1.0"
@@ -367,14 +465,6 @@ dependencies = [
  "serde-big-array",
 ]
 
-[[package]]
-name = "btweb"
-version = "0.1.0"
-dependencies = [
- "hyper",
- "tokio",
-]
-
 [[package]]
 name = "bumpalo"
 version = "3.11.1"
@@ -389,9 +479,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
 name = "bytes"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
+checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
 
 [[package]]
 name = "caps"
@@ -533,6 +623,16 @@ dependencies = [
  "yaml-rust",
 ]
 
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "const-oid"
 version = "0.9.1"
@@ -663,6 +763,16 @@ dependencies = [
  "syn 1.0.100",
 ]
 
+[[package]]
+name = "ctrlc"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04d778600249295e82b6ab12e291ed9029407efee0cfb7baf67157edc65964df"
+dependencies = [
+ "nix 0.26.2",
+ "windows-sys 0.48.0",
+]
+
 [[package]]
 name = "cxx"
 version = "1.0.82"
@@ -835,6 +945,15 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 
+[[package]]
+name = "form_urlencoded"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
+dependencies = [
+ "percent-encoding",
+]
+
 [[package]]
 name = "fuchsia-cprng"
 version = "0.1.1"
@@ -989,6 +1108,171 @@ version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 
+[[package]]
+name = "gloo"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a4bef6b277b3ab073253d4bca60761240cf8d6998f4bd142211957b69a61b20"
+dependencies = [
+ "gloo-console",
+ "gloo-dialogs",
+ "gloo-events",
+ "gloo-file",
+ "gloo-history",
+ "gloo-net",
+ "gloo-render",
+ "gloo-storage",
+ "gloo-timers",
+ "gloo-utils",
+ "gloo-worker",
+]
+
+[[package]]
+name = "gloo-console"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-dialogs"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-events"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-file"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7"
+dependencies = [
+ "gloo-events",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-history"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd451019e0b7a2b8a7a7b23e74916601abf1135c54664e57ff71dcc26dfcdeb7"
+dependencies = [
+ "gloo-events",
+ "gloo-utils",
+ "serde",
+ "serde-wasm-bindgen",
+ "serde_urlencoded",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-net"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-sink",
+ "gloo-utils",
+ "js-sys",
+ "pin-project",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-render"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764"
+dependencies = [
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-storage"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480"
+dependencies = [
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-timers"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "gloo-utils"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5"
+dependencies = [
+ "js-sys",
+ "serde",
+ "serde_json",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gloo-worker"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a"
+dependencies = [
+ "anymap2",
+ "bincode",
+ "gloo-console",
+ "gloo-utils",
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+]
+
 [[package]]
 name = "h2"
 version = "0.3.19"
@@ -1056,6 +1340,15 @@ version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f558a64ac9af88b5ba400d99b579451af0d39c6d360980045b91aac966d705e2"
 
+[[package]]
+name = "html-escape"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476"
+dependencies = [
+ "utf8-width",
+]
+
 [[package]]
 name = "http"
 version = "0.2.9"
@@ -1078,6 +1371,12 @@ dependencies = [
  "pin-project-lite",
 ]
 
+[[package]]
+name = "http-range-header"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
+
 [[package]]
 name = "httparse"
 version = "1.8.0"
@@ -1144,6 +1443,15 @@ dependencies = [
  "cxx-build",
 ]
 
+[[package]]
+name = "implicit-clone"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7"
+dependencies = [
+ "indexmap",
+]
+
 [[package]]
 name = "indexmap"
 version = "1.9.2"
@@ -1186,9 +1494,9 @@ dependencies = [
 
 [[package]]
 name = "itoa"
-version = "1.0.4"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
 
 [[package]]
 name = "js-sys"
@@ -1277,6 +1585,12 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "matchit"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40"
+
 [[package]]
 name = "mbox"
 version = "0.6.0"
@@ -1312,6 +1626,22 @@ dependencies = [
  "autocfg",
 ]
 
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mime_guess"
+version = "2.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
+dependencies = [
+ "mime",
+ "unicase",
+]
+
 [[package]]
 name = "minimal-lexical"
 version = "0.2.1"
@@ -1365,6 +1695,18 @@ dependencies = [
  "pin-utils",
 ]
 
+[[package]]
+name = "nix"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+ "static_assertions",
+]
+
 [[package]]
 name = "nom"
 version = "7.1.1"
@@ -1539,6 +1881,12 @@ dependencies = [
  "base64",
 ]
 
+[[package]]
+name = "percent-encoding"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
+
 [[package]]
 name = "pest"
 version = "2.6.0"
@@ -1618,6 +1966,26 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "pin-project"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.13",
+]
+
 [[package]]
 name = "pin-project-lite"
 version = "0.2.9"
@@ -1630,6 +1998,17 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
+[[package]]
+name = "pinned"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b"
+dependencies = [
+ "futures",
+ "rustversion",
+ "thiserror",
+]
+
 [[package]]
 name = "pkg-config"
 version = "0.3.25"
@@ -1681,6 +2060,40 @@ version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
+[[package]]
+name = "prettyplease"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+dependencies = [
+ "proc-macro2",
+ "syn 1.0.100",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.100",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.56"
@@ -1690,6 +2103,23 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "prokio"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488"
+dependencies = [
+ "futures",
+ "gloo",
+ "num_cpus",
+ "once_cell",
+ "pin-project",
+ "pinned",
+ "tokio",
+ "tokio-stream",
+ "wasm-bindgen-futures",
+]
+
 [[package]]
 name = "quinn"
 version = "0.9.3"
@@ -1859,9 +2289,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.6.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1870,9 +2300,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.27"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
 
 [[package]]
 name = "remove_dir_all"
@@ -2111,6 +2541,17 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde-wasm-bindgen"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
+dependencies = [
+ "js-sys",
+ "serde",
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "serde_bytes"
 version = "0.11.7"
@@ -2142,6 +2583,27 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7f05c1d5476066defcdfacce1f52fc3cae3af1d3089727100c02ae92e5abbe0"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
 [[package]]
 name = "sha2"
 version = "0.10.6"
@@ -2274,6 +2736,12 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
 [[package]]
 name = "synstructure"
 version = "0.12.6"
@@ -2387,9 +2855,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
-version = "1.24.2"
+version = "1.25.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
+checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
 dependencies = [
  "autocfg",
  "bytes",
@@ -2462,6 +2930,53 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d1d42a9b3f3ec46ba828e8d376aec14592ea199f70a06a548587ecd1c4ab658"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-core",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-range-header",
+ "httpdate",
+ "mime",
+ "mime_guess",
+ "percent-encoding",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
 [[package]]
 name = "tower-service"
 version = "0.3.2"
@@ -2475,6 +2990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
 dependencies = [
  "cfg-if",
+ "log",
  "pin-project-lite",
  "tracing-attributes",
  "tracing-core",
@@ -2551,6 +3067,15 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
 
+[[package]]
+name = "unicase"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+dependencies = [
+ "version_check",
+]
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.4"
@@ -2575,6 +3100,12 @@ version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
 
+[[package]]
+name = "utf8-width"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5190c9442dcdaf0ddd50f37420417d219ae5261bbf5db120d0f9bab996c9cba1"
+
 [[package]]
 name = "vcpkg"
 version = "0.2.15"
@@ -2665,6 +3196,18 @@ dependencies = [
  "wasm-bindgen-shared",
 ]
 
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
 [[package]]
 name = "wasm-bindgen-macro"
 version = "0.2.83"
@@ -2949,6 +3492,49 @@ dependencies = [
  "linked-hash-map",
 ]
 
+[[package]]
+name = "yew"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc"
+dependencies = [
+ "base64ct",
+ "bincode",
+ "console_error_panic_hook",
+ "futures",
+ "gloo",
+ "html-escape",
+ "implicit-clone",
+ "indexmap",
+ "js-sys",
+ "prokio",
+ "rustversion",
+ "serde",
+ "slab",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "yew-macro",
+]
+
+[[package]]
+name = "yew-macro"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b64c253c1d401f1ea868ca9988db63958cfa15a69f739101f338d6f05eea8301"
+dependencies = [
+ "boolinator",
+ "once_cell",
+ "prettyplease",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.100",
+]
+
 [[package]]
 name = "zerocopy"
 version = "0.6.1"

+ 18 - 0
crates/btconsole-client/Cargo.toml

@@ -0,0 +1,18 @@
+[package]
+name = "btconsole-client"
+version = "0.1.0"
+authors = ["Matthew Carr <mdcarr941@gmail.com>"]
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[features]
+default = ["csr"]
+csr = ["yew/csr", "yew/hydration"]
+
+[[bin]]
+name = "btconsole-client-bin"
+required-features = ["csr"]
+
+[dependencies]
+yew = { version="0.20" }

+ 3 - 0
crates/btconsole-client/Trunk.toml

@@ -0,0 +1,3 @@
+[build]
+target = "index.html"
+dist = "../../target/dist"

+ 10 - 0
crates/btconsole-client/index.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <title>Trunk Template</title>
+    <link data-trunk rel="sass" href="index.scss" />
+    <link data-trunk rel="rust" data-bin="btconsole-client-bin" data-cargo-features="csr" />
+  </head>
+  <body><!-- __BODY__ --></body>
+</html>

+ 35 - 0
crates/btconsole-client/index.scss

@@ -0,0 +1,35 @@
+html,
+body {
+  height: 100%;
+  margin: 0;
+}
+
+body {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+
+  background: linear-gradient(to bottom right, #444444, #009a5b);
+  font-size: 1.5rem;
+}
+
+main {
+  color: #fff6d5;
+  font-family: sans-serif;
+  text-align: center;
+}
+
+.logo {
+  height: 20em;
+}
+
+.heart:after {
+  content: "❤️";
+
+  font-size: 1.75em;
+}
+
+h1 + .subtitle {
+  display: block;
+  margin-top: -1em;
+}

+ 12 - 0
crates/btconsole-client/src/app.rs

@@ -0,0 +1,12 @@
+use yew::prelude::*;
+
+#[function_component(App)]
+pub fn app() -> Html {
+    html! {
+        <main>
+            <img class="logo" src="https://yew.rs/img/logo.png" alt="Yew logo" />
+            <h1>{ "Hello World!" }</h1>
+            <span class="subtitle">{ "from Yew with " }<i class="heart" /></span>
+        </main>
+    }
+}

+ 6 - 0
crates/btconsole-client/src/bin/btconsole-client-bin.rs

@@ -0,0 +1,6 @@
+use btconsole_client::App;
+use yew;
+
+fn main() {
+    yew::Renderer::<App>::new().hydrate();
+}

+ 2 - 0
crates/btconsole-client/src/lib.rs

@@ -0,0 +1,2 @@
+mod app;
+pub use app::App;

+ 27 - 0
crates/btconsole/Cargo.toml

@@ -0,0 +1,27 @@
+[package]
+name = "btconsole"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[[bin]]
+name = "btconsole-bin"
+required-features = ["tokio/rt-multi-thread", "env_logger", "ctrlc"]
+
+[dependencies]
+btlib = { path = "../btlib" }
+btconsole-client = { path = "../btconsole-client", default-features=false }
+yew = { version="0.20", default-features=false, features=["ssr"] }
+hyper = { version = "0.14.26", features = ["server", "tcp", "http1", "http2", "runtime"] }
+tokio = { version = "1.23.0", features = ["macros", "rt"] }
+tower-http = { version = "0.4.0", features = ["fs"] }
+bytes = { version = "1.4.0", features = ["std"] }
+anyhow = { version = "1.0.66", features = ["std", "backtrace"] }
+tower = { version = "0.4", features = ["util"] }
+axum = "0.6.18"
+futures = "0.3.25"
+log = "0.4.17"
+regex = "1.8.1"
+env_logger = { version = "0.9.0", optional = true }
+ctrlc = { version = "3.3.0", optional = true }

+ 4 - 0
crates/btconsole/autobuild.sh

@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+# Automatically builds btconsole and btconsole-client when files in their respective crates change.
+set -euo pipefail
+cargo watch -- sh -c 'cd ../btconsole-client && trunk build && cd - && cargo run --features="tokio/rt-multi-thread env_logger ctrlc" --bin btconsole-bin'

+ 27 - 0
crates/btconsole/src/bin/btconsole-bin.rs

@@ -0,0 +1,27 @@
+use btconsole::{ServerConfig, start_server};
+use btlib::{Result, log::BuilderExt};
+use std::{
+    net::SocketAddr,
+    path::PathBuf,
+};
+
+const DEFAULT_LOG_LEVEL: &str = "info";
+
+#[tokio::main]
+async fn main() -> Result<()> {
+        if std::env::var("RUST_LOG").is_err() {
+            std::env::set_var("RUST_LOG", DEFAULT_LOG_LEVEL)
+        }
+        env_logger::Builder::from_default_env().btformat().init();
+
+        let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
+        let dist_path = PathBuf::from("../../target/dist");
+        let config = ServerConfig::new(addr, dist_path);
+        let mut server = start_server(config).await.unwrap();
+        let mut signal = Some(server.take_shutdown_signal().unwrap());
+        ctrlc::set_handler(move || {
+            signal.take().map(|tx| tx.send(()).expect("failed to send shutdown signal"));
+        }).expect("failed to install the CTRL-C handler");
+
+        server.has_shutdown().await
+}

+ 96 - 0
crates/btconsole/src/lib.rs

@@ -0,0 +1,96 @@
+use std::{
+    path::{PathBuf},
+    net::SocketAddr,
+    result::Result as StdResult,
+};
+
+use btconsole_client::App;
+use btlib::{bterr, Result};
+use bytes::{Bytes, BytesMut};
+use tokio::{task::JoinHandle, sync::oneshot};
+use yew::ServerRenderer;
+use axum::{Router, response::Html, routing::get};
+use tower_http::services::ServeDir;
+use tower::Service;
+use futures::FutureExt;
+use regex::Regex;
+
+pub struct ServerConfig {
+    addr: SocketAddr,
+    dist_path: PathBuf,
+}
+
+impl ServerConfig {
+    pub fn new(addr: SocketAddr, asset_path: PathBuf) -> Self {
+        Self {
+            addr,
+            dist_path: asset_path,
+        }
+    }
+}
+
+pub struct ServerHandle {
+    handle: JoinHandle<StdResult<(), hyper::Error>>,
+    stop: Option<oneshot::Sender<()>>,
+}
+
+impl ServerHandle {
+    fn new(handle: JoinHandle<StdResult<(), hyper::Error>>, stop: oneshot::Sender<()>) -> Self {
+        Self { handle, stop: Some(stop) }
+    }
+
+    pub fn signal_shutdown(&mut self) -> Result<()> {
+        if let Some(stop) = self.stop.take() {
+            stop.send(()).map_err(|_| bterr!("failed to send stop signal to server"))?;
+        }
+        Ok(())
+    }
+
+    pub fn take_shutdown_signal(&mut self) -> Option<oneshot::Sender<()>> {
+        self.stop.take()
+    }
+
+    pub async fn has_shutdown(self) -> Result<()> {
+        self.handle.await?.map_err(|err| err.into())
+    }
+}
+
+pub async fn start_server(config: ServerConfig) -> Result<ServerHandle> {
+    let rendered: Bytes = {
+        let index_path = config.dist_path.join("index.html");
+        let (index, rendered) = tokio::join!(
+            tokio::fs::read_to_string(&index_path),
+            ServerRenderer::<App>::new().render(),
+        );
+        let index = index?;
+        let regex = Regex::new("<\\/?body>")?;
+        let fields = regex.split(&index);
+        let mut bytes = BytesMut::with_capacity(index.len() + rendered.len() + 13);
+        for (index, entry) in fields.enumerate() {
+            if 1 == index {
+                bytes.extend_from_slice(b"<body>");
+                bytes.extend_from_slice(rendered.as_bytes());
+                bytes.extend_from_slice(b"</body>");
+            } else {
+                bytes.extend_from_slice(entry.as_bytes());
+            }
+        }
+        bytes.into()
+    };
+    let mut serve_dir = ServeDir::new(config.dist_path)
+        .append_index_html_on_directories(false);
+
+    let app = Router::new()
+        .route("/", get(|| async move { Html(rendered.clone()) } ))
+        .route("/*path", get(move |req| serve_dir.call(req)));
+
+    log::info!("listening at http://{}", config.addr);
+
+    let (tx, rx) = oneshot::channel::<()>();
+    let server = axum::Server::bind(&config.addr)
+        .serve(app.into_make_service())
+        .with_graceful_shutdown(rx.map(|result| result.unwrap()));
+    let handle = tokio::spawn(server);
+
+    Ok(ServerHandle::new(handle, tx))
+}