From 341e1849475df163bbbd0568b72b6ecf7d5d544c Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Fri, 29 Nov 2024 23:14:52 -0500 Subject: [PATCH 1/7] Set up a database and store a character sheet in it --- Cargo.lock | 816 ++---------------- Cargo.toml | 4 +- visions/server/Cargo.toml | 2 + visions/server/migrations/01-charsheet/up.sql | 9 + visions/server/soren-jensen.json | 1 + visions/server/src/asset_db.rs | 2 - visions/server/src/core.rs | 13 +- visions/server/src/database.rs | 192 +++++ visions/server/src/main.rs | 25 +- 9 files changed, 302 insertions(+), 762 deletions(-) create mode 100644 visions/server/migrations/01-charsheet/up.sql create mode 100644 visions/server/soren-jensen.json create mode 100644 visions/server/src/database.rs diff --git a/Cargo.lock b/Cargo.lock index 0ca9863..ee0e392 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,12 +23,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - [[package]] name = "ahash" version = "0.8.11" @@ -37,7 +31,7 @@ checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", - "version_check 0.9.5", + "version_check", "zerocopy", ] @@ -234,7 +228,7 @@ dependencies = [ "futures-lite", "gloo-timers", "kv-log-macro", - "log 0.4.22", + "log", "memchr", "once_cell", "pin-project-lite", @@ -290,15 +284,6 @@ dependencies = [ "uuid 0.4.0", ] -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.4.0", -] - [[package]] name = "autocfg" version = "1.4.0" @@ -326,16 +311,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" -dependencies = [ - "byteorder", - "safemem", -] - [[package]] name = "base64" version = "0.21.7" @@ -433,12 +408,6 @@ dependencies = [ "piper", ] -[[package]] -name = "build_html" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225eb82ce9e70dcc0cfa6e404d0f353326b6e163bf500ec4711cec317d11935c" - [[package]] name = "bumpalo" version = "3.16.0" @@ -552,7 +521,7 @@ checksum = "d59ae0466b83e838b81a54256c39d5d7c20b9d7daa10510a242d9b75abd5936e" dependencies = [ "chrono", "chrono-tz-build", - "phf 0.11.2", + "phf", "serde 1.0.210", ] @@ -563,8 +532,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "433e39f13c9a060046954e0592a8d0a4bcb1040125cbf91cb8ee58964cfb350f" dependencies = [ "parse-zoneinfo", - "phf 0.11.2", - "phf_codegen 0.11.2", + "phf", + "phf_codegen", ] [[package]] @@ -618,15 +587,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -682,16 +642,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "cookie" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" -dependencies = [ - "time 0.3.36", - "version_check 0.9.5", -] - [[package]] name = "cookie-factory" version = "0.3.3" @@ -885,16 +835,6 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" -[[package]] -name = "deflate" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" -dependencies = [ - "adler32", - "byteorder", -] - [[package]] name = "der" version = "0.7.9" @@ -906,15 +846,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - [[package]] name = "digest" version = "0.10.7" @@ -1001,19 +932,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "env_logger" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" -dependencies = [ - "humantime", - "is-terminal", - "log 0.4.22", - "regex", - "termcolor", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -1121,37 +1039,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "file-service" -version = "0.2.0" -dependencies = [ - "authdb", - "base64ct", - "build_html", - "bytes", - "chrono", - "clap", - "cookie", - "cool_asserts", - "futures-util", - "hex-string", - "http 0.2.12", - "image 0.23.14", - "log 0.4.22", - "logger", - "mime 0.3.17", - "mime_guess 2.0.5", - "pretty_env_logger", - "serde 1.0.210", - "serde_json", - "sha2", - "tempdir", - "thiserror 1.0.64", - "tokio", - "uuid 0.4.0", - "warp", -] - [[package]] name = "fitnesstrax" version = "0.6.0" @@ -1287,7 +1174,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "percent-encoding 2.3.1", + "percent-encoding", ] [[package]] @@ -1496,7 +1383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check 0.9.5", + "version_check", ] [[package]] @@ -1511,17 +1398,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gif" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" -dependencies = [ - "color_quant", - "weezl", + "wasi", ] [[package]] @@ -1877,7 +1754,7 @@ dependencies = [ "headers-core", "http 0.2.12", "httpdate", - "mime 0.3.17", + "mime", "sha1", ] @@ -1930,15 +1807,9 @@ dependencies = [ "glib", "glib-build-tools 0.18.0", "gtk4", - "image 0.24.9", + "image", ] -[[package]] -name = "hex-string" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848ec2dd093df965a34b434580d94852197fc83feac5b2c1962399bbf2cb4f0b" - [[package]] name = "hkdf" version = "0.12.4" @@ -2011,31 +1882,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.10.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" -dependencies = [ - "base64 0.9.3", - "httparse", - "language-tags", - "log 0.3.9", - "mime 0.2.6", - "num_cpus", - "time 0.1.45", - "traitobject", - "typeable", - "unicase 1.4.2", - "url 1.7.2", -] - [[package]] name = "hyper" version = "0.14.30" @@ -2067,7 +1913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.30", + "hyper", "native-tls", "tokio", "tokio-native-tls", @@ -2106,17 +1952,6 @@ dependencies = [ "libadwaita", ] -[[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.5.0" @@ -2136,25 +1971,6 @@ dependencies = [ "thiserror 1.0.64", ] -[[package]] -name = "image" -version = "0.23.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" -dependencies = [ - "bytemuck", - "byteorder", - "color_quant", - "gif 0.11.4", - "jpeg-decoder 0.1.22", - "num-iter", - "num-rational", - "num-traits", - "png 0.16.8", - "scoped_threadpool", - "tiff 0.6.1", -] - [[package]] name = "image" version = "0.24.9" @@ -2165,12 +1981,12 @@ dependencies = [ "byteorder", "color_quant", "exr", - "gif 0.13.1", - "jpeg-decoder 0.3.1", + "gif", + "jpeg-decoder", "num-traits", - "png 0.17.14", + "png", "qoi", - "tiff 0.9.1", + "tiff", ] [[package]] @@ -2233,33 +2049,6 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" -[[package]] -name = "iron" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6d308ca2d884650a8bf9ed2ff4cb13fbb2207b71f64cda11dc9b892067295e8" -dependencies = [ - "hyper 0.10.16", - "log 0.3.9", - "mime_guess 1.8.8", - "modifier", - "num_cpus", - "plugin", - "typemap", - "url 1.7.2", -] - -[[package]] -name = "is-terminal" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" -dependencies = [ - "hermit-abi 0.4.0", - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -2281,15 +2070,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "jpeg-decoder" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" -dependencies = [ - "rayon", -] - [[package]] name = "jpeg-decoder" version = "0.3.1" @@ -2314,15 +2094,9 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" dependencies = [ - "log 0.4.22", + "log", ] -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" - [[package]] name = "lazy_static" version = "1.5.0" @@ -2444,7 +2218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3302702afa434ffa30847a83305f0a69d6abd74293b6554c18ec85c7ef30c980" dependencies = [ "anyhow", - "version_check 0.9.5", + "version_check", ] [[package]] @@ -2467,19 +2241,10 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "autocfg 1.4.0", + "autocfg", "scopeguard", ] -[[package]] -name = "log" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" -dependencies = [ - "log 0.4.22", -] - [[package]] name = "log" version = "0.4.22" @@ -2489,23 +2254,6 @@ dependencies = [ "value-bag", ] -[[package]] -name = "logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c9172cb4c2f6c52117e25570983edcbb322f130b1031ae5d5d6b1abe7eeb493" -dependencies = [ - "iron", - "log 0.3.9", - "time 0.1.45", -] - -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "md-5" version = "0.10.6" @@ -2528,7 +2276,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ - "autocfg 1.4.0", + "autocfg", ] [[package]] @@ -2542,41 +2290,20 @@ dependencies = [ "tokio", ] -[[package]] -name = "mime" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" -dependencies = [ - "log 0.3.9", -] - [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "mime_guess" -version = "1.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216929a5ee4dd316b1702eedf5e74548c123d370f47841ceaac38ca154690ca3" -dependencies = [ - "mime 0.2.6", - "phf 0.7.24", - "phf_codegen 0.7.24", - "unicase 1.4.2", -] - [[package]] name = "mime_guess" version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ - "mime 0.3.17", - "unicase 2.7.0", + "mime", + "unicase", ] [[package]] @@ -2585,25 +2312,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" -dependencies = [ - "adler32", -] - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg 1.4.0", -] - [[package]] name = "miniz_oxide" version = "0.7.4" @@ -2631,16 +2339,10 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.52.0", ] -[[package]] -name = "modifier" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f5c9112cb662acd3b204077e0de5bc66305fa8df65c8019d5adb10e9ab6e58" - [[package]] name = "multer" version = "2.1.0" @@ -2652,11 +2354,11 @@ dependencies = [ "futures-util", "http 0.2.12", "httparse", - "log 0.4.22", + "log", "memchr", - "mime 0.3.17", + "mime", "spin", - "version_check 0.9.5", + "version_check", ] [[package]] @@ -2676,7 +2378,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ "libc", - "log 0.4.22", + "log", "openssl", "openssl-probe", "openssl-sys", @@ -2738,12 +2440,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" @@ -2759,18 +2455,7 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ - "autocfg 1.4.0", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg 1.4.0", + "autocfg", "num-integer", "num-traits", ] @@ -2781,20 +2466,10 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "autocfg 1.4.0", + "autocfg", "libm", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.9", - "libc", -] - [[package]] name = "object" version = "0.36.5" @@ -2883,7 +2558,7 @@ dependencies = [ "glib", "glib-build-tools 0.17.10", "gtk4", - "image 0.24.9", + "image", "libadwaita", "otg-core", "pango", @@ -2970,44 +2645,19 @@ dependencies = [ "base64ct", ] -[[package]] -name = "percent-encoding" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" - [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "phf" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3da44b85f8e8dfaec21adae67f95d93244b2ecf6ad2a692320598dcc8e6dd18" -dependencies = [ - "phf_shared 0.7.24", -] - [[package]] name = "phf" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_codegen" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" -dependencies = [ - "phf_generator 0.7.24", - "phf_shared 0.7.24", + "phf_shared", ] [[package]] @@ -3016,18 +2666,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", -] - -[[package]] -name = "phf_generator" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" -dependencies = [ - "phf_shared 0.7.24", - "rand 0.6.5", + "phf_generator", + "phf_shared", ] [[package]] @@ -3036,27 +2676,17 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ - "phf_shared 0.11.2", + "phf_shared", "rand 0.8.5", ] -[[package]] -name = "phf_shared" -version = "0.7.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -dependencies = [ - "siphasher 0.2.3", - "unicase 1.4.2", -] - [[package]] name = "phf_shared" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" dependencies = [ - "siphasher 0.3.11", + "siphasher", ] [[package]] @@ -3157,27 +2787,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" -[[package]] -name = "plugin" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0" -dependencies = [ - "typemap", -] - -[[package]] -name = "png" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "deflate", - "miniz_oxide 0.3.7", -] - [[package]] name = "png" version = "0.17.14" @@ -3206,12 +2815,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.20" @@ -3221,16 +2824,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "pretty_env_logger" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" -dependencies = [ - "env_logger", - "log 0.4.22", -] - [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -3261,7 +2854,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "version_check 0.9.5", + "version_check", ] [[package]] @@ -3272,7 +2865,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "version_check 0.9.5", + "version_check", ] [[package]] @@ -3296,8 +2889,8 @@ dependencies = [ "lazy_static", "num-traits", "rand 0.8.5", - "rand_chacha 0.3.1", - "rand_xorshift 0.3.0", + "rand_chacha", + "rand_xorshift", "regex-syntax", "rusty-fork", "tempfile", @@ -3351,25 +2944,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -dependencies = [ - "autocfg 0.1.8", - "libc", - "rand_chacha 0.1.1", - "rand_core 0.4.2", - "rand_hc", - "rand_isaac", - "rand_jitter", - "rand_os", - "rand_pcg", - "rand_xorshift 0.1.1", - "winapi", -] - [[package]] name = "rand" version = "0.8.5" @@ -3377,20 +2951,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", + "rand_chacha", "rand_core 0.6.4", ] -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.3.1", -] - [[package]] name = "rand_chacha" version = "0.3.1" @@ -3425,68 +2989,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -dependencies = [ - "rand_core 0.3.1", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -dependencies = [ - "libc", - "rand_core 0.4.2", - "winapi", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -dependencies = [ - "cloudabi", - "fuchsia-cprng", - "libc", - "rand_core 0.4.2", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -dependencies = [ - "autocfg 0.1.8", - "rand_core 0.4.2", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -dependencies = [ - "rand_core 0.3.1", -] - [[package]] name = "rand_xorshift" version = "0.3.0" @@ -3563,15 +3065,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "reqwest" version = "0.11.27" @@ -3586,15 +3079,15 @@ dependencies = [ "h2", "http 0.2.12", "http-body", - "hyper 0.14.30", + "hyper", "hyper-tls", "ipnet", "js-sys", - "log 0.4.22", - "mime 0.3.17", + "log", + "mime", "native-tls", "once_cell", - "percent-encoding 2.3.1", + "percent-encoding", "pin-project-lite", "rustls-pemfile", "serde 1.0.210", @@ -3605,7 +3098,7 @@ dependencies = [ "tokio", "tokio-native-tls", "tower-service", - "url 2.5.2", + "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -3660,7 +3153,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "923b42e802f7dc20a0a6b5e097ba7c83fe4289da07e49156fecf6af08aa9cd1c" dependencies = [ "include_dir", - "log 0.4.22", + "log", "rusqlite", ] @@ -3725,12 +3218,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "schannel" version = "0.1.26" @@ -3746,12 +3233,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" -[[package]] -name = "scoped_threadpool" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" - [[package]] name = "scopeguard" version = "1.2.0" @@ -3881,7 +3362,7 @@ dependencies = [ "memchr", "ryu", "serde 1.0.210", - "version_check 0.9.5", + "version_check", ] [[package]] @@ -3965,12 +3446,6 @@ dependencies = [ "pango", ] -[[package]] -name = "siphasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" - [[package]] name = "siphasher" version = "0.3.11" @@ -3983,7 +3458,7 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "autocfg 1.4.0", + "autocfg", ] [[package]] @@ -4075,11 +3550,11 @@ dependencies = [ "hashlink", "hex", "indexmap", - "log 0.4.22", + "log", "memchr", "once_cell", "paste", - "percent-encoding 2.3.1", + "percent-encoding", "serde 1.0.210", "serde_json", "sha2", @@ -4089,7 +3564,7 @@ dependencies = [ "tokio", "tokio-stream", "tracing", - "url 2.5.2", + "url", ] [[package]] @@ -4128,7 +3603,7 @@ dependencies = [ "syn 2.0.87", "tempfile", "tokio", - "url 2.5.2", + "url", ] [[package]] @@ -4155,11 +3630,11 @@ dependencies = [ "hkdf", "hmac", "itoa", - "log 0.4.22", + "log", "md-5", "memchr", "once_cell", - "percent-encoding 2.3.1", + "percent-encoding", "rand 0.8.5", "rsa", "serde 1.0.210", @@ -4195,7 +3670,7 @@ dependencies = [ "hmac", "home", "itoa", - "log 0.4.22", + "log", "md-5", "memchr", "once_cell", @@ -4225,13 +3700,13 @@ dependencies = [ "futures-intrusive", "futures-util", "libsqlite3-sys", - "log 0.4.22", - "percent-encoding 2.3.1", + "log", + "percent-encoding", "serde 1.0.210", "serde_urlencoded", "sqlx-core", "tracing", - "url 2.5.2", + "url", ] [[package]] @@ -4325,16 +3800,6 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" -[[package]] -name = "tempdir" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -dependencies = [ - "rand 0.4.6", - "remove_dir_all", -] - [[package]] name = "tempfile" version = "3.13.0" @@ -4348,15 +3813,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "1.0.64" @@ -4397,17 +3853,6 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "tiff" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" -dependencies = [ - "jpeg-decoder 0.1.22", - "miniz_oxide 0.4.4", - "weezl", -] - [[package]] name = "tiff" version = "0.9.1" @@ -4415,52 +3860,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ "flate2", - "jpeg-decoder 0.3.1", + "jpeg-decoder", "weezl", ] -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "itoa", - "num-conv", - "powerfmt", - "serde 1.0.210", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "timezone-testing" version = "0.1.0" @@ -4550,7 +3953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", - "log 0.4.22", + "log", "tokio", "tungstenite", ] @@ -4625,7 +4028,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log 0.4.22", + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4651,12 +4054,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "traitobject" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" - [[package]] name = "tree" version = "0.1.0" @@ -4678,11 +4075,11 @@ dependencies = [ "data-encoding", "http 1.1.0", "httparse", - "log 0.4.22", + "log", "rand 0.8.5", "sha1", "thiserror 1.0.64", - "url 2.5.2", + "url", "utf-8", ] @@ -4695,21 +4092,6 @@ dependencies = [ "rustc-hash", ] -[[package]] -name = "typeable" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" - -[[package]] -name = "typemap" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" -dependencies = [ - "unsafe-any", -] - [[package]] name = "typenum" version = "1.17.0" @@ -4762,22 +4144,13 @@ dependencies = [ "tinystr", ] -[[package]] -name = "unicase" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" -dependencies = [ - "version_check 0.1.5", -] - [[package]] name = "unicase" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ - "version_check 0.9.5", + "version_check", ] [[package]] @@ -4825,26 +4198,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" -[[package]] -name = "unsafe-any" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" -dependencies = [ - "traitobject", -] - -[[package]] -name = "url" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" -dependencies = [ - "idna 0.1.5", - "matches", - "percent-encoding 1.0.1", -] - [[package]] name = "url" version = "2.5.2" @@ -4852,8 +4205,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.5.0", - "percent-encoding 2.3.1", + "idna", + "percent-encoding", ] [[package]] @@ -4921,12 +4274,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.5" @@ -4941,8 +4288,10 @@ dependencies = [ "cool_asserts", "futures", "http 1.1.0", - "mime 0.3.17", - "mime_guess 2.0.5", + "include_dir", + "lazy_static", + "mime", + "mime_guess", "rusqlite", "rusqlite_migration", "serde 1.0.210", @@ -4985,12 +4334,12 @@ dependencies = [ "futures-util", "headers", "http 0.2.12", - "hyper 0.14.30", - "log 0.4.22", - "mime 0.3.17", - "mime_guess 2.0.5", + "hyper", + "log", + "mime", + "mime_guess", "multer", - "percent-encoding 2.3.1", + "percent-encoding", "pin-project", "scoped-tls", "serde 1.0.210", @@ -5003,12 +4352,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5039,7 +4382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", - "log 0.4.22", + "log", "once_cell", "proc-macro2", "quote", @@ -5130,15 +4473,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 2f82d52..3424f9d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [workspace] resolver = "2" members = [ - "authdb", + # "authdb", # "bike-lights/bike", "bike-lights/core", "bike-lights/simulator", @@ -14,7 +14,7 @@ members = [ "cyberpunk-splash", "dashboard", "emseries", - "file-service", + # "file-service", "fitnesstrax/core", "fitnesstrax/app", "fluent-ergonomics", diff --git a/visions/server/Cargo.toml b/visions/server/Cargo.toml index 6af94ff..7eede30 100644 --- a/visions/server/Cargo.toml +++ b/visions/server/Cargo.toml @@ -22,6 +22,8 @@ urlencoding = "2.1.3" thiserror = "2.0.3" rusqlite = "0.32.1" rusqlite_migration = { version = "1.3.1", features = ["from-directory"] } +lazy_static = "1.5.0" +include_dir = "0.7.4" [dev-dependencies] cool_asserts = "2.0.3" diff --git a/visions/server/migrations/01-charsheet/up.sql b/visions/server/migrations/01-charsheet/up.sql new file mode 100644 index 0000000..575c5e4 --- /dev/null +++ b/visions/server/migrations/01-charsheet/up.sql @@ -0,0 +1,9 @@ +CREATE TABLE game( + uuid TEXT PRIMARY KEY +); + +CREATE TABLE charsheet( + uuid TEXT PRIMARY KEY, + gametype TEXT NOT NULL, + data TEXT +); diff --git a/visions/server/soren-jensen.json b/visions/server/soren-jensen.json new file mode 100644 index 0000000..a65c0b1 --- /dev/null +++ b/visions/server/soren-jensen.json @@ -0,0 +1 @@ +{ "type_": "Candela", "name": "Soren Jensen", "pronouns": "he/him", "circle": "Circle of the Bluest Sky", "style": "dapper gentleman", "catalyst": "a cursed book", "question": "What were the contents of that book?", "nerve": { "type_": "nerve", "drives": { "current": 1, "max": 2 }, "resistances": { "current": 0, "max": 3 }, "move": { "gilded": false, "score": 2 }, "strike": { "gilded": false, "score": 1 }, "control": { "gilded": true, "score": 0 } }, "cunning": { "type_": "cunning", "drives": { "current": 1, "max": 1 }, "resistances": { "current": 0, "max": 3 }, "sway": { "gilded": false, "score": 0 }, "read": { "gilded": false, "score": 0 }, "hide": { "gilded": false, "score": 0 } }, "intuition": { "type_": "intuition", "drives": { "current": 0, "max": 0 }, "resistances": { "current": 0, "max": 3 }, "survey": { "gilded": false, "score": 0 }, "focus": { "gilded": false, "score": 0 }, "sense": { "gilded": false, "score": 0 } }, "role": "Slink", "role_abilities": [ "Scout: If you have time to observe a location, you can spend 1 Intuition to ask a question: What do I notice here that others do not see? What in this place might be of use to us? What path should we follow?" ], "specialty": "Detective", "specialty_abilities": [ "Mind Palace: When you want to figure out how two clues might relate or what path they should point you towards, burn 1 Intution resistance. The GM will give you the information you have deduced." ] } diff --git a/visions/server/src/asset_db.rs b/visions/server/src/asset_db.rs index 4e724e5..7b7a786 100644 --- a/visions/server/src/asset_db.rs +++ b/visions/server/src/asset_db.rs @@ -84,7 +84,6 @@ impl FsAssets { let mut assets = HashMap::new(); for dir_ent in dir { - println!("{:?}", dir_ent); let path = dir_ent.unwrap().path(); let file_name = path.file_name().unwrap().to_str().unwrap(); assets.insert(AssetId::from(file_name), path.to_str().unwrap().to_owned()); @@ -97,7 +96,6 @@ impl FsAssets { impl Assets for FsAssets { fn assets<'a>(&'a self) -> AssetIter<'a> { - println!("FsAssets assets: {:?}", self.assets); AssetIter(self.assets.iter()) } diff --git a/visions/server/src/core.rs b/visions/server/src/core.rs index d8c8b69..1f7005a 100644 --- a/visions/server/src/core.rs +++ b/visions/server/src/core.rs @@ -11,8 +11,7 @@ use urlencoding::decode; use uuid::Uuid; use crate::{ - asset_db::{self, AssetId, Assets}, - types::{AppError, Message, Tabletop, RGB}, + asset_db::{self, AssetId, Assets}, database::Database, types::{AppError, Message, Tabletop, RGB} }; const DEFAULT_BACKGROUND_COLOR: RGB = RGB { @@ -27,7 +26,7 @@ struct WebsocketClient { } pub struct AppState { - pub asset_db: Box, + pub asset_store: Box, pub clients: HashMap, pub tabletop: Tabletop, @@ -42,7 +41,7 @@ impl Core { A: Assets + Sync + Send + 'static, { Self(Arc::new(RwLock::new(AppState { - asset_db: Box::new(assetdb), + asset_store: Box::new(assetdb), clients: HashMap::new(), tabletop: Tabletop { background_color: DEFAULT_BACKGROUND_COLOR, @@ -89,7 +88,7 @@ impl Core { self.0 .read() .unwrap() - .asset_db + .asset_store .get(asset_id.clone()) .map_err(|err| match err { asset_db::Error::NotFound => AppError::NotFound(format!("{}", asset_id)), @@ -99,14 +98,12 @@ impl Core { } pub fn available_images(&self) -> Vec { - println!("available_images"); self.0 .read() .unwrap() - .asset_db + .asset_store .assets() .filter_map(|(asset_id, value)| { - println!("[{:?}] {}", mime_guess::from_path(&value).first(), value); match mime_guess::from_path(&value).first() { Some(mime) if mime.type_() == mime::IMAGE => Some(asset_id.clone()), _ => None, diff --git a/visions/server/src/database.rs b/visions/server/src/database.rs new file mode 100644 index 0000000..7de5572 --- /dev/null +++ b/visions/server/src/database.rs @@ -0,0 +1,192 @@ +use std::{ + path::PathBuf, + sync::mpsc::{channel, Receiver, Sender}, + thread::JoinHandle, +}; + +use include_dir::{include_dir, Dir}; +use lazy_static::lazy_static; +use rusqlite::Connection; +use rusqlite_migration::Migrations; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use uuid::Uuid; + +static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations"); + +lazy_static! { + static ref MIGRATIONS: Migrations<'static> = + Migrations::from_directory(&MIGRATIONS_DIR).unwrap(); +} + +#[derive(Debug, Error)] +pub enum Error { + #[error("Duplicate item found for id {0}")] + DuplicateItem(String), + + #[error("Unexpected response for message")] + MessageMismatch, +} + +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct CharacterId(String); + +impl CharacterId { + pub fn new() -> Self { + CharacterId(format!("{}", Uuid::new_v4().hyphenated())) + } + + pub fn as_str<'a>(&'a self) -> &'a str { + &self.0 + } +} + +impl From<&str> for CharacterId { + fn from(s: &str) -> Self { + CharacterId(s.to_owned()) + } +} + +impl From for CharacterId { + fn from(s: String) -> Self { + CharacterId(s) + } +} + +#[derive(Clone, Debug)] +pub struct CharsheetRow { + id: String, + gametype: String, + data: serde_json::Value, +} + +pub trait Database { + async fn charsheet(&self, id: CharacterId) -> Result, Error>; +} + +pub struct DiskDb { + conn: Connection, +} + +impl DiskDb { + pub fn new(path: Option) -> Result { + let mut conn = match path { + None => Connection::open(":memory:").expect("to create a memory connection"), + Some(path) => Connection::open(path).expect("to create connection"), + }; + MIGRATIONS.to_latest(&mut conn).expect("to run migrations"); + Ok(DiskDb { conn }) + } + + fn charsheet(&self, id: CharacterId) -> Result, Error> { + let mut stmt = self + .conn + .prepare("SELECT uuid, gametype, data FROM charsheet WHERE uuid=?") + .unwrap(); + let items: Vec = stmt + .query_map([id.as_str()], |row| { + let data: String = row.get(2).unwrap(); + Ok(CharsheetRow { + id: row.get(0).unwrap(), + gametype: row.get(1).unwrap(), + data: serde_json::from_str(&data).unwrap(), + }) + }) + .unwrap() + .collect::, rusqlite::Error>>() + .unwrap(); + match &items[..] { + [] => Ok(None), + [item] => Ok(Some(item.clone())), + _ => unimplemented!(), + } + } + + fn save_charsheet( + &self, + char_id: Option, + game_type: String, + charsheet: serde_json::Value, + ) -> Result { + match char_id { + None => { + let char_id = CharacterId::new(); + let mut stmt = self + .conn + .prepare("INSERT INTO charsheet VALUES (?, ?, ?)") + .unwrap(); + stmt.execute((char_id.as_str(), game_type, charsheet.to_string())) + .unwrap(); + + Ok(char_id) + } + Some(char_id) => { + let mut stmt = self + .conn + .prepare("UPDATE charsheet SET data=? WHERE uuid=?") + .unwrap(); + stmt.execute((charsheet.to_string(), char_id.as_str())) + .unwrap(); + + Ok(char_id) + } + } + } +} + +/* +enum DatabaseRequest { + Charsheet(CharacterId), +} + +enum DatabaseResponse { + Charsheet(Option), +} + +pub struct DiskDb { + db_handle: JoinHandle<()>, + to_db: Sender, + from_db: Receiver, +} + +impl DiskDb { + pub fn new(path: PathBuf) -> DiskDb { + DiskDb { + db_handle, + to_db: interface_to_db_tx, + from_db: db_to_interface_rx, + } + } +} + +impl Database for DiskDb { + async fn charsheet(&self, id: CharacterId) -> Result, Error> { + self.to_db(DatabaseRequest::Charsheet(id)).unwrap(); + match self.from_db.recv() { + Ok(DatabaseResponse::Charsheet(sheet)) => Ok(sheet), + Ok(_) => Err(Error::MessageMismatch), + Err(_) => unimplemented!(), + } + } +} +*/ + +#[cfg(test)] +mod test { + use cool_asserts::assert_matches; + + use super::*; + + const soren: &'static str = r#"{ "type_": "Candela", "name": "Soren Jensen", "pronouns": "he/him", "circle": "Circle of the Bluest Sky", "style": "dapper gentleman", "catalyst": "a cursed book", "question": "What were the contents of that book?", "nerve": { "type_": "nerve", "drives": { "current": 1, "max": 2 }, "resistances": { "current": 0, "max": 3 }, "move": { "gilded": false, "score": 2 }, "strike": { "gilded": false, "score": 1 }, "control": { "gilded": true, "score": 0 } }, "cunning": { "type_": "cunning", "drives": { "current": 1, "max": 1 }, "resistances": { "current": 0, "max": 3 }, "sway": { "gilded": false, "score": 0 }, "read": { "gilded": false, "score": 0 }, "hide": { "gilded": false, "score": 0 } }, "intuition": { "type_": "intuition", "drives": { "current": 0, "max": 0 }, "resistances": { "current": 0, "max": 3 }, "survey": { "gilded": false, "score": 0 }, "focus": { "gilded": false, "score": 0 }, "sense": { "gilded": false, "score": 0 } }, "role": "Slink", "role_abilities": [ "Scout: If you have time to observe a location, you can spend 1 Intuition to ask a question: What do I notice here that others do not see? What in this place might be of use to us? What path should we follow?" ], "specialty": "Detective", "specialty_abilities": [ "Mind Palace: When you want to figure out how two clues might relate or what path they should point you towards, burn 1 Intution resistance. The GM will give you the information you have deduced." ] }"#; + + #[test] + fn it_can_retrieve_a_charsheet() { + let db = DiskDb::new(None).unwrap(); + + assert_matches!(db.charsheet(CharacterId::from("1")), Ok(None)); + + let js: serde_json::Value = serde_json::from_str(soren).unwrap(); + let soren_id = db.save_charsheet(None, "candela".to_owned(), js.clone()).unwrap(); + assert_matches!(db.charsheet(soren_id).unwrap(), Some(CharsheetRow{ data, .. }) => assert_eq!(js, data)); + } +} diff --git a/visions/server/src/main.rs b/visions/server/src/main.rs index 4f433c6..80c8ae1 100644 --- a/visions/server/src/main.rs +++ b/visions/server/src/main.rs @@ -1,11 +1,15 @@ -use asset_db::{AssetId, FsAssets}; -use authdb::AuthError; -use handlers::{ - handle_available_images, handle_connect_websocket, handle_file, handle_register_client, handle_set_background_image, handle_unregister_client, RegisterRequest -}; use std::{ convert::Infallible, - net::{IpAddr, Ipv4Addr, SocketAddr}, path::PathBuf, + net::{IpAddr, Ipv4Addr, SocketAddr}, + path::PathBuf, +}; + +use asset_db::{AssetId, FsAssets}; +use authdb::AuthError; +use database::{CharacterId, Database}; +use handlers::{ + handle_available_images, handle_connect_websocket, handle_file, handle_register_client, + handle_set_background_image, handle_unregister_client, RegisterRequest, }; use warp::{ // header, @@ -16,8 +20,8 @@ use warp::{ mod asset_db; mod core; +mod database; mod handlers; - mod types; #[derive(Debug)] @@ -96,7 +100,9 @@ async fn handle_rejection(err: warp::Rejection) -> Result Date: Sat, 30 Nov 2024 11:48:35 -0500 Subject: [PATCH 2/7] Create a shareable connection to the database --- visions/server/Cargo.toml | 3 +- visions/server/Taskfile.yml | 2 +- visions/server/src/core.rs | 16 ++-- visions/server/src/database.rs | 132 ++++++++++++++++++++++++--------- visions/server/src/main.rs | 8 +- 5 files changed, 114 insertions(+), 47 deletions(-) diff --git a/visions/server/Cargo.toml b/visions/server/Cargo.toml index 7eede30..5bc25d1 100644 --- a/visions/server/Cargo.toml +++ b/visions/server/Cargo.toml @@ -15,7 +15,6 @@ warp = { version = "0.3" } mime_guess = "2.0.5" mime = "0.3.17" uuid = { version = "1.11.0", features = ["v4"] } -futures = "0.3.31" tokio-stream = "0.1.16" typeshare = "1.0.4" urlencoding = "2.1.3" @@ -24,6 +23,8 @@ rusqlite = "0.32.1" rusqlite_migration = { version = "1.3.1", features = ["from-directory"] } lazy_static = "1.5.0" include_dir = "0.7.4" +async-trait = "0.1.83" +futures = "0.3.31" [dev-dependencies] cool_asserts = "2.0.3" diff --git a/visions/server/Taskfile.yml b/visions/server/Taskfile.yml index 8785126..82a446d 100644 --- a/visions/server/Taskfile.yml +++ b/visions/server/Taskfile.yml @@ -7,7 +7,7 @@ tasks: test: cmds: - - cargo watch -x test + - cargo watch -x 'test -- --nocapture' dev: cmds: diff --git a/visions/server/src/core.rs b/visions/server/src/core.rs index 1f7005a..0ecf2c1 100644 --- a/visions/server/src/core.rs +++ b/visions/server/src/core.rs @@ -1,13 +1,10 @@ use std::{ collections::HashMap, - io::Read, - path::PathBuf, sync::{Arc, RwLock}, }; use mime::Mime; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; -use urlencoding::decode; use uuid::Uuid; use crate::{ @@ -27,6 +24,7 @@ struct WebsocketClient { pub struct AppState { pub asset_store: Box, + pub db: Box, pub clients: HashMap, pub tabletop: Tabletop, @@ -36,12 +34,14 @@ pub struct AppState { pub struct Core(Arc>); impl Core { - pub fn new(assetdb: A) -> Self + pub fn new(assetdb: A, db: DB) -> Self where A: Assets + Sync + Send + 'static, + DB: Database + Sync + Send + 'static, { Self(Arc::new(RwLock::new(AppState { asset_store: Box::new(assetdb), + db: Box::new(db), clients: HashMap::new(), tabletop: Tabletop { background_color: DEFAULT_BACKGROUND_COLOR, @@ -135,11 +135,13 @@ impl Core { #[cfg(test)] mod test { + use std::path::PathBuf; + use super::*; use cool_asserts::assert_matches; - use crate::asset_db::mocks::MemoryAssets; + use crate::{asset_db::mocks::MemoryAssets, database::{DbConn, DiskDb}}; fn test_core() -> Core { let assets = MemoryAssets::new(vec![ @@ -169,7 +171,9 @@ mod test { String::from("abcdefg").into_bytes(), ), ]); - Core::new(assets) + let memory_db: Option = None; + let conn = DbConn::new(memory_db); + Core::new(assets, conn) } #[tokio::test] diff --git a/visions/server/src/database.rs b/visions/server/src/database.rs index 7de5572..a960ae6 100644 --- a/visions/server/src/database.rs +++ b/visions/server/src/database.rs @@ -1,15 +1,16 @@ use std::{ - path::PathBuf, - sync::mpsc::{channel, Receiver, Sender}, + path::{Path, PathBuf}, thread::JoinHandle, }; +use async_trait::async_trait; use include_dir::{include_dir, Dir}; use lazy_static::lazy_static; use rusqlite::Connection; use rusqlite_migration::Migrations; use serde::{Deserialize, Serialize}; use thiserror::Error; +use tokio::sync::{mpsc, oneshot}; use uuid::Uuid; static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations"); @@ -26,6 +27,25 @@ pub enum Error { #[error("Unexpected response for message")] MessageMismatch, + + #[error("No response to request")] + NoResponse +} + +#[derive(Debug)] +enum Request { + Charsheet(CharacterId), +} + +#[derive(Debug)] +struct DatabaseRequest { + tx: oneshot::Sender, + req: Request, +} + +#[derive(Debug)] +enum DatabaseResponse { + Charsheet(Option), } #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -60,8 +80,12 @@ pub struct CharsheetRow { data: serde_json::Value, } -pub trait Database { - async fn charsheet(&self, id: CharacterId) -> Result, Error>; +#[async_trait] +pub trait Database: Send + Sync { + async fn charsheet( + &mut self, + id: CharacterId, + ) -> Result, Error>; } pub struct DiskDb { @@ -69,7 +93,10 @@ pub struct DiskDb { } impl DiskDb { - pub fn new(path: Option) -> Result { + pub fn new

(path: Option

) -> Result + where + P: AsRef, + { let mut conn = match path { None => Connection::open(":memory:").expect("to create a memory connection"), Some(path) => Connection::open(path).expect("to create connection"), @@ -134,42 +161,66 @@ impl DiskDb { } } -/* -enum DatabaseRequest { - Charsheet(CharacterId), -} - -enum DatabaseResponse { - Charsheet(Option), -} - -pub struct DiskDb { - db_handle: JoinHandle<()>, - to_db: Sender, - from_db: Receiver, -} - -impl DiskDb { - pub fn new(path: PathBuf) -> DiskDb { - DiskDb { - db_handle, - to_db: interface_to_db_tx, - from_db: db_to_interface_rx, +async fn db_handler(db: DiskDb, mut requestor: mpsc::Receiver) { + println!("Starting db_handler"); + while let Some(DatabaseRequest{ tx, req }) = requestor.recv().await { + println!("Request received: {:?}", req); + match req { + Request::Charsheet(id) => { + let sheet = db.charsheet(id); + println!("sheet retrieved: {:?}", sheet); + match sheet { + Ok(sheet) => tx.send(DatabaseResponse::Charsheet(sheet)).unwrap(), + _ => unimplemented!(), + } + } } } + println!("ending db_handler"); +} + +pub struct DbConn { + conn: mpsc::Sender, + handle: tokio::task::JoinHandle<()>, +} + +impl DbConn { + pub fn new

(path: Option

) -> Self + where + P: AsRef, + { + let (tx, rx) = mpsc::channel(5); + let db = DiskDb::new(path).unwrap(); + + let handle = tokio::spawn(async move { db_handler(db, rx).await; }); + + Self { conn: tx, handle } + } } -impl Database for DiskDb { - async fn charsheet(&self, id: CharacterId) -> Result, Error> { - self.to_db(DatabaseRequest::Charsheet(id)).unwrap(); - match self.from_db.recv() { - Ok(DatabaseResponse::Charsheet(sheet)) => Ok(sheet), +#[async_trait] +impl Database for DbConn { + async fn charsheet( + &mut self, + id: CharacterId, + ) -> Result, Error> { + let (tx, rx) = oneshot::channel::(); + + let request = DatabaseRequest{ + tx, + req: Request::Charsheet(id), + }; + self.conn.send(request).await.unwrap(); + match rx.await { + Ok(DatabaseResponse::Charsheet(row)) => Ok(row), Ok(_) => Err(Error::MessageMismatch), - Err(_) => unimplemented!(), + Err(err) => { + println!("error: {:?}", err); + Err(Error::NoResponse) + } } } } -*/ #[cfg(test)] mod test { @@ -181,12 +232,23 @@ mod test { #[test] fn it_can_retrieve_a_charsheet() { - let db = DiskDb::new(None).unwrap(); + let no_path: Option = None; + let db = DiskDb::new(no_path).unwrap(); assert_matches!(db.charsheet(CharacterId::from("1")), Ok(None)); let js: serde_json::Value = serde_json::from_str(soren).unwrap(); - let soren_id = db.save_charsheet(None, "candela".to_owned(), js.clone()).unwrap(); + let soren_id = db + .save_charsheet(None, "candela".to_owned(), js.clone()) + .unwrap(); assert_matches!(db.charsheet(soren_id).unwrap(), Some(CharsheetRow{ data, .. }) => assert_eq!(js, data)); } + + #[tokio::test] + async fn it_can_retrieve_a_charsheet_through_conn() { + let memory_db: Option = None; + let mut conn = DbConn::new(memory_db); + + assert_matches!(conn.charsheet(CharacterId::from("1")).await, Ok(None)); + } } diff --git a/visions/server/src/main.rs b/visions/server/src/main.rs index 80c8ae1..115fe8e 100644 --- a/visions/server/src/main.rs +++ b/visions/server/src/main.rs @@ -6,7 +6,7 @@ use std::{ use asset_db::{AssetId, FsAssets}; use authdb::AuthError; -use database::{CharacterId, Database}; +use database::DbConn; use handlers::{ handle_available_images, handle_connect_websocket, handle_file, handle_register_client, handle_set_background_image, handle_unregister_client, RegisterRequest, @@ -100,9 +100,9 @@ async fn handle_rejection(err: warp::Rejection) -> Result Date: Sat, 30 Nov 2024 12:05:31 -0500 Subject: [PATCH 3/7] Switch all channels to async-std --- Cargo.lock | 2 ++ visions/server/Cargo.toml | 1 + visions/server/Taskfile.yml | 3 ++- visions/server/src/database.rs | 20 +++++++++++--------- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee0e392..1bcb68c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4284,6 +4284,8 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" name = "visions" version = "0.1.0" dependencies = [ + "async-std", + "async-trait", "authdb", "cool_asserts", "futures", diff --git a/visions/server/Cargo.toml b/visions/server/Cargo.toml index 5bc25d1..8dc0ed8 100644 --- a/visions/server/Cargo.toml +++ b/visions/server/Cargo.toml @@ -25,6 +25,7 @@ lazy_static = "1.5.0" include_dir = "0.7.4" async-trait = "0.1.83" futures = "0.3.31" +async-std = "1.13.0" [dev-dependencies] cool_asserts = "2.0.3" diff --git a/visions/server/Taskfile.yml b/visions/server/Taskfile.yml index 82a446d..d54c919 100644 --- a/visions/server/Taskfile.yml +++ b/visions/server/Taskfile.yml @@ -7,7 +7,8 @@ tasks: test: cmds: - - cargo watch -x 'test -- --nocapture' + # - cargo watch -x 'test -- --nocapture' + - cargo watch -x 'nextest run' dev: cmds: diff --git a/visions/server/src/database.rs b/visions/server/src/database.rs index a960ae6..2f0e9d9 100644 --- a/visions/server/src/database.rs +++ b/visions/server/src/database.rs @@ -10,7 +10,7 @@ use rusqlite::Connection; use rusqlite_migration::Migrations; use serde::{Deserialize, Serialize}; use thiserror::Error; -use tokio::sync::{mpsc, oneshot}; +use async_std::channel::{Sender, Receiver, bounded}; use uuid::Uuid; static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations"); @@ -39,7 +39,7 @@ enum Request { #[derive(Debug)] struct DatabaseRequest { - tx: oneshot::Sender, + tx: Sender, req: Request, } @@ -161,16 +161,18 @@ impl DiskDb { } } -async fn db_handler(db: DiskDb, mut requestor: mpsc::Receiver) { +async fn db_handler(db: DiskDb, mut requestor: Receiver) { println!("Starting db_handler"); - while let Some(DatabaseRequest{ tx, req }) = requestor.recv().await { + while let Ok(DatabaseRequest{ tx, req }) = requestor.recv().await { println!("Request received: {:?}", req); match req { Request::Charsheet(id) => { let sheet = db.charsheet(id); println!("sheet retrieved: {:?}", sheet); match sheet { - Ok(sheet) => tx.send(DatabaseResponse::Charsheet(sheet)).unwrap(), + Ok(sheet) => { + tx.send(DatabaseResponse::Charsheet(sheet)).await.unwrap(); + }, _ => unimplemented!(), } } @@ -180,7 +182,7 @@ async fn db_handler(db: DiskDb, mut requestor: mpsc::Receiver) } pub struct DbConn { - conn: mpsc::Sender, + conn: Sender, handle: tokio::task::JoinHandle<()>, } @@ -189,7 +191,7 @@ impl DbConn { where P: AsRef, { - let (tx, rx) = mpsc::channel(5); + let (tx, rx) = bounded::(5); let db = DiskDb::new(path).unwrap(); let handle = tokio::spawn(async move { db_handler(db, rx).await; }); @@ -204,14 +206,14 @@ impl Database for DbConn { &mut self, id: CharacterId, ) -> Result, Error> { - let (tx, rx) = oneshot::channel::(); + let (tx, rx) = bounded::(1); let request = DatabaseRequest{ tx, req: Request::Charsheet(id), }; self.conn.send(request).await.unwrap(); - match rx.await { + match rx.recv().await { Ok(DatabaseResponse::Charsheet(row)) => Ok(row), Ok(_) => Err(Error::MessageMismatch), Err(err) => { -- 2.44.1 From 970e957143e65a3fe46493aadfaffd5ddb3f0f6b Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sat, 30 Nov 2024 12:20:38 -0500 Subject: [PATCH 4/7] Prepopulate the database --- visions/server/src/database.rs | 38 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/visions/server/src/database.rs b/visions/server/src/database.rs index 2f0e9d9..07464ea 100644 --- a/visions/server/src/database.rs +++ b/visions/server/src/database.rs @@ -3,6 +3,7 @@ use std::{ thread::JoinHandle, }; +use async_std::channel::{bounded, Receiver, Sender}; use async_trait::async_trait; use include_dir::{include_dir, Dir}; use lazy_static::lazy_static; @@ -10,7 +11,6 @@ use rusqlite::Connection; use rusqlite_migration::Migrations; use serde::{Deserialize, Serialize}; use thiserror::Error; -use async_std::channel::{Sender, Receiver, bounded}; use uuid::Uuid; static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations"); @@ -29,7 +29,7 @@ pub enum Error { MessageMismatch, #[error("No response to request")] - NoResponse + NoResponse, } #[derive(Debug)] @@ -82,10 +82,7 @@ pub struct CharsheetRow { #[async_trait] pub trait Database: Send + Sync { - async fn charsheet( - &mut self, - id: CharacterId, - ) -> Result, Error>; + async fn charsheet(&mut self, id: CharacterId) -> Result, Error>; } pub struct DiskDb { @@ -102,6 +99,20 @@ impl DiskDb { Some(path) => Connection::open(path).expect("to create connection"), }; MIGRATIONS.to_latest(&mut conn).expect("to run migrations"); + + { + let mut stmt = conn.prepare("SELECT count(*) FROM charsheet").unwrap(); + let mut sheets = stmt.query([]).unwrap(); + if sheets.next().unwrap().unwrap().get::(0) == Ok(0) { + let char_id = CharacterId::new(); + let mut stmt = conn + .prepare("INSERT INTO charsheet VALUES (?, ?, ?)") + .unwrap(); + stmt.execute((char_id.as_str(), "Candela", r#"{ "type_": "Candela", "name": "Soren Jensen", "pronouns": "he/him", "circle": "Circle of the Bluest Sky", "style": "dapper gentleman", "catalyst": "a cursed book", "question": "What were the contents of that book?", "nerve": { "type_": "nerve", "drives": { "current": 1, "max": 2 }, "resistances": { "current": 0, "max": 3 }, "move": { "gilded": false, "score": 2 }, "strike": { "gilded": false, "score": 1 }, "control": { "gilded": true, "score": 0 } }, "cunning": { "type_": "cunning", "drives": { "current": 1, "max": 1 }, "resistances": { "current": 0, "max": 3 }, "sway": { "gilded": false, "score": 0 }, "read": { "gilded": false, "score": 0 }, "hide": { "gilded": false, "score": 0 } }, "intuition": { "type_": "intuition", "drives": { "current": 0, "max": 0 }, "resistances": { "current": 0, "max": 3 }, "survey": { "gilded": false, "score": 0 }, "focus": { "gilded": false, "score": 0 }, "sense": { "gilded": false, "score": 0 } }, "role": "Slink", "role_abilities": [ "Scout: If you have time to observe a location, you can spend 1 Intuition to ask a question: What do I notice here that others do not see? What in this place might be of use to us? What path should we follow?" ], "specialty": "Detective", "specialty_abilities": [ "Mind Palace: When you want to figure out how two clues might relate or what path they should point you towards, burn 1 Intution resistance. The GM will give you the information you have deduced." ] }"#)) + .unwrap(); + } + } + Ok(DiskDb { conn }) } @@ -163,7 +174,7 @@ impl DiskDb { async fn db_handler(db: DiskDb, mut requestor: Receiver) { println!("Starting db_handler"); - while let Ok(DatabaseRequest{ tx, req }) = requestor.recv().await { + while let Ok(DatabaseRequest { tx, req }) = requestor.recv().await { println!("Request received: {:?}", req); match req { Request::Charsheet(id) => { @@ -172,7 +183,7 @@ async fn db_handler(db: DiskDb, mut requestor: Receiver) { match sheet { Ok(sheet) => { tx.send(DatabaseResponse::Charsheet(sheet)).await.unwrap(); - }, + } _ => unimplemented!(), } } @@ -194,7 +205,9 @@ impl DbConn { let (tx, rx) = bounded::(5); let db = DiskDb::new(path).unwrap(); - let handle = tokio::spawn(async move { db_handler(db, rx).await; }); + let handle = tokio::spawn(async move { + db_handler(db, rx).await; + }); Self { conn: tx, handle } } @@ -202,13 +215,10 @@ impl DbConn { #[async_trait] impl Database for DbConn { - async fn charsheet( - &mut self, - id: CharacterId, - ) -> Result, Error> { + async fn charsheet(&mut self, id: CharacterId) -> Result, Error> { let (tx, rx) = bounded::(1); - let request = DatabaseRequest{ + let request = DatabaseRequest { tx, req: Request::Charsheet(id), }; -- 2.44.1 From 995390ae4bac5991d1e860be9a411b6084f504b3 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sat, 30 Nov 2024 15:24:57 -0500 Subject: [PATCH 5/7] Just make the entire core asynchronous --- visions/server/src/core.rs | 68 +++++++++++++++++++++------------- visions/server/src/database.rs | 4 +- visions/server/src/handlers.rs | 31 +++++++++++++--- visions/server/src/main.rs | 11 +++++- 4 files changed, 80 insertions(+), 34 deletions(-) diff --git a/visions/server/src/core.rs b/visions/server/src/core.rs index 0ecf2c1..c4e4631 100644 --- a/visions/server/src/core.rs +++ b/visions/server/src/core.rs @@ -1,14 +1,14 @@ -use std::{ - collections::HashMap, - sync::{Arc, RwLock}, -}; +use std::{collections::HashMap, sync::Arc}; +use async_std::sync::RwLock; use mime::Mime; use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use uuid::Uuid; use crate::{ - asset_db::{self, AssetId, Assets}, database::Database, types::{AppError, Message, Tabletop, RGB} + asset_db::{self, AssetId, Assets}, + database::{CharacterId, Database}, + types::{AppError, Message, Tabletop, RGB}, }; const DEFAULT_BACKGROUND_COLOR: RGB = RGB { @@ -50,8 +50,8 @@ impl Core { }))) } - pub fn register_client(&self) -> String { - let mut state = self.0.write().unwrap(); + pub async fn register_client(&self) -> String { + let mut state = self.0.write().await; let uuid = Uuid::new_v4().simple().to_string(); let client = WebsocketClient { sender: None }; @@ -60,13 +60,13 @@ impl Core { uuid } - pub fn unregister_client(&self, client_id: String) { - let mut state = self.0.write().unwrap(); + pub async fn unregister_client(&self, client_id: String) { + let mut state = self.0.write().await; let _ = state.clients.remove(&client_id); } - pub fn connect_client(&self, client_id: String) -> UnboundedReceiver { - let mut state = self.0.write().unwrap(); + pub async fn connect_client(&self, client_id: String) -> UnboundedReceiver { + let mut state = self.0.write().await; match state.clients.get_mut(&client_id) { Some(client) => { @@ -80,14 +80,14 @@ impl Core { } } - pub fn tabletop(&self) -> Tabletop { - self.0.read().unwrap().tabletop.clone() + pub async fn tabletop(&self) -> Tabletop { + self.0.read().await.tabletop.clone() } pub async fn get_asset(&self, asset_id: AssetId) -> Result<(Mime, Vec), AppError> { self.0 .read() - .unwrap() + .await .asset_store .get(asset_id.clone()) .map_err(|err| match err { @@ -97,24 +97,24 @@ impl Core { }) } - pub fn available_images(&self) -> Vec { + pub async fn available_images(&self) -> Vec { self.0 .read() - .unwrap() + .await .asset_store .assets() - .filter_map(|(asset_id, value)| { - match mime_guess::from_path(&value).first() { + .filter_map( + |(asset_id, value)| match mime_guess::from_path(&value).first() { Some(mime) if mime.type_() == mime::IMAGE => Some(asset_id.clone()), _ => None, - } - }) + }, + ) .collect() } - pub fn set_background_image(&self, asset: AssetId) -> Result<(), AppError> { + pub async fn set_background_image(&self, asset: AssetId) -> Result<(), AppError> { let tabletop = { - let mut state = self.0.write().unwrap(); + let mut state = self.0.write().await; state.tabletop.background_image = Some(asset.clone()); state.tabletop.clone() }; @@ -122,8 +122,23 @@ impl Core { Ok(()) } - pub fn publish(&self, message: Message) { - let state = self.0.read().unwrap(); + pub async fn get_charsheet( + &self, + id: CharacterId, + ) -> Result, AppError> { + Ok(self + .0 + .write() + .await + .db + .charsheet(id) + .await + .unwrap() + .map(|cr| cr.data)) + } + + pub async fn publish(&self, message: Message) { + let state = self.0.read().await; state.clients.values().for_each(|client| { if let Some(ref sender) = client.sender { @@ -141,7 +156,10 @@ mod test { use cool_asserts::assert_matches; - use crate::{asset_db::mocks::MemoryAssets, database::{DbConn, DiskDb}}; + use crate::{ + asset_db::mocks::MemoryAssets, + database::{DbConn, DiskDb}, + }; fn test_core() -> Core { let assets = MemoryAssets::new(vec![ diff --git a/visions/server/src/database.rs b/visions/server/src/database.rs index 07464ea..1c75ec3 100644 --- a/visions/server/src/database.rs +++ b/visions/server/src/database.rs @@ -77,7 +77,7 @@ impl From for CharacterId { pub struct CharsheetRow { id: String, gametype: String, - data: serde_json::Value, + pub data: serde_json::Value, } #[async_trait] @@ -172,7 +172,7 @@ impl DiskDb { } } -async fn db_handler(db: DiskDb, mut requestor: Receiver) { +async fn db_handler(db: DiskDb, requestor: Receiver) { println!("Starting db_handler"); while let Ok(DatabaseRequest { tx, req }) = requestor.recv().await { println!("Request received: {:?}", req); diff --git a/visions/server/src/handlers.rs b/visions/server/src/handlers.rs index 4597d5a..615b447 100644 --- a/visions/server/src/handlers.rs +++ b/visions/server/src/handlers.rs @@ -4,7 +4,7 @@ use futures::{SinkExt, StreamExt}; use serde::{Deserialize, Serialize}; use warp::{http::Response, http::StatusCode, reply::Reply, ws::Message}; -use crate::{asset_db::AssetId, core::Core, types::AppError}; +use crate::{asset_db::AssetId, core::Core, database::CharacterId, types::AppError}; /* pub async fn handle_auth( @@ -62,6 +62,7 @@ pub async fn handle_available_images(core: Core) -> impl Reply { handler(async move { let image_paths: Vec = core .available_images() + .await .into_iter() .map(|path| format!("{}", path.as_str())) .collect(); @@ -85,7 +86,7 @@ pub struct RegisterResponse { pub async fn handle_register_client(core: Core, _request: RegisterRequest) -> impl Reply { handler(async move { - let client_id = core.register_client(); + let client_id = core.register_client().await; Ok(Response::builder() .header("Access-Control-Allow-Origin", "*") @@ -105,7 +106,10 @@ pub async fn handle_unregister_client(core: Core, client_id: String) -> impl Rep handler(async move { core.unregister_client(client_id); - Ok(Response::builder().status(StatusCode::NO_CONTENT).body(vec![]).unwrap()) + Ok(Response::builder() + .status(StatusCode::NO_CONTENT) + .body(vec![]) + .unwrap()) }) .await } @@ -120,10 +124,10 @@ pub async fn handle_connect_websocket( let core = core.clone(); async move { let (mut ws_sender, _) = socket.split(); - let mut receiver = core.connect_client(client_id.clone()); + let mut receiver = core.connect_client(client_id.clone()).await; tokio::task::spawn(async move { - let tabletop = core.tabletop(); + let tabletop = core.tabletop().await; let _ = ws_sender .send(Message::text( serde_json::to_string(&crate::types::Message::UpdateTabletop(tabletop)) @@ -155,3 +159,20 @@ pub async fn handle_set_background_image(core: Core, image_name: String) -> impl }) .await } + +pub async fn handle_get_charsheet(core: Core, charid: String) -> impl Reply { + handler(async move { + let sheet = core.get_charsheet(CharacterId::from(charid)).await.unwrap(); + + match sheet { + Some(sheet) => Ok(Response::builder() + .body(serde_json::to_vec(&sheet).unwrap()) + .unwrap()), + None => Ok(Response::builder() + .status(StatusCode::NOT_FOUND) + .body(vec![]) + .unwrap()), + } + }) + .await +} diff --git a/visions/server/src/main.rs b/visions/server/src/main.rs index 115fe8e..d695fdc 100644 --- a/visions/server/src/main.rs +++ b/visions/server/src/main.rs @@ -8,8 +8,7 @@ use asset_db::{AssetId, FsAssets}; use authdb::AuthError; use database::DbConn; use handlers::{ - handle_available_images, handle_connect_websocket, handle_file, handle_register_client, - handle_set_background_image, handle_unregister_client, RegisterRequest, + handle_available_images, handle_connect_websocket, handle_file, handle_get_charsheet, handle_register_client, handle_set_background_image, handle_unregister_client, RegisterRequest }; use warp::{ // header, @@ -161,6 +160,13 @@ pub async fn main() { }) .with(log); + let route_get_charsheet = warp::path!("api" / "v1" / "charsheet" / String) + .and(warp::get()) + .then({ + let core = core.clone(); + move |charid| handle_get_charsheet(core.clone(), charid) + }); + let filter = route_register_client .or(route_unregister_client) .or(route_websocket) @@ -168,6 +174,7 @@ pub async fn main() { .or(route_available_images) .or(route_set_bg_image_options) .or(route_set_bg_image) + .or(route_get_charsheet) .recover(handle_rejection); let server = warp::serve(filter); -- 2.44.1 From d8ea2aac405824f2f907b1772fe1753b8f038fc4 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sat, 30 Nov 2024 18:43:20 -0500 Subject: [PATCH 6/7] Retrieve the charsheet from the database and render it in the UI --- visions/server/src/core.rs | 2 +- visions/server/src/handlers.rs | 4 +++- visions/ui/Taskfile.yml | 2 ++ visions/ui/package-lock.json | 2 +- visions/ui/src/App.tsx | 12 +++++++++++- visions/ui/src/client.ts | 6 ++++++ visions/ui/src/plugins/Candela/Charsheet.tsx | 4 +++- .../ui/src/plugins/Candela/CharsheetPanel.tsx | 4 +++- visions/ui/src/views/PlayerView/PlayerView.tsx | 16 ++++++++++++++-- visions/visions-types/Taskfile.yml | 1 + visions/visions-types/package-lock.json | 8 ++++---- visions/visions-types/package.json | 2 +- 12 files changed, 50 insertions(+), 13 deletions(-) diff --git a/visions/server/src/core.rs b/visions/server/src/core.rs index c4e4631..9bb4c9f 100644 --- a/visions/server/src/core.rs +++ b/visions/server/src/core.rs @@ -118,7 +118,7 @@ impl Core { state.tabletop.background_image = Some(asset.clone()); state.tabletop.clone() }; - self.publish(Message::UpdateTabletop(tabletop)); + self.publish(Message::UpdateTabletop(tabletop)).await; Ok(()) } diff --git a/visions/server/src/handlers.rs b/visions/server/src/handlers.rs index 615b447..1dbefec 100644 --- a/visions/server/src/handlers.rs +++ b/visions/server/src/handlers.rs @@ -148,7 +148,7 @@ pub async fn handle_connect_websocket( pub async fn handle_set_background_image(core: Core, image_name: String) -> impl Reply { handler(async move { - let _ = core.set_background_image(AssetId::from(image_name)); + let _ = core.set_background_image(AssetId::from(image_name)).await; Ok(Response::builder() .header("Access-Control-Allow-Origin", "*") @@ -166,6 +166,8 @@ pub async fn handle_get_charsheet(core: Core, charid: String) -> impl Reply { match sheet { Some(sheet) => Ok(Response::builder() + .header("Access-Control-Allow-Origin", "*") + .header("Content-Type", "application/json") .body(serde_json::to_vec(&sheet).unwrap()) .unwrap()), None => Ok(Response::builder() diff --git a/visions/ui/Taskfile.yml b/visions/ui/Taskfile.yml index 823ef51..172d8e6 100644 --- a/visions/ui/Taskfile.yml +++ b/visions/ui/Taskfile.yml @@ -3,5 +3,7 @@ version: '3' tasks: dev: cmds: + - cd ../visions-types && task build + - npm install - npm run start diff --git a/visions/ui/package-lock.json b/visions/ui/package-lock.json index aac5bfd..807f246 100644 --- a/visions/ui/package-lock.json +++ b/visions/ui/package-lock.json @@ -33,7 +33,7 @@ "version": "0.0.1", "license": "ISC", "dependencies": { - "typescript": "^5.6.3" + "typescript": "^5.7.2" } }, "node_modules/@adobe/css-tools": { diff --git a/visions/ui/src/App.tsx b/visions/ui/src/App.tsx index fb49c72..cd6db11 100644 --- a/visions/ui/src/App.tsx +++ b/visions/ui/src/App.tsx @@ -12,6 +12,16 @@ interface AppProps { client: Client; } +const CandelaCharsheet = ({ client }: { client: Client }) => { + let [sheet, setSheet] = useState(undefined); + useEffect( + () => { client.charsheet("db7a2585-5dcf-4909-8743-2741111f8b9a").then((c) => setSheet(c)); }, + [client, setSheet] + ); + + return sheet ? :

+} + const App = ({ client }: AppProps) => { console.log("rendering app"); const [websocketUrl, setWebsocketUrl] = useState(undefined); @@ -32,7 +42,7 @@ const App = ({ client }: AppProps) => { }, { path: "/candela", - element: + element: }, { path: "/design", diff --git a/visions/ui/src/client.ts b/visions/ui/src/client.ts index 486eec2..b5f43b9 100644 --- a/visions/ui/src/client.ts +++ b/visions/ui/src/client.ts @@ -44,4 +44,10 @@ export class Client { url.pathname = `/api/v1/tabletop/bg_image`; return fetch(url, { method: 'PUT', headers: [['Content-Type', 'application/json']], body: JSON.stringify(name) }); } + + async charsheet(id: string) { + const url = new URL(this.base); + url.pathname = `/api/v1/charsheet/${id}`; + return fetch(url).then((response) => response.json()); + } } diff --git a/visions/ui/src/plugins/Candela/Charsheet.tsx b/visions/ui/src/plugins/Candela/Charsheet.tsx index c6095f4..362a7df 100644 --- a/visions/ui/src/plugins/Candela/Charsheet.tsx +++ b/visions/ui/src/plugins/Candela/Charsheet.tsx @@ -88,7 +88,7 @@ const AbilitiesElement = ({ role, role_abilities, specialty, specialty_abilities ); } -const CharsheetElement_ = ({ sheet }: CharsheetProps) => { +export const CharsheetElement = ({ sheet }: CharsheetProps) => { return (
Candela Obscura
@@ -115,6 +115,7 @@ const CharsheetElement_ = ({ sheet }: CharsheetProps) => {
); } +/* export const CharsheetElement = () => { const sheet = { type_: 'Candela', @@ -160,3 +161,4 @@ export const CharsheetElement = () => { return } +*/ diff --git a/visions/ui/src/plugins/Candela/CharsheetPanel.tsx b/visions/ui/src/plugins/Candela/CharsheetPanel.tsx index ea3d82c..109479b 100644 --- a/visions/ui/src/plugins/Candela/CharsheetPanel.tsx +++ b/visions/ui/src/plugins/Candela/CharsheetPanel.tsx @@ -66,7 +66,7 @@ const ActionGroupElement = ({ group }: ActionGroupElementProps) => { } -const CharsheetPanelElement_ = ({ sheet }: CharsheetPanelProps) => { +export const CharsheetPanelElement = ({ sheet }: CharsheetPanelProps) => { return (

{sheet.name} ({sheet.pronouns})

@@ -88,6 +88,7 @@ const CharsheetPanelElement_ = ({ sheet }: CharsheetPanelProps) => {
); } +/* export const CharsheetPanelElement = () => { const sheet = { type_: 'Candela', @@ -133,3 +134,4 @@ export const CharsheetPanelElement = () => { return } +*/ diff --git a/visions/ui/src/views/PlayerView/PlayerView.tsx b/visions/ui/src/views/PlayerView/PlayerView.tsx index 586d46f..9f12c60 100644 --- a/visions/ui/src/views/PlayerView/PlayerView.tsx +++ b/visions/ui/src/views/PlayerView/PlayerView.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import './PlayerView.css'; import { WebsocketContext } from '../../components/WebsocketProvider'; import { Client } from '../../client'; @@ -12,13 +12,25 @@ interface PlayerViewProps { export const PlayerView = ({ client }: PlayerViewProps) => { const { tabletop } = useContext(WebsocketContext); + const [charsheet, setCharsheet] = useState(undefined); + + useEffect( + () => { + client.charsheet("db7a2585-5dcf-4909-8743-2741111f8b9a").then((c) => { + setCharsheet(c) + }); + }, + [client, setCharsheet] + ); + const backgroundColor = tabletop.backgroundColor; const tabletopColorStyle = `rgb(${backgroundColor.red}, ${backgroundColor.green}, ${backgroundColor.blue})`; const backgroundUrl = tabletop.backgroundImage ? client.imageUrl(tabletop.backgroundImage) : undefined; return (
-
+
+ {charsheet ? :
}
) } diff --git a/visions/visions-types/Taskfile.yml b/visions/visions-types/Taskfile.yml index 4d2d386..d082755 100644 --- a/visions/visions-types/Taskfile.yml +++ b/visions/visions-types/Taskfile.yml @@ -3,5 +3,6 @@ version: '3' tasks: build: cmds: + - npm install typescript - typeshare --lang typescript --output-file visions.ts ../server/src - npx tsc diff --git a/visions/visions-types/package-lock.json b/visions/visions-types/package-lock.json index f339d13..6e7699d 100644 --- a/visions/visions-types/package-lock.json +++ b/visions/visions-types/package-lock.json @@ -9,13 +9,13 @@ "version": "0.0.1", "license": "ISC", "dependencies": { - "typescript": "^5.6.3" + "typescript": "^5.7.2" } }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/visions/visions-types/package.json b/visions/visions-types/package.json index 034f812..25aa15c 100644 --- a/visions/visions-types/package.json +++ b/visions/visions-types/package.json @@ -9,6 +9,6 @@ "author": "", "license": "ISC", "dependencies": { - "typescript": "^5.6.3" + "typescript": "^5.7.2" } } -- 2.44.1 From 82c8f3f96e7840a9680ddbf4f6fd49c30877b1d2 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sat, 30 Nov 2024 18:55:51 -0500 Subject: [PATCH 7/7] Clean up the database schema --- visions/server/migrations/01-charsheet/up.sql | 13 +++++--- visions/server/src/database.rs | 32 ++++++++++++------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/visions/server/migrations/01-charsheet/up.sql b/visions/server/migrations/01-charsheet/up.sql index 575c5e4..7f538ec 100644 --- a/visions/server/migrations/01-charsheet/up.sql +++ b/visions/server/migrations/01-charsheet/up.sql @@ -1,9 +1,12 @@ -CREATE TABLE game( - uuid TEXT PRIMARY KEY +CREATE TABLE games( + uuid TEXT PRIMARY KEY, + name TEXT ); -CREATE TABLE charsheet( +CREATE TABLE characters( uuid TEXT PRIMARY KEY, - gametype TEXT NOT NULL, - data TEXT + game TEXT, + data TEXT, + + FOREIGN KEY(game) REFERENCES games(uuid) ); diff --git a/visions/server/src/database.rs b/visions/server/src/database.rs index 1c75ec3..f2a8b7c 100644 --- a/visions/server/src/database.rs +++ b/visions/server/src/database.rs @@ -89,6 +89,25 @@ pub struct DiskDb { conn: Connection, } +fn setup_test_database(conn: &Connection) { + let mut gamecount_stmt = conn.prepare("SELECT count(*) FROM games").unwrap(); + let mut count = gamecount_stmt.query([]).unwrap(); + if count.next().unwrap().unwrap().get::(0) == Ok(0) { + let game_id = format!("{}", Uuid::new_v4()); + let char_id = CharacterId::new(); + + let mut game_stmt = conn.prepare("INSERT INTO games VALUES (?, ?)").unwrap(); + game_stmt.execute((game_id.clone(), "Circle of Bluest Sky")); + + let mut sheet_stmt = conn + .prepare("INSERT INTO characters VALUES (?, ?, ?)") + .unwrap(); + + sheet_stmt.execute((char_id.as_str(), game_id, r#"{ "type_": "Candela", "name": "Soren Jensen", "pronouns": "he/him", "circle": "Circle of the Bluest Sky", "style": "dapper gentleman", "catalyst": "a cursed book", "question": "What were the contents of that book?", "nerve": { "type_": "nerve", "drives": { "current": 1, "max": 2 }, "resistances": { "current": 0, "max": 3 }, "move": { "gilded": false, "score": 2 }, "strike": { "gilded": false, "score": 1 }, "control": { "gilded": true, "score": 0 } }, "cunning": { "type_": "cunning", "drives": { "current": 1, "max": 1 }, "resistances": { "current": 0, "max": 3 }, "sway": { "gilded": false, "score": 0 }, "read": { "gilded": false, "score": 0 }, "hide": { "gilded": false, "score": 0 } }, "intuition": { "type_": "intuition", "drives": { "current": 0, "max": 0 }, "resistances": { "current": 0, "max": 3 }, "survey": { "gilded": false, "score": 0 }, "focus": { "gilded": false, "score": 0 }, "sense": { "gilded": false, "score": 0 } }, "role": "Slink", "role_abilities": [ "Scout: If you have time to observe a location, you can spend 1 Intuition to ask a question: What do I notice here that others do not see? What in this place might be of use to us? What path should we follow?" ], "specialty": "Detective", "specialty_abilities": [ "Mind Palace: When you want to figure out how two clues might relate or what path they should point you towards, burn 1 Intution resistance. The GM will give you the information you have deduced." ] }"#)) + .unwrap(); + } +} + impl DiskDb { pub fn new

(path: Option

) -> Result where @@ -100,18 +119,7 @@ impl DiskDb { }; MIGRATIONS.to_latest(&mut conn).expect("to run migrations"); - { - let mut stmt = conn.prepare("SELECT count(*) FROM charsheet").unwrap(); - let mut sheets = stmt.query([]).unwrap(); - if sheets.next().unwrap().unwrap().get::(0) == Ok(0) { - let char_id = CharacterId::new(); - let mut stmt = conn - .prepare("INSERT INTO charsheet VALUES (?, ?, ?)") - .unwrap(); - stmt.execute((char_id.as_str(), "Candela", r#"{ "type_": "Candela", "name": "Soren Jensen", "pronouns": "he/him", "circle": "Circle of the Bluest Sky", "style": "dapper gentleman", "catalyst": "a cursed book", "question": "What were the contents of that book?", "nerve": { "type_": "nerve", "drives": { "current": 1, "max": 2 }, "resistances": { "current": 0, "max": 3 }, "move": { "gilded": false, "score": 2 }, "strike": { "gilded": false, "score": 1 }, "control": { "gilded": true, "score": 0 } }, "cunning": { "type_": "cunning", "drives": { "current": 1, "max": 1 }, "resistances": { "current": 0, "max": 3 }, "sway": { "gilded": false, "score": 0 }, "read": { "gilded": false, "score": 0 }, "hide": { "gilded": false, "score": 0 } }, "intuition": { "type_": "intuition", "drives": { "current": 0, "max": 0 }, "resistances": { "current": 0, "max": 3 }, "survey": { "gilded": false, "score": 0 }, "focus": { "gilded": false, "score": 0 }, "sense": { "gilded": false, "score": 0 } }, "role": "Slink", "role_abilities": [ "Scout: If you have time to observe a location, you can spend 1 Intuition to ask a question: What do I notice here that others do not see? What in this place might be of use to us? What path should we follow?" ], "specialty": "Detective", "specialty_abilities": [ "Mind Palace: When you want to figure out how two clues might relate or what path they should point you towards, burn 1 Intution resistance. The GM will give you the information you have deduced." ] }"#)) - .unwrap(); - } - } + setup_test_database(&conn); Ok(DiskDb { conn }) } -- 2.44.1