├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── demo.tape └── src ├── backend.rs ├── ball.rs ├── game.rs ├── main.rs ├── paddle.rs ├── physics.rs └── server.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "adler2" 22 | version = "2.0.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 25 | 26 | [[package]] 27 | name = "aead" 28 | version = "0.5.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" 31 | dependencies = [ 32 | "crypto-common", 33 | "generic-array", 34 | ] 35 | 36 | [[package]] 37 | name = "aes" 38 | version = "0.8.4" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" 41 | dependencies = [ 42 | "cfg-if", 43 | "cipher", 44 | "cpufeatures", 45 | ] 46 | 47 | [[package]] 48 | name = "aes-gcm" 49 | version = "0.10.3" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" 52 | dependencies = [ 53 | "aead", 54 | "aes", 55 | "cipher", 56 | "ctr", 57 | "ghash", 58 | "subtle", 59 | ] 60 | 61 | [[package]] 62 | name = "aho-corasick" 63 | version = "1.1.3" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 66 | dependencies = [ 67 | "memchr", 68 | ] 69 | 70 | [[package]] 71 | name = "allocator-api2" 72 | version = "0.2.18" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 75 | 76 | [[package]] 77 | name = "android-tzdata" 78 | version = "0.1.1" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 81 | 82 | [[package]] 83 | name = "android_system_properties" 84 | version = "0.1.5" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 87 | dependencies = [ 88 | "libc", 89 | ] 90 | 91 | [[package]] 92 | name = "argon2" 93 | version = "0.5.3" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" 96 | dependencies = [ 97 | "base64ct", 98 | "blake2", 99 | "cpufeatures", 100 | "password-hash", 101 | ] 102 | 103 | [[package]] 104 | name = "autocfg" 105 | version = "1.4.0" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 108 | 109 | [[package]] 110 | name = "backtrace" 111 | version = "0.3.71" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 114 | dependencies = [ 115 | "addr2line", 116 | "cc", 117 | "cfg-if", 118 | "libc", 119 | "miniz_oxide 0.7.4", 120 | "object", 121 | "rustc-demangle", 122 | ] 123 | 124 | [[package]] 125 | name = "base16ct" 126 | version = "0.2.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" 129 | 130 | [[package]] 131 | name = "base64ct" 132 | version = "1.6.0" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" 135 | 136 | [[package]] 137 | name = "bcrypt-pbkdf" 138 | version = "0.10.0" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "6aeac2e1fe888769f34f05ac343bbef98b14d1ffb292ab69d4608b3abc86f2a2" 141 | dependencies = [ 142 | "blowfish", 143 | "pbkdf2", 144 | "sha2", 145 | ] 146 | 147 | [[package]] 148 | name = "bitflags" 149 | version = "2.6.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 152 | 153 | [[package]] 154 | name = "blake2" 155 | version = "0.10.6" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" 158 | dependencies = [ 159 | "digest", 160 | ] 161 | 162 | [[package]] 163 | name = "block-buffer" 164 | version = "0.10.4" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 167 | dependencies = [ 168 | "generic-array", 169 | ] 170 | 171 | [[package]] 172 | name = "block-padding" 173 | version = "0.3.3" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" 176 | dependencies = [ 177 | "generic-array", 178 | ] 179 | 180 | [[package]] 181 | name = "blowfish" 182 | version = "0.9.1" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" 185 | dependencies = [ 186 | "byteorder", 187 | "cipher", 188 | ] 189 | 190 | [[package]] 191 | name = "bumpalo" 192 | version = "3.16.0" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 195 | 196 | [[package]] 197 | name = "byteorder" 198 | version = "1.5.0" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 201 | 202 | [[package]] 203 | name = "bytes" 204 | version = "1.8.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" 207 | 208 | [[package]] 209 | name = "cassowary" 210 | version = "0.3.0" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" 213 | 214 | [[package]] 215 | name = "castaway" 216 | version = "0.2.3" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" 219 | dependencies = [ 220 | "rustversion", 221 | ] 222 | 223 | [[package]] 224 | name = "cbc" 225 | version = "0.1.2" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" 228 | dependencies = [ 229 | "cipher", 230 | ] 231 | 232 | [[package]] 233 | name = "cc" 234 | version = "1.1.30" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "b16803a61b81d9eabb7eae2588776c4c1e584b738ede45fdbb4c972cec1e9945" 237 | dependencies = [ 238 | "shlex", 239 | ] 240 | 241 | [[package]] 242 | name = "cfg-if" 243 | version = "1.0.0" 244 | source = "registry+https://github.com/rust-lang/crates.io-index" 245 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 246 | 247 | [[package]] 248 | name = "cfg_aliases" 249 | version = "0.2.1" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 252 | 253 | [[package]] 254 | name = "chacha20" 255 | version = "0.9.1" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" 258 | dependencies = [ 259 | "cfg-if", 260 | "cipher", 261 | "cpufeatures", 262 | ] 263 | 264 | [[package]] 265 | name = "chrono" 266 | version = "0.4.38" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 269 | dependencies = [ 270 | "android-tzdata", 271 | "iana-time-zone", 272 | "js-sys", 273 | "num-traits", 274 | "wasm-bindgen", 275 | "windows-targets", 276 | ] 277 | 278 | [[package]] 279 | name = "cipher" 280 | version = "0.4.4" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" 283 | dependencies = [ 284 | "crypto-common", 285 | "inout", 286 | ] 287 | 288 | [[package]] 289 | name = "color-eyre" 290 | version = "0.6.3" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" 293 | dependencies = [ 294 | "backtrace", 295 | "color-spantrace", 296 | "eyre", 297 | "indenter", 298 | "once_cell", 299 | "owo-colors", 300 | "tracing-error", 301 | ] 302 | 303 | [[package]] 304 | name = "color-spantrace" 305 | version = "0.2.1" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" 308 | dependencies = [ 309 | "once_cell", 310 | "owo-colors", 311 | "tracing-core", 312 | "tracing-error", 313 | ] 314 | 315 | [[package]] 316 | name = "compact_str" 317 | version = "0.8.0" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" 320 | dependencies = [ 321 | "castaway", 322 | "cfg-if", 323 | "itoa", 324 | "rustversion", 325 | "ryu", 326 | "static_assertions", 327 | ] 328 | 329 | [[package]] 330 | name = "const-oid" 331 | version = "0.9.6" 332 | source = "registry+https://github.com/rust-lang/crates.io-index" 333 | checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 334 | 335 | [[package]] 336 | name = "core-foundation-sys" 337 | version = "0.8.7" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 340 | 341 | [[package]] 342 | name = "cpufeatures" 343 | version = "0.2.14" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" 346 | dependencies = [ 347 | "libc", 348 | ] 349 | 350 | [[package]] 351 | name = "crc32fast" 352 | version = "1.4.2" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 355 | dependencies = [ 356 | "cfg-if", 357 | ] 358 | 359 | [[package]] 360 | name = "crossterm" 361 | version = "0.28.1" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" 364 | dependencies = [ 365 | "bitflags", 366 | "crossterm_winapi", 367 | "mio", 368 | "parking_lot", 369 | "rustix", 370 | "signal-hook", 371 | "signal-hook-mio", 372 | "winapi", 373 | ] 374 | 375 | [[package]] 376 | name = "crossterm_winapi" 377 | version = "0.9.1" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 380 | dependencies = [ 381 | "winapi", 382 | ] 383 | 384 | [[package]] 385 | name = "crypto-bigint" 386 | version = "0.5.5" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" 389 | dependencies = [ 390 | "generic-array", 391 | "rand_core", 392 | "subtle", 393 | "zeroize", 394 | ] 395 | 396 | [[package]] 397 | name = "crypto-common" 398 | version = "0.1.6" 399 | source = "registry+https://github.com/rust-lang/crates.io-index" 400 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 401 | dependencies = [ 402 | "generic-array", 403 | "rand_core", 404 | "typenum", 405 | ] 406 | 407 | [[package]] 408 | name = "ctr" 409 | version = "0.9.2" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" 412 | dependencies = [ 413 | "cipher", 414 | ] 415 | 416 | [[package]] 417 | name = "curve25519-dalek" 418 | version = "4.1.3" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" 421 | dependencies = [ 422 | "cfg-if", 423 | "cpufeatures", 424 | "curve25519-dalek-derive", 425 | "digest", 426 | "fiat-crypto", 427 | "rustc_version", 428 | "subtle", 429 | "zeroize", 430 | ] 431 | 432 | [[package]] 433 | name = "curve25519-dalek-derive" 434 | version = "0.1.1" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" 437 | dependencies = [ 438 | "proc-macro2", 439 | "quote", 440 | "syn", 441 | ] 442 | 443 | [[package]] 444 | name = "data-encoding" 445 | version = "2.6.0" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" 448 | 449 | [[package]] 450 | name = "delegate" 451 | version = "0.13.3" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "b9b6483c2bbed26f97861cf57651d4f2b731964a28cd2257f934a4b452480d21" 454 | dependencies = [ 455 | "proc-macro2", 456 | "quote", 457 | "syn", 458 | ] 459 | 460 | [[package]] 461 | name = "der" 462 | version = "0.7.9" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" 465 | dependencies = [ 466 | "const-oid", 467 | "pem-rfc7468", 468 | "zeroize", 469 | ] 470 | 471 | [[package]] 472 | name = "digest" 473 | version = "0.10.7" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 476 | dependencies = [ 477 | "block-buffer", 478 | "const-oid", 479 | "crypto-common", 480 | "subtle", 481 | ] 482 | 483 | [[package]] 484 | name = "dirs" 485 | version = "6.0.0" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" 488 | dependencies = [ 489 | "dirs-sys", 490 | ] 491 | 492 | [[package]] 493 | name = "dirs-sys" 494 | version = "0.5.0" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" 497 | dependencies = [ 498 | "libc", 499 | "option-ext", 500 | "redox_users", 501 | "windows-sys 0.59.0", 502 | ] 503 | 504 | [[package]] 505 | name = "ecdsa" 506 | version = "0.16.9" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" 509 | dependencies = [ 510 | "der", 511 | "digest", 512 | "elliptic-curve", 513 | "rfc6979", 514 | "signature", 515 | "spki", 516 | ] 517 | 518 | [[package]] 519 | name = "ed25519" 520 | version = "2.2.3" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" 523 | dependencies = [ 524 | "pkcs8", 525 | "signature", 526 | ] 527 | 528 | [[package]] 529 | name = "ed25519-dalek" 530 | version = "2.1.1" 531 | source = "registry+https://github.com/rust-lang/crates.io-index" 532 | checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" 533 | dependencies = [ 534 | "curve25519-dalek", 535 | "ed25519", 536 | "rand_core", 537 | "serde", 538 | "sha2", 539 | "subtle", 540 | "zeroize", 541 | ] 542 | 543 | [[package]] 544 | name = "either" 545 | version = "1.13.0" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 548 | 549 | [[package]] 550 | name = "elliptic-curve" 551 | version = "0.13.8" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" 554 | dependencies = [ 555 | "base16ct", 556 | "crypto-bigint", 557 | "digest", 558 | "ff", 559 | "generic-array", 560 | "group", 561 | "hkdf", 562 | "pem-rfc7468", 563 | "pkcs8", 564 | "rand_core", 565 | "sec1", 566 | "subtle", 567 | "zeroize", 568 | ] 569 | 570 | [[package]] 571 | name = "enum_dispatch" 572 | version = "0.3.13" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" 575 | dependencies = [ 576 | "once_cell", 577 | "proc-macro2", 578 | "quote", 579 | "syn", 580 | ] 581 | 582 | [[package]] 583 | name = "equivalent" 584 | version = "1.0.1" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 587 | 588 | [[package]] 589 | name = "errno" 590 | version = "0.3.9" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 593 | dependencies = [ 594 | "libc", 595 | "windows-sys 0.52.0", 596 | ] 597 | 598 | [[package]] 599 | name = "eyre" 600 | version = "0.6.12" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" 603 | dependencies = [ 604 | "indenter", 605 | "once_cell", 606 | ] 607 | 608 | [[package]] 609 | name = "ff" 610 | version = "0.13.0" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" 613 | dependencies = [ 614 | "rand_core", 615 | "subtle", 616 | ] 617 | 618 | [[package]] 619 | name = "fiat-crypto" 620 | version = "0.2.9" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" 623 | 624 | [[package]] 625 | name = "flate2" 626 | version = "1.0.34" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" 629 | dependencies = [ 630 | "crc32fast", 631 | "miniz_oxide 0.8.0", 632 | ] 633 | 634 | [[package]] 635 | name = "foldhash" 636 | version = "0.1.3" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" 639 | 640 | [[package]] 641 | name = "futures" 642 | version = "0.3.31" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 645 | dependencies = [ 646 | "futures-channel", 647 | "futures-core", 648 | "futures-executor", 649 | "futures-io", 650 | "futures-sink", 651 | "futures-task", 652 | "futures-util", 653 | ] 654 | 655 | [[package]] 656 | name = "futures-channel" 657 | version = "0.3.31" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 660 | dependencies = [ 661 | "futures-core", 662 | "futures-sink", 663 | ] 664 | 665 | [[package]] 666 | name = "futures-core" 667 | version = "0.3.31" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 670 | 671 | [[package]] 672 | name = "futures-executor" 673 | version = "0.3.31" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 676 | dependencies = [ 677 | "futures-core", 678 | "futures-task", 679 | "futures-util", 680 | ] 681 | 682 | [[package]] 683 | name = "futures-io" 684 | version = "0.3.31" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 687 | 688 | [[package]] 689 | name = "futures-macro" 690 | version = "0.3.31" 691 | source = "registry+https://github.com/rust-lang/crates.io-index" 692 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 693 | dependencies = [ 694 | "proc-macro2", 695 | "quote", 696 | "syn", 697 | ] 698 | 699 | [[package]] 700 | name = "futures-sink" 701 | version = "0.3.31" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 704 | 705 | [[package]] 706 | name = "futures-task" 707 | version = "0.3.31" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 710 | 711 | [[package]] 712 | name = "futures-util" 713 | version = "0.3.31" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 716 | dependencies = [ 717 | "futures-channel", 718 | "futures-core", 719 | "futures-io", 720 | "futures-macro", 721 | "futures-sink", 722 | "futures-task", 723 | "memchr", 724 | "pin-project-lite", 725 | "pin-utils", 726 | "slab", 727 | ] 728 | 729 | [[package]] 730 | name = "generic-array" 731 | version = "0.14.7" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 734 | dependencies = [ 735 | "typenum", 736 | "version_check", 737 | "zeroize", 738 | ] 739 | 740 | [[package]] 741 | name = "getrandom" 742 | version = "0.2.15" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 745 | dependencies = [ 746 | "cfg-if", 747 | "js-sys", 748 | "libc", 749 | "wasi", 750 | "wasm-bindgen", 751 | ] 752 | 753 | [[package]] 754 | name = "ghash" 755 | version = "0.5.1" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" 758 | dependencies = [ 759 | "opaque-debug", 760 | "polyval", 761 | ] 762 | 763 | [[package]] 764 | name = "gimli" 765 | version = "0.28.1" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 768 | 769 | [[package]] 770 | name = "group" 771 | version = "0.13.0" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" 774 | dependencies = [ 775 | "ff", 776 | "rand_core", 777 | "subtle", 778 | ] 779 | 780 | [[package]] 781 | name = "hashbrown" 782 | version = "0.15.2" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" 785 | dependencies = [ 786 | "allocator-api2", 787 | "equivalent", 788 | "foldhash", 789 | ] 790 | 791 | [[package]] 792 | name = "heck" 793 | version = "0.5.0" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 796 | 797 | [[package]] 798 | name = "hermit-abi" 799 | version = "0.3.9" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 802 | 803 | [[package]] 804 | name = "hex" 805 | version = "0.4.3" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 808 | 809 | [[package]] 810 | name = "hex-literal" 811 | version = "0.4.1" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" 814 | 815 | [[package]] 816 | name = "hkdf" 817 | version = "0.12.4" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" 820 | dependencies = [ 821 | "hmac", 822 | ] 823 | 824 | [[package]] 825 | name = "hmac" 826 | version = "0.12.1" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 829 | dependencies = [ 830 | "digest", 831 | ] 832 | 833 | [[package]] 834 | name = "home" 835 | version = "0.5.9" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" 838 | dependencies = [ 839 | "windows-sys 0.52.0", 840 | ] 841 | 842 | [[package]] 843 | name = "iana-time-zone" 844 | version = "0.1.61" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 847 | dependencies = [ 848 | "android_system_properties", 849 | "core-foundation-sys", 850 | "iana-time-zone-haiku", 851 | "js-sys", 852 | "wasm-bindgen", 853 | "windows-core 0.52.0", 854 | ] 855 | 856 | [[package]] 857 | name = "iana-time-zone-haiku" 858 | version = "0.1.2" 859 | source = "registry+https://github.com/rust-lang/crates.io-index" 860 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 861 | dependencies = [ 862 | "cc", 863 | ] 864 | 865 | [[package]] 866 | name = "indenter" 867 | version = "0.3.3" 868 | source = "registry+https://github.com/rust-lang/crates.io-index" 869 | checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" 870 | 871 | [[package]] 872 | name = "indoc" 873 | version = "2.0.5" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" 876 | 877 | [[package]] 878 | name = "inout" 879 | version = "0.1.3" 880 | source = "registry+https://github.com/rust-lang/crates.io-index" 881 | checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" 882 | dependencies = [ 883 | "block-padding", 884 | "generic-array", 885 | ] 886 | 887 | [[package]] 888 | name = "instability" 889 | version = "0.3.2" 890 | source = "registry+https://github.com/rust-lang/crates.io-index" 891 | checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" 892 | dependencies = [ 893 | "quote", 894 | "syn", 895 | ] 896 | 897 | [[package]] 898 | name = "internal-russh-forked-ssh-key" 899 | version = "0.6.10+upstream-0.6.7" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "33555bd765ace379fe85d97bb6d48b5783054f6048a7d5ec24cd9155e490e266" 902 | dependencies = [ 903 | "argon2", 904 | "bcrypt-pbkdf", 905 | "ecdsa", 906 | "ed25519-dalek", 907 | "hex", 908 | "hmac", 909 | "num-bigint-dig", 910 | "p256", 911 | "p384", 912 | "p521", 913 | "rand_core", 914 | "rsa", 915 | "sec1", 916 | "sha1", 917 | "sha2", 918 | "signature", 919 | "ssh-cipher", 920 | "ssh-encoding", 921 | "subtle", 922 | "zeroize", 923 | ] 924 | 925 | [[package]] 926 | name = "itertools" 927 | version = "0.13.0" 928 | source = "registry+https://github.com/rust-lang/crates.io-index" 929 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 930 | dependencies = [ 931 | "either", 932 | ] 933 | 934 | [[package]] 935 | name = "itoa" 936 | version = "1.0.11" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 939 | 940 | [[package]] 941 | name = "js-sys" 942 | version = "0.3.72" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" 945 | dependencies = [ 946 | "wasm-bindgen", 947 | ] 948 | 949 | [[package]] 950 | name = "lazy_static" 951 | version = "1.5.0" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 954 | dependencies = [ 955 | "spin", 956 | ] 957 | 958 | [[package]] 959 | name = "libc" 960 | version = "0.2.171" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" 963 | 964 | [[package]] 965 | name = "libm" 966 | version = "0.2.8" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 969 | 970 | [[package]] 971 | name = "libredox" 972 | version = "0.1.3" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 975 | dependencies = [ 976 | "bitflags", 977 | "libc", 978 | ] 979 | 980 | [[package]] 981 | name = "linux-raw-sys" 982 | version = "0.4.14" 983 | source = "registry+https://github.com/rust-lang/crates.io-index" 984 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 985 | 986 | [[package]] 987 | name = "lock_api" 988 | version = "0.4.12" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 991 | dependencies = [ 992 | "autocfg", 993 | "scopeguard", 994 | ] 995 | 996 | [[package]] 997 | name = "log" 998 | version = "0.4.22" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 1001 | 1002 | [[package]] 1003 | name = "lru" 1004 | version = "0.12.5" 1005 | source = "registry+https://github.com/rust-lang/crates.io-index" 1006 | checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" 1007 | dependencies = [ 1008 | "hashbrown", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "matchers" 1013 | version = "0.1.0" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 1016 | dependencies = [ 1017 | "regex-automata 0.1.10", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "md5" 1022 | version = "0.7.0" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" 1025 | 1026 | [[package]] 1027 | name = "memchr" 1028 | version = "2.7.4" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1031 | 1032 | [[package]] 1033 | name = "miniz_oxide" 1034 | version = "0.7.4" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 1037 | dependencies = [ 1038 | "adler", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "miniz_oxide" 1043 | version = "0.8.0" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 1046 | dependencies = [ 1047 | "adler2", 1048 | ] 1049 | 1050 | [[package]] 1051 | name = "mio" 1052 | version = "1.0.2" 1053 | source = "registry+https://github.com/rust-lang/crates.io-index" 1054 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 1055 | dependencies = [ 1056 | "hermit-abi", 1057 | "libc", 1058 | "log", 1059 | "wasi", 1060 | "windows-sys 0.52.0", 1061 | ] 1062 | 1063 | [[package]] 1064 | name = "nix" 1065 | version = "0.29.0" 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" 1067 | checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" 1068 | dependencies = [ 1069 | "bitflags", 1070 | "cfg-if", 1071 | "cfg_aliases", 1072 | "libc", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "nu-ansi-term" 1077 | version = "0.46.0" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 1080 | dependencies = [ 1081 | "overload", 1082 | "winapi", 1083 | ] 1084 | 1085 | [[package]] 1086 | name = "num-bigint" 1087 | version = "0.4.6" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 1090 | dependencies = [ 1091 | "num-integer", 1092 | "num-traits", 1093 | "rand", 1094 | ] 1095 | 1096 | [[package]] 1097 | name = "num-bigint-dig" 1098 | version = "0.8.4" 1099 | source = "registry+https://github.com/rust-lang/crates.io-index" 1100 | checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" 1101 | dependencies = [ 1102 | "byteorder", 1103 | "lazy_static", 1104 | "libm", 1105 | "num-integer", 1106 | "num-iter", 1107 | "num-traits", 1108 | "rand", 1109 | "smallvec", 1110 | "zeroize", 1111 | ] 1112 | 1113 | [[package]] 1114 | name = "num-integer" 1115 | version = "0.1.46" 1116 | source = "registry+https://github.com/rust-lang/crates.io-index" 1117 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1118 | dependencies = [ 1119 | "num-traits", 1120 | ] 1121 | 1122 | [[package]] 1123 | name = "num-iter" 1124 | version = "0.1.45" 1125 | source = "registry+https://github.com/rust-lang/crates.io-index" 1126 | checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 1127 | dependencies = [ 1128 | "autocfg", 1129 | "num-integer", 1130 | "num-traits", 1131 | ] 1132 | 1133 | [[package]] 1134 | name = "num-traits" 1135 | version = "0.2.19" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1138 | dependencies = [ 1139 | "autocfg", 1140 | "libm", 1141 | ] 1142 | 1143 | [[package]] 1144 | name = "object" 1145 | version = "0.32.2" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 1148 | dependencies = [ 1149 | "memchr", 1150 | ] 1151 | 1152 | [[package]] 1153 | name = "once_cell" 1154 | version = "1.20.2" 1155 | source = "registry+https://github.com/rust-lang/crates.io-index" 1156 | checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 1157 | 1158 | [[package]] 1159 | name = "opaque-debug" 1160 | version = "0.3.1" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" 1163 | 1164 | [[package]] 1165 | name = "option-ext" 1166 | version = "0.2.0" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" 1169 | 1170 | [[package]] 1171 | name = "overload" 1172 | version = "0.1.1" 1173 | source = "registry+https://github.com/rust-lang/crates.io-index" 1174 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 1175 | 1176 | [[package]] 1177 | name = "owo-colors" 1178 | version = "3.5.0" 1179 | source = "registry+https://github.com/rust-lang/crates.io-index" 1180 | checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" 1181 | 1182 | [[package]] 1183 | name = "p256" 1184 | version = "0.13.2" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" 1187 | dependencies = [ 1188 | "ecdsa", 1189 | "elliptic-curve", 1190 | "primeorder", 1191 | "sha2", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "p384" 1196 | version = "0.13.0" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" 1199 | dependencies = [ 1200 | "ecdsa", 1201 | "elliptic-curve", 1202 | "primeorder", 1203 | "sha2", 1204 | ] 1205 | 1206 | [[package]] 1207 | name = "p521" 1208 | version = "0.13.3" 1209 | source = "registry+https://github.com/rust-lang/crates.io-index" 1210 | checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" 1211 | dependencies = [ 1212 | "base16ct", 1213 | "ecdsa", 1214 | "elliptic-curve", 1215 | "primeorder", 1216 | "rand_core", 1217 | "sha2", 1218 | ] 1219 | 1220 | [[package]] 1221 | name = "pageant" 1222 | version = "0.0.3" 1223 | source = "registry+https://github.com/rust-lang/crates.io-index" 1224 | checksum = "bdd27df01428302f915ea74737fe88170dd1bab4cbd00ff9548ca85618fcd4e4" 1225 | dependencies = [ 1226 | "bytes", 1227 | "delegate", 1228 | "futures", 1229 | "log", 1230 | "rand", 1231 | "thiserror 1.0.64", 1232 | "tokio", 1233 | "windows", 1234 | ] 1235 | 1236 | [[package]] 1237 | name = "parking_lot" 1238 | version = "0.12.3" 1239 | source = "registry+https://github.com/rust-lang/crates.io-index" 1240 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1241 | dependencies = [ 1242 | "lock_api", 1243 | "parking_lot_core", 1244 | ] 1245 | 1246 | [[package]] 1247 | name = "parking_lot_core" 1248 | version = "0.9.10" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1251 | dependencies = [ 1252 | "cfg-if", 1253 | "libc", 1254 | "redox_syscall", 1255 | "smallvec", 1256 | "windows-targets", 1257 | ] 1258 | 1259 | [[package]] 1260 | name = "password-hash" 1261 | version = "0.5.0" 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" 1263 | checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" 1264 | dependencies = [ 1265 | "base64ct", 1266 | "rand_core", 1267 | "subtle", 1268 | ] 1269 | 1270 | [[package]] 1271 | name = "paste" 1272 | version = "1.0.15" 1273 | source = "registry+https://github.com/rust-lang/crates.io-index" 1274 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1275 | 1276 | [[package]] 1277 | name = "pbkdf2" 1278 | version = "0.12.2" 1279 | source = "registry+https://github.com/rust-lang/crates.io-index" 1280 | checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" 1281 | dependencies = [ 1282 | "digest", 1283 | "hmac", 1284 | ] 1285 | 1286 | [[package]] 1287 | name = "pem-rfc7468" 1288 | version = "0.7.0" 1289 | source = "registry+https://github.com/rust-lang/crates.io-index" 1290 | checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 1291 | dependencies = [ 1292 | "base64ct", 1293 | ] 1294 | 1295 | [[package]] 1296 | name = "pin-project-lite" 1297 | version = "0.2.14" 1298 | source = "registry+https://github.com/rust-lang/crates.io-index" 1299 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 1300 | 1301 | [[package]] 1302 | name = "pin-utils" 1303 | version = "0.1.0" 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" 1305 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1306 | 1307 | [[package]] 1308 | name = "pkcs1" 1309 | version = "0.7.5" 1310 | source = "registry+https://github.com/rust-lang/crates.io-index" 1311 | checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" 1312 | dependencies = [ 1313 | "der", 1314 | "pkcs8", 1315 | "spki", 1316 | ] 1317 | 1318 | [[package]] 1319 | name = "pkcs5" 1320 | version = "0.7.1" 1321 | source = "registry+https://github.com/rust-lang/crates.io-index" 1322 | checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" 1323 | dependencies = [ 1324 | "aes", 1325 | "cbc", 1326 | "der", 1327 | "pbkdf2", 1328 | "scrypt", 1329 | "sha2", 1330 | "spki", 1331 | ] 1332 | 1333 | [[package]] 1334 | name = "pkcs8" 1335 | version = "0.10.2" 1336 | source = "registry+https://github.com/rust-lang/crates.io-index" 1337 | checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" 1338 | dependencies = [ 1339 | "der", 1340 | "pkcs5", 1341 | "rand_core", 1342 | "spki", 1343 | ] 1344 | 1345 | [[package]] 1346 | name = "poly1305" 1347 | version = "0.8.0" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" 1350 | dependencies = [ 1351 | "cpufeatures", 1352 | "opaque-debug", 1353 | "universal-hash", 1354 | ] 1355 | 1356 | [[package]] 1357 | name = "polyval" 1358 | version = "0.6.2" 1359 | source = "registry+https://github.com/rust-lang/crates.io-index" 1360 | checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" 1361 | dependencies = [ 1362 | "cfg-if", 1363 | "cpufeatures", 1364 | "opaque-debug", 1365 | "universal-hash", 1366 | ] 1367 | 1368 | [[package]] 1369 | name = "pong-russh" 1370 | version = "0.1.0" 1371 | dependencies = [ 1372 | "color-eyre", 1373 | "crossterm", 1374 | "delegate", 1375 | "dirs", 1376 | "futures", 1377 | "ratatui", 1378 | "russh", 1379 | "scopeguard", 1380 | "tokio", 1381 | "tracing", 1382 | "tracing-subscriber", 1383 | ] 1384 | 1385 | [[package]] 1386 | name = "ppv-lite86" 1387 | version = "0.2.20" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1390 | dependencies = [ 1391 | "zerocopy", 1392 | ] 1393 | 1394 | [[package]] 1395 | name = "primeorder" 1396 | version = "0.13.6" 1397 | source = "registry+https://github.com/rust-lang/crates.io-index" 1398 | checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" 1399 | dependencies = [ 1400 | "elliptic-curve", 1401 | ] 1402 | 1403 | [[package]] 1404 | name = "proc-macro2" 1405 | version = "1.0.94" 1406 | source = "registry+https://github.com/rust-lang/crates.io-index" 1407 | checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" 1408 | dependencies = [ 1409 | "unicode-ident", 1410 | ] 1411 | 1412 | [[package]] 1413 | name = "quote" 1414 | version = "1.0.37" 1415 | source = "registry+https://github.com/rust-lang/crates.io-index" 1416 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1417 | dependencies = [ 1418 | "proc-macro2", 1419 | ] 1420 | 1421 | [[package]] 1422 | name = "rand" 1423 | version = "0.8.5" 1424 | source = "registry+https://github.com/rust-lang/crates.io-index" 1425 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1426 | dependencies = [ 1427 | "libc", 1428 | "rand_chacha", 1429 | "rand_core", 1430 | ] 1431 | 1432 | [[package]] 1433 | name = "rand_chacha" 1434 | version = "0.3.1" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1437 | dependencies = [ 1438 | "ppv-lite86", 1439 | "rand_core", 1440 | ] 1441 | 1442 | [[package]] 1443 | name = "rand_core" 1444 | version = "0.6.4" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1447 | dependencies = [ 1448 | "getrandom", 1449 | ] 1450 | 1451 | [[package]] 1452 | name = "ratatui" 1453 | version = "0.29.0" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" 1456 | dependencies = [ 1457 | "bitflags", 1458 | "cassowary", 1459 | "compact_str", 1460 | "crossterm", 1461 | "indoc", 1462 | "instability", 1463 | "itertools", 1464 | "lru", 1465 | "paste", 1466 | "strum", 1467 | "unicode-segmentation", 1468 | "unicode-truncate", 1469 | "unicode-width 0.2.0", 1470 | ] 1471 | 1472 | [[package]] 1473 | name = "redox_syscall" 1474 | version = "0.5.7" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 1477 | dependencies = [ 1478 | "bitflags", 1479 | ] 1480 | 1481 | [[package]] 1482 | name = "redox_users" 1483 | version = "0.5.0" 1484 | source = "registry+https://github.com/rust-lang/crates.io-index" 1485 | checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" 1486 | dependencies = [ 1487 | "getrandom", 1488 | "libredox", 1489 | "thiserror 2.0.12", 1490 | ] 1491 | 1492 | [[package]] 1493 | name = "regex" 1494 | version = "1.11.0" 1495 | source = "registry+https://github.com/rust-lang/crates.io-index" 1496 | checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" 1497 | dependencies = [ 1498 | "aho-corasick", 1499 | "memchr", 1500 | "regex-automata 0.4.8", 1501 | "regex-syntax 0.8.5", 1502 | ] 1503 | 1504 | [[package]] 1505 | name = "regex-automata" 1506 | version = "0.1.10" 1507 | source = "registry+https://github.com/rust-lang/crates.io-index" 1508 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 1509 | dependencies = [ 1510 | "regex-syntax 0.6.29", 1511 | ] 1512 | 1513 | [[package]] 1514 | name = "regex-automata" 1515 | version = "0.4.8" 1516 | source = "registry+https://github.com/rust-lang/crates.io-index" 1517 | checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" 1518 | dependencies = [ 1519 | "aho-corasick", 1520 | "memchr", 1521 | "regex-syntax 0.8.5", 1522 | ] 1523 | 1524 | [[package]] 1525 | name = "regex-syntax" 1526 | version = "0.6.29" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 1529 | 1530 | [[package]] 1531 | name = "regex-syntax" 1532 | version = "0.8.5" 1533 | source = "registry+https://github.com/rust-lang/crates.io-index" 1534 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1535 | 1536 | [[package]] 1537 | name = "rfc6979" 1538 | version = "0.4.0" 1539 | source = "registry+https://github.com/rust-lang/crates.io-index" 1540 | checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" 1541 | dependencies = [ 1542 | "hmac", 1543 | "subtle", 1544 | ] 1545 | 1546 | [[package]] 1547 | name = "rsa" 1548 | version = "0.9.6" 1549 | source = "registry+https://github.com/rust-lang/crates.io-index" 1550 | checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" 1551 | dependencies = [ 1552 | "const-oid", 1553 | "digest", 1554 | "num-bigint-dig", 1555 | "num-integer", 1556 | "num-traits", 1557 | "pkcs1", 1558 | "pkcs8", 1559 | "rand_core", 1560 | "sha2", 1561 | "signature", 1562 | "spki", 1563 | "subtle", 1564 | "zeroize", 1565 | ] 1566 | 1567 | [[package]] 1568 | name = "russh" 1569 | version = "0.51.1" 1570 | source = "registry+https://github.com/rust-lang/crates.io-index" 1571 | checksum = "c4816b748109d26daa30e72d231a3a6e42f9e2fffe6c08cbfed63113db0ce884" 1572 | dependencies = [ 1573 | "aes", 1574 | "aes-gcm", 1575 | "bitflags", 1576 | "block-padding", 1577 | "byteorder", 1578 | "bytes", 1579 | "cbc", 1580 | "chacha20", 1581 | "ctr", 1582 | "curve25519-dalek", 1583 | "data-encoding", 1584 | "delegate", 1585 | "der", 1586 | "digest", 1587 | "ecdsa", 1588 | "ed25519-dalek", 1589 | "elliptic-curve", 1590 | "enum_dispatch", 1591 | "flate2", 1592 | "futures", 1593 | "generic-array", 1594 | "getrandom", 1595 | "hex-literal", 1596 | "hmac", 1597 | "home", 1598 | "inout", 1599 | "internal-russh-forked-ssh-key", 1600 | "log", 1601 | "md5", 1602 | "num-bigint", 1603 | "once_cell", 1604 | "p256", 1605 | "p384", 1606 | "p521", 1607 | "pageant", 1608 | "pbkdf2", 1609 | "pkcs1", 1610 | "pkcs5", 1611 | "pkcs8", 1612 | "poly1305", 1613 | "rand", 1614 | "rand_core", 1615 | "rsa", 1616 | "russh-cryptovec", 1617 | "russh-util", 1618 | "sec1", 1619 | "sha1", 1620 | "sha2", 1621 | "signature", 1622 | "spki", 1623 | "ssh-encoding", 1624 | "subtle", 1625 | "thiserror 1.0.64", 1626 | "tokio", 1627 | "typenum", 1628 | "zeroize", 1629 | ] 1630 | 1631 | [[package]] 1632 | name = "russh-cryptovec" 1633 | version = "0.51.1" 1634 | source = "registry+https://github.com/rust-lang/crates.io-index" 1635 | checksum = "5b722480e7117893a8a14ba22298dca3387b24fa1c40eb06f3105b82875c7497" 1636 | dependencies = [ 1637 | "libc", 1638 | "log", 1639 | "nix", 1640 | "ssh-encoding", 1641 | "winapi", 1642 | ] 1643 | 1644 | [[package]] 1645 | name = "russh-util" 1646 | version = "0.51.0" 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" 1648 | checksum = "c0c3f1ba8f8ccbe1af83c1a5dc325ecb0b6e176d1444c241c1d684ab216d1b24" 1649 | dependencies = [ 1650 | "chrono", 1651 | "tokio", 1652 | "wasm-bindgen", 1653 | "wasm-bindgen-futures", 1654 | ] 1655 | 1656 | [[package]] 1657 | name = "rustc-demangle" 1658 | version = "0.1.24" 1659 | source = "registry+https://github.com/rust-lang/crates.io-index" 1660 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1661 | 1662 | [[package]] 1663 | name = "rustc_version" 1664 | version = "0.4.1" 1665 | source = "registry+https://github.com/rust-lang/crates.io-index" 1666 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1667 | dependencies = [ 1668 | "semver", 1669 | ] 1670 | 1671 | [[package]] 1672 | name = "rustix" 1673 | version = "0.38.37" 1674 | source = "registry+https://github.com/rust-lang/crates.io-index" 1675 | checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" 1676 | dependencies = [ 1677 | "bitflags", 1678 | "errno", 1679 | "libc", 1680 | "linux-raw-sys", 1681 | "windows-sys 0.52.0", 1682 | ] 1683 | 1684 | [[package]] 1685 | name = "rustversion" 1686 | version = "1.0.17" 1687 | source = "registry+https://github.com/rust-lang/crates.io-index" 1688 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 1689 | 1690 | [[package]] 1691 | name = "ryu" 1692 | version = "1.0.18" 1693 | source = "registry+https://github.com/rust-lang/crates.io-index" 1694 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1695 | 1696 | [[package]] 1697 | name = "salsa20" 1698 | version = "0.10.2" 1699 | source = "registry+https://github.com/rust-lang/crates.io-index" 1700 | checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" 1701 | dependencies = [ 1702 | "cipher", 1703 | ] 1704 | 1705 | [[package]] 1706 | name = "scopeguard" 1707 | version = "1.2.0" 1708 | source = "registry+https://github.com/rust-lang/crates.io-index" 1709 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1710 | 1711 | [[package]] 1712 | name = "scrypt" 1713 | version = "0.11.0" 1714 | source = "registry+https://github.com/rust-lang/crates.io-index" 1715 | checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" 1716 | dependencies = [ 1717 | "pbkdf2", 1718 | "salsa20", 1719 | "sha2", 1720 | ] 1721 | 1722 | [[package]] 1723 | name = "sec1" 1724 | version = "0.7.3" 1725 | source = "registry+https://github.com/rust-lang/crates.io-index" 1726 | checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" 1727 | dependencies = [ 1728 | "base16ct", 1729 | "der", 1730 | "generic-array", 1731 | "pkcs8", 1732 | "subtle", 1733 | "zeroize", 1734 | ] 1735 | 1736 | [[package]] 1737 | name = "semver" 1738 | version = "1.0.23" 1739 | source = "registry+https://github.com/rust-lang/crates.io-index" 1740 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 1741 | 1742 | [[package]] 1743 | name = "serde" 1744 | version = "1.0.210" 1745 | source = "registry+https://github.com/rust-lang/crates.io-index" 1746 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 1747 | dependencies = [ 1748 | "serde_derive", 1749 | ] 1750 | 1751 | [[package]] 1752 | name = "serde_derive" 1753 | version = "1.0.210" 1754 | source = "registry+https://github.com/rust-lang/crates.io-index" 1755 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 1756 | dependencies = [ 1757 | "proc-macro2", 1758 | "quote", 1759 | "syn", 1760 | ] 1761 | 1762 | [[package]] 1763 | name = "sha1" 1764 | version = "0.10.6" 1765 | source = "registry+https://github.com/rust-lang/crates.io-index" 1766 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1767 | dependencies = [ 1768 | "cfg-if", 1769 | "cpufeatures", 1770 | "digest", 1771 | ] 1772 | 1773 | [[package]] 1774 | name = "sha2" 1775 | version = "0.10.8" 1776 | source = "registry+https://github.com/rust-lang/crates.io-index" 1777 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1778 | dependencies = [ 1779 | "cfg-if", 1780 | "cpufeatures", 1781 | "digest", 1782 | ] 1783 | 1784 | [[package]] 1785 | name = "sharded-slab" 1786 | version = "0.1.7" 1787 | source = "registry+https://github.com/rust-lang/crates.io-index" 1788 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1789 | dependencies = [ 1790 | "lazy_static", 1791 | ] 1792 | 1793 | [[package]] 1794 | name = "shlex" 1795 | version = "1.3.0" 1796 | source = "registry+https://github.com/rust-lang/crates.io-index" 1797 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1798 | 1799 | [[package]] 1800 | name = "signal-hook" 1801 | version = "0.3.17" 1802 | source = "registry+https://github.com/rust-lang/crates.io-index" 1803 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 1804 | dependencies = [ 1805 | "libc", 1806 | "signal-hook-registry", 1807 | ] 1808 | 1809 | [[package]] 1810 | name = "signal-hook-mio" 1811 | version = "0.2.4" 1812 | source = "registry+https://github.com/rust-lang/crates.io-index" 1813 | checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" 1814 | dependencies = [ 1815 | "libc", 1816 | "mio", 1817 | "signal-hook", 1818 | ] 1819 | 1820 | [[package]] 1821 | name = "signal-hook-registry" 1822 | version = "1.4.2" 1823 | source = "registry+https://github.com/rust-lang/crates.io-index" 1824 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1825 | dependencies = [ 1826 | "libc", 1827 | ] 1828 | 1829 | [[package]] 1830 | name = "signature" 1831 | version = "2.2.0" 1832 | source = "registry+https://github.com/rust-lang/crates.io-index" 1833 | checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 1834 | dependencies = [ 1835 | "digest", 1836 | "rand_core", 1837 | ] 1838 | 1839 | [[package]] 1840 | name = "slab" 1841 | version = "0.4.9" 1842 | source = "registry+https://github.com/rust-lang/crates.io-index" 1843 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1844 | dependencies = [ 1845 | "autocfg", 1846 | ] 1847 | 1848 | [[package]] 1849 | name = "smallvec" 1850 | version = "1.13.2" 1851 | source = "registry+https://github.com/rust-lang/crates.io-index" 1852 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1853 | 1854 | [[package]] 1855 | name = "socket2" 1856 | version = "0.5.7" 1857 | source = "registry+https://github.com/rust-lang/crates.io-index" 1858 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1859 | dependencies = [ 1860 | "libc", 1861 | "windows-sys 0.52.0", 1862 | ] 1863 | 1864 | [[package]] 1865 | name = "spin" 1866 | version = "0.9.8" 1867 | source = "registry+https://github.com/rust-lang/crates.io-index" 1868 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1869 | 1870 | [[package]] 1871 | name = "spki" 1872 | version = "0.7.3" 1873 | source = "registry+https://github.com/rust-lang/crates.io-index" 1874 | checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" 1875 | dependencies = [ 1876 | "base64ct", 1877 | "der", 1878 | ] 1879 | 1880 | [[package]] 1881 | name = "ssh-cipher" 1882 | version = "0.2.0" 1883 | source = "registry+https://github.com/rust-lang/crates.io-index" 1884 | checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" 1885 | dependencies = [ 1886 | "aes", 1887 | "aes-gcm", 1888 | "cbc", 1889 | "chacha20", 1890 | "cipher", 1891 | "ctr", 1892 | "poly1305", 1893 | "ssh-encoding", 1894 | "subtle", 1895 | ] 1896 | 1897 | [[package]] 1898 | name = "ssh-encoding" 1899 | version = "0.2.0" 1900 | source = "registry+https://github.com/rust-lang/crates.io-index" 1901 | checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" 1902 | dependencies = [ 1903 | "base64ct", 1904 | "bytes", 1905 | "pem-rfc7468", 1906 | "sha2", 1907 | ] 1908 | 1909 | [[package]] 1910 | name = "static_assertions" 1911 | version = "1.1.0" 1912 | source = "registry+https://github.com/rust-lang/crates.io-index" 1913 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1914 | 1915 | [[package]] 1916 | name = "strum" 1917 | version = "0.26.3" 1918 | source = "registry+https://github.com/rust-lang/crates.io-index" 1919 | checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 1920 | dependencies = [ 1921 | "strum_macros", 1922 | ] 1923 | 1924 | [[package]] 1925 | name = "strum_macros" 1926 | version = "0.26.4" 1927 | source = "registry+https://github.com/rust-lang/crates.io-index" 1928 | checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 1929 | dependencies = [ 1930 | "heck", 1931 | "proc-macro2", 1932 | "quote", 1933 | "rustversion", 1934 | "syn", 1935 | ] 1936 | 1937 | [[package]] 1938 | name = "subtle" 1939 | version = "2.6.1" 1940 | source = "registry+https://github.com/rust-lang/crates.io-index" 1941 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1942 | 1943 | [[package]] 1944 | name = "syn" 1945 | version = "2.0.100" 1946 | source = "registry+https://github.com/rust-lang/crates.io-index" 1947 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 1948 | dependencies = [ 1949 | "proc-macro2", 1950 | "quote", 1951 | "unicode-ident", 1952 | ] 1953 | 1954 | [[package]] 1955 | name = "thiserror" 1956 | version = "1.0.64" 1957 | source = "registry+https://github.com/rust-lang/crates.io-index" 1958 | checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" 1959 | dependencies = [ 1960 | "thiserror-impl 1.0.64", 1961 | ] 1962 | 1963 | [[package]] 1964 | name = "thiserror" 1965 | version = "2.0.12" 1966 | source = "registry+https://github.com/rust-lang/crates.io-index" 1967 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 1968 | dependencies = [ 1969 | "thiserror-impl 2.0.12", 1970 | ] 1971 | 1972 | [[package]] 1973 | name = "thiserror-impl" 1974 | version = "1.0.64" 1975 | source = "registry+https://github.com/rust-lang/crates.io-index" 1976 | checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" 1977 | dependencies = [ 1978 | "proc-macro2", 1979 | "quote", 1980 | "syn", 1981 | ] 1982 | 1983 | [[package]] 1984 | name = "thiserror-impl" 1985 | version = "2.0.12" 1986 | source = "registry+https://github.com/rust-lang/crates.io-index" 1987 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 1988 | dependencies = [ 1989 | "proc-macro2", 1990 | "quote", 1991 | "syn", 1992 | ] 1993 | 1994 | [[package]] 1995 | name = "thread_local" 1996 | version = "1.1.8" 1997 | source = "registry+https://github.com/rust-lang/crates.io-index" 1998 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1999 | dependencies = [ 2000 | "cfg-if", 2001 | "once_cell", 2002 | ] 2003 | 2004 | [[package]] 2005 | name = "tokio" 2006 | version = "1.44.2" 2007 | source = "registry+https://github.com/rust-lang/crates.io-index" 2008 | checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" 2009 | dependencies = [ 2010 | "backtrace", 2011 | "bytes", 2012 | "libc", 2013 | "mio", 2014 | "pin-project-lite", 2015 | "socket2", 2016 | "tokio-macros", 2017 | "windows-sys 0.52.0", 2018 | ] 2019 | 2020 | [[package]] 2021 | name = "tokio-macros" 2022 | version = "2.5.0" 2023 | source = "registry+https://github.com/rust-lang/crates.io-index" 2024 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 2025 | dependencies = [ 2026 | "proc-macro2", 2027 | "quote", 2028 | "syn", 2029 | ] 2030 | 2031 | [[package]] 2032 | name = "tracing" 2033 | version = "0.1.41" 2034 | source = "registry+https://github.com/rust-lang/crates.io-index" 2035 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 2036 | dependencies = [ 2037 | "pin-project-lite", 2038 | "tracing-attributes", 2039 | "tracing-core", 2040 | ] 2041 | 2042 | [[package]] 2043 | name = "tracing-attributes" 2044 | version = "0.1.28" 2045 | source = "registry+https://github.com/rust-lang/crates.io-index" 2046 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 2047 | dependencies = [ 2048 | "proc-macro2", 2049 | "quote", 2050 | "syn", 2051 | ] 2052 | 2053 | [[package]] 2054 | name = "tracing-core" 2055 | version = "0.1.33" 2056 | source = "registry+https://github.com/rust-lang/crates.io-index" 2057 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 2058 | dependencies = [ 2059 | "once_cell", 2060 | "valuable", 2061 | ] 2062 | 2063 | [[package]] 2064 | name = "tracing-error" 2065 | version = "0.2.0" 2066 | source = "registry+https://github.com/rust-lang/crates.io-index" 2067 | checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" 2068 | dependencies = [ 2069 | "tracing", 2070 | "tracing-subscriber", 2071 | ] 2072 | 2073 | [[package]] 2074 | name = "tracing-log" 2075 | version = "0.2.0" 2076 | source = "registry+https://github.com/rust-lang/crates.io-index" 2077 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 2078 | dependencies = [ 2079 | "log", 2080 | "once_cell", 2081 | "tracing-core", 2082 | ] 2083 | 2084 | [[package]] 2085 | name = "tracing-subscriber" 2086 | version = "0.3.19" 2087 | source = "registry+https://github.com/rust-lang/crates.io-index" 2088 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 2089 | dependencies = [ 2090 | "matchers", 2091 | "nu-ansi-term", 2092 | "once_cell", 2093 | "regex", 2094 | "sharded-slab", 2095 | "smallvec", 2096 | "thread_local", 2097 | "tracing", 2098 | "tracing-core", 2099 | "tracing-log", 2100 | ] 2101 | 2102 | [[package]] 2103 | name = "typenum" 2104 | version = "1.17.0" 2105 | source = "registry+https://github.com/rust-lang/crates.io-index" 2106 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 2107 | 2108 | [[package]] 2109 | name = "unicode-ident" 2110 | version = "1.0.13" 2111 | source = "registry+https://github.com/rust-lang/crates.io-index" 2112 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 2113 | 2114 | [[package]] 2115 | name = "unicode-segmentation" 2116 | version = "1.12.0" 2117 | source = "registry+https://github.com/rust-lang/crates.io-index" 2118 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 2119 | 2120 | [[package]] 2121 | name = "unicode-truncate" 2122 | version = "1.1.0" 2123 | source = "registry+https://github.com/rust-lang/crates.io-index" 2124 | checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" 2125 | dependencies = [ 2126 | "itertools", 2127 | "unicode-segmentation", 2128 | "unicode-width 0.1.14", 2129 | ] 2130 | 2131 | [[package]] 2132 | name = "unicode-width" 2133 | version = "0.1.14" 2134 | source = "registry+https://github.com/rust-lang/crates.io-index" 2135 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 2136 | 2137 | [[package]] 2138 | name = "unicode-width" 2139 | version = "0.2.0" 2140 | source = "registry+https://github.com/rust-lang/crates.io-index" 2141 | checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 2142 | 2143 | [[package]] 2144 | name = "universal-hash" 2145 | version = "0.5.1" 2146 | source = "registry+https://github.com/rust-lang/crates.io-index" 2147 | checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" 2148 | dependencies = [ 2149 | "crypto-common", 2150 | "subtle", 2151 | ] 2152 | 2153 | [[package]] 2154 | name = "valuable" 2155 | version = "0.1.0" 2156 | source = "registry+https://github.com/rust-lang/crates.io-index" 2157 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 2158 | 2159 | [[package]] 2160 | name = "version_check" 2161 | version = "0.9.5" 2162 | source = "registry+https://github.com/rust-lang/crates.io-index" 2163 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2164 | 2165 | [[package]] 2166 | name = "wasi" 2167 | version = "0.11.0+wasi-snapshot-preview1" 2168 | source = "registry+https://github.com/rust-lang/crates.io-index" 2169 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2170 | 2171 | [[package]] 2172 | name = "wasm-bindgen" 2173 | version = "0.2.95" 2174 | source = "registry+https://github.com/rust-lang/crates.io-index" 2175 | checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" 2176 | dependencies = [ 2177 | "cfg-if", 2178 | "once_cell", 2179 | "wasm-bindgen-macro", 2180 | ] 2181 | 2182 | [[package]] 2183 | name = "wasm-bindgen-backend" 2184 | version = "0.2.95" 2185 | source = "registry+https://github.com/rust-lang/crates.io-index" 2186 | checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" 2187 | dependencies = [ 2188 | "bumpalo", 2189 | "log", 2190 | "once_cell", 2191 | "proc-macro2", 2192 | "quote", 2193 | "syn", 2194 | "wasm-bindgen-shared", 2195 | ] 2196 | 2197 | [[package]] 2198 | name = "wasm-bindgen-futures" 2199 | version = "0.4.45" 2200 | source = "registry+https://github.com/rust-lang/crates.io-index" 2201 | checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" 2202 | dependencies = [ 2203 | "cfg-if", 2204 | "js-sys", 2205 | "wasm-bindgen", 2206 | "web-sys", 2207 | ] 2208 | 2209 | [[package]] 2210 | name = "wasm-bindgen-macro" 2211 | version = "0.2.95" 2212 | source = "registry+https://github.com/rust-lang/crates.io-index" 2213 | checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" 2214 | dependencies = [ 2215 | "quote", 2216 | "wasm-bindgen-macro-support", 2217 | ] 2218 | 2219 | [[package]] 2220 | name = "wasm-bindgen-macro-support" 2221 | version = "0.2.95" 2222 | source = "registry+https://github.com/rust-lang/crates.io-index" 2223 | checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" 2224 | dependencies = [ 2225 | "proc-macro2", 2226 | "quote", 2227 | "syn", 2228 | "wasm-bindgen-backend", 2229 | "wasm-bindgen-shared", 2230 | ] 2231 | 2232 | [[package]] 2233 | name = "wasm-bindgen-shared" 2234 | version = "0.2.95" 2235 | source = "registry+https://github.com/rust-lang/crates.io-index" 2236 | checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" 2237 | 2238 | [[package]] 2239 | name = "web-sys" 2240 | version = "0.3.72" 2241 | source = "registry+https://github.com/rust-lang/crates.io-index" 2242 | checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" 2243 | dependencies = [ 2244 | "js-sys", 2245 | "wasm-bindgen", 2246 | ] 2247 | 2248 | [[package]] 2249 | name = "winapi" 2250 | version = "0.3.9" 2251 | source = "registry+https://github.com/rust-lang/crates.io-index" 2252 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2253 | dependencies = [ 2254 | "winapi-i686-pc-windows-gnu", 2255 | "winapi-x86_64-pc-windows-gnu", 2256 | ] 2257 | 2258 | [[package]] 2259 | name = "winapi-i686-pc-windows-gnu" 2260 | version = "0.4.0" 2261 | source = "registry+https://github.com/rust-lang/crates.io-index" 2262 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2263 | 2264 | [[package]] 2265 | name = "winapi-x86_64-pc-windows-gnu" 2266 | version = "0.4.0" 2267 | source = "registry+https://github.com/rust-lang/crates.io-index" 2268 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2269 | 2270 | [[package]] 2271 | name = "windows" 2272 | version = "0.58.0" 2273 | source = "registry+https://github.com/rust-lang/crates.io-index" 2274 | checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" 2275 | dependencies = [ 2276 | "windows-core 0.58.0", 2277 | "windows-targets", 2278 | ] 2279 | 2280 | [[package]] 2281 | name = "windows-core" 2282 | version = "0.52.0" 2283 | source = "registry+https://github.com/rust-lang/crates.io-index" 2284 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2285 | dependencies = [ 2286 | "windows-targets", 2287 | ] 2288 | 2289 | [[package]] 2290 | name = "windows-core" 2291 | version = "0.58.0" 2292 | source = "registry+https://github.com/rust-lang/crates.io-index" 2293 | checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" 2294 | dependencies = [ 2295 | "windows-implement", 2296 | "windows-interface", 2297 | "windows-result", 2298 | "windows-strings", 2299 | "windows-targets", 2300 | ] 2301 | 2302 | [[package]] 2303 | name = "windows-implement" 2304 | version = "0.58.0" 2305 | source = "registry+https://github.com/rust-lang/crates.io-index" 2306 | checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" 2307 | dependencies = [ 2308 | "proc-macro2", 2309 | "quote", 2310 | "syn", 2311 | ] 2312 | 2313 | [[package]] 2314 | name = "windows-interface" 2315 | version = "0.58.0" 2316 | source = "registry+https://github.com/rust-lang/crates.io-index" 2317 | checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" 2318 | dependencies = [ 2319 | "proc-macro2", 2320 | "quote", 2321 | "syn", 2322 | ] 2323 | 2324 | [[package]] 2325 | name = "windows-result" 2326 | version = "0.2.0" 2327 | source = "registry+https://github.com/rust-lang/crates.io-index" 2328 | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 2329 | dependencies = [ 2330 | "windows-targets", 2331 | ] 2332 | 2333 | [[package]] 2334 | name = "windows-strings" 2335 | version = "0.1.0" 2336 | source = "registry+https://github.com/rust-lang/crates.io-index" 2337 | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 2338 | dependencies = [ 2339 | "windows-result", 2340 | "windows-targets", 2341 | ] 2342 | 2343 | [[package]] 2344 | name = "windows-sys" 2345 | version = "0.52.0" 2346 | source = "registry+https://github.com/rust-lang/crates.io-index" 2347 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2348 | dependencies = [ 2349 | "windows-targets", 2350 | ] 2351 | 2352 | [[package]] 2353 | name = "windows-sys" 2354 | version = "0.59.0" 2355 | source = "registry+https://github.com/rust-lang/crates.io-index" 2356 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2357 | dependencies = [ 2358 | "windows-targets", 2359 | ] 2360 | 2361 | [[package]] 2362 | name = "windows-targets" 2363 | version = "0.52.6" 2364 | source = "registry+https://github.com/rust-lang/crates.io-index" 2365 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2366 | dependencies = [ 2367 | "windows_aarch64_gnullvm", 2368 | "windows_aarch64_msvc", 2369 | "windows_i686_gnu", 2370 | "windows_i686_gnullvm", 2371 | "windows_i686_msvc", 2372 | "windows_x86_64_gnu", 2373 | "windows_x86_64_gnullvm", 2374 | "windows_x86_64_msvc", 2375 | ] 2376 | 2377 | [[package]] 2378 | name = "windows_aarch64_gnullvm" 2379 | version = "0.52.6" 2380 | source = "registry+https://github.com/rust-lang/crates.io-index" 2381 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2382 | 2383 | [[package]] 2384 | name = "windows_aarch64_msvc" 2385 | version = "0.52.6" 2386 | source = "registry+https://github.com/rust-lang/crates.io-index" 2387 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2388 | 2389 | [[package]] 2390 | name = "windows_i686_gnu" 2391 | version = "0.52.6" 2392 | source = "registry+https://github.com/rust-lang/crates.io-index" 2393 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2394 | 2395 | [[package]] 2396 | name = "windows_i686_gnullvm" 2397 | version = "0.52.6" 2398 | source = "registry+https://github.com/rust-lang/crates.io-index" 2399 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2400 | 2401 | [[package]] 2402 | name = "windows_i686_msvc" 2403 | version = "0.52.6" 2404 | source = "registry+https://github.com/rust-lang/crates.io-index" 2405 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2406 | 2407 | [[package]] 2408 | name = "windows_x86_64_gnu" 2409 | version = "0.52.6" 2410 | source = "registry+https://github.com/rust-lang/crates.io-index" 2411 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2412 | 2413 | [[package]] 2414 | name = "windows_x86_64_gnullvm" 2415 | version = "0.52.6" 2416 | source = "registry+https://github.com/rust-lang/crates.io-index" 2417 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2418 | 2419 | [[package]] 2420 | name = "windows_x86_64_msvc" 2421 | version = "0.52.6" 2422 | source = "registry+https://github.com/rust-lang/crates.io-index" 2423 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2424 | 2425 | [[package]] 2426 | name = "zerocopy" 2427 | version = "0.7.35" 2428 | source = "registry+https://github.com/rust-lang/crates.io-index" 2429 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2430 | dependencies = [ 2431 | "byteorder", 2432 | "zerocopy-derive", 2433 | ] 2434 | 2435 | [[package]] 2436 | name = "zerocopy-derive" 2437 | version = "0.7.35" 2438 | source = "registry+https://github.com/rust-lang/crates.io-index" 2439 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2440 | dependencies = [ 2441 | "proc-macro2", 2442 | "quote", 2443 | "syn", 2444 | ] 2445 | 2446 | [[package]] 2447 | name = "zeroize" 2448 | version = "1.8.1" 2449 | source = "registry+https://github.com/rust-lang/crates.io-index" 2450 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 2451 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pong-russh" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | color-eyre = "0.6.3" 8 | crossterm = "0.28.1" 9 | delegate = "0.13.3" 10 | dirs = "6.0.0" 11 | futures = "0.3.30" 12 | ratatui = { version = "0.29.0" } 13 | russh = "0.51.1" 14 | scopeguard = "1.2.0" 15 | tokio = { version = "1.44.2", features = ["rt-multi-thread"] } 16 | tracing = "0.1.41" 17 | tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pong-russh 2 | 3 | A multiplayer Pong game built with Ratatui and Russh 4 | 5 | ![demo](https://github.com/joshka/pong-russh/assets/381361/a1433172-44cc-4c58-8316-bd5702008e19) 6 | 7 | To run: 8 | 9 | ```shell 10 | cargo run 11 | ssh -o StrictHostKeyChecking=no localhost -p 2222 12 | ``` 13 | -------------------------------------------------------------------------------- /demo.tape: -------------------------------------------------------------------------------- 1 | Output demo.gif 2 | 3 | Set Theme "Aardvark Blue" 4 | Set Width 800 5 | Set Height 600 6 | 7 | Hide 8 | Type "cargo run" Enter 9 | Sleep 2500ms 10 | Show 11 | Up 18 12 | Sleep 1500ms 13 | Type "sssssssssssssss" 14 | Sleep 3000ms 15 | Down 33 16 | Sleep 2000ms 17 | Type "wwwwwwwwwwwwwwwwwwwwwwwww" 18 | Sleep 3000ms 19 | Up 20 20 | Sleep 2000ms 21 | Up 20 22 | Sleep 2000ms 23 | Ctrl+C 24 | Hide 25 | -------------------------------------------------------------------------------- /src/backend.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Write}; 2 | 3 | use delegate::delegate; 4 | use ratatui::{ 5 | backend::{Backend, CrosstermBackend, WindowSize}, 6 | layout::Size, 7 | }; 8 | use russh::{server::Handle, ChannelId}; 9 | 10 | /// A backend that writes to an SSH terminal. 11 | /// 12 | /// This backend is a wrapper around the crossterm backend that writes to a terminal handle. It 13 | /// delegates most of the methods to the inner crossterm backend, but overrides the methods related 14 | /// to the terminal size and window size. 15 | #[derive(Debug)] 16 | pub struct SshBackend { 17 | inner: CrosstermBackend, 18 | size: Size, 19 | window_size: WindowSize, 20 | } 21 | 22 | impl SshBackend { 23 | pub fn new( 24 | channel_id: ChannelId, 25 | session_handle: Handle, 26 | col_width: u32, 27 | row_height: u32, 28 | pix_width: u32, 29 | pix_height: u32, 30 | ) -> Self { 31 | let terminal_handle = TerminalHandle::new(channel_id, session_handle); 32 | let size = Size::new(col_width as u16, row_height as u16); 33 | let window_size = WindowSize { 34 | columns_rows: Size::new(col_width as u16, row_height as u16), 35 | pixels: Size::new(pix_width as u16, pix_height as u16), 36 | }; 37 | Self { 38 | inner: CrosstermBackend::new(terminal_handle), 39 | size, 40 | window_size, 41 | } 42 | } 43 | } 44 | 45 | impl Backend for SshBackend { 46 | delegate! { 47 | to self.inner { 48 | #[allow(late_bound_lifetime_arguments)] 49 | fn draw<'a, I>(&mut self, content: I) -> std::io::Result<()> 50 | where 51 | I: Iterator; 52 | 53 | fn hide_cursor(&mut self) -> std::io::Result<()>; 54 | fn show_cursor(&mut self) -> std::io::Result<()>; 55 | #[allow(deprecated)] 56 | fn get_cursor(&mut self) -> std::io::Result<(u16, u16)>; 57 | #[allow(deprecated)] 58 | fn set_cursor(&mut self, x: u16, y: u16) -> std::io::Result<()>; 59 | fn get_cursor_position(&mut self) -> io::Result ; 60 | fn set_cursor_position>(&mut self, position: P) -> io::Result<()> ; 61 | fn clear(&mut self) -> std::io::Result<()>; 62 | 63 | } 64 | } 65 | // can't delegate as there is a conflict with the `Write` trait 66 | fn flush(&mut self) -> io::Result<()> { 67 | Backend::flush(&mut self.inner) 68 | } 69 | fn size(&self) -> io::Result { 70 | Ok(self.size) 71 | } 72 | fn window_size(&mut self) -> io::Result { 73 | Ok(self.window_size) 74 | } 75 | } 76 | 77 | #[derive(Clone)] 78 | pub struct TerminalHandle { 79 | handle: Handle, 80 | channel_id: ChannelId, 81 | // The sink collects the data which is finally flushed to the handle. 82 | sink: Vec, 83 | } 84 | 85 | impl TerminalHandle { 86 | pub fn new(channel_id: ChannelId, handle: Handle) -> Self { 87 | Self { 88 | handle, 89 | channel_id, 90 | sink: Vec::new(), 91 | } 92 | } 93 | } 94 | 95 | impl std::fmt::Debug for TerminalHandle { 96 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 97 | f.debug_struct("TerminalHandle") 98 | .field("handle", &"...") 99 | .field("sink", &self.sink) 100 | .field("channel_id", &self.channel_id) 101 | .finish() 102 | } 103 | } 104 | 105 | // The crossterm backend writes to the terminal handle. 106 | impl Write for TerminalHandle { 107 | fn write(&mut self, buf: &[u8]) -> io::Result { 108 | self.sink.extend_from_slice(buf); 109 | Ok(buf.len()) 110 | } 111 | 112 | fn flush(&mut self) -> io::Result<()> { 113 | let handle = self.handle.clone(); 114 | let channel_id = self.channel_id; 115 | let data = self.sink.clone().into(); 116 | futures::executor::block_on(async move { 117 | let result = handle.data(channel_id, data).await; 118 | if result.is_err() { 119 | eprintln!("Failed to send data: {:?}", result); 120 | } 121 | }); 122 | 123 | self.sink.clear(); 124 | Ok(()) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/ball.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use ratatui::prelude::*; 4 | 5 | use crate::{ 6 | paddle::Paddle, 7 | physics::{Point, Velocity}, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub struct Ball { 12 | pub pos: Point, 13 | pub vel: Velocity, 14 | } 15 | 16 | impl Default for Ball { 17 | fn default() -> Self { 18 | Self::new() 19 | } 20 | } 21 | 22 | impl Ball { 23 | const DEFAULT_INITIAL_VELOCITY: Velocity = Velocity::new(0.26, -0.23); 24 | 25 | /// Crete a new ball at the center of the screen with the default initial velocity. 26 | pub const fn new() -> Self { 27 | Self { 28 | pos: Point::CENTER, 29 | vel: Self::DEFAULT_INITIAL_VELOCITY, 30 | } 31 | } 32 | 33 | /// Serve the ball from the center of the screen with the existing velocity. 34 | pub fn serve(&mut self) { 35 | self.pos = Point::CENTER; 36 | } 37 | 38 | /// Move the ball by its current velocity. 39 | /// 40 | /// The ball will bounce off the top and bottom edges of the screen, reversing the vertical 41 | /// velocity component. 42 | /// 43 | /// The ball will move by the velocity components scaled by the time since the last update. 44 | /// This ensures that the ball moves at the same speed regardless of the screen size or 45 | /// refresh rate. 46 | pub fn update(&mut self, duration: Duration, player1: &Paddle, player2: &Paddle) { 47 | let dt = duration.as_secs_f32(); 48 | self.pos.x += self.vel.x * dt; 49 | self.pos.y += self.vel.y * dt; 50 | 51 | // bounce off the top and bottom edges 52 | if self.pos.y < 0.0 { 53 | self.pos.y = -self.pos.y; 54 | self.vel.y = -self.vel.y; 55 | } else if self.pos.y > 1.0 { 56 | self.pos.y = 2.0 - self.pos.y; 57 | self.vel.y = -self.vel.y; 58 | } 59 | 60 | // bounce off the paddles 61 | // todo: change direction based on where the ball hits the paddle 62 | // todo: increase horizontal speed based on number of hits 63 | // todo: calculate the intersection point of the ball and the paddle rather than just 64 | // checking if the ball is within the paddle's height 65 | if self.pos.x < 0.0 { 66 | if (player1.pos.y - Paddle::HEIGHT / 2.0 < self.pos.y) 67 | && (self.pos.y < player1.pos.y + Paddle::HEIGHT / 2.0) 68 | { 69 | self.pos.x = -self.pos.x; 70 | self.vel.x = -self.vel.x; 71 | 72 | let distance = self.pos.y - player1.pos.y; 73 | let angle = distance / (Paddle::HEIGHT / 2.0); 74 | // map onto the range of valid vertical velocities 75 | let index = ((angle * 3.0).round() as i32 + 3) as usize; 76 | self.vel.y = Velocity::VALID_Y[index]; 77 | } 78 | } else if self.pos.x > 1.0 79 | && (player2.pos.y - Paddle::HEIGHT / 2.0 < self.pos.y) 80 | && (self.pos.y < player2.pos.y + Paddle::HEIGHT / 2.0) 81 | { 82 | self.pos.x = 2.0 - self.pos.x; 83 | self.vel.x = -self.vel.x; 84 | 85 | let distance = self.pos.y - player2.pos.y; 86 | let angle = distance / (Paddle::HEIGHT / 2.0); 87 | // map onto the range of valid vertical velocities 88 | let index = ((angle * 3.0).round() as i32 + 3) as usize; 89 | self.vel.y = Velocity::VALID_Y[index]; 90 | } 91 | } 92 | } 93 | 94 | impl Widget for &Ball { 95 | fn render(self, area: Rect, buf: &mut Buffer) { 96 | if !(0.0..=1.0).contains(&self.pos.x) || !(0.0..=1.0).contains(&self.pos.y) { 97 | return; 98 | } 99 | // use block characters that represent 1/8th of a cell to draw the paddles 100 | const TOP_BARS: [&str; 9] = ["█", "▇", "▆", "▅", "▄", "▃", "▂", "▁", " "]; 101 | const BOTTOM_BARS: [&str; 9] = [" ", "▔", "🮂", "🮃", "▀", "🮄", "🮅", "🮆", "█"]; 102 | let _x = self.pos.x * (area.width.saturating_sub(1)) as f32; 103 | let y = self.pos.y * (area.height.saturating_sub(1)) as f32; 104 | // draw the top character of the paddle by taking the fractional part of the top position 105 | let top_char = TOP_BARS[(y.fract() * 8.0).round() as usize]; 106 | let bottom_char = BOTTOM_BARS[(y.fract() * 8.0).round() as usize]; 107 | let pos = self.pos.to_screen(area); 108 | let ball_area = Rect::new(pos.x, pos.y, 1, 1); 109 | Span::raw(top_char).render(ball_area, buf); 110 | let ball_area = Rect::new(pos.x, pos.y + 1, 1, 1); 111 | Span::raw(bottom_char).render(ball_area, buf); 112 | 113 | // let debug = format!("{:3}, {:3}, {:5.2}, {:5.2}", pos.x, pos.y, x, y); 114 | // let last_row = area.rows().last().unwrap(); 115 | // Line::from(debug).centered().render(last_row, buf); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/game.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, Instant}; 2 | 3 | use color_eyre::eyre::bail; 4 | use ratatui::{ 5 | prelude::*, 6 | widgets::{Block, Clear}, 7 | }; 8 | use tracing::info; 9 | 10 | use crate::{ball::Ball, paddle::Paddle, server::SshTerminal}; 11 | 12 | #[derive(Debug)] 13 | pub struct Game { 14 | ball: Ball, 15 | left_paddle: Paddle, 16 | right_paddle: Paddle, 17 | score: (u32, u32), 18 | serve_time: Option, 19 | last_update: Option, 20 | clients: [Option; 2], 21 | } 22 | 23 | impl Default for Game { 24 | fn default() -> Self { 25 | Self::new() 26 | } 27 | } 28 | 29 | impl Game { 30 | // Wait for a fixed duration before serving the ball 31 | const SERVE_DURATION: Duration = Duration::from_millis(1500); 32 | 33 | pub fn new() -> Self { 34 | Self { 35 | ball: Ball::new(), 36 | left_paddle: Paddle::new(0.0, 0.5), 37 | right_paddle: Paddle::new(1.0, 0.5), 38 | score: (0, 0), 39 | serve_time: None, 40 | last_update: None, 41 | clients: [None, None], 42 | } 43 | } 44 | 45 | pub fn connect_player(&mut self, client_id: usize) -> color_eyre::Result<()> { 46 | if self.clients[0].is_none() { 47 | info!("Player 1 connected"); 48 | self.clients[0] = Some(client_id); 49 | } else if self.clients[1].is_none() { 50 | info!("Player 2 connected"); 51 | self.clients[1] = Some(client_id); 52 | } else { 53 | bail!("Game is full"); 54 | } 55 | if self.clients.iter().all(Option::is_some) { 56 | info!("Both players connected, starting game"); 57 | self.score = (0, 0); 58 | self.serve(); 59 | } 60 | Ok(()) 61 | } 62 | 63 | pub fn disconnect_player(&mut self, client_id: usize) { 64 | if let Some(id) = self.clients.iter_mut().find(|id| **id == Some(client_id)) { 65 | info!("Player disconnected"); 66 | *id = None; 67 | } 68 | } 69 | 70 | pub fn draw(&mut self, terminal: &mut SshTerminal) -> color_eyre::Result<()> { 71 | terminal.draw(|frame| frame.render_widget(self, frame.area()))?; 72 | Ok(()) 73 | } 74 | 75 | pub fn move_up(&mut self, client_id: usize) { 76 | if self.clients[0] 77 | .as_ref() 78 | .map_or(false, |id| *id == client_id) 79 | { 80 | self.left_paddle.move_up(); 81 | } else if self.clients[1] 82 | .as_ref() 83 | .map_or(false, |id| *id == client_id) 84 | { 85 | self.right_paddle.move_up(); 86 | } 87 | } 88 | 89 | pub fn move_down(&mut self, client_id: usize) { 90 | if self.clients[0] 91 | .as_ref() 92 | .map_or(false, |id| *id == client_id) 93 | { 94 | self.left_paddle.move_down(); 95 | } else if self.clients[1] 96 | .as_ref() 97 | .map_or(false, |id| *id == client_id) 98 | { 99 | self.right_paddle.move_down(); 100 | } 101 | } 102 | 103 | pub fn update(&mut self) { 104 | if self 105 | .serve_time 106 | .map_or(true, |t| t.elapsed() < Self::SERVE_DURATION) 107 | { 108 | return; 109 | } 110 | let duration = self.last_update.map_or(Duration::ZERO, |t| t.elapsed()); 111 | self.last_update = Some(Instant::now()); 112 | self.ball 113 | .update(duration, &self.left_paddle, &self.right_paddle); 114 | 115 | if self.ball.pos.x < 0.0 { 116 | self.score.1 += 1; 117 | self.serve(); 118 | } else if self.ball.pos.x > 1.0 { 119 | self.score.0 += 1; 120 | self.serve(); 121 | } 122 | } 123 | 124 | pub fn serve(&mut self) { 125 | info!("Serving ball"); 126 | self.ball.serve(); 127 | self.serve_time = Some(Instant::now()); 128 | self.last_update = None; 129 | } 130 | } 131 | 132 | impl Widget for &mut Game { 133 | fn render(self, area: Rect, buf: &mut Buffer) { 134 | Clear.render(area, buf); 135 | let block = Block::bordered() 136 | .title("Pong") 137 | .title_alignment(Alignment::Center) 138 | .style((Color::White, Color::DarkGray)); 139 | (&block).render(area, buf); 140 | let area = block.inner(area); 141 | Line::from(format!("Score: {} - {}", self.score.0, self.score.1)) 142 | .centered() 143 | .render(area, buf); 144 | self.ball.render(area, buf); 145 | self.left_paddle.render(area, buf); 146 | self.right_paddle.render(area, buf); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::Result; 2 | use tracing::{debug, level_filters::LevelFilter}; 3 | use tracing_subscriber::EnvFilter; 4 | 5 | mod backend; 6 | mod ball; 7 | mod game; 8 | mod paddle; 9 | mod physics; 10 | mod server; 11 | 12 | #[tokio::main] 13 | async fn main() -> Result<()> { 14 | color_eyre::install()?; 15 | init_tracing()?; 16 | let mut server = server::AppServer::new()?; 17 | server.run().await?; 18 | Ok(()) 19 | } 20 | 21 | fn init_tracing() -> Result<()> { 22 | let filter = EnvFilter::builder() 23 | .with_default_directive(LevelFilter::INFO.into()) 24 | .from_env()? 25 | .add_directive("pong_russh=debug".parse()?); 26 | tracing_subscriber::fmt() 27 | .compact() 28 | .with_env_filter(filter) 29 | .init(); 30 | debug!("Tracing initialized"); 31 | Ok(()) 32 | } 33 | -------------------------------------------------------------------------------- /src/paddle.rs: -------------------------------------------------------------------------------- 1 | use crate::physics::Point; 2 | use ratatui::prelude::*; 3 | 4 | /// Represents a paddle in the game. 5 | /// 6 | /// The x coordinate of the paddle is fixed, so it only moves up and down. 7 | #[derive(Debug, Default)] 8 | pub struct Paddle { 9 | pub pos: Point, 10 | } 11 | 12 | impl Paddle { 13 | // const WIDTH: f32 = 0.01; 14 | pub const HEIGHT: f32 = 0.15; 15 | const MOVE_DELTA: f32 = 0.025; 16 | 17 | pub const fn new(x: f32, y: f32) -> Self { 18 | Self { 19 | pos: Point { x, y }, 20 | } 21 | } 22 | 23 | /// Move the paddle up by a small amount 24 | pub fn move_up(&mut self) { 25 | self.pos.y = f32::max(self.pos.y - Self::MOVE_DELTA, Self::HEIGHT / 2.0); 26 | } 27 | 28 | /// Move the paddle down by a small amount 29 | pub fn move_down(&mut self) { 30 | self.pos.y = f32::min(self.pos.y + Self::MOVE_DELTA, 1.0 - Self::HEIGHT / 2.0); 31 | } 32 | } 33 | 34 | impl Widget for &Paddle { 35 | fn render(self, area: Rect, buf: &mut Buffer) { 36 | // use block characters that represent 1/8th of a cell to draw the paddles 37 | const TOP_BARS: [&str; 9] = ["█", "▇", "▆", "▅", "▄", "▃", "▂", "▁", " "]; 38 | const BOTTOM_BARS: [&str; 9] = [" ", "▔", "🮂", "🮃", "▀", "🮄", "🮅", "🮆", "█"]; 39 | let x = (self.pos.x * (area.width.saturating_sub(1)) as f32) as u16 + area.x; 40 | let top = (self.pos.y - Paddle::HEIGHT / 2.0) * area.height as f32; 41 | let bottom = (self.pos.y + Paddle::HEIGHT / 2.0) * area.height as f32; 42 | // draw the top character of the paddle by taking the fractional part of the top position 43 | let index = (top.fract() * 8.0).round() as usize; 44 | let top_char = TOP_BARS[index]; 45 | let top = top as u16 + area.y; 46 | buf.set_string(x, top, top_char, Style::default()); 47 | // draw the bottom character of the paddle by taking the fractional part of the bottom position 48 | let index = (bottom.fract() * 8.0).round() as usize; 49 | let bottom_char = BOTTOM_BARS[index]; 50 | let bottom = bottom as u16 + area.y; 51 | buf.set_string(x, bottom, bottom_char, Style::default()); 52 | 53 | // fill in the middle of the paddle with block characters 54 | for y in top + 1..bottom { 55 | buf.set_string(x, y, "█", Style::default()); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/physics.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{layout::Position, prelude::*}; 2 | 3 | /// A coordinate point in the range [0.0, 1.0] representing a position in the game world. 4 | /// 5 | /// (0.5, 0.5) is the center of the screen. 6 | #[derive(Debug, Default, Clone, Copy)] 7 | pub struct Point { 8 | pub x: f32, 9 | pub y: f32, 10 | } 11 | 12 | impl Point { 13 | pub const CENTER: Self = Self::new(0.5, 0.5); 14 | 15 | pub const fn new(x: f32, y: f32) -> Self { 16 | Self { x, y } 17 | } 18 | 19 | /// Convert a point in the game world to a point on the screen. 20 | /// 21 | /// Screen coordinates are in the range [0, width] and [0, height]. 22 | pub fn to_screen(self, area: Rect) -> Position { 23 | // ensure that the resulting position is within the screen bounds 24 | let max_width = (area.width.saturating_sub(1)) as f32; 25 | let max_height = (area.height.saturating_sub(1)) as f32; 26 | Position { 27 | x: (self.x * max_width) as u16 + area.x, 28 | y: (self.y * max_height) as u16 + area.y, 29 | } 30 | } 31 | } 32 | 33 | /// A velocity vector in the range [-1.0, 1.0] representing a direction and speed. 34 | /// 35 | /// The x component is the horizontal velocity, with negative values moving left and positive 36 | /// values moving right. The y component is the vertical velocity, with negative values moving 37 | /// up and positive values moving down. 38 | /// 39 | /// The unit of the velocity is the fraction of the screen width or height moved per second. 40 | /// Velocity is independent of the screen size, so the same velocity will move the same distance 41 | /// regardless of the screen size. 42 | /// 43 | /// There are only a few valid values for the velocity components to align with the original 44 | /// Pong game. (See for more details.) These have 45 | /// been scaled to coordinates in the range [-1.0, 1.0] to make them independent of the screen 46 | /// size and rounded slightly to make them easier to work with. 47 | /// 48 | /// - Vertical velocity: -0.69, -0.46, -0.23, 0.0, 0.23, 0.46, 0.69 49 | /// - Horizontal velocity: -0.53, -0.39. -0.26, 0.26, 0.39, 0.53 50 | #[derive(Debug, Default)] 51 | pub struct Velocity { 52 | pub x: f32, 53 | pub y: f32, 54 | } 55 | 56 | impl Velocity { 57 | // pub const VALID_X: [f32; 6] = [-0.53, -0.39, -0.26, 0.26, 0.39, 0.53]; 58 | pub const VALID_Y: [f32; 7] = [-0.69, -0.46, -0.23, 0.0, 0.23, 0.46, 0.69]; 59 | 60 | pub const fn new(x: f32, y: f32) -> Self { 61 | Self { x, y } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/server.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | net::{Ipv4Addr, SocketAddr}, 4 | sync::Arc, 5 | time::Duration, 6 | }; 7 | 8 | use color_eyre::{ 9 | eyre::{Context, OptionExt}, 10 | Result, 11 | }; 12 | use ratatui::Terminal; 13 | use russh::{ 14 | keys::{ 15 | ssh_key::{rand_core::OsRng, Algorithm, LineEnding}, 16 | PrivateKey, PublicKey, 17 | }, 18 | server::{Auth, Config, Handler, Msg, Server, Session}, 19 | Channel, ChannelId, Pty, 20 | }; 21 | use tokio::{sync::Mutex, time::sleep}; 22 | use tracing::{info, instrument}; 23 | 24 | use crate::{backend::SshBackend, game::Game}; 25 | 26 | pub type SshTerminal = Terminal; 27 | 28 | #[derive(Debug, Clone)] 29 | pub struct AppServer { 30 | client_counter: usize, 31 | game: Arc>, 32 | terminals: Arc>>, 33 | key: PrivateKey, 34 | } 35 | 36 | impl AppServer { 37 | pub fn new() -> Result { 38 | let key = load_or_generate_key()?; 39 | Ok(Self { 40 | client_counter: 0, 41 | game: Arc::new(Mutex::new(Game::new())), 42 | terminals: Arc::new(Mutex::new(HashMap::new())), 43 | key, 44 | }) 45 | } 46 | 47 | pub async fn run(&mut self) -> Result<()> { 48 | let game = self.game.clone(); 49 | let terminals = self.terminals.clone(); 50 | tokio::spawn(async move { 51 | loop { 52 | sleep(tokio::time::Duration::from_millis(16)).await; 53 | game.lock().await.update(); 54 | for terminal in terminals.lock().await.values_mut() { 55 | game.lock().await.draw(terminal).unwrap(); 56 | } 57 | } 58 | }); 59 | 60 | let config = Arc::new(Config { 61 | inactivity_timeout: Some(Duration::from_secs(3600)), 62 | auth_rejection_time: Duration::from_secs(3), 63 | auth_rejection_time_initial: Some(Duration::from_secs(0)), 64 | keys: vec![self.key.clone()], 65 | ..Default::default() 66 | }); 67 | 68 | let addr = Ipv4Addr::UNSPECIFIED; 69 | let port = 2222; 70 | info!("Listening on {}:{}", addr, port); 71 | self.run_on_address(config, (addr, port)).await?; 72 | Ok(()) 73 | } 74 | } 75 | 76 | fn load_or_generate_key() -> Result { 77 | let path = dirs::config_local_dir() 78 | .ok_or_eyre("Failed to get config local dir")? 79 | .join("pong_russh") 80 | .join("host_key"); 81 | let key = if path.exists() { 82 | info!("Loading host key from {}", path.display()); 83 | PrivateKey::read_openssh_file(&path).wrap_err("Failed to read host key from file")? 84 | } else { 85 | info!( 86 | "Host key not found at {}. Generating new host key", 87 | path.display() 88 | ); 89 | let key = PrivateKey::random(&mut OsRng, Algorithm::Ed25519) 90 | .wrap_err("Failed to generate host key")?; 91 | std::fs::create_dir_all(path.parent().unwrap()) 92 | .wrap_err("Failed to create directory for host key")?; 93 | key.write_openssh_file(&path, LineEnding::LF) 94 | .wrap_err("Failed to write host key to file")?; 95 | key 96 | }; 97 | Ok(key) 98 | } 99 | 100 | impl Server for AppServer { 101 | type Handler = AppHandler; 102 | fn new_client(&mut self, _peer_addr: Option) -> AppHandler { 103 | self.client_counter += 1; 104 | info!("New client connected: {}", self.client_counter); 105 | AppHandler::new( 106 | self.client_counter, 107 | self.game.clone(), 108 | self.terminals.clone(), 109 | ) 110 | } 111 | } 112 | 113 | #[derive(Debug)] 114 | pub struct AppHandler { 115 | pub client_id: usize, 116 | pub game: Arc>, 117 | pub terminals: Arc>>, 118 | } 119 | 120 | impl AppHandler { 121 | pub fn new( 122 | id: usize, 123 | game: Arc>, 124 | terminals: Arc>>, 125 | ) -> Self { 126 | Self { 127 | client_id: id, 128 | game, 129 | terminals, 130 | } 131 | } 132 | } 133 | 134 | impl Handler for AppHandler { 135 | type Error = color_eyre::Report; 136 | 137 | #[instrument(skip(self, _public_key), err)] 138 | async fn auth_publickey( 139 | &mut self, 140 | _user: &str, 141 | _public_key: &PublicKey, 142 | ) -> Result { 143 | info!(client_id = ?self.client_id, "Authenticating client"); 144 | Ok(Auth::Accept) 145 | } 146 | 147 | #[instrument(skip(self, _session), err)] 148 | async fn channel_open_session( 149 | &mut self, 150 | _channel: Channel, 151 | _session: &mut Session, 152 | ) -> Result { 153 | info!(client_id = ?self.client_id, "Opening session"); 154 | let mut game = self.game.lock().await; 155 | game.connect_player(self.client_id)?; 156 | Ok(true) 157 | } 158 | 159 | #[instrument(skip(self, _session), err)] 160 | async fn channel_close( 161 | &mut self, 162 | _channel: ChannelId, 163 | _session: &mut Session, 164 | ) -> Result<(), Self::Error> { 165 | info!(client_id = ?self.client_id, "Closing session"); 166 | self.game.lock().await.disconnect_player(self.client_id); 167 | self.terminals.lock().await.remove(&self.client_id); 168 | Ok(()) 169 | } 170 | 171 | async fn data( 172 | &mut self, 173 | channel_id: ChannelId, 174 | data: &[u8], 175 | session: &mut Session, 176 | ) -> Result<(), Self::Error> { 177 | match data { 178 | // Pressing 'q' closes the connection. 179 | b"q" => { 180 | let _ = session.close(channel_id); 181 | } 182 | // Pressing 'c' resets the counter for the app. 183 | // Every client sees the counter reset. 184 | b"w" => self.game.lock().await.move_up(self.client_id), 185 | b"s" => self.game.lock().await.move_down(self.client_id), 186 | _ => {} 187 | } 188 | 189 | Ok(()) 190 | } 191 | 192 | #[instrument(skip(self, _modes, session), err)] 193 | async fn pty_request( 194 | &mut self, 195 | channel_id: ChannelId, 196 | term: &str, 197 | col_width: u32, 198 | row_height: u32, 199 | pix_width: u32, 200 | pix_height: u32, 201 | _modes: &[(Pty, u32)], 202 | session: &mut Session, 203 | ) -> Result<(), Self::Error> { 204 | info!(client_id = ?self.client_id, "Creating terminal"); 205 | let terminal = Terminal::new(SshBackend::new( 206 | channel_id, 207 | session.handle(), 208 | col_width, 209 | row_height, 210 | pix_width, 211 | pix_height, 212 | ))?; 213 | let mut terminals = self.terminals.lock().await; 214 | terminals.insert(self.client_id, terminal); 215 | 216 | Ok(()) 217 | } 218 | 219 | /// The client's pseudo-terminal window size has changed. 220 | #[instrument(skip(self, session), err)] 221 | async fn window_change_request( 222 | &mut self, 223 | channel_id: ChannelId, 224 | col_width: u32, 225 | row_height: u32, 226 | pix_width: u32, 227 | pix_height: u32, 228 | session: &mut Session, 229 | ) -> Result<(), Self::Error> { 230 | info!(client_id = ?self.client_id, "Resizing terminal"); 231 | let terminal = Terminal::new(SshBackend::new( 232 | channel_id, 233 | session.handle(), 234 | col_width, 235 | row_height, 236 | pix_width, 237 | pix_height, 238 | ))?; 239 | let mut terminals = self.terminals.lock().await; 240 | terminals.insert(self.client_id, terminal); 241 | 242 | Ok(()) 243 | } 244 | } 245 | 246 | #[cfg(test)] 247 | pub mod tests { 248 | use russh::keys::PrivateKey; 249 | 250 | use super::*; 251 | 252 | #[tokio::test] 253 | async fn test_auth() { 254 | let key = PrivateKey::random(&mut OsRng, ssh_key::Algorithm::Ed25519).unwrap(); 255 | let public_key = key.public_key(); 256 | let addr = None; 257 | let mut handler = AppServer::new().unwrap().new_client(addr); 258 | let result = handler.auth_publickey("test", &public_key); 259 | assert_eq!(result.await.unwrap(), Auth::Accept); 260 | } 261 | } 262 | --------------------------------------------------------------------------------