├── .gitignore ├── .vscode └── settings.json ├── Cargo.lock ├── Cargo.toml ├── README.md ├── crab.png ├── src ├── lib.rs └── main.rs └── tests ├── lesson-1.rs ├── lesson-2.rs ├── lesson-3.rs └── lesson-4.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true 3 | } -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "alloc-no-stdlib" 13 | version = "2.0.4" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 16 | 17 | [[package]] 18 | name = "alloc-stdlib" 19 | version = "0.2.2" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 22 | dependencies = [ 23 | "alloc-no-stdlib", 24 | ] 25 | 26 | [[package]] 27 | name = "async-compression" 28 | version = "0.3.15" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" 31 | dependencies = [ 32 | "brotli", 33 | "flate2", 34 | "futures-core", 35 | "memchr", 36 | "pin-project-lite", 37 | "tokio", 38 | ] 39 | 40 | [[package]] 41 | name = "async-trait" 42 | version = "0.1.58" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" 45 | dependencies = [ 46 | "proc-macro2", 47 | "quote", 48 | "syn", 49 | ] 50 | 51 | [[package]] 52 | name = "autocfg" 53 | version = "1.1.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 56 | 57 | [[package]] 58 | name = "axum" 59 | version = "0.6.0-rc.2" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "d2628a243073c55aef15a1c1fe45c87f21b84f9e89ca9e7b262a180d3d03543d" 62 | dependencies = [ 63 | "async-trait", 64 | "axum-core", 65 | "bitflags", 66 | "bytes", 67 | "futures-util", 68 | "http", 69 | "http-body", 70 | "hyper", 71 | "itoa", 72 | "matchit", 73 | "memchr", 74 | "mime", 75 | "percent-encoding", 76 | "pin-project-lite", 77 | "serde", 78 | "serde_json", 79 | "serde_urlencoded", 80 | "sync_wrapper", 81 | "tokio", 82 | "tower", 83 | "tower-http", 84 | "tower-layer", 85 | "tower-service", 86 | ] 87 | 88 | [[package]] 89 | name = "axum-core" 90 | version = "0.3.0-rc.2" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "473bd0762170028bb6b5068be9e97de2a9f0af3bf2084498d840498f47194d3d" 93 | dependencies = [ 94 | "async-trait", 95 | "bytes", 96 | "futures-util", 97 | "http", 98 | "http-body", 99 | "mime", 100 | "tower-layer", 101 | "tower-service", 102 | ] 103 | 104 | [[package]] 105 | name = "base64" 106 | version = "0.13.1" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" 109 | 110 | [[package]] 111 | name = "bitflags" 112 | version = "1.3.2" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 115 | 116 | [[package]] 117 | name = "brotli" 118 | version = "3.3.4" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" 121 | dependencies = [ 122 | "alloc-no-stdlib", 123 | "alloc-stdlib", 124 | "brotli-decompressor", 125 | ] 126 | 127 | [[package]] 128 | name = "brotli-decompressor" 129 | version = "2.3.2" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" 132 | dependencies = [ 133 | "alloc-no-stdlib", 134 | "alloc-stdlib", 135 | ] 136 | 137 | [[package]] 138 | name = "bytes" 139 | version = "1.2.1" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" 142 | 143 | [[package]] 144 | name = "cfg-if" 145 | version = "1.0.0" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 148 | 149 | [[package]] 150 | name = "crc32fast" 151 | version = "1.3.2" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 154 | dependencies = [ 155 | "cfg-if", 156 | ] 157 | 158 | [[package]] 159 | name = "fastrand" 160 | version = "1.8.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" 163 | dependencies = [ 164 | "instant", 165 | ] 166 | 167 | [[package]] 168 | name = "filedescriptor" 169 | version = "0.8.2" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" 172 | dependencies = [ 173 | "libc", 174 | "thiserror", 175 | "winapi", 176 | ] 177 | 178 | [[package]] 179 | name = "flate2" 180 | version = "1.0.24" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" 183 | dependencies = [ 184 | "crc32fast", 185 | "miniz_oxide", 186 | ] 187 | 188 | [[package]] 189 | name = "fnv" 190 | version = "1.0.7" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 193 | 194 | [[package]] 195 | name = "form_urlencoded" 196 | version = "1.1.0" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" 199 | dependencies = [ 200 | "percent-encoding", 201 | ] 202 | 203 | [[package]] 204 | name = "futures" 205 | version = "0.3.25" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" 208 | dependencies = [ 209 | "futures-channel", 210 | "futures-core", 211 | "futures-executor", 212 | "futures-io", 213 | "futures-sink", 214 | "futures-task", 215 | "futures-util", 216 | ] 217 | 218 | [[package]] 219 | name = "futures-channel" 220 | version = "0.3.25" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" 223 | dependencies = [ 224 | "futures-core", 225 | "futures-sink", 226 | ] 227 | 228 | [[package]] 229 | name = "futures-core" 230 | version = "0.3.25" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" 233 | 234 | [[package]] 235 | name = "futures-executor" 236 | version = "0.3.25" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" 239 | dependencies = [ 240 | "futures-core", 241 | "futures-task", 242 | "futures-util", 243 | ] 244 | 245 | [[package]] 246 | name = "futures-io" 247 | version = "0.3.25" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" 250 | 251 | [[package]] 252 | name = "futures-macro" 253 | version = "0.3.25" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" 256 | dependencies = [ 257 | "proc-macro2", 258 | "quote", 259 | "syn", 260 | ] 261 | 262 | [[package]] 263 | name = "futures-sink" 264 | version = "0.3.25" 265 | source = "registry+https://github.com/rust-lang/crates.io-index" 266 | checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" 267 | 268 | [[package]] 269 | name = "futures-task" 270 | version = "0.3.25" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" 273 | 274 | [[package]] 275 | name = "futures-util" 276 | version = "0.3.25" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" 279 | dependencies = [ 280 | "futures-channel", 281 | "futures-core", 282 | "futures-io", 283 | "futures-macro", 284 | "futures-sink", 285 | "futures-task", 286 | "memchr", 287 | "pin-project-lite", 288 | "pin-utils", 289 | "slab", 290 | ] 291 | 292 | [[package]] 293 | name = "gag" 294 | version = "1.0.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "a713bee13966e9fbffdf7193af71d54a6b35a0bb34997cd6c9519ebeb5005972" 297 | dependencies = [ 298 | "filedescriptor", 299 | "tempfile", 300 | ] 301 | 302 | [[package]] 303 | name = "h2" 304 | version = "0.3.15" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" 307 | dependencies = [ 308 | "bytes", 309 | "fnv", 310 | "futures-core", 311 | "futures-sink", 312 | "futures-util", 313 | "http", 314 | "indexmap", 315 | "slab", 316 | "tokio", 317 | "tokio-util", 318 | "tracing", 319 | ] 320 | 321 | [[package]] 322 | name = "hashbrown" 323 | version = "0.12.3" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 326 | 327 | [[package]] 328 | name = "hermit-abi" 329 | version = "0.1.19" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 332 | dependencies = [ 333 | "libc", 334 | ] 335 | 336 | [[package]] 337 | name = "http" 338 | version = "0.2.8" 339 | source = "registry+https://github.com/rust-lang/crates.io-index" 340 | checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" 341 | dependencies = [ 342 | "bytes", 343 | "fnv", 344 | "itoa", 345 | ] 346 | 347 | [[package]] 348 | name = "http-body" 349 | version = "0.4.5" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" 352 | dependencies = [ 353 | "bytes", 354 | "http", 355 | "pin-project-lite", 356 | ] 357 | 358 | [[package]] 359 | name = "http-range-header" 360 | version = "0.3.0" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" 363 | 364 | [[package]] 365 | name = "httparse" 366 | version = "1.8.0" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 369 | 370 | [[package]] 371 | name = "httpdate" 372 | version = "1.0.2" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" 375 | 376 | [[package]] 377 | name = "hyper" 378 | version = "0.14.22" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" 381 | dependencies = [ 382 | "bytes", 383 | "futures-channel", 384 | "futures-core", 385 | "futures-util", 386 | "h2", 387 | "http", 388 | "http-body", 389 | "httparse", 390 | "httpdate", 391 | "itoa", 392 | "pin-project-lite", 393 | "socket2", 394 | "tokio", 395 | "tower-service", 396 | "tracing", 397 | "want", 398 | ] 399 | 400 | [[package]] 401 | name = "indexmap" 402 | version = "1.9.1" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 405 | dependencies = [ 406 | "autocfg", 407 | "hashbrown", 408 | ] 409 | 410 | [[package]] 411 | name = "instant" 412 | version = "0.1.12" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 415 | dependencies = [ 416 | "cfg-if", 417 | ] 418 | 419 | [[package]] 420 | name = "itoa" 421 | version = "1.0.4" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" 424 | 425 | [[package]] 426 | name = "lazy_static" 427 | version = "1.4.0" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 430 | 431 | [[package]] 432 | name = "libc" 433 | version = "0.2.137" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" 436 | 437 | [[package]] 438 | name = "lock_api" 439 | version = "0.4.9" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" 442 | dependencies = [ 443 | "autocfg", 444 | "scopeguard", 445 | ] 446 | 447 | [[package]] 448 | name = "log" 449 | version = "0.4.17" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 452 | dependencies = [ 453 | "cfg-if", 454 | ] 455 | 456 | [[package]] 457 | name = "matchers" 458 | version = "0.1.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 461 | dependencies = [ 462 | "regex-automata", 463 | ] 464 | 465 | [[package]] 466 | name = "matchit" 467 | version = "0.6.0" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "3dfc802da7b1cf80aefffa0c7b2f77247c8b32206cc83c270b61264f5b360a80" 470 | 471 | [[package]] 472 | name = "memchr" 473 | version = "2.5.0" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 476 | 477 | [[package]] 478 | name = "microservice-rust-workshop" 479 | version = "0.1.0" 480 | dependencies = [ 481 | "axum", 482 | "futures", 483 | "gag", 484 | "hyper", 485 | "tokio", 486 | "tower", 487 | "tower-http", 488 | "tracing", 489 | "tracing-subscriber", 490 | ] 491 | 492 | [[package]] 493 | name = "mime" 494 | version = "0.3.16" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 497 | 498 | [[package]] 499 | name = "miniz_oxide" 500 | version = "0.5.4" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" 503 | dependencies = [ 504 | "adler", 505 | ] 506 | 507 | [[package]] 508 | name = "mio" 509 | version = "0.8.5" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" 512 | dependencies = [ 513 | "libc", 514 | "log", 515 | "wasi", 516 | "windows-sys", 517 | ] 518 | 519 | [[package]] 520 | name = "nu-ansi-term" 521 | version = "0.46.0" 522 | source = "registry+https://github.com/rust-lang/crates.io-index" 523 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 524 | dependencies = [ 525 | "overload", 526 | "winapi", 527 | ] 528 | 529 | [[package]] 530 | name = "num_cpus" 531 | version = "1.13.1" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 534 | dependencies = [ 535 | "hermit-abi", 536 | "libc", 537 | ] 538 | 539 | [[package]] 540 | name = "once_cell" 541 | version = "1.15.0" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" 544 | 545 | [[package]] 546 | name = "overload" 547 | version = "0.1.1" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 550 | 551 | [[package]] 552 | name = "parking_lot" 553 | version = "0.12.1" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" 556 | dependencies = [ 557 | "lock_api", 558 | "parking_lot_core", 559 | ] 560 | 561 | [[package]] 562 | name = "parking_lot_core" 563 | version = "0.9.4" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" 566 | dependencies = [ 567 | "cfg-if", 568 | "libc", 569 | "redox_syscall", 570 | "smallvec", 571 | "windows-sys", 572 | ] 573 | 574 | [[package]] 575 | name = "percent-encoding" 576 | version = "2.2.0" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" 579 | 580 | [[package]] 581 | name = "pin-project" 582 | version = "1.0.12" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" 585 | dependencies = [ 586 | "pin-project-internal", 587 | ] 588 | 589 | [[package]] 590 | name = "pin-project-internal" 591 | version = "1.0.12" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" 594 | dependencies = [ 595 | "proc-macro2", 596 | "quote", 597 | "syn", 598 | ] 599 | 600 | [[package]] 601 | name = "pin-project-lite" 602 | version = "0.2.9" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 605 | 606 | [[package]] 607 | name = "pin-utils" 608 | version = "0.1.0" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 611 | 612 | [[package]] 613 | name = "proc-macro2" 614 | version = "1.0.47" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 617 | dependencies = [ 618 | "unicode-ident", 619 | ] 620 | 621 | [[package]] 622 | name = "quote" 623 | version = "1.0.21" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" 626 | dependencies = [ 627 | "proc-macro2", 628 | ] 629 | 630 | [[package]] 631 | name = "redox_syscall" 632 | version = "0.2.16" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 635 | dependencies = [ 636 | "bitflags", 637 | ] 638 | 639 | [[package]] 640 | name = "regex" 641 | version = "1.6.0" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" 644 | dependencies = [ 645 | "regex-syntax", 646 | ] 647 | 648 | [[package]] 649 | name = "regex-automata" 650 | version = "0.1.10" 651 | source = "registry+https://github.com/rust-lang/crates.io-index" 652 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 653 | dependencies = [ 654 | "regex-syntax", 655 | ] 656 | 657 | [[package]] 658 | name = "regex-syntax" 659 | version = "0.6.27" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" 662 | 663 | [[package]] 664 | name = "remove_dir_all" 665 | version = "0.5.3" 666 | source = "registry+https://github.com/rust-lang/crates.io-index" 667 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 668 | dependencies = [ 669 | "winapi", 670 | ] 671 | 672 | [[package]] 673 | name = "ryu" 674 | version = "1.0.11" 675 | source = "registry+https://github.com/rust-lang/crates.io-index" 676 | checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" 677 | 678 | [[package]] 679 | name = "scopeguard" 680 | version = "1.1.0" 681 | source = "registry+https://github.com/rust-lang/crates.io-index" 682 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 683 | 684 | [[package]] 685 | name = "serde" 686 | version = "1.0.147" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" 689 | 690 | [[package]] 691 | name = "serde_json" 692 | version = "1.0.87" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" 695 | dependencies = [ 696 | "itoa", 697 | "ryu", 698 | "serde", 699 | ] 700 | 701 | [[package]] 702 | name = "serde_urlencoded" 703 | version = "0.7.1" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 706 | dependencies = [ 707 | "form_urlencoded", 708 | "itoa", 709 | "ryu", 710 | "serde", 711 | ] 712 | 713 | [[package]] 714 | name = "sharded-slab" 715 | version = "0.1.4" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" 718 | dependencies = [ 719 | "lazy_static", 720 | ] 721 | 722 | [[package]] 723 | name = "signal-hook-registry" 724 | version = "1.4.0" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" 727 | dependencies = [ 728 | "libc", 729 | ] 730 | 731 | [[package]] 732 | name = "slab" 733 | version = "0.4.7" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" 736 | dependencies = [ 737 | "autocfg", 738 | ] 739 | 740 | [[package]] 741 | name = "smallvec" 742 | version = "1.10.0" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 745 | 746 | [[package]] 747 | name = "socket2" 748 | version = "0.4.7" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" 751 | dependencies = [ 752 | "libc", 753 | "winapi", 754 | ] 755 | 756 | [[package]] 757 | name = "syn" 758 | version = "1.0.103" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" 761 | dependencies = [ 762 | "proc-macro2", 763 | "quote", 764 | "unicode-ident", 765 | ] 766 | 767 | [[package]] 768 | name = "sync_wrapper" 769 | version = "0.1.1" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" 772 | 773 | [[package]] 774 | name = "tempfile" 775 | version = "3.3.0" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 778 | dependencies = [ 779 | "cfg-if", 780 | "fastrand", 781 | "libc", 782 | "redox_syscall", 783 | "remove_dir_all", 784 | "winapi", 785 | ] 786 | 787 | [[package]] 788 | name = "thiserror" 789 | version = "1.0.37" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" 792 | dependencies = [ 793 | "thiserror-impl", 794 | ] 795 | 796 | [[package]] 797 | name = "thiserror-impl" 798 | version = "1.0.37" 799 | source = "registry+https://github.com/rust-lang/crates.io-index" 800 | checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" 801 | dependencies = [ 802 | "proc-macro2", 803 | "quote", 804 | "syn", 805 | ] 806 | 807 | [[package]] 808 | name = "thread_local" 809 | version = "1.1.4" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" 812 | dependencies = [ 813 | "once_cell", 814 | ] 815 | 816 | [[package]] 817 | name = "tokio" 818 | version = "1.21.2" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" 821 | dependencies = [ 822 | "autocfg", 823 | "bytes", 824 | "libc", 825 | "memchr", 826 | "mio", 827 | "num_cpus", 828 | "parking_lot", 829 | "pin-project-lite", 830 | "signal-hook-registry", 831 | "socket2", 832 | "tokio-macros", 833 | "winapi", 834 | ] 835 | 836 | [[package]] 837 | name = "tokio-macros" 838 | version = "1.8.0" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" 841 | dependencies = [ 842 | "proc-macro2", 843 | "quote", 844 | "syn", 845 | ] 846 | 847 | [[package]] 848 | name = "tokio-util" 849 | version = "0.7.4" 850 | source = "registry+https://github.com/rust-lang/crates.io-index" 851 | checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" 852 | dependencies = [ 853 | "bytes", 854 | "futures-core", 855 | "futures-sink", 856 | "pin-project-lite", 857 | "tokio", 858 | "tracing", 859 | ] 860 | 861 | [[package]] 862 | name = "tower" 863 | version = "0.4.13" 864 | source = "registry+https://github.com/rust-lang/crates.io-index" 865 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" 866 | dependencies = [ 867 | "futures-core", 868 | "futures-util", 869 | "pin-project", 870 | "pin-project-lite", 871 | "tokio", 872 | "tower-layer", 873 | "tower-service", 874 | "tracing", 875 | ] 876 | 877 | [[package]] 878 | name = "tower-http" 879 | version = "0.3.4" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" 882 | dependencies = [ 883 | "async-compression", 884 | "base64", 885 | "bitflags", 886 | "bytes", 887 | "futures-core", 888 | "futures-util", 889 | "http", 890 | "http-body", 891 | "http-range-header", 892 | "pin-project-lite", 893 | "tokio", 894 | "tokio-util", 895 | "tower", 896 | "tower-layer", 897 | "tower-service", 898 | "tracing", 899 | ] 900 | 901 | [[package]] 902 | name = "tower-layer" 903 | version = "0.3.2" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" 906 | 907 | [[package]] 908 | name = "tower-service" 909 | version = "0.3.2" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" 912 | 913 | [[package]] 914 | name = "tracing" 915 | version = "0.1.37" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" 918 | dependencies = [ 919 | "cfg-if", 920 | "log", 921 | "pin-project-lite", 922 | "tracing-attributes", 923 | "tracing-core", 924 | ] 925 | 926 | [[package]] 927 | name = "tracing-attributes" 928 | version = "0.1.23" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" 931 | dependencies = [ 932 | "proc-macro2", 933 | "quote", 934 | "syn", 935 | ] 936 | 937 | [[package]] 938 | name = "tracing-core" 939 | version = "0.1.30" 940 | source = "registry+https://github.com/rust-lang/crates.io-index" 941 | checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" 942 | dependencies = [ 943 | "once_cell", 944 | "valuable", 945 | ] 946 | 947 | [[package]] 948 | name = "tracing-log" 949 | version = "0.1.3" 950 | source = "registry+https://github.com/rust-lang/crates.io-index" 951 | checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" 952 | dependencies = [ 953 | "lazy_static", 954 | "log", 955 | "tracing-core", 956 | ] 957 | 958 | [[package]] 959 | name = "tracing-subscriber" 960 | version = "0.3.16" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" 963 | dependencies = [ 964 | "matchers", 965 | "nu-ansi-term", 966 | "once_cell", 967 | "regex", 968 | "sharded-slab", 969 | "smallvec", 970 | "thread_local", 971 | "tracing", 972 | "tracing-core", 973 | "tracing-log", 974 | ] 975 | 976 | [[package]] 977 | name = "try-lock" 978 | version = "0.2.3" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 981 | 982 | [[package]] 983 | name = "unicode-ident" 984 | version = "1.0.5" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 987 | 988 | [[package]] 989 | name = "valuable" 990 | version = "0.1.0" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 993 | 994 | [[package]] 995 | name = "want" 996 | version = "0.3.0" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 999 | dependencies = [ 1000 | "log", 1001 | "try-lock", 1002 | ] 1003 | 1004 | [[package]] 1005 | name = "wasi" 1006 | version = "0.11.0+wasi-snapshot-preview1" 1007 | source = "registry+https://github.com/rust-lang/crates.io-index" 1008 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1009 | 1010 | [[package]] 1011 | name = "winapi" 1012 | version = "0.3.9" 1013 | source = "registry+https://github.com/rust-lang/crates.io-index" 1014 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1015 | dependencies = [ 1016 | "winapi-i686-pc-windows-gnu", 1017 | "winapi-x86_64-pc-windows-gnu", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "winapi-i686-pc-windows-gnu" 1022 | version = "0.4.0" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1025 | 1026 | [[package]] 1027 | name = "winapi-x86_64-pc-windows-gnu" 1028 | version = "0.4.0" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1031 | 1032 | [[package]] 1033 | name = "windows-sys" 1034 | version = "0.42.0" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1037 | dependencies = [ 1038 | "windows_aarch64_gnullvm", 1039 | "windows_aarch64_msvc", 1040 | "windows_i686_gnu", 1041 | "windows_i686_msvc", 1042 | "windows_x86_64_gnu", 1043 | "windows_x86_64_gnullvm", 1044 | "windows_x86_64_msvc", 1045 | ] 1046 | 1047 | [[package]] 1048 | name = "windows_aarch64_gnullvm" 1049 | version = "0.42.0" 1050 | source = "registry+https://github.com/rust-lang/crates.io-index" 1051 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 1052 | 1053 | [[package]] 1054 | name = "windows_aarch64_msvc" 1055 | version = "0.42.0" 1056 | source = "registry+https://github.com/rust-lang/crates.io-index" 1057 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 1058 | 1059 | [[package]] 1060 | name = "windows_i686_gnu" 1061 | version = "0.42.0" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 1064 | 1065 | [[package]] 1066 | name = "windows_i686_msvc" 1067 | version = "0.42.0" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 1070 | 1071 | [[package]] 1072 | name = "windows_x86_64_gnu" 1073 | version = "0.42.0" 1074 | source = "registry+https://github.com/rust-lang/crates.io-index" 1075 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 1076 | 1077 | [[package]] 1078 | name = "windows_x86_64_gnullvm" 1079 | version = "0.42.0" 1080 | source = "registry+https://github.com/rust-lang/crates.io-index" 1081 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 1082 | 1083 | [[package]] 1084 | name = "windows_x86_64_msvc" 1085 | version = "0.42.0" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 1088 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "microservice-rust-workshop" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | axum = "0.6.0-rc.1" 10 | tokio = { version = "1.21.2", features = ["full"] } 11 | tower = { version = "0.4", features = ["util"] } 12 | tower-http = { version = "0.3.4", features = [ 13 | "add-extension", 14 | "auth", 15 | "compression-full", 16 | "limit", 17 | "trace", 18 | ] } 19 | tracing = "0.1.37" 20 | tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } 21 | hyper = { version = "0.14", features = ["full"] } 22 | gag = "1.0.0" 23 | futures = "0.3.25" 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Workshop Repo: Microservice with Tokio and Rust 2 | 3 | *NOTE*: This is Work-In-Progress! Please check for updates a day before the workshop. 4 | 5 | This GitHub repo will contain all the examples and workshops files we create during our time together. 6 | 7 | ## Install Rust 8 | 9 | [Rustup](https://rustup.rs) provides you with all the software to compile and run Rust applications, e.g. 10 | 11 | 1. Cargo - build tool and package manager 12 | 2. `rustfmt` - Auto-formatting tool for Rust code 13 | 3. `clippy` - Linting for common mistakes 14 | 15 | [and many more](https://rust-lang.github.io/rustup-components-history/). *Rustup* also allows you to install different compile targets and multiple toolchains, as well as keeping your toolchains up to date. 16 | 17 | After installing, you should have a set of new command line tools available. 18 | 19 | ### Verify your Rust installation: 20 | 21 | 1. Open a Terminal/Shell/Command Line of your choice 22 | 2. Check out this repo 23 | 3. Navigate to this repository 24 | 4. Enter 25 | 26 | ```bash 27 | $ cargo build 28 | ``` 29 | 5. Open your browser at https://localhost:3000 30 | 31 | ## Recommended Editor 32 | 33 | During the workshop, we will use [Visual Studio Code](https://code.visualstudio.com/) as editor. It's free, fast and very extensible. Making yourself familiar with VS Code is highly recommended. 34 | 35 | However, working with VS Code is not required. If you have a preferred editor with Rust support you're more productive with, please feel free to use whatever you like. What we highyly recommend though, is checking if your editor has support for [Rust analyzer](https://rust-analyzer.github.io/). 36 | 37 | ## Recommended VS Code Extensions 38 | 39 | To work effeciently, please install a couple of extensions that help you developing Rust. *Note*: Please don't install the recommendend Rust extension. It's outdated and the community decided to move to other tools. You can search and install VS Code extensions through the menu on the side 40 | 41 | We recommend the following extensions: 42 | 43 | - [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer). This is the main extension for Rust development, with the best language support available. *Note*: This extension is also available for other IDEs and editors, check out [their website](https://rust-analyzer.github.io/) 44 | 45 | - [crates](https://marketplace.visualstudio.com/items?itemName=serayuzgur.crates). This extension helps installing dependencies from crates.io 46 | 47 | - [Better TOML](https://marketplace.visualstudio.com/items?itemName=bungcip.better-toml). TOML is the format that the dependency manager Cargo uses to manage dependencies. This extension helps formatting and editing TOML files 48 | 49 | - [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb). All Rust code is compiled against LLVM. This extension helps debugging LLVM code inside VS Code 50 | 51 | - [Error Lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens). Inline errors 52 | 53 | ## Tasks 54 | 55 | Our goal is to build a simple in memory key value store. We use Axum as basis and work with Tower services to get certain features right. In the `tests` folder you find four test files accompanying all four exercises. Remove the `ignore` macros at the beginning of each test to run it. Note that some tests require to be run with the `--nocapture` flag. 56 | 57 | Those are the four exercises. 58 | 59 | ### Exercise 1: *Axum* 60 | 61 | - Create an Axum Router for two routes. 62 | - The root (`"/"`) says `

Hello Axum

` 63 | - The path `"/hello"` says either `

Hello Unknown Visitor

` or subsitutes `Unknown Visitor` with a name provided by parameter `name` 64 | 65 | ### Exercise 2: *Key Value Store* 66 | 67 | - Store data in a Key Value Store (provided by a `HashMap` shared state) 68 | - Use `/kv/:key` to store data via `POST`, and to retrieve data via `GET` 69 | - Return a 404 if there is no data saved 70 | - *Stretch goals*: 71 | - Locking an `RwLock` might result in a `PoisonError`. Make sure you can convert a `PoisonError` to a `Response`. Tip: implement your custom error and implement `IntoResponse` 72 | - The default request size is 2MB. You want to store data up to 8MB in your key value store. Deactivate the default body limit, and set your own request body limit. Tip: Use a Service Builder and respective Layers from `axum` and `tower_http` 73 | 74 | ### Exercise 2a: *Image Processing* 75 | 76 | - Next to data, store meta information on the type of data stored (images, text, see content-type) 77 | - For images, provide a route `/kv/:key/thumbnail` that returns a thumbnail of the image 78 | - For images, provide a route `/kv/:key/grayscale` that returns a grayscale version of the image 79 | - Use the `image` crate to process images: https://crates.io/crates/image 80 | - Apply proper error handling creating a custom error type called `ImageProcessingError` and implementing `IntoResponse` for it 81 | - Create a struct called `ImageResponse` and implement `IntoResponse` for it. This struct should contain the image data and the content type 82 | 83 | ### Exercise 3: *Custom Services* 84 | - Write a logging service that logs every request to stdout 85 | - *Stretch goal* 86 | - Log before executing the request and after 87 | 88 | Run tests with `--nocapture`! 89 | 90 | ### Exercise 4: *Built-in Services* 91 | - Define nested routes for administrative tasks, be sure to add authorization so only authorized people can access. A plain auth token is ok, if you want to go fancy create your own implementation. 92 | - Stretch goal: If you haven't worked on the upload limit, try doing it now. 93 | 94 | ### Exercise 5: *Validation* 95 | 96 | _This is a stretch goal if time permits, combining a few techniques in Rust that are very common, such as:_ 97 | 98 | - Using `serde` to deserialize JSON 99 | - Using `validator` to validate user input 100 | - Using enums to describe state 101 | - Implementing common traits: `Default`, `Debug`, `Deserialize`, `Serialize` 102 | - Async traits 103 | - Custom Errors and error mapping 104 | 105 | Task: Define `user` routes to create a new user, and to validate them, e.g 106 | 107 | `POST /user` with body 108 | 109 | ```json 110 | { 111 | "username": "myusername", 112 | "email": "john@doe.com", 113 | "age": 42 114 | } 115 | ``` 116 | 117 | By default, users are not active. A call to 118 | 119 | `POST /user/{username}/activate` 120 | 121 | activates the user. 122 | 123 | 124 | - Store data in a Key Value Store (provided by a `HashMap` shared state) 125 | - Use Serde to parse the body 126 | - Use `validator` to validate the data 127 | - Define a `ValidatedJson` type that makes sure that any struct that has the `Validate` trait and `DeserializeOwned` trait can be used as an extractor (see `FromRequest`). 128 | -------------------------------------------------------------------------------- /crab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ddprrt/microservice-rust-workshop/4a868fce73506eb6a797903cf957d75487032c4a/crab.png -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | sync::{Arc, RwLock}, 4 | }; 5 | 6 | use axum::{body::Bytes, Router}; 7 | 8 | /// Custom type for a shared state 9 | pub type SharedState = Arc>; 10 | #[derive(Default)] 11 | pub struct AppState { 12 | db: HashMap, 13 | } 14 | 15 | pub fn router(_state: &SharedState) -> Router { 16 | todo!() 17 | } 18 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::net::SocketAddr; 2 | 3 | use microservice_rust_workshop::{router, SharedState}; 4 | 5 | type BoxError = Box; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<(), BoxError> { 9 | let state = SharedState::default(); 10 | let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); 11 | 12 | let app = router(&state); 13 | 14 | axum::Server::bind(&addr) 15 | .serve(app.into_make_service()) 16 | .await?; 17 | 18 | Ok(()) 19 | } 20 | -------------------------------------------------------------------------------- /tests/lesson-1.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | body::Body, 3 | http::{Request, StatusCode}, 4 | }; 5 | 6 | use microservice_rust_workshop::{router, SharedState}; 7 | use tower::Service; // for `call` 8 | 9 | #[tokio::test] 10 | async fn hello_world() { 11 | let state = SharedState::default(); 12 | let mut app = router(&state); 13 | 14 | let response = app 15 | .call(Request::builder().uri("/").body(Body::empty()).unwrap()) 16 | .await 17 | .unwrap(); 18 | 19 | assert_eq!(response.status(), StatusCode::OK); 20 | 21 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 22 | assert_eq!(&body[..], b"

Hello Axum

"); 23 | } 24 | 25 | #[ignore] 26 | #[tokio::test] 27 | async fn say_hi_unknown() { 28 | let state = SharedState::default(); 29 | let mut app = router(&state); 30 | 31 | let response = app 32 | .call( 33 | Request::builder() 34 | .uri("/hello") 35 | .body(Body::empty()) 36 | .unwrap(), 37 | ) 38 | .await 39 | .unwrap(); 40 | 41 | assert_eq!(response.status(), StatusCode::OK); 42 | 43 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 44 | assert_eq!(&body[..], b"

Hello Unknown Visitor

"); 45 | } 46 | 47 | #[ignore] 48 | #[tokio::test] 49 | async fn say_hi_stefan() { 50 | let state = SharedState::default(); 51 | let mut app = router(&state); 52 | 53 | // `Router` implements `tower::Service>` so we can 54 | // call it like any tower service, no need to run an HTTP server. 55 | let response = app 56 | .call( 57 | Request::builder() 58 | .uri("/hello?name=Stefan") 59 | .body(Body::empty()) 60 | .unwrap(), 61 | ) 62 | .await 63 | .unwrap(); 64 | 65 | assert_eq!(response.status(), StatusCode::OK); 66 | 67 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 68 | assert_eq!(&body[..], b"

Hello Stefan

"); 69 | } 70 | -------------------------------------------------------------------------------- /tests/lesson-2.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | body::Body, 3 | http::{Request, StatusCode}, 4 | }; 5 | 6 | use microservice_rust_workshop::{router, SharedState}; 7 | use tower::Service; // for `call` 8 | 9 | #[ignore] 10 | #[tokio::test] 11 | async fn basic_db_test() { 12 | let state = SharedState::default(); 13 | let mut app = router(&state); 14 | 15 | let response = app 16 | .call( 17 | Request::builder() 18 | .uri("/kv/test") 19 | .method("POST") 20 | .body("Hello World".into()) 21 | .unwrap(), 22 | ) 23 | .await 24 | .unwrap(); 25 | 26 | assert_eq!(response.status(), StatusCode::OK); 27 | 28 | let response = app 29 | .call( 30 | Request::builder() 31 | .uri("/kv/test") 32 | .method("GET") 33 | .body(Body::empty()) 34 | .unwrap(), 35 | ) 36 | .await 37 | .unwrap(); 38 | 39 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 40 | assert_eq!(&body[..], b"Hello World"); 41 | } 42 | 43 | #[ignore] 44 | #[tokio::test] 45 | async fn big_request() { 46 | let state = SharedState::default(); 47 | let mut app = router(&state); 48 | let bytes = include_bytes!("../crab.png"); 49 | 50 | let response = app 51 | .call( 52 | Request::builder() 53 | .uri("/kv/crab") 54 | .method("POST") 55 | .body(bytes[..].into()) 56 | .unwrap(), 57 | ) 58 | .await 59 | .unwrap(); 60 | 61 | assert_eq!(response.status(), StatusCode::OK); 62 | 63 | let response = app 64 | .call( 65 | Request::builder() 66 | .uri("/kv/crab") 67 | .method("GET") 68 | .body(Body::empty()) 69 | .unwrap(), 70 | ) 71 | .await 72 | .unwrap(); 73 | 74 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 75 | assert_eq!(body.len(), bytes.len()); 76 | assert_eq!(&body[..], &bytes[..]); 77 | } 78 | 79 | #[ignore] 80 | #[tokio::test] 81 | async fn no_entry() { 82 | let state = SharedState::default(); 83 | let mut app = router(&state); 84 | 85 | let response = app 86 | .call( 87 | Request::builder() 88 | .uri("/kv/crab") 89 | .method("GET") 90 | .body(Body::empty()) 91 | .unwrap(), 92 | ) 93 | .await 94 | .unwrap(); 95 | 96 | assert_eq!(response.status(), StatusCode::NOT_FOUND); 97 | } 98 | -------------------------------------------------------------------------------- /tests/lesson-3.rs: -------------------------------------------------------------------------------- 1 | use std::io::Read; 2 | 3 | use axum::{ 4 | body::Body, 5 | http::{Request, StatusCode}, 6 | }; 7 | 8 | use gag::BufferRedirect; 9 | use microservice_rust_workshop::{router, SharedState}; 10 | use tower::Service; // for `call` 11 | 12 | #[ignore] 13 | #[tokio::test] 14 | async fn log_test() { 15 | // Run with nocapture 16 | let state = SharedState::default(); 17 | let mut app = router(&state); 18 | 19 | let mut buf = BufferRedirect::stdout().unwrap(); 20 | 21 | let response = app 22 | .call(Request::builder().uri("/").body(Body::empty()).unwrap()) 23 | .await 24 | .unwrap(); 25 | 26 | assert_eq!(response.status(), StatusCode::OK); 27 | 28 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 29 | assert_eq!(&body[..], b"

Hello Axum

"); 30 | 31 | let mut output = String::new(); 32 | buf.read_to_string(&mut output).unwrap(); 33 | 34 | assert_eq!("processing GET /", output.trim()); 35 | } 36 | 37 | #[ignore] 38 | #[tokio::test] 39 | async fn advanced_log_test() { 40 | // Run with nocapture 41 | let state = SharedState::default(); 42 | let mut app = router(&state); 43 | 44 | let mut buf = BufferRedirect::stdout().unwrap(); 45 | 46 | let response = app 47 | .call(Request::builder().uri("/").body(Body::empty()).unwrap()) 48 | .await 49 | .unwrap(); 50 | 51 | assert_eq!(response.status(), StatusCode::OK); 52 | 53 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 54 | assert_eq!(&body[..], b"

Hello Axum

"); 55 | 56 | let mut output = String::new(); 57 | buf.read_to_string(&mut output).unwrap(); 58 | 59 | assert_eq!("processing GET /\nend processing GET /", output.trim()); 60 | } 61 | -------------------------------------------------------------------------------- /tests/lesson-4.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | body::Body, 3 | http::{Request, StatusCode}, 4 | }; 5 | 6 | use microservice_rust_workshop::{router, SharedState}; 7 | use tower::Service; // for `call` 8 | 9 | #[ignore] 10 | #[tokio::test] 11 | async fn no_auth() { 12 | let state = SharedState::default(); 13 | let mut app = router(&state); 14 | 15 | // Add something 16 | let response = app 17 | .call( 18 | Request::builder() 19 | .uri("/kv/test") 20 | .method("POST") 21 | .body("Hello World".into()) 22 | .unwrap(), 23 | ) 24 | .await 25 | .unwrap(); 26 | 27 | assert_eq!(response.status(), StatusCode::OK); 28 | 29 | // Check if it's there 30 | let response = app 31 | .call( 32 | Request::builder() 33 | .uri("/kv/test") 34 | .method("GET") 35 | .body(Body::empty()) 36 | .unwrap(), 37 | ) 38 | .await 39 | .unwrap(); 40 | 41 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 42 | assert_eq!(&body[..], b"Hello World"); 43 | 44 | let response = app 45 | .call( 46 | Request::builder() 47 | .uri("/admin/kv") 48 | .method("DELETE") 49 | .body(Body::empty()) 50 | .unwrap(), 51 | ) 52 | .await 53 | .unwrap(); 54 | 55 | assert_eq!(response.status(), StatusCode::UNAUTHORIZED); 56 | } 57 | 58 | #[ignore] 59 | #[tokio::test] 60 | async fn delete_entries() { 61 | let state = SharedState::default(); 62 | let mut app = router(&state); 63 | 64 | // Add something 65 | let response = app 66 | .call( 67 | Request::builder() 68 | .uri("/kv/test") 69 | .method("POST") 70 | .body("Hello World".into()) 71 | .unwrap(), 72 | ) 73 | .await 74 | .unwrap(); 75 | 76 | assert_eq!(response.status(), StatusCode::OK); 77 | 78 | // Check if it's there 79 | let response = app 80 | .call( 81 | Request::builder() 82 | .uri("/kv/test") 83 | .method("GET") 84 | .body(Body::empty()) 85 | .unwrap(), 86 | ) 87 | .await 88 | .unwrap(); 89 | 90 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 91 | assert_eq!(&body[..], b"Hello World"); 92 | 93 | let response = app 94 | .call( 95 | Request::builder() 96 | .uri("/admin/kv") 97 | .method("DELETE") 98 | .header("Authorization", "Bearer secret") 99 | .body(Body::empty()) 100 | .unwrap(), 101 | ) 102 | .await 103 | .unwrap(); 104 | 105 | assert_eq!(response.status(), StatusCode::OK); 106 | 107 | let response = app 108 | .call( 109 | Request::builder() 110 | .uri("/kv/test") 111 | .method("GET") 112 | .body(Body::empty()) 113 | .unwrap(), 114 | ) 115 | .await 116 | .unwrap(); 117 | 118 | assert_eq!(response.status(), StatusCode::NOT_FOUND); 119 | } 120 | 121 | #[ignore] 122 | #[tokio::test] 123 | async fn delete_keys() { 124 | let state = SharedState::default(); 125 | let mut app = router(&state); 126 | 127 | // Add something 128 | let response = app 129 | .call( 130 | Request::builder() 131 | .uri("/kv/test") 132 | .method("POST") 133 | .body("Hello World".into()) 134 | .unwrap(), 135 | ) 136 | .await 137 | .unwrap(); 138 | 139 | assert_eq!(response.status(), StatusCode::OK); 140 | 141 | // Check if it's there 142 | let response = app 143 | .call( 144 | Request::builder() 145 | .uri("/kv/test") 146 | .method("GET") 147 | .body(Body::empty()) 148 | .unwrap(), 149 | ) 150 | .await 151 | .unwrap(); 152 | 153 | let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); 154 | assert_eq!(&body[..], b"Hello World"); 155 | 156 | let response = app 157 | .call( 158 | Request::builder() 159 | .uri("/admin/kv/test") 160 | .method("DELETE") 161 | .header("Authorization", "Bearer secret") 162 | .body(Body::empty()) 163 | .unwrap(), 164 | ) 165 | .await 166 | .unwrap(); 167 | 168 | assert_eq!(response.status(), StatusCode::OK); 169 | 170 | let response = app 171 | .call( 172 | Request::builder() 173 | .uri("/kv/test") 174 | .method("GET") 175 | .body(Body::empty()) 176 | .unwrap(), 177 | ) 178 | .await 179 | .unwrap(); 180 | 181 | assert_eq!(response.status(), StatusCode::NOT_FOUND); 182 | } 183 | --------------------------------------------------------------------------------