From dac95b7daea670da8cf7258a65739418b89249ee Mon Sep 17 00:00:00 2001 From: Sebastian Hugentobler Date: Fri, 18 Mar 2022 19:35:34 +0100 Subject: [PATCH] Implement the http variant of the bank server. During that process, many shortcomings with the socket server and the bank lib were fixed. I am aware a massive commit like this is not ideal. --- .idea/bank-server.iml | 1 + Cargo.lock | 1074 ++++++++++++++++- Cargo.toml | 3 +- bank/Cargo.lock | 78 +- bank/src/account.rs | 33 +- bank/src/bank.rs | 43 +- bank/src/lib.rs | 2 +- http-server/Cargo.toml | 16 + http-server/src/handlers/close_account.rs | 29 + http-server/src/handlers/create_account.rs | 29 + http-server/src/handlers/deposit.rs | 31 + http-server/src/handlers/error.rs | 53 + http-server/src/handlers/get_account.rs | 44 + http-server/src/handlers/get_account_nrs.rs | 11 + http-server/src/handlers/mod.rs | 16 + http-server/src/handlers/pong.rs | 6 + http-server/src/handlers/transfer.rs | 44 + http-server/src/handlers/withdraw.rs | 31 + http-server/src/main.rs | 204 ++++ socket-server/Cargo.toml | 3 +- socket-server/src/commands/close_account.rs | 11 +- socket-server/src/commands/create_account.rs | 9 +- socket-server/src/commands/deposit.rs | 11 +- socket-server/src/commands/error.rs | 35 + socket-server/src/commands/fail.rs | 9 +- socket-server/src/commands/get_account.rs | 11 +- socket-server/src/commands/get_account_nrs.rs | 6 +- socket-server/src/commands/mod.rs | 40 +- socket-server/src/commands/pong.rs | 2 +- socket-server/src/commands/withdraw.rs | 12 +- socket-server/src/main.rs | 2 +- socket-server/src/protocol.rs | 23 +- socket-server/src/server.rs | 7 +- socket-server/src/threadpool.rs | 8 +- 34 files changed, 1797 insertions(+), 140 deletions(-) create mode 100644 http-server/Cargo.toml create mode 100644 http-server/src/handlers/close_account.rs create mode 100644 http-server/src/handlers/create_account.rs create mode 100644 http-server/src/handlers/deposit.rs create mode 100644 http-server/src/handlers/error.rs create mode 100644 http-server/src/handlers/get_account.rs create mode 100644 http-server/src/handlers/get_account_nrs.rs create mode 100644 http-server/src/handlers/mod.rs create mode 100644 http-server/src/handlers/pong.rs create mode 100644 http-server/src/handlers/transfer.rs create mode 100644 http-server/src/handlers/withdraw.rs create mode 100644 http-server/src/main.rs create mode 100644 socket-server/src/commands/error.rs diff --git a/.idea/bank-server.iml b/.idea/bank-server.iml index 750846a..8258077 100644 --- a/.idea/bank-server.iml +++ b/.idea/bank-server.iml @@ -3,6 +3,7 @@ + diff --git a/Cargo.lock b/Cargo.lock index b8a592f..899ebe5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,202 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "actix-codec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "log", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util 0.7.0", +] + +[[package]] +name = "actix-http" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5885cb81a0d4d0d322864bea1bb6c2a8144626b4fdc625d4c51eba197e7797a" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha-1", + "smallvec", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb60846b52c118f2f04a56cc90880a274271c489b2498623d58176f8ca21fa80" +dependencies = [ + "bytestring", + "firestorm", + "http", + "log", + "regex", + "serde", +] + +[[package]] +name = "actix-rt" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ea16c295198e958ef31930a6ef37d0fb64e9ca3b6116e6b93a8bdae96ee1000" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da34f8e659ea1b077bb4637948b815cd3768ad5a188fdcd74ff4d84240cd824" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "num_cpus", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e491cbaac2e7fc788dfff99ff48ef317e23b3cf63dbaf7aaab6418f40f92aa94" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e5ebffd51d50df56a3ae0de0e59487340ca456f05dd0b90c0a7a6dd6a74d31" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7525bedf54704abb1d469e88d7e7e9226df73778798a69cea5022d53b2ae91bc" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -11,6 +207,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "anyhow" version = "1.0.56" @@ -28,6 +239,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + [[package]] name = "bank" version = "0.1.0" @@ -37,12 +254,155 @@ dependencies = [ "uuid", ] +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "3.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f838e47a451d5a8fa552371f80024dd6ace9b7acdf25c4c3d0f9bc6816fb1c39" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "bytestring" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +dependencies = [ + "jobserver", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "encoding_rs" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +dependencies = [ + "cfg-if", +] + [[package]] name = "env_logger" version = "0.7.1" @@ -56,6 +416,80 @@ dependencies = [ "termcolor", ] +[[package]] +name = "firestorm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d6188b8804df28032815ea256b6955c9625c24da7525f387a7af02fbb8f01" + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.5" @@ -64,9 +498,34 @@ checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "h2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util 0.6.9", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -76,6 +535,43 @@ dependencies = [ "libc", ] +[[package]] +name = "http" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-server" +version = "0.1.0" +dependencies = [ + "actix-web", + "anyhow", + "bank", + "log", + "pretty_env_logger", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "httparse" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + [[package]] name = "humantime" version = "1.3.0" @@ -85,12 +581,87 @@ dependencies = [ "quick-error", ] +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad5c14e80759d0939d013e6ca49930e59fc53dd8e5009132f76240c179380c09" +[[package]] +name = "local-channel" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6246c68cf195087205a0512559c97e15eaf95198bf0e206d662092cdcb03fe9f" +dependencies = [ + "futures-core", + "futures-sink", + "futures-util", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "902eb695eb0591864543cbfbf6d742510642a605a61fc5e97fe6ceb5a30ac4fb" + +[[package]] +name = "lock_api" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -100,12 +671,144 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + [[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "mio" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba42135c6a5917b9db9cd7b293e5409e1c6b041e6f9825e92e55a894c63b6f8" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "wasi 0.11.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "ntapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +dependencies = [ + "winapi", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c539a50b93a303167eded6e8dff5220cd39447409fb659f4cd24b1f72fe4f133" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "paste" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + [[package]] name = "pretty_env_logger" version = "0.4.0" @@ -140,6 +843,45 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.5.5" @@ -157,6 +899,108 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa", + "ryu", + "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 = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + [[package]] name = "socket-server" version = "0.1.0" @@ -165,6 +1009,17 @@ dependencies = [ "bank", "log", "pretty_env_logger", + "thiserror", +] + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", ] [[package]] @@ -207,12 +1062,145 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "winapi", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64910e1b9c1901aaf5375561e35b9c057d95ff41a44ede043a03e09279eabaf1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "uuid" version = "0.8.2" @@ -222,12 +1210,24 @@ dependencies = [ "getrandom", ] +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "winapi" version = "0.3.9" @@ -258,3 +1258,75 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" + +[[package]] +name = "windows_i686_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" + +[[package]] +name = "windows_i686_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" + +[[package]] +name = "zstd" +version = "0.10.0+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b1365becbe415f3f0fcd024e2f7b45bacfb5bdd055f0dc113571394114e7bdd" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "4.1.4+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f7cd17c9af1a4d6c24beb1cc54b17e2ef7b593dc92f19e9d9acad8b182bbaee" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.6.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc49afa5c8d634e75761feda8c592051e7eeb4683ba827211eb0d731d3402ea8" +dependencies = [ + "cc", + "libc", +] diff --git a/Cargo.toml b/Cargo.toml index b198635..fb768ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,6 @@ members = [ "bank", - "socket-server" + "http-server", + "socket-server", ] \ No newline at end of file diff --git a/bank/Cargo.lock b/bank/Cargo.lock index 5950b41..7b32f96 100644 --- a/bank/Cargo.lock +++ b/bank/Cargo.lock @@ -8,7 +8,7 @@ version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ - "memchr", + "memchr", ] [[package]] @@ -23,20 +23,20 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "hermit-abi", + "libc", + "winapi", ] [[package]] name = "bank-server" version = "0.1.0" dependencies = [ - "anyhow", - "log", - "pretty_env_logger", - "thiserror", - "uuid", + "anyhow", + "log", + "pretty_env_logger", + "thiserror", + "uuid", ] [[package]] @@ -51,11 +51,11 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", + "atty", + "humantime", + "log", + "regex", + "termcolor", ] [[package]] @@ -64,9 +64,9 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" dependencies = [ - "cfg-if", - "libc", - "wasi", + "cfg-if", + "libc", + "wasi", ] [[package]] @@ -75,7 +75,7 @@ version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ - "libc", + "libc", ] [[package]] @@ -84,7 +84,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error", + "quick-error", ] [[package]] @@ -99,7 +99,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if", + "cfg-if", ] [[package]] @@ -114,8 +114,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" dependencies = [ - "env_logger", - "log", + "env_logger", + "log", ] [[package]] @@ -124,7 +124,7 @@ version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" dependencies = [ - "unicode-xid", + "unicode-xid", ] [[package]] @@ -139,7 +139,7 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" dependencies = [ - "proc-macro2", + "proc-macro2", ] [[package]] @@ -148,9 +148,9 @@ version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] @@ -165,9 +165,9 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] @@ -176,7 +176,7 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ - "winapi-util", + "winapi-util", ] [[package]] @@ -185,7 +185,7 @@ version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ - "thiserror-impl", + "thiserror-impl", ] [[package]] @@ -194,9 +194,9 @@ version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -211,7 +211,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom", + "getrandom", ] [[package]] @@ -226,8 +226,8 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] @@ -242,7 +242,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi", ] [[package]] diff --git a/bank/src/account.rs b/bank/src/account.rs index 3fad577..4e027f6 100644 --- a/bank/src/account.rs +++ b/bank/src/account.rs @@ -1,19 +1,20 @@ use std::hash::{Hash, Hasher}; -use anyhow::{bail, Result}; use thiserror::Error; use uuid::Uuid; -use crate::account::AccountError::{Inactive, InvalidAmount, Overdraw}; - #[derive(Error, Debug)] pub enum AccountError { #[error("can not overdraw account")] - Overdraw(), + Overdraw, #[error("account is inactive")] - Inactive(), + Inactive, #[error("amount must be > 0")] - InvalidAmount(), + InvalidAmount, + #[error("account does not exist")] + NotFound, + #[error("account still has a balance")] + AccountNotZero, } #[derive(Debug, Clone)] @@ -52,33 +53,35 @@ impl Hash for Account { impl Account { #[cfg(test)] pub fn new() -> Self { - Self { ..Default::default() } + Self { + ..Default::default() + } } - pub fn deposit(&mut self, amount: f64) -> Result<()> { + pub fn deposit(&mut self, amount: f64) -> Result { self.check_account(amount)?; self.balance += amount; - Ok(()) + Ok(self.balance) } - pub fn withdraw(&mut self, amount: f64) -> Result<()> { + pub fn withdraw(&mut self, amount: f64) -> Result { self.check_account(amount)?; if self.balance - amount < 0 as f64 { - bail!(Overdraw()); + return Err(AccountError::Overdraw); } self.balance -= amount; - Ok(()) + Ok(self.balance) } - fn check_account(&self, amount: f64) -> Result<()> { + fn check_account(&self, amount: f64) -> Result<(), AccountError> { if !self.is_active { - bail!(Inactive()); + return Err(AccountError::Inactive); } if amount < 0 as f64 { - bail!(InvalidAmount()); + return Err(AccountError::InvalidAmount); } Ok(()) diff --git a/bank/src/bank.rs b/bank/src/bank.rs index 8505e50..242623c 100644 --- a/bank/src/bank.rs +++ b/bank/src/bank.rs @@ -1,38 +1,61 @@ use std::collections::{HashMap, HashSet}; -use std::sync::RwLock; +use std::sync::{RwLock, RwLockReadGuard}; #[cfg(test)] use anyhow::Result; -use crate::account::Account; +use crate::account::{Account, AccountError}; +#[derive(Default)] pub struct Bank { pub accounts: HashMap>, } impl Bank { pub fn new() -> Self { - Self { accounts: HashMap::new() } + Default::default() + } + + pub fn account_action Result>( + bank: RwLockReadGuard<'_, Bank>, + nr: &str, + action: F, + ) -> Result { + match bank.accounts.get(nr) { + None => Err(AccountError::NotFound), + Some(account) => { + let mut account = account.write().unwrap(); + action(&mut account) + } + } } pub fn account_numbers(&self) -> HashSet { - self.accounts.iter() + self.accounts + .iter() .filter(|(_, acc)| acc.read().unwrap().is_active) .map(|(_, acc)| acc.read().unwrap().number.clone()) .collect() } pub fn create_account(&mut self, owner: String) -> String { - let acc = Account { owner, ..Default::default() }; - let number = acc.number.clone(); + let acc = Account { + owner, + ..Default::default() + }; + let nr = acc.number.clone(); self.accounts.insert(acc.number.clone(), RwLock::new(acc)); - number + nr } - #[cfg(test)] - pub fn transfer(&self, from: &mut Account, to: &mut Account, amount: f64) -> Result<()> { + pub fn transfer( + &self, + from: &mut Account, + to: &mut Account, + amount: f64, + ) -> Result<(), AccountError> { from.withdraw(amount)?; to.deposit(amount)?; @@ -83,4 +106,4 @@ mod tests { assert_eq!(1, bank.account_numbers().len()); } -} \ No newline at end of file +} diff --git a/bank/src/lib.rs b/bank/src/lib.rs index 0e75320..8aec007 100644 --- a/bank/src/lib.rs +++ b/bank/src/lib.rs @@ -1,2 +1,2 @@ pub mod account; -pub mod bank; \ No newline at end of file +pub mod bank; diff --git a/http-server/Cargo.toml b/http-server/Cargo.toml new file mode 100644 index 0000000..8c225f3 --- /dev/null +++ b/http-server/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "http-server" +version = "0.1.0" +edition = "2021" + +[dependencies] +bank = { path = "../bank" } +actix-web = "4.0.1" +anyhow = "1.0.55" +thiserror = "1.0.30" +log = "0.4.14" +pretty_env_logger = "0.4.0" +serde = { version = "1.0.136", features = ["derive"] } + +[dev-dependencies] +serde_json = "1.0.79" \ No newline at end of file diff --git a/http-server/src/handlers/close_account.rs b/http-server/src/handlers/close_account.rs new file mode 100644 index 0000000..d89c948 --- /dev/null +++ b/http-server/src/handlers/close_account.rs @@ -0,0 +1,29 @@ +use actix_web::{post, web, HttpResponse, Responder, Result}; + +use bank::account::AccountError; +use bank::bank::Bank; + +use crate::handlers::error::HttpAccountError; +use crate::AppState; + +#[post("/{nr}/close")] +pub async fn route(info: web::Path, data: web::Data) -> Result { + let nr = info.into_inner(); + + info!("closing account {}...", nr); + + let bank = data.bank.read().unwrap(); + match Bank::account_action(bank, &nr, |account| { + // TODO: make the error handling part of the passivate method + if account.balance > 0_f64 { + Err(AccountError::AccountNotZero) + } else if account.passivate() { + Ok(0_f64) + } else { + Err(AccountError::Inactive) + } + }) { + Err(e) => Err(HttpAccountError(e).into()), + Ok(_) => Ok(HttpResponse::Ok().finish()), + } +} diff --git a/http-server/src/handlers/create_account.rs b/http-server/src/handlers/create_account.rs new file mode 100644 index 0000000..75fba20 --- /dev/null +++ b/http-server/src/handlers/create_account.rs @@ -0,0 +1,29 @@ +use actix_web::{post, web, Responder, Result}; +use serde::{Deserialize, Serialize}; + +use crate::AppState; + +#[derive(Serialize, Deserialize)] +pub struct OwnerData { + pub(crate) owner: String, +} + +#[derive(Serialize)] +struct AccountCreated { + nr: String, +} + +#[post("")] +pub async fn route( + form: web::Form, + data: web::Data, +) -> Result { + let owner = &form.owner; + info!("creating new account with owner {}...", owner); + + let mut bank = data.bank.write().unwrap(); + let nr = bank.create_account(owner.clone()); + info!("created account {}", nr); + + Ok(web::Json(AccountCreated { nr })) +} diff --git a/http-server/src/handlers/deposit.rs b/http-server/src/handlers/deposit.rs new file mode 100644 index 0000000..6f01823 --- /dev/null +++ b/http-server/src/handlers/deposit.rs @@ -0,0 +1,31 @@ +use actix_web::{post, web, HttpResponse, Responder, Result}; +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, +} + +#[post("/{nr}/deposit")] +pub async fn route( + info: web::Path, + form: web::Form, + data: web::Data, +) -> Result { + let nr = info.into_inner(); + let amount = form.amount; + + info!("depositing {} into account {}...", amount, nr); + + let bank = data.bank.read().unwrap(); + match Bank::account_action(bank, &nr, |account| account.deposit(amount)) { + Err(e) => Err(HttpAccountError(e).into()), + Ok(_) => Ok(HttpResponse::Ok().finish()), + } +} diff --git a/http-server/src/handlers/error.rs b/http-server/src/handlers/error.rs new file mode 100644 index 0000000..4e45d6c --- /dev/null +++ b/http-server/src/handlers/error.rs @@ -0,0 +1,53 @@ +use std::fmt::{Display, Formatter}; +use std::ops::Deref; + +use actix_web::http::StatusCode; +use actix_web::{HttpResponse, ResponseError}; +use serde::Serialize; +use thiserror::Error; + +use bank::account::AccountError; + +#[derive(Serialize)] +struct ErrorResponse { + code: u16, + message: String, +} + +#[derive(Debug, Error)] +pub struct HttpAccountError(pub AccountError); + +impl Display for HttpAccountError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.deref()) + } +} + +impl Deref for HttpAccountError { + type Target = AccountError; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl ResponseError for HttpAccountError { + fn status_code(&self) -> StatusCode { + match self.deref() { + AccountError::NotFound => StatusCode::NOT_FOUND, + AccountError::InvalidAmount => StatusCode::BAD_REQUEST, + AccountError::Inactive => StatusCode::BAD_REQUEST, + AccountError::Overdraw => StatusCode::BAD_REQUEST, + AccountError::AccountNotZero => StatusCode::EXPECTATION_FAILED, + } + } + + fn error_response(&self) -> HttpResponse { + let status_code = self.status_code(); + let error_response = ErrorResponse { + code: status_code.as_u16(), + message: self.to_string(), + }; + HttpResponse::build(status_code).json(error_response) + } +} diff --git a/http-server/src/handlers/get_account.rs b/http-server/src/handlers/get_account.rs new file mode 100644 index 0000000..fb1e5d9 --- /dev/null +++ b/http-server/src/handlers/get_account.rs @@ -0,0 +1,44 @@ +use std::ops::Deref; + +use actix_web::{get, web, Responder, Result}; +use serde::Serialize; + +use bank::account::{Account, AccountError}; + +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, data: web::Data) -> Result { + let nr = info.into_inner(); + info!("getting 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(); + let account: JsonAccount = account.deref().into(); + Ok(web::Json(account)) + } + } +} diff --git a/http-server/src/handlers/get_account_nrs.rs b/http-server/src/handlers/get_account_nrs.rs new file mode 100644 index 0000000..42df3be --- /dev/null +++ b/http-server/src/handlers/get_account_nrs.rs @@ -0,0 +1,11 @@ +use actix_web::{get, web, Responder, Result}; + +use crate::AppState; + +#[get("")] +pub async fn route(data: web::Data) -> Result { + info!("getting account numbers..."); + + let bank = data.bank.read().unwrap(); + Ok(web::Json(bank.account_numbers())) +} diff --git a/http-server/src/handlers/mod.rs b/http-server/src/handlers/mod.rs new file mode 100644 index 0000000..d93c68f --- /dev/null +++ b/http-server/src/handlers/mod.rs @@ -0,0 +1,16 @@ +use serde::{Deserialize, Serialize}; + +pub mod close_account; +pub mod create_account; +pub mod deposit; +pub mod error; +pub mod get_account; +pub mod get_account_nrs; +pub mod pong; +pub mod transfer; +pub mod withdraw; + +#[derive(Deserialize, Serialize)] +pub struct AmountData { + pub(crate) amount: f64, +} diff --git a/http-server/src/handlers/pong.rs b/http-server/src/handlers/pong.rs new file mode 100644 index 0000000..c69c42a --- /dev/null +++ b/http-server/src/handlers/pong.rs @@ -0,0 +1,6 @@ +use actix_web::{get, HttpResponse}; + +#[get("/ping")] +pub async fn route() -> HttpResponse { + HttpResponse::Ok().finish() +} diff --git a/http-server/src/handlers/transfer.rs b/http-server/src/handlers/transfer.rs new file mode 100644 index 0000000..d4a9f70 --- /dev/null +++ b/http-server/src/handlers/transfer.rs @@ -0,0 +1,44 @@ +use actix_web::{post, web, HttpResponse, Responder, Result}; +use serde::{Deserialize, Serialize}; + +use bank::account::AccountError; + +use crate::handlers::error::HttpAccountError; +use crate::AppState; + +#[derive(Deserialize, Serialize)] +pub struct TransferData { + pub(crate) from: String, + pub(crate) to: String, + pub(crate) amount: f64, +} + +#[post("/transfer")] +pub async fn route( + form: web::Form, + data: web::Data, +) -> Result { + let from = form.from.clone(); + let to = form.to.clone(); + let amount = form.amount; + + info!("transfering {} from {} to {}...", from, to, amount); + + let bank = data.bank.read().unwrap(); + let from_acc = bank + .accounts + .get(&from) + .ok_or(HttpAccountError(AccountError::NotFound))?; + let mut from_acc = from_acc.write().unwrap(); + + let to_acc = bank + .accounts + .get(&to) + .ok_or(HttpAccountError(AccountError::NotFound))?; + let mut to_acc = to_acc.write().unwrap(); + + match bank.transfer(&mut from_acc, &mut to_acc, amount) { + Err(e) => Err(HttpAccountError(e).into()), + Ok(_) => Ok(HttpResponse::Ok().finish()), + } +} diff --git a/http-server/src/handlers/withdraw.rs b/http-server/src/handlers/withdraw.rs new file mode 100644 index 0000000..526243f --- /dev/null +++ b/http-server/src/handlers/withdraw.rs @@ -0,0 +1,31 @@ +use actix_web::{post, web, HttpResponse, Responder, Result}; +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, +} + +#[post("/{nr}/withdraw")] +pub async fn route( + info: web::Path, + form: web::Form, + data: web::Data, +) -> Result { + let nr = info.into_inner(); + let amount = form.amount; + + info!("withdrawing {} from account {}...", amount, nr); + + let bank = data.bank.read().unwrap(); + match Bank::account_action(bank, &nr, |account| account.withdraw(amount)) { + Err(e) => Err(HttpAccountError(e).into()), + Ok(_) => Ok(HttpResponse::Ok().finish()), + } +} diff --git a/http-server/src/main.rs b/http-server/src/main.rs new file mode 100644 index 0000000..7bb0d19 --- /dev/null +++ b/http-server/src/main.rs @@ -0,0 +1,204 @@ +#[macro_use] +extern crate log; + +use std::sync::RwLock; + +use actix_web::middleware::TrailingSlash; +use actix_web::web::{Data, ServiceConfig}; +use actix_web::{middleware, web, App, HttpServer}; + +use bank::bank::Bank; + +use crate::handlers::close_account; +use crate::handlers::create_account; +use crate::handlers::deposit; +use crate::handlers::get_account; +use crate::handlers::get_account_nrs; +use crate::handlers::pong; +use crate::handlers::transfer; +use crate::handlers::withdraw; + +pub struct AppState { + bank: RwLock, +} + +mod handlers; + +fn config_app(app_data: Data) -> Box { + Box::new(move |cfg: &mut ServiceConfig| { + cfg.app_data(app_data.clone()).service(pong::route).service( + web::scope("/accounts") + .service(create_account::route) + .service(get_account::route) + .service(deposit::route) + .service(close_account::route) + .service(get_account_nrs::route) + .service(withdraw::route) + .service(transfer::route), + ); + }) +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + pretty_env_logger::init(); + + let app_data = web::Data::new(AppState { + bank: RwLock::new(Bank::new()), + }); + + HttpServer::new(move || { + App::new() + .wrap(middleware::NormalizePath::new(TrailingSlash::Trim)) + .configure(config_app(app_data.clone())) + }) + .bind(("127.0.0.1", 8080))? + .run() + .await +} + +#[cfg(test)] +mod tests { + use actix_web::body::to_bytes; + use actix_web::{test, App}; + use serde_json::Value; + + use crate::create_account::OwnerData; + use crate::handlers::AmountData; + use crate::transfer::TransferData; + + use super::*; + + #[actix_web::test] + async fn test_routes() { + // TODO: split up into separate tests + + let app_data = web::Data::new(AppState { + bank: RwLock::new(Bank::new()), + }); + + let app = test::init_service( + App::new() + .wrap(middleware::NormalizePath::new(TrailingSlash::Trim)) + .configure(config_app(app_data)), + ) + .await; + + let payload = OwnerData { + owner: "aaa".to_string(), + }; + let req = test::TestRequest::post() + .uri("/accounts") + .set_form(&payload) + .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 nr = response_json["nr"].as_str().unwrap(); + + assert!(!nr.is_empty()); + + let payload = AmountData { amount: 10_f64 }; + let req = test::TestRequest::post() + .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() + .uri(&format!("/accounts/{}/deposit", nr)) + .set_form(&payload) + .to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); + + 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(); + assert_eq!(10_f64, balance); + + let payload = OwnerData { + owner: "bbb".to_string(), + }; + let req = test::TestRequest::post() + .uri("/accounts") + .set_form(&payload) + .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 nr2 = response_json["nr"].as_str().unwrap(); + + let payload = TransferData { + from: nr.to_string(), + to: nr2.to_string(), + amount: 5_f64, + }; + let req = test::TestRequest::post() + .uri("/accounts/transfer") + .set_form(&payload) + .to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); + + let req = test::TestRequest::get() + .uri(&format!("/accounts/{}", nr2)) + .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(); + assert_eq!(5_f64, balance); + + let req = test::TestRequest::post() + .uri(&format!("/accounts/{}/close", 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() + .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::get().uri("/accounts").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 nrs = response_json.as_array().unwrap(); + assert_eq!(2, nrs.len()); + + let req = test::TestRequest::post() + .uri(&format!("/accounts/{}/close", nr)) + .to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); + + let req = test::TestRequest::get().uri("/accounts").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 nrs = response_json.as_array().unwrap(); + assert_eq!(1, nrs.len()); + } +} diff --git a/socket-server/Cargo.toml b/socket-server/Cargo.toml index 571ad38..8ed54cd 100644 --- a/socket-server/Cargo.toml +++ b/socket-server/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" bank = { path = "../bank" } anyhow = "1.0.55" log = "0.4.14" -pretty_env_logger = "0.4.0" \ No newline at end of file +pretty_env_logger = "0.4.0" +thiserror = "1.0.30" \ No newline at end of file diff --git a/socket-server/src/commands/close_account.rs b/socket-server/src/commands/close_account.rs index dcae949..da643f8 100644 --- a/socket-server/src/commands/close_account.rs +++ b/socket-server/src/commands/close_account.rs @@ -12,7 +12,12 @@ use crate::protocol; pub struct CloseAccount; impl Command for CloseAccount { - fn execute(&self, bank: Arc>, data: &[u8], mut stream: &TcpStream) -> Result { + fn execute( + &self, + bank: Arc>, + data: &[u8], + mut stream: &TcpStream, + ) -> Result { debug!("account nr bytes {:?}", data); let nr = String::from_utf8_lossy(data).to_string(); @@ -25,9 +30,9 @@ impl Command for CloseAccount { let mut acc = acc.write().unwrap(); stream.write(&protocol::account_passivate(acc.passivate()))? } - None => stream.write(&protocol::account_passivate(false))? + None => stream.write(&protocol::account_passivate(false))?, }; Ok(written) } -} \ No newline at end of file +} diff --git a/socket-server/src/commands/create_account.rs b/socket-server/src/commands/create_account.rs index 1f7f5a6..0e323f4 100644 --- a/socket-server/src/commands/create_account.rs +++ b/socket-server/src/commands/create_account.rs @@ -12,7 +12,12 @@ use crate::protocol; pub struct CreateAccount; impl Command for CreateAccount { - fn execute(&self, bank: Arc>, data: &[u8], mut stream: &TcpStream) -> Result { + fn execute( + &self, + bank: Arc>, + data: &[u8], + mut stream: &TcpStream, + ) -> Result { debug!("owner nr bytes {:?}", data); let owner = String::from_utf8_lossy(data); @@ -25,4 +30,4 @@ impl Command for CreateAccount { let written = stream.write(&protocol::account_nr(&nr))?; Ok(written) } -} \ No newline at end of file +} diff --git a/socket-server/src/commands/deposit.rs b/socket-server/src/commands/deposit.rs index 3014bff..848b03b 100644 --- a/socket-server/src/commands/deposit.rs +++ b/socket-server/src/commands/deposit.rs @@ -12,7 +12,12 @@ use crate::protocol; pub struct Deposit; impl Command for Deposit { - fn execute(&self, bank: Arc>, data: &[u8], mut stream: &TcpStream) -> Result { + fn execute( + &self, + bank: Arc>, + data: &[u8], + mut stream: &TcpStream, + ) -> Result { let value_bytes: [u8; 8] = <[u8; 8]>::try_from(data[..8].to_vec().as_slice())?; debug!("value bytes {:?}", value_bytes); @@ -30,9 +35,9 @@ impl Command for Deposit { let mut acc = acc.write().unwrap(); stream.write(&protocol::deposit(acc.deposit(amount)))? } - None => stream.write(&protocol::error(2))? + None => stream.write(&protocol::error(2))?, }; Ok(written) } -} \ No newline at end of file +} diff --git a/socket-server/src/commands/error.rs b/socket-server/src/commands/error.rs new file mode 100644 index 0000000..6e50b3c --- /dev/null +++ b/socket-server/src/commands/error.rs @@ -0,0 +1,35 @@ +use std::fmt::{Display, Formatter}; +use std::ops::Deref; + +use thiserror::Error; + +use bank::account::AccountError; + +#[derive(Debug, Error)] +pub struct SocketAccountError(pub AccountError); + +impl Display for SocketAccountError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.deref()) + } +} + +impl Deref for SocketAccountError { + type Target = AccountError; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for u8 { + fn from(e: SocketAccountError) -> Self { + match e.deref() { + AccountError::Overdraw => 11, + AccountError::Inactive => 12, + AccountError::InvalidAmount => 13, + AccountError::NotFound => 14, + AccountError::AccountNotZero => 15, + } + } +} diff --git a/socket-server/src/commands/fail.rs b/socket-server/src/commands/fail.rs index 8bbec29..7e0f911 100644 --- a/socket-server/src/commands/fail.rs +++ b/socket-server/src/commands/fail.rs @@ -12,9 +12,14 @@ use crate::protocol; pub struct Fail; impl Command for Fail { - fn execute(&self, _: Arc>, error_code: &[u8], mut stream: &TcpStream) -> Result { + fn execute( + &self, + _: Arc>, + error_code: &[u8], + mut stream: &TcpStream, + ) -> Result { error!("sending error code {}", error_code[0]); let written = stream.write(&protocol::error(error_code[0]))?; Ok(written) } -} \ No newline at end of file +} diff --git a/socket-server/src/commands/get_account.rs b/socket-server/src/commands/get_account.rs index 9ec584c..0b80542 100644 --- a/socket-server/src/commands/get_account.rs +++ b/socket-server/src/commands/get_account.rs @@ -12,7 +12,12 @@ use crate::protocol; pub struct GetAccount; impl Command for GetAccount { - fn execute(&self, bank: Arc>, data: &[u8], mut stream: &TcpStream) -> Result { + fn execute( + &self, + bank: Arc>, + data: &[u8], + mut stream: &TcpStream, + ) -> Result { debug!("account nr bytes {:?}", data); let nr = String::from_utf8_lossy(data).to_string(); @@ -25,9 +30,9 @@ impl Command for GetAccount { let acc = acc.read().unwrap(); stream.write(&protocol::account(&acc))? } - None => stream.write(&protocol::error(2))? + None => stream.write(&protocol::error(2))?, }; Ok(written) } -} \ No newline at end of file +} diff --git a/socket-server/src/commands/get_account_nrs.rs b/socket-server/src/commands/get_account_nrs.rs index e83319d..63c7ef6 100644 --- a/socket-server/src/commands/get_account_nrs.rs +++ b/socket-server/src/commands/get_account_nrs.rs @@ -16,12 +16,10 @@ impl Command for GetAccountNrs { info!("getting account numbers..."); let bank = bank.read().unwrap(); - let nrs: Vec = bank.account_numbers() - .into_iter() - .collect(); + let nrs: Vec = bank.account_numbers().into_iter().collect(); let written = stream.write(&protocol::account_nrs(&nrs))?; Ok(written) } -} \ No newline at end of file +} diff --git a/socket-server/src/commands/mod.rs b/socket-server/src/commands/mod.rs index 07b4252..678d185 100644 --- a/socket-server/src/commands/mod.rs +++ b/socket-server/src/commands/mod.rs @@ -4,7 +4,6 @@ use std::sync::{Arc, RwLock}; use anyhow::Result; -use bank::account::AccountError; use bank::bank::Bank; use crate::commands::close_account::CloseAccount; @@ -16,13 +15,14 @@ use crate::commands::get_account_nrs::GetAccountNrs; use crate::commands::pong::Pong; use crate::commands::withdraw::Withdraw; -mod pong; -mod fail; mod close_account; mod create_account; mod deposit; +pub mod error; +mod fail; mod get_account; mod get_account_nrs; +mod pong; mod withdraw; pub trait Command: Sync + Send { @@ -33,26 +33,6 @@ pub struct Commands { cmds: HashMap>, } -struct WebAccountError(AccountError); - -impl From<&WebAccountError> for u8 { - fn from(error: &WebAccountError) -> Self { - match error.0 { - AccountError::Overdraw() => 11, - AccountError::Inactive() => 12, - AccountError::InvalidAmount() => 13, - } - } -} - -pub fn error_to_code(error: &AccountError) -> u8 { - match error { - AccountError::Overdraw() => 11, - AccountError::Inactive() => 12, - AccountError::InvalidAmount() => 13, - } -} - impl Commands { pub fn new() -> Self { let mut cmds = HashMap::new(); @@ -64,15 +44,19 @@ impl Commands { cmds.insert(6_u8, Box::new(Deposit) as Box); cmds.insert(7_u8, Box::new(Withdraw) as Box); - Self { - cmds - } + Self { cmds } } - pub fn run(&self, bank: Arc>, cmd: u8, data: &[u8], stream: &TcpStream) -> Result { + pub fn run( + &self, + bank: Arc>, + cmd: u8, + data: &[u8], + stream: &TcpStream, + ) -> Result { match self.cmds.get(&cmd) { None => Fail.execute(bank, &[1_u8], stream), - Some(cmd) => cmd.execute(bank, data, stream) + Some(cmd) => cmd.execute(bank, data, stream), } } } diff --git a/socket-server/src/commands/pong.rs b/socket-server/src/commands/pong.rs index cb04404..6c7d57b 100644 --- a/socket-server/src/commands/pong.rs +++ b/socket-server/src/commands/pong.rs @@ -17,4 +17,4 @@ impl Command for Pong { let written = stream.write(&[protocol::PONG])?; Ok(written) } -} \ No newline at end of file +} diff --git a/socket-server/src/commands/withdraw.rs b/socket-server/src/commands/withdraw.rs index a90eb82..f430148 100644 --- a/socket-server/src/commands/withdraw.rs +++ b/socket-server/src/commands/withdraw.rs @@ -12,7 +12,12 @@ use crate::protocol; pub struct Withdraw; impl Command for Withdraw { - fn execute(&self, bank: Arc>, data: &[u8], mut stream: &TcpStream) -> Result { + fn execute( + &self, + bank: Arc>, + data: &[u8], + mut stream: &TcpStream, + ) -> Result { let value_bytes: [u8; 8] = <[u8; 8]>::try_from(data[..8].to_vec().as_slice())?; debug!("value bytes {:?}", value_bytes); @@ -24,15 +29,14 @@ impl Command for Withdraw { info!("withdrawing {} from {}...", amount, nr); let bank = bank.read().unwrap(); - let written = match bank.accounts.get(&nr) { Some(acc) => { let mut acc = acc.write().unwrap(); stream.write(&protocol::withdraw(acc.withdraw(amount)))? } - None => stream.write(&protocol::error(2))? + None => stream.write(&protocol::error(2))?, }; Ok(written) } -} \ No newline at end of file +} diff --git a/socket-server/src/main.rs b/socket-server/src/main.rs index 28694bd..66608fb 100644 --- a/socket-server/src/main.rs +++ b/socket-server/src/main.rs @@ -1,10 +1,10 @@ #[macro_use] extern crate log; +mod commands; mod protocol; mod server; mod threadpool; -mod commands; fn main() { pretty_env_logger::init(); diff --git a/socket-server/src/protocol.rs b/socket-server/src/protocol.rs index 76797e6..c2c1125 100644 --- a/socket-server/src/protocol.rs +++ b/socket-server/src/protocol.rs @@ -2,18 +2,11 @@ use anyhow::Result; use bank::account::{Account, AccountError}; -use crate::commands::error_to_code; +use crate::commands::error::SocketAccountError; pub const START: [u8; 2] = [0xde, 0xad]; pub const PONG: u8 = 0b0010_0000; -fn to_error_code(error: anyhow::Error, default: u8) -> u8 { - match error.root_cause().downcast_ref::() { - Some(e) => error_to_code(e), - None => default, - } -} - pub fn account_nr(nr: &str) -> Vec { let mut response = vec![PONG]; response.append(&mut nr.as_bytes().to_vec()); @@ -26,17 +19,17 @@ pub fn account_passivate(was_passivated: bool) -> Vec { vec![PONG | is_active_byte] } -pub fn deposit(result: Result<()>) -> Vec { +pub fn deposit(result: Result) -> Vec { match result { - Err(e) => error(to_error_code(e, 10)).to_vec(), - Ok(_) => vec![PONG] + Err(e) => error(SocketAccountError(e).into()).to_vec(), + Ok(_) => vec![PONG], } } -pub fn withdraw(result: Result<()>) -> Vec { +pub fn withdraw(result: Result) -> Vec { match result { - Err(e) => error(to_error_code(e, 10)).to_vec(), - Ok(_) => vec![PONG] + Err(e) => error(SocketAccountError(e).into()).to_vec(), + Ok(_) => vec![PONG], } } @@ -85,4 +78,4 @@ pub fn account(account: &Account) -> Vec { pub fn error(code: u8) -> [u8; 1] { [0b0100_0000 | code] -} \ No newline at end of file +} diff --git a/socket-server/src/server.rs b/socket-server/src/server.rs index f17490d..ee4d4e5 100644 --- a/socket-server/src/server.rs +++ b/socket-server/src/server.rs @@ -33,7 +33,11 @@ pub fn run(host: &str, threads: usize) -> Result<()> { Ok(()) } -fn handle_connection(bank: Arc>, cmds: &Commands, mut stream: TcpStream) -> Result<()> { +fn handle_connection( + bank: Arc>, + cmds: &Commands, + mut stream: TcpStream, +) -> Result<()> { const BUF_SIZE: usize = 64; let mut data: Vec = vec![]; @@ -58,4 +62,3 @@ fn handle_connection(bank: Arc>, cmds: &Commands, mut stream: TcpSt Ok(()) } - diff --git a/socket-server/src/threadpool.rs b/socket-server/src/threadpool.rs index 22267d3..9b150fe 100644 --- a/socket-server/src/threadpool.rs +++ b/socket-server/src/threadpool.rs @@ -1,5 +1,5 @@ -use std::sync::Arc; use std::sync::mpsc; +use std::sync::Arc; use std::sync::Mutex; use std::thread; @@ -33,8 +33,8 @@ impl ThreadPool { } pub fn execute(&self, f: F) - where - F: FnOnce() + Send + 'static, + where + F: FnOnce() + Send + 'static, { let job = Box::new(f); @@ -91,4 +91,4 @@ impl Worker { thread: Some(thread), } } -} \ No newline at end of file +}