1
Fork 0
This commit is contained in:
Tobias Berger 2023-07-02 17:11:46 +02:00
parent 996f4db048
commit f661ef1fcf
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
15 changed files with 63437 additions and 367 deletions

3
.cargo/config.toml Normal file
View file

@ -0,0 +1,3 @@
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/usr/bin/mold"]

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
style.css text eol=style.css

170
.gitignore vendored
View file

@ -1,169 +1,3 @@
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore # Added by cargo
# Logs /target
logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# Runtime data
pids
_.pid
_.seed
\*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
\*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
\*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
\*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.\*

671
Cargo.lock generated Normal file
View file

@ -0,0 +1,671 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-channel"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]]
name = "futures-sink"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
[[package]]
name = "futures-task"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
[[package]]
name = "futures-util"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "gimli"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]]
name = "h2"
version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "http"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "1.0.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "951dfc2e32ac02d67c90c0d65bd27009a635dc9b381a2cc7d284ab01e3a0150d"
dependencies = [
"bytes",
"http",
]
[[package]]
name = "http-body-util"
version = "0.1.0-rc.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92445bc9cc14bfa0a3ce56817dc3b5bcc227a168781a356b702410789cec0d10"
dependencies = [
"bytes",
"futures-util",
"http",
"http-body",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
[[package]]
name = "httpdate"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "hyper"
version = "1.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b75264b2003a3913f118d35c586e535293b3e22e41f074930762929d071e092"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"tokio",
"tracing",
"want",
]
[[package]]
name = "idna"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "minesweeper-nojs"
version = "0.1.0"
dependencies = [
"http-body-util",
"hyper",
"tokio",
"url",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "mio"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [
"libc",
"wasi",
"windows-sys",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "percent-encoding"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
name = "pin-project-lite"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "socket2"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "2.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
dependencies = [
"autocfg",
"backtrace",
"bytes",
"libc",
"mio",
"num_cpus",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys",
]
[[package]]
name = "tokio-macros"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-util"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
"tracing",
]
[[package]]
name = "tracing"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
dependencies = [
"once_cell",
]
[[package]]
name = "try-lock"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "unicode-bidi"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "url"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
]
[[package]]
name = "want"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
dependencies = [
"try-lock",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"

38
Cargo.toml Normal file
View file

@ -0,0 +1,38 @@
[package]
name = "minesweeper-nojs"
version = "0.1.0"
edition = "2021"
[profile.release]
strip = "symbols"
lto = "thin"
opt-level = 3
codegen-units = 1
[profile.dev.package."*"]
opt-level = 3
codegen-units = 1
[profile.release.package."*"]
opt-level = 3
codegen-units = 1
[profile.dev.build-override]
opt-level = 3
codegen-units = 1
[dependencies]
url = "2.4.0"
[dependencies.hyper]
version = "1.0.0-rc.3"
features = ["full"]
[dependencies.tokio]
version = "1"
features = ["full"]
[dependencies.http-body-util]
version = "0.1.0-rc.2"
[profile.release.build-override]
opt-level = 3
codegen-units = 1

1
build.sh Executable file
View file

@ -0,0 +1 @@
RUSTFLAGS="-C target-cpu=native" cargo build --release

BIN
bun.lockb

Binary file not shown.

16809
inputs.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,14 +0,0 @@
#!/usr/bin/just --justfile
# Lists all recipes
list:
{{ just_executable() }} --list
# Run a dev server anad recompile on file change
dev:
bun --port=55782 --watch ./server.ts
# Run a production server
run:
NODE_ENV="production" bun run --port=55782 ./server.ts

132
server.ts
View file

@ -1,132 +0,0 @@
const PORT = 55782;
const HEADERS_BOARD = {
headers: {
"Content-Type": "text/html",
"Cache-Control": "no-store",
},
};
const HEADERS_STYLESHEET = {
headers: {
"Content-Type": "text/css",
},
};
const HEADERS_FAVICON = {
headers: {
"Content-Type": "image/x-icon",
},
};
async function htmlDoc(body: string, head: string): Promise<string> {
return `<!DOCTYPE html><html><head><link rel=stylesheet href=style.css><link rel=icon type="image/x-icon" href=favicon.ico>${head}</head><body>${body}</body></html>`
}
async function createBoard(width: number, height: number, mines: number): Promise<string> {
console.debug("width", width, "height", height, "mines", mines);
if (width <= 0 || height <= 0 || mines <= 0) {
return await htmlDoc("NO 0 VALUES ALLOWED", "");
}
if (width > 50 || height > 50) {
return await htmlDoc("NO FIELDS LARGER THAN 50 IN EITHER DIMENSION", "");
}
if (mines >= width * height) {
return await htmlDoc("TOO MANY MINES", "");
}
const board = new Array(width * height).fill(false);
let safeIdx = board.map((_, idx) => idx);
for (let placedMines = 0; placedMines < mines; placedMines++) {
const mineIdx = safeIdx.splice(Math.floor(Math.random() * safeIdx.length), 1)[0];
board[mineIdx] = true;
}
function getNeighbors(idx: number): number {
let count = 0;
const leftEdge = (idx % width) === 0;
const topEdge = idx < width;
const bottomEdge = idx > (height * (width - 1));
const rightEdge = (idx % width) === (width - 1);
if (!leftEdge && board[idx - 1]) {
count++;
}
if (!leftEdge && !topEdge && board[idx - 1 - width]) {
count++;
}
if (!leftEdge && !bottomEdge && board[idx - 1 + width]) {
count++;
}
if (!rightEdge && board[idx + 1]) {
count++;
}
if (!rightEdge && !topEdge && board[idx + 1 - width]) {
count++;
}
if (!rightEdge && !bottomEdge && board[idx + 1 + width]) {
count++;
}
if (!topEdge && board[idx - width]) {
count++;
}
if (!bottomEdge && board[idx + width]) {
count++;
}
return count;
}
let table = "<table><tbody><tr>";
let inputs = "";
let style = "<style>";
for (const [idx, fieldIsMine] of board.entries()) {
table += `<td><label for=input_${idx}></label></td>`;
if ((idx % width) === (width - 1)) {
table += "</tr><tr>"
}
inputs += `<input id=input_${idx} type=checkbox ${fieldIsMine ? "data-mine" : "data-safe"}></input>`;
style += `#input_${idx}:checked ~ main label[for="input_${idx}"]::before { content: "${fieldIsMine ? "X" : getNeighbors(idx)}"; }\n`
+ `#input_${idx}:checked ~ main label[for="input_${idx}"] { pointer-events: none; }\n`;
}
table += "</tr></tbody></table>"
style += "</style>"
return await htmlDoc(`${inputs}<main>${table}</main><footer><span id=lost>HAHA you lost >:)</span><span id=won>YAY, you WON!!!</span></footer>`, style)
}
Bun.serve({
PORT,
async fetch(request) {
const url = new URL(request.url);
if (url.pathname === "/favicon.ico") {
return new Response(await Bun.file("favicon.ico").arrayBuffer(), FAVICON_STYLESHEET);
}
if (url.pathname === "/style.css") {
return new Response(await Bun.file("style.css").text(), HEADERS_STYLESHEET);
}
if (url.pathname !== "/") {
return new Response("Not found", { status: 404, statusText: "Not found" });
}
let height = Number.parseInt(url.searchParams.get("height") ?? "NaN");
if (Number.isNaN(height)) {
height = 10;
}
let width = Number.parseInt(url.searchParams.get("width") ?? "NaN");
if (Number.isNaN(width)) {
width = 10;
}
let mines = Number.parseInt(url.searchParams.get("mines") ?? "NaN");
if (Number.isNaN(mines)) {
mines = 10;
}
return new Response(await createBoard(width, height, mines), HEADERS_BOARD);
}
})

12033
src/html_prealloc_sizes.rs Normal file

File diff suppressed because it is too large Load diff

253
src/main.rs Normal file
View file

@ -0,0 +1,253 @@
mod html_prealloc_sizes;
const MAX_SIZE: usize = 50;
const PORT: u16 = 55782;
const STYLESHEET: &[u8; 776] = include_bytes!("../style.css");
const FAVICON: &[u8; 111468] = include_bytes!("../favicon.ico");
use http_body_util::Full;
use hyper::{
body::{Bytes, Incoming},
server::conn::http1,
service::service_fn,
Method, Request, Response, StatusCode,
};
use std::{
collections::HashMap,
convert::Infallible,
net::SocketAddr,
time::{Duration, SystemTime, UNIX_EPOCH},
};
use tokio::net::TcpListener;
// Pseudorandom number generator from the "Xorshift RNGs" paper by George Marsaglia.
//
// https://github.com/rust-lang/rust/blob/1.55.0/library/core/src/slice/sort.rs#L559-L573
pub fn random_numbers() -> impl Iterator<Item = usize> {
let mut random = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::new(0, 0))
.as_nanos() as usize;
std::iter::repeat_with(move || {
random ^= random << 13;
random ^= random >> 17;
random ^= random << 5;
random
})
}
fn not_found() -> Response<Full<Bytes>> {
Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Full::new(Bytes::from_static(b"Not found")))
.unwrap()
}
fn invalid_method() -> Response<Full<Bytes>> {
Response::builder()
.status(StatusCode::METHOD_NOT_ALLOWED)
.body(Full::new(Bytes::from_static(b"Method not allowed")))
.unwrap()
}
async fn stylesheet() -> Response<Full<Bytes>> {
Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "text/css")
.body(Full::new(Bytes::from_static(STYLESHEET)))
.unwrap()
}
async fn favicon() -> Response<Full<Bytes>> {
Response::builder()
.status(StatusCode::OK)
.header("Content-Type", "image/x-icon")
.body(Full::new(Bytes::from_static(FAVICON)))
.unwrap()
}
async fn create_board(width: usize, height: usize, mines: usize) -> Response<Full<Bytes>> {
if width == 0 || height == 0 || mines == 0 {
Response::builder()
.status(StatusCode::RANGE_NOT_SATISFIABLE)
.header("Cache-Control", "no-store")
.body(Full::new(Bytes::from_static(b"NO 0 VALUES ALLOWED")))
.unwrap()
} else if width > MAX_SIZE || height > MAX_SIZE {
Response::builder()
.status(StatusCode::RANGE_NOT_SATISFIABLE)
.header("Cache-Control", "no-store")
.body(Full::new(Bytes::from_static(b"FIELD TOO BIG")))
.unwrap()
} else if mines >= width * height {
Response::builder()
.status(StatusCode::RANGE_NOT_SATISFIABLE)
.header("Cache-Control", "no-store")
.body(Full::new(Bytes::from_static(
b"MUST BE LESS MINES THAN FIELDS",
)))
.unwrap()
} else {
let mut fields = vec![false; width * height];
let mut safe_indices = (0..width * height).collect::<Vec<_>>();
for (placed_mines, random) in (0..mines).zip(random_numbers()) {
let mine_index = safe_indices.swap_remove(random % ((width * height) - placed_mines));
fields[mine_index] = true;
}
let get_neighbors = |idx: usize| -> u8 {
let mut neighbor_count = 0;
let left_edge = (idx % width) == 0;
let top_edge = idx < width;
let bottom_edge = idx >= ((height - 1) * width);
let right_edge = (idx % width) == (width - 1);
if !left_edge && fields[idx - 1] {
neighbor_count += 1;
}
if !left_edge && !top_edge && fields[idx - 1 - width] {
neighbor_count += 1;
}
if !left_edge && !bottom_edge && fields[idx - 1 + width] {
neighbor_count += 1;
}
if !right_edge && fields[idx + 1] {
neighbor_count += 1;
}
if !right_edge && !top_edge && fields[idx + 1 - width] {
neighbor_count += 1;
}
if !right_edge && !bottom_edge && fields[idx + 1 + width] {
neighbor_count += 1;
}
if !top_edge && fields[idx - width] {
neighbor_count += 1;
}
if !bottom_edge && fields[idx + width] {
neighbor_count += 1;
}
neighbor_count
};
// Can't figure out how to do regression, otherwise I'd make these into functions to calculate the
// allocated size based on width and height
let sizes = html_prealloc_sizes::get_sizes(width, height);
let mut style_string = String::with_capacity(sizes.style);
let mut inputs_string = String::with_capacity(sizes.inputs);
let mut table_string = String::with_capacity(sizes.table);
for (field_index, &field_is_mine) in fields.iter().enumerate() {
table_string += format!("<td><label for=input_{field_index}></label></td>").as_str();
if (field_index % width) == (width - 1) && field_index != width * height - 1 {
table_string += "</tr><tr>";
}
inputs_string += format!(
"<input id=input_{field_index} type=checkbox data-{}></input>",
if field_is_mine { "mine" } else { "safe" }
)
.as_str();
style_string += format!(
"#input_{field_index}:checked ~ main label[for=\"input_{field_index}\"]::before {{ content: \"{}\"; }}\n#input_{field_index}:checked ~ main label[for=\"input_{field_index}\"] {{ pointer-events: none; }}",
if field_is_mine { "X".into() } else { get_neighbors(field_index).to_string() })
.as_str();
}
if style_string.capacity() > sizes.style {
dbg!(style_string.capacity(), style_string.len(), sizes.style);
}
if inputs_string.capacity() > sizes.inputs {
dbg!(inputs_string.capacity(), inputs_string.len(), sizes.inputs);
}
if table_string.capacity() > sizes.table {
dbg!(table_string.capacity(), table_string.len(), sizes.table);
}
let response_string = format!(
"<!DOCTYPE html><html><head><link rel=stylesheet href=style.css><link rel=icon type=\"image/x-icon\" href=favicon.ico><style>{}</style></head><body>{}<main><table><tbody><tr>{}</tr></tbody></table></main><footer><span id=lost>HAHA you lost >:)</span><span id=won>YAY, you WON!!!</span></footer></body></html>",
style_string,
inputs_string,
table_string
);
// To debug sizes for allocation
// let response_string = format!(
// "{{\"style\":{},\"inputs\":{},\"table\":{}}}",
// style_string.len(),
// inputs_string.len(),
// table_string.len()
// );
Response::builder()
.status(StatusCode::OK)
.header("Cache-Control", "no-store")
.body(Full::from(response_string))
.unwrap()
}
}
async fn respond(req: Request<Incoming>) -> Result<Response<Full<Bytes>>, Infallible> {
if !matches!(req.method(), &Method::GET) {
Ok(invalid_method())
} else {
let (width, height, mines) = match req.uri().query() {
Some(query) => {
let options_map = url::form_urlencoded::parse(query.as_bytes())
.into_owned()
.collect::<HashMap<String, String>>();
let height = options_map
.get("height")
.unwrap_or(&"NaN".to_string())
.parse()
.unwrap_or(10);
let width = options_map
.get("width")
.unwrap_or(&"NaN".to_string())
.parse()
.unwrap_or(10);
let mines = options_map
.get("mines")
.unwrap_or(&"NaN".to_string())
.parse()
.unwrap_or(10);
(width, height, mines)
}
None => (10, 10, 10),
};
Ok(match req.uri().path() {
"/style.css" => stylesheet().await,
"/favicon.ico" => favicon().await,
"/" => create_board(width, height, mines).await,
_ => not_found(),
})
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = SocketAddr::from(([0, 0, 0, 0], PORT));
let listener = TcpListener::bind(addr).await?;
loop {
let (stream, _) = listener.accept().await?;
// Spawn a tokio task to serve multiple connections concurrently
tokio::task::spawn(async move {
// Finally, we bind the incoming connection to our `hello` service
if let Err(err) = http1::Builder::new()
// `service_fn` converts our function in a `Service`
.serve_connection(stream, service_fn(respond))
.await
{
println!("Error serving connection: {err:?}");
}
});
}
}

View file

@ -1,53 +1 @@
html { html{height:100%;background:black;color:white;font-size:40px}label,label::before{display:block;width:50px;height:50px;cursor:pointer}label::before{text-align:center}body{height:100%;display:flex;justify-content:center;align-items:center;flex-direction:column}main{margin:auto;color:white}input{display:none;pointer-events:none}label{pointer-events:none}input~main label::before{content:"?"}#won{display:initial;user-select:none}input[data-safe]:not(:checked)~footer #won{display:none}input[data-safe]:not(:checked)~main label{pointer-events:initial}#lost{display:none;user-select:none}input[data-mine]:checked~footer #lost{display:initial}input[data-mine]:checked~main label{pointer-events:none}footer{display:flex;justify-content:center}#lost,#won{position:absolute;top:10px}
height: 100%;
background: black;
color: white;
font-size: 40px;
}
label, label::before {
display: block;
width: 50px;
height: 50px;
cursor: pointer;
}
label::before {
text-align: center;
}
body {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
main {
margin: auto;
color: white;
}
input { display:none; label: pointer-events: none; }
label { pointer-events: none; }
input ~ main label::before { content: "O"; }
#won { display: initial; user-select: none; }
input[data-safe]:not(:checked) ~ footer #won { display: none; }
input[data-safe]:not(:checked) ~ main label { pointer-events: initial; }
#lost { display: none; user-select: none ; }
input[data-mine]:checked ~ footer #lost { display: initial; }
input[data-mine]:checked ~ main label { pointer-events: none; }
footer {
display: flex;
justify-content: center;
}
#lost, #won {
position: absolute;
top: 10px;
}

16816
style.json Normal file

File diff suppressed because it is too large Load diff

16809
table.json Normal file

File diff suppressed because it is too large Load diff