add http-client
This commit is contained in:
parent
ac2904588c
commit
8bcd555d71
@ -5,6 +5,8 @@
|
||||
<sourceFolder url="file://$MODULE_DIR$/bank/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/http-server/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/socket-server/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/http-client/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/http-lib/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
|
356
Cargo.lock
generated
356
Cargo.lock
generated
@ -19,6 +19,21 @@ dependencies = [
|
||||
"tokio-util 0.7.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-cors"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "414360eed71ba2d5435b185ba43ecbe281dfab5df3898286d6b7be8074372c92"
|
||||
dependencies = [
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"derive_more",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.0.4"
|
||||
@ -228,6 +243,12 @@ version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
|
||||
|
||||
[[package]]
|
||||
name = "anymap2"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@ -259,6 +280,15 @@ version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@ -274,6 +304,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 = "brotli"
|
||||
version = "3.3.3"
|
||||
@ -295,6 +331,12 @@ dependencies = [
|
||||
"alloc-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.1.0"
|
||||
@ -325,6 +367,16 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[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 = "convert_case"
|
||||
version = "0.4.0"
|
||||
@ -496,8 +548,116 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23947965eee55e3e97a5cd142dd4c10631cc349b48cecca0ed230fd296f568cd"
|
||||
dependencies = [
|
||||
"gloo-console",
|
||||
"gloo-dialogs",
|
||||
"gloo-events",
|
||||
"gloo-file",
|
||||
"gloo-render",
|
||||
"gloo-storage",
|
||||
"gloo-timers",
|
||||
"gloo-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-console"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3907f786f65bbb4f419e918b0c5674175ef1c231ecda93b2dbd65fd1e8882637"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-dialogs"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ffb557a2ea2ed283f1334423d303a336fad55fb8572d51ae488f828b1464b40"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-events"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "088514ec8ef284891c762c88a66b639b3a730134714692ee31829765c5bc814f"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-file"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa5d6084efa4a2b182ef3a8649cb6506cb4843f22cf907c6e0a799944248ae90"
|
||||
dependencies = [
|
||||
"gloo-events",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-render"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b4cda6e149df3bb4a3c6a343873903e5bcc2448a9877d61bb8274806ad67f6e"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-storage"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5057761927af1b1929d02b1f49cf83553dd347a473ee7c8bb08420f2673ffc"
|
||||
dependencies = [
|
||||
"gloo-utils",
|
||||
"js-sys",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-utils"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05c77af6f96a4f9e27c8ac23a88407381a31f4a74c3fb985c85aa79b8d898136"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -545,12 +705,39 @@ dependencies = [
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bank",
|
||||
"gloo-console",
|
||||
"http-lib",
|
||||
"js-sys",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"yew",
|
||||
"yew-agent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-lib"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bank",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-cors",
|
||||
"actix-web",
|
||||
"bank",
|
||||
"http-lib",
|
||||
"log",
|
||||
"pretty_env_logger",
|
||||
"serde",
|
||||
@ -615,6 +802,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.3.2"
|
||||
@ -817,6 +1013,30 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[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",
|
||||
"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.36"
|
||||
@ -912,6 +1132,12 @@ version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls-hkt"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2e9d7eaddb227e8fbaaa71136ae0e1e913ca159b86c7da82f3e8f0044ad3a63"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@ -1226,6 +1452,84 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
@ -1300,6 +1604,58 @@ version = "0.32.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
|
||||
|
||||
[[package]]
|
||||
name = "yew"
|
||||
version = "0.19.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a1ccb53e57d3f7d847338cf5758befa811cabe207df07f543c06f502f9998cd"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"gloo",
|
||||
"gloo-utils",
|
||||
"indexmap",
|
||||
"js-sys",
|
||||
"scoped-tls-hkt",
|
||||
"slab",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"yew-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-agent"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616700dc3851945658c44ba4477ede6b77c795462fbbb9b0ad9a8b6273a3ca77"
|
||||
dependencies = [
|
||||
"anymap2",
|
||||
"bincode",
|
||||
"gloo-console",
|
||||
"gloo-utils",
|
||||
"js-sys",
|
||||
"serde",
|
||||
"slab",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"yew",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-macro"
|
||||
version = "0.19.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fab79082b556d768d6e21811869c761893f0450e1d550a67892b9bce303b7bb"
|
||||
dependencies = [
|
||||
"boolinator",
|
||||
"lazy_static",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.10.0+zstd.1.5.2"
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
members = [
|
||||
"bank",
|
||||
"http-client",
|
||||
"http-lib",
|
||||
"http-server",
|
||||
"socket-server",
|
||||
]
|
@ -5,4 +5,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0.30"
|
||||
uuid = { version = "0.8.2", features = ["v4"] }
|
||||
uuid = { version = "0.8.2", features = ["v4", "wasm-bindgen"] }
|
@ -42,8 +42,6 @@ impl PartialEq for Account {
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Account {}
|
||||
|
||||
impl Hash for Account {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.number.hash(state);
|
||||
|
17
http-client/Cargo.toml
Normal file
17
http-client/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "http-client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bank = { path = "../bank" }
|
||||
http-lib = { path = "../http-lib" }
|
||||
gloo-console = "0.2.1"
|
||||
js-sys = "0.3.56"
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
||||
serde_json = "1.0.79"
|
||||
wasm-bindgen = "0.2.79"
|
||||
wasm-bindgen-futures = "0.4.29"
|
||||
web-sys = { version = "0.3.56", features = ["Headers", "HtmlSelectElement", "Request", "RequestInit", "RequestMode", "Response", "Window"] }
|
||||
yew = "0.19.3"
|
||||
yew-agent = "0.1.0"
|
588
http-client/dist/index-57ce73b23c43b0f5.js
vendored
Normal file
588
http-client/dist/index-57ce73b23c43b0f5.js
vendored
Normal file
@ -0,0 +1,588 @@
|
||||
|
||||
let wasm;
|
||||
|
||||
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
let cachegetUint8Memory0 = null;
|
||||
function getUint8Memory0() {
|
||||
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetUint8Memory0;
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
const heap = new Array(32).fill(undefined);
|
||||
|
||||
heap.push(undefined, null, true, false);
|
||||
|
||||
let heap_next = heap.length;
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
function getObject(idx) { return heap[idx]; }
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
let cachedTextEncoder = new TextEncoder('utf-8');
|
||||
|
||||
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||
? function (arg, view) {
|
||||
return cachedTextEncoder.encodeInto(arg, view);
|
||||
}
|
||||
: function (arg, view) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length
|
||||
};
|
||||
});
|
||||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length);
|
||||
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len);
|
||||
|
||||
const mem = getUint8Memory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3);
|
||||
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let cachegetInt32Memory0 = null;
|
||||
function getInt32Memory0() {
|
||||
if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetInt32Memory0;
|
||||
}
|
||||
|
||||
let cachegetFloat64Memory0 = null;
|
||||
function getFloat64Memory0() {
|
||||
if (cachegetFloat64Memory0 === null || cachegetFloat64Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetFloat64Memory0 = new Float64Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetFloat64Memory0;
|
||||
}
|
||||
|
||||
function dropObject(idx) {
|
||||
if (idx < 36) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
function debugString(val) {
|
||||
// primitive types
|
||||
const type = typeof val;
|
||||
if (type == 'number' || type == 'boolean' || val == null) {
|
||||
return `${val}`;
|
||||
}
|
||||
if (type == 'string') {
|
||||
return `"${val}"`;
|
||||
}
|
||||
if (type == 'symbol') {
|
||||
const description = val.description;
|
||||
if (description == null) {
|
||||
return 'Symbol';
|
||||
} else {
|
||||
return `Symbol(${description})`;
|
||||
}
|
||||
}
|
||||
if (type == 'function') {
|
||||
const name = val.name;
|
||||
if (typeof name == 'string' && name.length > 0) {
|
||||
return `Function(${name})`;
|
||||
} else {
|
||||
return 'Function';
|
||||
}
|
||||
}
|
||||
// objects
|
||||
if (Array.isArray(val)) {
|
||||
const length = val.length;
|
||||
let debug = '[';
|
||||
if (length > 0) {
|
||||
debug += debugString(val[0]);
|
||||
}
|
||||
for(let i = 1; i < length; i++) {
|
||||
debug += ', ' + debugString(val[i]);
|
||||
}
|
||||
debug += ']';
|
||||
return debug;
|
||||
}
|
||||
// Test for built-in
|
||||
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
|
||||
let className;
|
||||
if (builtInMatches.length > 1) {
|
||||
className = builtInMatches[1];
|
||||
} else {
|
||||
// Failed to match the standard '[object ClassName]'
|
||||
return toString.call(val);
|
||||
}
|
||||
if (className == 'Object') {
|
||||
// we're a user defined class or Object
|
||||
// JSON.stringify avoids problems with cycles, and is generally much
|
||||
// easier than looping through ownProperties of `val`.
|
||||
try {
|
||||
return 'Object(' + JSON.stringify(val) + ')';
|
||||
} catch (_) {
|
||||
return 'Object';
|
||||
}
|
||||
}
|
||||
// errors
|
||||
if (val instanceof Error) {
|
||||
return `${val.name}: ${val.message}\n${val.stack}`;
|
||||
}
|
||||
// TODO we could test for more things here, like `Set`s and `Map`s.
|
||||
return className;
|
||||
}
|
||||
|
||||
function makeClosure(arg0, arg1, dtor, f) {
|
||||
const state = { a: arg0, b: arg1, cnt: 1, dtor };
|
||||
const real = (...args) => {
|
||||
// First up with a closure we increment the internal reference
|
||||
// count. This ensures that the Rust closure environment won't
|
||||
// be deallocated while we're invoking it.
|
||||
state.cnt++;
|
||||
try {
|
||||
return f(state.a, state.b, ...args);
|
||||
} finally {
|
||||
if (--state.cnt === 0) {
|
||||
wasm.__wbindgen_export_2.get(state.dtor)(state.a, state.b);
|
||||
state.a = 0;
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
real.original = state;
|
||||
|
||||
return real;
|
||||
}
|
||||
function __wbg_adapter_22(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__Fn__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0b1695f7853f7e5d(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function makeMutClosure(arg0, arg1, dtor, f) {
|
||||
const state = { a: arg0, b: arg1, cnt: 1, dtor };
|
||||
const real = (...args) => {
|
||||
// First up with a closure we increment the internal reference
|
||||
// count. This ensures that the Rust closure environment won't
|
||||
// be deallocated while we're invoking it.
|
||||
state.cnt++;
|
||||
const a = state.a;
|
||||
state.a = 0;
|
||||
try {
|
||||
return f(a, state.b, ...args);
|
||||
} finally {
|
||||
if (--state.cnt === 0) {
|
||||
wasm.__wbindgen_export_2.get(state.dtor)(a, state.b);
|
||||
|
||||
} else {
|
||||
state.a = a;
|
||||
}
|
||||
}
|
||||
};
|
||||
real.original = state;
|
||||
|
||||
return real;
|
||||
}
|
||||
function __wbg_adapter_25(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h0235f8df8778306b(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
let cachegetUint32Memory0 = null;
|
||||
function getUint32Memory0() {
|
||||
if (cachegetUint32Memory0 === null || cachegetUint32Memory0.buffer !== wasm.memory.buffer) {
|
||||
cachegetUint32Memory0 = new Uint32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachegetUint32Memory0;
|
||||
}
|
||||
|
||||
function getArrayJsValueFromWasm0(ptr, len) {
|
||||
const mem = getUint32Memory0();
|
||||
const slice = mem.subarray(ptr / 4, ptr / 4 + len);
|
||||
const result = [];
|
||||
for (let i = 0; i < slice.length; i++) {
|
||||
result.push(takeObject(slice[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function handleError(f, args) {
|
||||
try {
|
||||
return f.apply(this, args);
|
||||
} catch (e) {
|
||||
wasm.__wbindgen_exn_store(addHeapObject(e));
|
||||
}
|
||||
}
|
||||
|
||||
async function load(module, imports) {
|
||||
if (typeof Response === 'function' && module instanceof Response) {
|
||||
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
||||
try {
|
||||
return await WebAssembly.instantiateStreaming(module, imports);
|
||||
|
||||
} catch (e) {
|
||||
if (module.headers.get('Content-Type') != 'application/wasm') {
|
||||
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
|
||||
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bytes = await module.arrayBuffer();
|
||||
return await WebAssembly.instantiate(bytes, imports);
|
||||
|
||||
} else {
|
||||
const instance = await WebAssembly.instantiate(module, imports);
|
||||
|
||||
if (instance instanceof WebAssembly.Instance) {
|
||||
return { instance, module };
|
||||
|
||||
} else {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function init(input) {
|
||||
if (typeof input === 'undefined') {
|
||||
input = new URL('index-57ce73b23c43b0f5_bg.wasm', import.meta.url);
|
||||
}
|
||||
const imports = {};
|
||||
imports.wbg = {};
|
||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||
var ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
var ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
|
||||
var ret = getObject(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_number_new = function(arg0) {
|
||||
var ret = arg0;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_number_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
var ret = typeof(obj) === 'number' ? obj : undefined;
|
||||
getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret);
|
||||
};
|
||||
imports.wbg.__wbg_error_09919627ac0992f5 = function(arg0, arg1) {
|
||||
try {
|
||||
console.error(getStringFromWasm0(arg0, arg1));
|
||||
} finally {
|
||||
wasm.__wbindgen_free(arg0, arg1);
|
||||
}
|
||||
};
|
||||
imports.wbg.__wbg_new_693216e109162396 = function() {
|
||||
var ret = new Error();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_stack_0ddaca5d1abfb52f = function(arg0, arg1) {
|
||||
var ret = getObject(arg1).stack;
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
imports.wbg.__wbg_warn_2aa0e7178e1d35f6 = function(arg0, arg1) {
|
||||
var v0 = getArrayJsValueFromWasm0(arg0, arg1).slice();
|
||||
wasm.__wbindgen_free(arg0, arg1 * 4);
|
||||
console.warn(...v0);
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_Window_434ce1849eb4e0fc = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof Window;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_document_5edd43643d1060d9 = function(arg0) {
|
||||
var ret = getObject(arg0).document;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_fetch_427498e0ccea81f4 = function(arg0, arg1) {
|
||||
var ret = getObject(arg0).fetch(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_body_7538539844356c1c = function(arg0) {
|
||||
var ret = getObject(arg0).body;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_createElement_d017b8d2af99bab9 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).createElement(getStringFromWasm0(arg1, arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_createElementNS_fd4a7e49f74039e1 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
var ret = getObject(arg0).createElementNS(arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_createTextNode_39a0de25d14bcde5 = function(arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).createTextNode(getStringFromWasm0(arg1, arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_value_d3a30bc2c7caf357 = function(arg0, arg1) {
|
||||
var ret = getObject(arg1).value;
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_setvalue_6a34bab301f38bf2 = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_target_e560052e31e4567c = function(arg0) {
|
||||
var ret = getObject(arg0).target;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_cancelBubble_17d7988ab2fbe4c9 = function(arg0) {
|
||||
var ret = getObject(arg0).cancelBubble;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_headers_1a60dec7fbd28a3b = function(arg0) {
|
||||
var ret = getObject(arg0).headers;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_newwithstrandinit_c07f0662ece15bc6 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
var ret = new Request(getStringFromWasm0(arg0, arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_setchecked_f6ead3490df88a7f = function(arg0, arg1) {
|
||||
getObject(arg0).checked = arg1 !== 0;
|
||||
};
|
||||
imports.wbg.__wbg_value_fc1c354d1a0e9714 = function(arg0, arg1) {
|
||||
var ret = getObject(arg1).value;
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_setvalue_ce4a23f487065c07 = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_addEventListener_55682f77717d7665 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).addEventListener(getStringFromWasm0(arg1, arg2), getObject(arg3), getObject(arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_parentElement_96e1e07348340043 = function(arg0) {
|
||||
var ret = getObject(arg0).parentElement;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_lastChild_e2b014abab089e08 = function(arg0) {
|
||||
var ret = getObject(arg0).lastChild;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_setnodeValue_f175b74a390f8fda = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).nodeValue = arg1 === 0 ? undefined : getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_appendChild_3fe5090c665d3bb4 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = getObject(arg0).appendChild(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_insertBefore_4f09909023feac91 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).insertBefore(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_removeChild_f4a83c9698136bbb = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = getObject(arg0).removeChild(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_instanceof_Element_c9423704dd5d9b1d = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof Element;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_namespaceURI_e9a971e6c1ce68db = function(arg0, arg1) {
|
||||
var ret = getObject(arg1).namespaceURI;
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_removeAttribute_1adaecf6b4d35a09 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
getObject(arg0).removeAttribute(getStringFromWasm0(arg1, arg2));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_setAttribute_1776fcc9b98d464e = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).setAttribute(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_set_f9448486a94c9aef = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).set(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_value_d4cea9e999ffb147 = function(arg0, arg1) {
|
||||
var ret = getObject(arg1).value;
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_Response_ea36d565358a42f7 = function(arg0) {
|
||||
var ret = getObject(arg0) instanceof Response;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_text_aeba5a5bbfef7f15 = function() { return handleError(function (arg0) {
|
||||
var ret = getObject(arg0).text();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||
const obj = takeObject(arg0).original;
|
||||
if (obj.cnt-- == 1) {
|
||||
obj.a = 0;
|
||||
return true;
|
||||
}
|
||||
var ret = false;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_newnoargs_f579424187aa1717 = function(arg0, arg1) {
|
||||
var ret = new Function(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_call_89558c3e96703ca1 = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = getObject(arg0).call(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_valueOf_39e0d6bc7e4232b9 = function(arg0) {
|
||||
var ret = getObject(arg0).valueOf();
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_is_3d73f4d91adacc37 = function(arg0, arg1) {
|
||||
var ret = Object.is(getObject(arg0), getObject(arg1));
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_new_d3138911a89329b0 = function() {
|
||||
var ret = new Object();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_resolve_4f8f547f26b30b27 = function(arg0) {
|
||||
var ret = Promise.resolve(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_then_a6860c82b90816ca = function(arg0, arg1) {
|
||||
var ret = getObject(arg0).then(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_then_58a04e42527f52c6 = function(arg0, arg1, arg2) {
|
||||
var ret = getObject(arg0).then(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_globalThis_d61b1f48a57191ae = function() { return handleError(function () {
|
||||
var ret = globalThis.globalThis;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_self_e23d74ae45fb17d1 = function() { return handleError(function () {
|
||||
var ret = self.self;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_window_b4be7f48b24ac56e = function() { return handleError(function () {
|
||||
var ret = window.window;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_global_e7669da72fd7f239 = function() { return handleError(function () {
|
||||
var ret = global.global;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||
var ret = getObject(arg0) === undefined;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_get_8bbb82393651dd9c = function() { return handleError(function (arg0, arg1) {
|
||||
var ret = Reflect.get(getObject(arg0), getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_set_c42875065132a932 = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
var ret = Reflect.set(getObject(arg0), getObject(arg1), getObject(arg2));
|
||||
return ret;
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||
var ret = debugString(getObject(arg1));
|
||||
var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper4145 = function(arg0, arg1, arg2) {
|
||||
var ret = makeClosure(arg0, arg1, 178, __wbg_adapter_22);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper7730 = function(arg0, arg1, arg2) {
|
||||
var ret = makeMutClosure(arg0, arg1, 194, __wbg_adapter_25);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
|
||||
input = fetch(input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const { instance, module } = await load(await input, imports);
|
||||
|
||||
wasm = instance.exports;
|
||||
init.__wbindgen_wasm_module = module;
|
||||
wasm.__wbindgen_start();
|
||||
return wasm;
|
||||
}
|
||||
|
||||
export default init;
|
||||
|
BIN
http-client/dist/index-57ce73b23c43b0f5_bg.wasm
vendored
Normal file
BIN
http-client/dist/index-57ce73b23c43b0f5_bg.wasm
vendored
Normal file
Binary file not shown.
34
http-client/dist/index.html
vendored
Normal file
34
http-client/dist/index.html
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html><html lang="en"><head>
|
||||
<meta charset="utf-8">
|
||||
<title>Vesys Bank</title>
|
||||
<link rel="stylesheet" href="/styles-3143ec7e42adb2c6.css">
|
||||
|
||||
<link rel="preload" href="/index-57ce73b23c43b0f5_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||
<link rel="modulepreload" href="/index-57ce73b23c43b0f5.js"></head>
|
||||
<body><script type="module">import init from '/index-57ce73b23c43b0f5.js';init('/index-57ce73b23c43b0f5_bg.wasm');</script><script>(function () {
|
||||
var url = 'ws://' + window.location.host + '/_trunk/ws';
|
||||
var poll_interval = 5000;
|
||||
var reload_upon_connect = () => {
|
||||
window.setTimeout(
|
||||
() => {
|
||||
// when we successfully reconnect, we'll force a
|
||||
// reload (since we presumably lost connection to
|
||||
// trunk due to it being killed, so it will have
|
||||
// rebuilt on restart)
|
||||
var ws = new WebSocket(url);
|
||||
ws.onopen = () => window.location.reload();
|
||||
ws.onclose = reload_upon_connect;
|
||||
},
|
||||
poll_interval);
|
||||
};
|
||||
|
||||
var ws = new WebSocket(url);
|
||||
ws.onmessage = (ev) => {
|
||||
const msg = JSON.parse(ev.data);
|
||||
if (msg.reload) {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
ws.onclose = reload_upon_connect;
|
||||
})()
|
||||
</script></body></html>
|
123
http-client/dist/styles-3143ec7e42adb2c6.css
vendored
Normal file
123
http-client/dist/styles-3143ec7e42adb2c6.css
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
html, body {
|
||||
font-style: normal;
|
||||
font-family: monospace;
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.account {
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.account__grid {
|
||||
border-style: none;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.account__title {
|
||||
background-color: black;
|
||||
color: white;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
|
||||
.account__label {
|
||||
margin: 0 .5em 0 .5em;
|
||||
font-weight: bold;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.account__amount {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.account__accounts {
|
||||
grid-column: 2 / 4;
|
||||
grid-row: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.account__button {
|
||||
color: black;
|
||||
border-radius: 0;
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
border-width: .2em;
|
||||
padding: 1em;
|
||||
margin: .5em;
|
||||
background: linear-gradient(to top, darkgray, 20%, lightgray);
|
||||
box-shadow: 3px 3px 3px black;
|
||||
}
|
||||
|
||||
.account__button:hover:enabled {
|
||||
background: linear-gradient(lightgray, darkgray);
|
||||
}
|
||||
|
||||
.account__button:active:enabled {
|
||||
transform: translateY(3px);
|
||||
}
|
||||
|
||||
.account__button:disabled {
|
||||
background: lightgray;
|
||||
color: darkgray;
|
||||
}
|
||||
|
||||
.account__input {
|
||||
padding: 1em;
|
||||
color: black;
|
||||
margin: .5em;
|
||||
background-color: #eeeeee;
|
||||
border-radius: 0;
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
border-width: .2em;
|
||||
box-shadow: 3px 3px 3px black;
|
||||
}
|
||||
|
||||
.accounts {
|
||||
list-style-type: none;
|
||||
padding: 0.5em;
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.accounts__item {
|
||||
border-style: solid;
|
||||
border-bottom-color: lightgray;
|
||||
border-width: 0 0 0.1em 0;
|
||||
}
|
||||
|
||||
.accounts__item-selected {
|
||||
border-bottom-color: darkgray;
|
||||
background-color: #eeeeee;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error {
|
||||
background: red;
|
||||
padding: 1em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 1em;
|
||||
min-width: 370px;
|
||||
}
|
||||
|
||||
@media (max-width: 605px) {
|
||||
.account__grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.account__accounts {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
8
http-client/index.html
Normal file
8
http-client/index.html
Normal file
@ -0,0 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Vesys Bank</title>
|
||||
<link data-trunk rel="css" type="text/css" href="styles.css">
|
||||
</head>
|
||||
</html>
|
34
http-client/src/api.rs
Normal file
34
http-client/src/api.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
use http_lib::json_account::JsonAccount;
|
||||
|
||||
use crate::client;
|
||||
use crate::client::FetchError;
|
||||
|
||||
pub async fn fetch_account_nrs(host: &str) -> Result<Vec<String>, FetchError> {
|
||||
match client::get_json(&format! {"{}/accounts", host}).await? {
|
||||
None => Ok(vec![]),
|
||||
Some(response) => {
|
||||
let nrs: Vec<String> = serde_json::from_str(&response)?;
|
||||
Ok(nrs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fetch_account(host: &str, nr: &str) -> Result<JsonAccount, FetchError> {
|
||||
match client::get_json(&format! {"{}/accounts/{}", host, nr}).await? {
|
||||
None => Err(FetchError {
|
||||
err: JsValue::from_str("no such account"),
|
||||
}),
|
||||
Some(response) => {
|
||||
let acc: JsonAccount = serde_json::from_str(&response)?;
|
||||
Ok(acc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_balance(host: &str, nr: &str, balance: f64) -> Result<(), FetchError> {
|
||||
let data = JsValue::from_str(&format!("amount={}", balance));
|
||||
client::put(&format! {"{}/accounts/{}", host, nr}, data).await?;
|
||||
Ok(())
|
||||
}
|
67
http-client/src/client.rs
Normal file
67
http-client/src/client.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use wasm_bindgen::{JsCast, JsValue};
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FetchError {
|
||||
pub err: JsValue,
|
||||
}
|
||||
impl Display for FetchError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
Debug::fmt(&self.err, f)
|
||||
}
|
||||
}
|
||||
impl Error for FetchError {}
|
||||
|
||||
impl From<JsValue> for FetchError {
|
||||
fn from(value: JsValue) -> Self {
|
||||
Self { err: value }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for FetchError {
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
Self {
|
||||
err: JsValue::from(e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_request(request: Request) -> Result<Response, FetchError> {
|
||||
let window = web_sys::window().unwrap();
|
||||
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
||||
let resp: Response = resp_value.dyn_into()?;
|
||||
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
pub async fn get_json(url: &str) -> Result<Option<String>, FetchError> {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
opts.mode(RequestMode::Cors);
|
||||
|
||||
let request = Request::new_with_str_and_init(url, &opts)?;
|
||||
|
||||
request.headers().set("Accept", "application/json")?;
|
||||
|
||||
let resp = self::send_request(request).await?;
|
||||
let text = JsFuture::from(resp.text()?).await?;
|
||||
|
||||
Ok(text.as_string())
|
||||
}
|
||||
|
||||
pub async fn put(url: &str, data: JsValue) -> Result<(), FetchError> {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("PUT");
|
||||
opts.mode(RequestMode::Cors);
|
||||
opts.body(Some(&data));
|
||||
|
||||
let request = Request::new_with_str_and_init(url, &opts)?;
|
||||
request.headers().set("Content-Type", "application/x-www-form-urlencoded")?;
|
||||
self::send_request(request).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
151
http-client/src/components/account.rs
Normal file
151
http-client/src/components/account.rs
Normal file
@ -0,0 +1,151 @@
|
||||
use std::str::FromStr;
|
||||
use web_sys::{HtmlInputElement, HtmlSelectElement};
|
||||
use yew::{classes, html, Component, Context, Html, Properties, NodeRef};
|
||||
use yew_agent::{Dispatched, Dispatcher};
|
||||
use crate::event_bus::{EventBus, Request};
|
||||
use crate::events::Event;
|
||||
|
||||
pub enum Msg {
|
||||
AmountChanged,
|
||||
Deposit,
|
||||
Withdraw,
|
||||
Transfer,
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct AccountProps {
|
||||
pub account_nrs: Vec<String>,
|
||||
pub balance: f64,
|
||||
pub nr: String,
|
||||
pub owner: String,
|
||||
}
|
||||
|
||||
pub struct Account {
|
||||
amount_ref: NodeRef,
|
||||
transfer_account_ref: NodeRef,
|
||||
amount_valid: bool,
|
||||
event_bus: Dispatcher<EventBus>,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
fn amount(&self) -> f64 {
|
||||
if let Some(amount_el) = self.amount_ref.cast::<HtmlInputElement>() {
|
||||
f64::from_str(&amount_el.value()).unwrap_or_default()
|
||||
} else { 0_f64 }
|
||||
}
|
||||
|
||||
fn is_amount_valid(&self) -> bool {
|
||||
self.amount() > 0_f64
|
||||
}
|
||||
|
||||
fn selected_transfer_account(&self) -> Option<String> {
|
||||
if let Some(transfer_account_el) = self.transfer_account_ref.cast::<HtmlSelectElement>() {
|
||||
let value: String = transfer_account_el.value();
|
||||
if value.is_empty() || value == "undefined" { None } else { Some(value) }
|
||||
} else { None }
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Account {
|
||||
type Message = Msg;
|
||||
type Properties = AccountProps;
|
||||
|
||||
fn create(_: &Context<Self>) -> Self {
|
||||
Self {
|
||||
amount_ref: NodeRef::default(),
|
||||
transfer_account_ref: NodeRef::default(),
|
||||
amount_valid: false,
|
||||
event_bus: EventBus::dispatcher(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::AmountChanged => {
|
||||
let is_amount_valid = self.is_amount_valid();
|
||||
let needs_redraw = is_amount_valid != self.amount_valid;
|
||||
self.amount_valid = is_amount_valid;
|
||||
|
||||
needs_redraw
|
||||
}
|
||||
Msg::Deposit => {
|
||||
let amount = self.amount() + ctx.props().balance;
|
||||
self.event_bus.send(Request::EventBusMsg(Event::SetBalance(amount, ctx.props().nr.clone())));
|
||||
|
||||
false
|
||||
}
|
||||
Msg::Withdraw => {
|
||||
let amount = ctx.props().balance - self.amount();
|
||||
if amount > 0_f64 {
|
||||
self.event_bus.send(Request::EventBusMsg(Event::SetBalance(amount, ctx.props().nr.clone())));
|
||||
} else {
|
||||
self.event_bus.send(Request::EventBusMsg(Event::ShowError("Balance can not be overdrawn".into())));
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
Msg::Transfer => {
|
||||
let amount = ctx.props().balance - self.amount();
|
||||
|
||||
if amount > 0_f64 {
|
||||
self.event_bus.send(Request::EventBusMsg(Event::SetBalance(amount, ctx.props().nr.clone())));
|
||||
} else {
|
||||
let transfer_nr = self.selected_transfer_account();
|
||||
if let Some(transfer_nr) = transfer_nr {
|
||||
self.event_bus.send(Request::EventBusMsg(Event::Transfer(amount, ctx.props().nr.clone(), transfer_nr)));
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
let onchange = ctx
|
||||
.link()
|
||||
.callback(|_| Msg::AmountChanged);
|
||||
|
||||
let on_deposit = ctx
|
||||
.link()
|
||||
.callback(|_| Msg::Deposit);
|
||||
|
||||
let on_withdraw = ctx
|
||||
.link()
|
||||
.callback(|_| Msg::Withdraw);
|
||||
|
||||
let on_transfer = ctx
|
||||
.link()
|
||||
.callback(|_| Msg::Transfer);
|
||||
|
||||
html! {
|
||||
<>
|
||||
<section class={classes!("account")}>
|
||||
<fieldset class={classes!("account__grid")}>
|
||||
<legend class={classes!("account__title")}>
|
||||
{&ctx.props().owner} {" — "} {&ctx.props().balance}
|
||||
</legend>
|
||||
|
||||
<div class={classes!("account__amount")}>
|
||||
<label class={classes!("account__label")} for="amount">{"Amount"}</label>
|
||||
<input {onchange} ref={self.amount_ref.clone()} class={classes!("account__input")} id="amount" type="number" step="0.01" min="0" placeholder="0.0" />
|
||||
</div>
|
||||
|
||||
<div class={classes!("account__accounts")}>
|
||||
<label class={classes!("account__label")} for="accounts">{"Remote Account"}</label>
|
||||
<select ref={self.transfer_account_ref.clone()} class={classes!("account__input")} name="accounts" id="account-select">
|
||||
{ for ctx.props().account_nrs.iter().filter(|e| {
|
||||
ctx.props().nr != e.to_string()
|
||||
}).map(|e| html!{ <option value={e.to_string()}>{e}</option> }) }
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button onclick={on_deposit} class={classes!("account__button")} disabled={!self.amount_valid}>{"deposit"}</button>
|
||||
<button onclick={on_withdraw} class={classes!("account__button")} disabled={!self.amount_valid}>{"withdraw"}</button>
|
||||
<button onclick={on_transfer} class={classes!("account__button")} disabled={!self.amount_valid}>{"transfer"}</button>
|
||||
</fieldset>
|
||||
</section>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
73
http-client/src/components/accounts.rs
Normal file
73
http-client/src/components/accounts.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use crate::event_bus::{EventBus, Request};
|
||||
use crate::events::Event;
|
||||
use yew::{classes, html, Classes, Component, Context, Html, Properties};
|
||||
use yew_agent::{Dispatched, Dispatcher};
|
||||
|
||||
pub enum Msg {
|
||||
SelectAccountNr(String),
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct AccountsProps {
|
||||
pub account_nrs: Vec<String>,
|
||||
pub selected_nr: String
|
||||
}
|
||||
|
||||
pub struct Accounts {
|
||||
event_bus: Dispatcher<EventBus>,
|
||||
}
|
||||
|
||||
impl Component for Accounts {
|
||||
type Message = Msg;
|
||||
type Properties = AccountsProps;
|
||||
|
||||
fn create(_: &Context<Self>) -> Self {
|
||||
Self {
|
||||
event_bus: EventBus::dispatcher(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::SelectAccountNr(nr) => {
|
||||
self.event_bus
|
||||
.send(Request::EventBusMsg(Event::SelectAccountNr(nr)));
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<>
|
||||
<ul class={classes!("accounts")}>
|
||||
{ for ctx.props().account_nrs.iter().map(|e| self.account_entry(e, &ctx.props().selected_nr, ctx)) }
|
||||
</ul>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Accounts {
|
||||
fn account_entry(
|
||||
&self,
|
||||
nr: &str,
|
||||
selected_nr: &str,
|
||||
ctx: &Context<Self>,
|
||||
) -> Html {
|
||||
let mut class = Classes::from("accounts__item");
|
||||
if selected_nr == nr {
|
||||
class.push("accounts__item-selected");
|
||||
}
|
||||
|
||||
let nr = nr.to_string();
|
||||
let account_nr = nr.clone();
|
||||
let onclick = ctx
|
||||
.link()
|
||||
.callback(move |_| Msg::SelectAccountNr(nr.clone()));
|
||||
|
||||
html! {
|
||||
<li {onclick} class={class}>{account_nr}</li>
|
||||
}
|
||||
}
|
||||
}
|
117
http-client/src/components/main.rs
Normal file
117
http-client/src/components/main.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use crate::components::account::Account;
|
||||
use crate::components::accounts::Accounts;
|
||||
use crate::event_bus::EventBus;
|
||||
use crate::events::Event;
|
||||
use crate::api;
|
||||
use yew::{classes, html, Component, Context, Html};
|
||||
use yew_agent::{Bridge, Bridged};
|
||||
|
||||
pub struct Main {
|
||||
_subscriber: Box<dyn Bridge<EventBus>>,
|
||||
error: Option<String>,
|
||||
account_nrs: Vec<String>,
|
||||
selected_balance: f64,
|
||||
selected_owner: String,
|
||||
selected_nr: String,
|
||||
}
|
||||
|
||||
impl Main {
|
||||
fn set_selected_account(&self, ctx: &Context<Self>, nr: String) {
|
||||
ctx.link().send_future(async move {
|
||||
match api::fetch_account("http://localhost:8000", &nr).await {
|
||||
Err(e) => Event::ShowError(e.to_string()),
|
||||
Ok(account) => Event::SetSelectedAccount(account),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for Main {
|
||||
type Message = Event;
|
||||
type Properties = ();
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
Self {
|
||||
error: None,
|
||||
_subscriber: EventBus::bridge(ctx.link().callback(|x| x)),
|
||||
account_nrs: vec![],
|
||||
selected_balance: 0_f64,
|
||||
selected_nr: "".into(),
|
||||
selected_owner: "".into()
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Event::GetAccountNrs => {
|
||||
ctx.link().send_future(async {
|
||||
match api::fetch_account_nrs("http://localhost:8000").await {
|
||||
Err(e) => Event::ShowError(e.to_string()),
|
||||
Ok(nrs) => Event::SetAccountNrs(nrs),
|
||||
}
|
||||
});
|
||||
false
|
||||
}
|
||||
Event::SetAccountNrs(nrs) => {
|
||||
if self.account_nrs.is_empty() && !nrs.is_empty() {
|
||||
let nr = nrs[0].clone();
|
||||
self.set_selected_account(ctx, nr);
|
||||
}
|
||||
|
||||
self.account_nrs = nrs;
|
||||
true
|
||||
}
|
||||
Event::ShowError(error) => {
|
||||
self.error = Some(error);
|
||||
true
|
||||
}
|
||||
Event::SetSelectedAccount(account) => {
|
||||
self.selected_balance = account.balance;
|
||||
self.selected_owner = account.owner;
|
||||
self.selected_nr = account.number;
|
||||
|
||||
true
|
||||
}
|
||||
Event::SelectAccountNr(nr) => {
|
||||
self.set_selected_account(ctx, nr);
|
||||
true
|
||||
}
|
||||
Event::SetBalance(balance, nr) => {
|
||||
ctx.link().send_future(async move {
|
||||
match api::set_balance("http://localhost:8000", &nr, balance).await {
|
||||
Err(e) => Event::ShowError(e.to_string()),
|
||||
Ok(_) => Event::SelectAccountNr(nr),
|
||||
}
|
||||
});
|
||||
false
|
||||
}
|
||||
Event::Transfer(amount, from, to) => {
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self, _: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<>
|
||||
if let Some(error_msg) = &self.error {
|
||||
<div class={classes!("error")}>{ error_msg }</div>
|
||||
}
|
||||
<main class={classes!("content")}>
|
||||
<h1>
|
||||
{"welcome to your vaults"}
|
||||
</h1>
|
||||
<Accounts account_nrs={self.account_nrs.clone()} selected_nr={self.selected_nr.clone()} />
|
||||
<Account balance={self.selected_balance} owner={self.selected_owner.clone()} nr={self.selected_nr.clone()} account_nrs={self.account_nrs.clone()} />
|
||||
</main>
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
||||
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
|
||||
if first_render {
|
||||
ctx.link().send_message(Event::GetAccountNrs);
|
||||
}
|
||||
}
|
||||
}
|
3
http-client/src/components/mod.rs
Normal file
3
http-client/src/components/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod account;
|
||||
pub mod accounts;
|
||||
pub mod main;
|
48
http-client/src/event_bus.rs
Normal file
48
http-client/src/event_bus.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use crate::events::Event;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use yew_agent::{Agent, AgentLink, Context, HandlerId};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum Request {
|
||||
EventBusMsg(Event),
|
||||
}
|
||||
|
||||
pub struct EventBus {
|
||||
link: AgentLink<EventBus>,
|
||||
subscribers: HashSet<HandlerId>,
|
||||
}
|
||||
|
||||
impl Agent for EventBus {
|
||||
type Reach = Context<Self>;
|
||||
type Message = ();
|
||||
type Input = Request;
|
||||
type Output = Event;
|
||||
|
||||
fn create(link: AgentLink<Self>) -> Self {
|
||||
Self {
|
||||
link,
|
||||
subscribers: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, _msg: Self::Message) {}
|
||||
|
||||
fn connected(&mut self, id: HandlerId) {
|
||||
self.subscribers.insert(id);
|
||||
}
|
||||
|
||||
fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) {
|
||||
match msg {
|
||||
Request::EventBusMsg(s) => {
|
||||
for sub in self.subscribers.iter() {
|
||||
self.link.respond(*sub, s.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn disconnected(&mut self, id: HandlerId) {
|
||||
self.subscribers.remove(&id);
|
||||
}
|
||||
}
|
13
http-client/src/events.rs
Normal file
13
http-client/src/events.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use http_lib::json_account::JsonAccount;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub enum Event {
|
||||
GetAccountNrs,
|
||||
SetAccountNrs(Vec<String>),
|
||||
SelectAccountNr(String),
|
||||
SetSelectedAccount(JsonAccount),
|
||||
SetBalance(f64, String),
|
||||
ShowError(String),
|
||||
Transfer(f64, String, String),
|
||||
}
|
11
http-client/src/main.rs
Normal file
11
http-client/src/main.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use crate::components::main::Main;
|
||||
|
||||
mod client;
|
||||
mod components;
|
||||
mod event_bus;
|
||||
mod events;
|
||||
mod api;
|
||||
|
||||
fn main() {
|
||||
yew::start_app::<Main>();
|
||||
}
|
123
http-client/styles.css
Normal file
123
http-client/styles.css
Normal file
@ -0,0 +1,123 @@
|
||||
html, body {
|
||||
font-style: normal;
|
||||
font-family: monospace;
|
||||
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.account {
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.account__grid {
|
||||
border-style: none;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
.account__title {
|
||||
background-color: black;
|
||||
color: white;
|
||||
padding: 3px 6px;
|
||||
}
|
||||
|
||||
.account__label {
|
||||
margin: 0 .5em 0 .5em;
|
||||
font-weight: bold;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
.account__amount {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.account__accounts {
|
||||
grid-column: 2 / 4;
|
||||
grid-row: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.account__button {
|
||||
color: black;
|
||||
border-radius: 0;
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
border-width: .2em;
|
||||
padding: 1em;
|
||||
margin: .5em;
|
||||
background: linear-gradient(to top, darkgray, 20%, lightgray);
|
||||
box-shadow: 3px 3px 3px black;
|
||||
}
|
||||
|
||||
.account__button:hover:enabled {
|
||||
background: linear-gradient(lightgray, darkgray);
|
||||
}
|
||||
|
||||
.account__button:active:enabled {
|
||||
transform: translateY(3px);
|
||||
}
|
||||
|
||||
.account__button:disabled {
|
||||
background: lightgray;
|
||||
color: darkgray;
|
||||
}
|
||||
|
||||
.account__input {
|
||||
padding: 1em;
|
||||
color: black;
|
||||
margin: .5em;
|
||||
background-color: #eeeeee;
|
||||
border-radius: 0;
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
border-width: .2em;
|
||||
box-shadow: 3px 3px 3px black;
|
||||
}
|
||||
|
||||
.accounts {
|
||||
list-style-type: none;
|
||||
padding: 0.5em;
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.accounts__item {
|
||||
border-style: solid;
|
||||
border-bottom-color: lightgray;
|
||||
border-width: 0 0 0.1em 0;
|
||||
}
|
||||
|
||||
.accounts__item-selected {
|
||||
border-bottom-color: darkgray;
|
||||
background-color: #eeeeee;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error {
|
||||
background: red;
|
||||
padding: 1em;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 1em;
|
||||
min-width: 370px;
|
||||
}
|
||||
|
||||
@media (max-width: 605px) {
|
||||
.account__grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.account__accounts {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
8
http-lib/Cargo.toml
Normal file
8
http-lib/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "http-lib"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bank = { path = "../bank" }
|
||||
serde = { version = "1.0.136", features = ["derive"] }
|
32
http-lib/src/json_account.rs
Normal file
32
http-lib/src/json_account.rs
Normal file
@ -0,0 +1,32 @@
|
||||
use bank::account::Account;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct JsonAccount {
|
||||
pub number: String,
|
||||
pub owner: String,
|
||||
pub balance: f64,
|
||||
pub is_active: bool,
|
||||
}
|
||||
|
||||
impl From<&Account> for JsonAccount {
|
||||
fn from(a: &Account) -> Self {
|
||||
JsonAccount {
|
||||
number: a.number.clone(),
|
||||
owner: a.owner.clone(),
|
||||
balance: a.balance,
|
||||
is_active: a.is_active,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JsonAccount> for Account {
|
||||
fn from(a: JsonAccount) -> Self {
|
||||
Account {
|
||||
number: a.number.clone(),
|
||||
owner: a.owner.clone(),
|
||||
balance: a.balance,
|
||||
is_active: a.is_active,
|
||||
}
|
||||
}
|
||||
}
|
1
http-lib/src/lib.rs
Normal file
1
http-lib/src/lib.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod json_account;
|
@ -5,6 +5,8 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bank = { path = "../bank" }
|
||||
http-lib = { path = "../http-lib" }
|
||||
actix-cors = "0.6.1"
|
||||
actix-web = "4.0.1"
|
||||
thiserror = "1.0.30"
|
||||
log = "0.4.14"
|
||||
|
@ -1,4 +1,4 @@
|
||||
use actix_web::{post, web, HttpResponse, Responder, Result};
|
||||
use actix_web::{delete, web, HttpResponse, Responder, Result};
|
||||
|
||||
use bank::account::AccountError;
|
||||
use bank::bank::Bank;
|
||||
@ -6,7 +6,7 @@ use bank::bank::Bank;
|
||||
use crate::handlers::error::HttpAccountError;
|
||||
use crate::AppState;
|
||||
|
||||
#[post("/{nr}/close")]
|
||||
#[delete("/{nr}")]
|
||||
pub async fn route(info: web::Path<String>, data: web::Data<AppState>) -> Result<impl Responder> {
|
||||
let nr = info.into_inner();
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use actix_web::{post, web, Responder, Result};
|
||||
use actix_web::{post, web, HttpResponse, Responder, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::AppState;
|
||||
@ -25,5 +25,5 @@ pub async fn route(
|
||||
let nr = bank.create_account(owner.clone());
|
||||
info!("created account {}", nr);
|
||||
|
||||
Ok(web::Json(AccountCreated { nr }))
|
||||
Ok(HttpResponse::Created().json(AccountCreated { nr }))
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use actix_web::{post, web, HttpResponse, Responder, Result};
|
||||
use actix_web::{patch, web, HttpResponse, Responder, Result};
|
||||
use serde::Serialize;
|
||||
|
||||
use bank::bank::Bank;
|
||||
@ -12,7 +12,7 @@ struct AccountCreated {
|
||||
nr: String,
|
||||
}
|
||||
|
||||
#[post("/{nr}/deposit")]
|
||||
#[patch("/{nr}/deposit")]
|
||||
pub async fn route(
|
||||
info: web::Path<String>,
|
||||
form: web::Form<AmountData>,
|
||||
|
@ -36,9 +36,9 @@ impl ResponseError for HttpAccountError {
|
||||
match self.deref() {
|
||||
AccountError::NotFound => StatusCode::NOT_FOUND,
|
||||
AccountError::InvalidAmount => StatusCode::BAD_REQUEST,
|
||||
AccountError::Inactive => StatusCode::BAD_REQUEST,
|
||||
AccountError::Inactive => StatusCode::GONE,
|
||||
AccountError::Overdraw => StatusCode::BAD_REQUEST,
|
||||
AccountError::AccountNotZero => StatusCode::EXPECTATION_FAILED,
|
||||
AccountError::AccountNotZero => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,32 +1,13 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use actix_web::{get, web, Responder, Result};
|
||||
use serde::Serialize;
|
||||
|
||||
use bank::account::{Account, AccountError};
|
||||
use bank::account::AccountError;
|
||||
use http_lib::json_account::JsonAccount;
|
||||
|
||||
use crate::handlers::error::HttpAccountError;
|
||||
use crate::AppState;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct JsonAccount {
|
||||
pub number: String,
|
||||
pub owner: String,
|
||||
pub balance: f64,
|
||||
pub is_active: bool,
|
||||
}
|
||||
|
||||
impl From<&Account> for JsonAccount {
|
||||
fn from(a: &Account) -> Self {
|
||||
JsonAccount {
|
||||
number: a.number.clone(),
|
||||
owner: a.owner.clone(),
|
||||
balance: a.balance,
|
||||
is_active: a.is_active,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/{nr}")]
|
||||
pub async fn route(info: web::Path<String>, data: web::Data<AppState>) -> Result<impl Responder> {
|
||||
let nr = info.into_inner();
|
||||
|
31
http-server/src/handlers/is_active.rs
Normal file
31
http-server/src/handlers/is_active.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use actix_web::{head, web, HttpResponse, Responder, Result};
|
||||
use bank::account::AccountError;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::handlers::error::HttpAccountError;
|
||||
use crate::AppState;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AccountCreated {
|
||||
nr: String,
|
||||
}
|
||||
|
||||
#[head("/{nr}")]
|
||||
pub async fn route(info: web::Path<String>, data: web::Data<AppState>) -> Result<impl Responder> {
|
||||
let nr = info.into_inner();
|
||||
|
||||
info!("checking account {}...", nr);
|
||||
|
||||
let bank = data.bank.read().unwrap();
|
||||
match bank.accounts.get(&nr) {
|
||||
None => Err(HttpAccountError(AccountError::NotFound).into()),
|
||||
Some(account) => {
|
||||
let account = account.read().unwrap();
|
||||
if account.is_active {
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
} else {
|
||||
Err(HttpAccountError(AccountError::Inactive).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,8 +6,10 @@ pub mod deposit;
|
||||
pub mod error;
|
||||
pub mod get_account;
|
||||
pub mod get_account_nrs;
|
||||
pub mod is_active;
|
||||
pub mod pong;
|
||||
pub mod transfer;
|
||||
pub mod update_account;
|
||||
pub mod withdraw;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
@ -1,4 +1,4 @@
|
||||
use actix_web::{post, web, HttpResponse, Responder, Result};
|
||||
use actix_web::{patch, web, HttpResponse, Responder, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use bank::account::AccountError;
|
||||
@ -13,7 +13,7 @@ pub struct TransferData {
|
||||
pub(crate) amount: f64,
|
||||
}
|
||||
|
||||
#[post("/transfer")]
|
||||
#[patch("/transfer")]
|
||||
pub async fn route(
|
||||
form: web::Form<TransferData>,
|
||||
data: web::Data<AppState>,
|
||||
|
39
http-server/src/handlers/update_account.rs
Normal file
39
http-server/src/handlers/update_account.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use actix_web::{put, web, HttpResponse, Responder, Result};
|
||||
use bank::account::AccountError;
|
||||
use serde::Serialize;
|
||||
|
||||
use bank::bank::Bank;
|
||||
|
||||
use crate::handlers::error::HttpAccountError;
|
||||
use crate::handlers::AmountData;
|
||||
use crate::AppState;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AccountCreated {
|
||||
nr: String,
|
||||
}
|
||||
|
||||
#[put("/{nr}")]
|
||||
pub async fn route(
|
||||
info: web::Path<String>,
|
||||
form: web::Form<AmountData>,
|
||||
data: web::Data<AppState>,
|
||||
) -> Result<impl Responder> {
|
||||
let nr = info.into_inner();
|
||||
let amount = form.amount;
|
||||
|
||||
if amount <= 0_f64 {
|
||||
return Err(HttpAccountError(AccountError::InvalidAmount).into());
|
||||
}
|
||||
|
||||
info!("updating account {}...", nr);
|
||||
|
||||
let bank = data.bank.read().unwrap();
|
||||
match Bank::account_action(bank, &nr, |account| {
|
||||
account.balance = amount;
|
||||
Ok(amount)
|
||||
}) {
|
||||
Err(e) => Err(HttpAccountError(e).into()),
|
||||
Ok(_) => Ok(HttpResponse::Ok().finish()),
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use actix_web::{post, web, HttpResponse, Responder, Result};
|
||||
use actix_web::{patch, web, HttpResponse, Responder, Result};
|
||||
use serde::Serialize;
|
||||
|
||||
use bank::bank::Bank;
|
||||
@ -12,7 +12,7 @@ struct AccountCreated {
|
||||
nr: String,
|
||||
}
|
||||
|
||||
#[post("/{nr}/withdraw")]
|
||||
#[patch("/{nr}/withdraw")]
|
||||
pub async fn route(
|
||||
info: web::Path<String>,
|
||||
form: web::Form<AmountData>,
|
||||
|
@ -3,6 +3,7 @@ extern crate log;
|
||||
|
||||
use std::sync::RwLock;
|
||||
|
||||
use actix_cors::Cors;
|
||||
use actix_web::middleware::TrailingSlash;
|
||||
use actix_web::web::{Data, ServiceConfig};
|
||||
use actix_web::{middleware, web, App, HttpServer};
|
||||
@ -14,8 +15,10 @@ use crate::handlers::create_account;
|
||||
use crate::handlers::deposit;
|
||||
use crate::handlers::get_account;
|
||||
use crate::handlers::get_account_nrs;
|
||||
use crate::handlers::is_active;
|
||||
use crate::handlers::pong;
|
||||
use crate::handlers::transfer;
|
||||
use crate::handlers::update_account;
|
||||
use crate::handlers::withdraw;
|
||||
|
||||
pub struct AppState {
|
||||
@ -34,7 +37,9 @@ fn config_app(app_data: Data<AppState>) -> Box<dyn Fn(&mut ServiceConfig)> {
|
||||
.service(close_account::route)
|
||||
.service(get_account_nrs::route)
|
||||
.service(withdraw::route)
|
||||
.service(transfer::route),
|
||||
.service(transfer::route)
|
||||
.service(is_active::route)
|
||||
.service(update_account::route),
|
||||
);
|
||||
})
|
||||
}
|
||||
@ -48,11 +53,14 @@ async fn main() -> std::io::Result<()> {
|
||||
});
|
||||
|
||||
HttpServer::new(move || {
|
||||
let cors = Cors::permissive();
|
||||
|
||||
App::new()
|
||||
.wrap(cors)
|
||||
.wrap(middleware::NormalizePath::new(TrailingSlash::Trim))
|
||||
.configure(config_app(app_data.clone()))
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.bind(("127.0.0.1", 8000))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
@ -60,6 +68,8 @@ async fn main() -> std::io::Result<()> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use actix_web::body::to_bytes;
|
||||
use actix_web::dev::Service;
|
||||
use actix_web::http::{Method as HttpMethod, StatusCode};
|
||||
use actix_web::{test, App};
|
||||
use serde_json::Value;
|
||||
|
||||
@ -92,7 +102,7 @@ mod tests {
|
||||
.set_form(&payload)
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
assert_eq!(resp.status(), StatusCode::CREATED);
|
||||
|
||||
let body_bytes = to_bytes(resp.into_body()).await.unwrap();
|
||||
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
|
||||
@ -100,15 +110,29 @@ mod tests {
|
||||
|
||||
assert!(!nr.is_empty());
|
||||
|
||||
let payload = AmountData { amount: 10_f64 };
|
||||
let req = test::TestRequest::post()
|
||||
let req = test::TestRequest::default()
|
||||
.method(HttpMethod::HEAD)
|
||||
.uri(&format!("/accounts/{}", nr))
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
|
||||
let req = test::TestRequest::default()
|
||||
.method(HttpMethod::HEAD)
|
||||
.uri("/accounts/muh")
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
|
||||
let payload = AmountData { amount: 5_f64 };
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/accounts/{}/withdraw", nr))
|
||||
.set_form(&payload)
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(!resp.status().is_success());
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/accounts/{}/deposit", nr))
|
||||
.set_form(&payload)
|
||||
.to_request();
|
||||
@ -121,6 +145,40 @@ mod tests {
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
|
||||
let body_bytes = to_bytes(resp.into_body()).await.unwrap();
|
||||
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
|
||||
let balance = response_json["balance"].as_f64().unwrap();
|
||||
assert_eq!(5_f64, balance);
|
||||
|
||||
let payload = AmountData { amount: 10_f64 };
|
||||
let req = test::TestRequest::put()
|
||||
.uri(&format!("/accounts/{}", nr))
|
||||
.set_form(&payload)
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
|
||||
let req = test::TestRequest::put()
|
||||
.uri("/accounts/muh")
|
||||
.set_form(&payload)
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_client_error());
|
||||
|
||||
let payload = AmountData { amount: -10_f64 };
|
||||
let req = test::TestRequest::put()
|
||||
.uri(&format!("/accounts/{}", nr))
|
||||
.set_form(&payload)
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_client_error());
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.uri(&format!("/accounts/{}", nr))
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
|
||||
let body_bytes = to_bytes(resp.into_body()).await.unwrap();
|
||||
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
|
||||
let balance = response_json["balance"].as_f64().unwrap();
|
||||
@ -134,7 +192,7 @@ mod tests {
|
||||
.set_form(&payload)
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
assert_eq!(resp.status(), StatusCode::CREATED);
|
||||
|
||||
let body_bytes = to_bytes(resp.into_body()).await.unwrap();
|
||||
let response_json: Value = serde_json::from_slice(&body_bytes).unwrap();
|
||||
@ -145,7 +203,7 @@ mod tests {
|
||||
to: nr2.to_string(),
|
||||
amount: 5_f64,
|
||||
};
|
||||
let req = test::TestRequest::post()
|
||||
let req = test::TestRequest::patch()
|
||||
.uri("/accounts/transfer")
|
||||
.set_form(&payload)
|
||||
.to_request();
|
||||
@ -163,14 +221,14 @@ mod tests {
|
||||
let balance = response_json["balance"].as_f64().unwrap();
|
||||
assert_eq!(5_f64, balance);
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/accounts/{}/close", nr))
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/accounts/{}", nr))
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(!resp.status().is_success());
|
||||
|
||||
let payload = AmountData { amount: 5_f64 };
|
||||
let req = test::TestRequest::post()
|
||||
let req = test::TestRequest::patch()
|
||||
.uri(&format!("/accounts/{}/withdraw", nr))
|
||||
.set_form(&payload)
|
||||
.to_request();
|
||||
@ -186,12 +244,19 @@ mod tests {
|
||||
let nrs = response_json.as_array().unwrap();
|
||||
assert_eq!(2, nrs.len());
|
||||
|
||||
let req = test::TestRequest::post()
|
||||
.uri(&format!("/accounts/{}/close", nr))
|
||||
let req = test::TestRequest::delete()
|
||||
.uri(&format!("/accounts/{}", nr))
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
|
||||
let req = test::TestRequest::default()
|
||||
.method(HttpMethod::HEAD)
|
||||
.uri(&format!("/accounts/{}", nr))
|
||||
.to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::GONE);
|
||||
|
||||
let req = test::TestRequest::get().uri("/accounts").to_request();
|
||||
let resp = test::call_service(&app, req).await;
|
||||
assert!(resp.status().is_success());
|
||||
|
Loading…
Reference in New Issue
Block a user