Implement migrations, and switch from sqlx to rusqlite
This commit is contained in:
parent
7cdc3a2755
commit
fee7fe77f8
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -10,3 +10,4 @@ shimmering/assets/b30_background.*
|
||||||
target
|
target
|
||||||
backups
|
backups
|
||||||
dump.sql
|
dump.sql
|
||||||
|
schema.sql
|
||||||
|
|
686
Cargo.lock
generated
686
Cargo.lock
generated
|
@ -60,12 +60,6 @@ version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
|
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "allocator-api2"
|
|
||||||
version = "0.2.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "android-tzdata"
|
name = "android-tzdata"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -133,15 +127,6 @@ dependencies = [
|
||||||
"syn 2.0.66",
|
"syn 2.0.66",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atoi"
|
|
||||||
version = "2.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
@ -198,12 +183,6 @@ version = "0.22.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base64ct"
|
|
||||||
version = "1.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit_field"
|
name = "bit_field"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
@ -218,12 +197,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitstream-io"
|
name = "bitstream-io"
|
||||||
|
@ -315,13 +291,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.99"
|
version = "1.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
|
checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"shlex",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -361,21 +337,6 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "concurrent-queue"
|
|
||||||
version = "2.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "const-oid"
|
|
||||||
version = "0.9.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
@ -437,21 +398,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc"
|
|
||||||
version = "3.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
|
|
||||||
dependencies = [
|
|
||||||
"crc-catalog",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crc-catalog"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
|
@ -489,15 +435,6 @@ dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-queue"
|
|
||||||
version = "0.3.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.20"
|
version = "0.8.20"
|
||||||
|
@ -585,17 +522,6 @@ version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "der"
|
|
||||||
version = "0.7.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
|
|
||||||
dependencies = [
|
|
||||||
"const-oid",
|
|
||||||
"pem-rfc7468",
|
|
||||||
"zeroize",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deranged"
|
name = "deranged"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
|
@ -624,9 +550,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"const-oid",
|
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
"subtle",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -659,12 +583,6 @@ dependencies = [
|
||||||
"libloading",
|
"libloading",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dotenvy"
|
|
||||||
version = "0.15.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dwrote"
|
name = "dwrote"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
@ -682,9 +600,6 @@ name = "either"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
|
@ -720,28 +635,6 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "etcetera"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"home",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "event-listener"
|
|
||||||
version = "5.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
|
|
||||||
dependencies = [
|
|
||||||
"concurrent-queue",
|
|
||||||
"parking",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "exr"
|
name = "exr"
|
||||||
version = "1.72.0"
|
version = "1.72.0"
|
||||||
|
@ -758,6 +651,18 @@ dependencies = [
|
||||||
"zune-inflate",
|
"zune-inflate",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-iterator"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fallible-streaming-iterator"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -795,9 +700,7 @@ version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"spin",
|
||||||
"futures-sink",
|
|
||||||
"spin 0.9.8",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -812,7 +715,7 @@ version = "0.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2845a73bbd781e691ab7c2a028c579727cd254942e8ced57ff73e0eafd60de87"
|
checksum = "2845a73bbd781e691ab7c2a028c579727cd254942e8ced57ff73e0eafd60de87"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
|
@ -873,7 +776,7 @@ version = "0.36.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5442dee36ca09604133580dc0553780e867936bb3cbef3275859e889026d2b17"
|
checksum = "5442dee36ca09604133580dc0553780e867936bb3cbef3275859e889026d2b17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"freetype-sys",
|
"freetype-sys",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
@ -919,28 +822,6 @@ version = "0.3.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-executor"
|
|
||||||
version = "0.3.30"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-intrusive"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"lock_api",
|
|
||||||
"parking_lot",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.30"
|
version = "0.3.30"
|
||||||
|
@ -1088,7 +969,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"allocator-api2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1112,39 +992,6 @@ version = "0.3.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hex"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hkdf"
|
|
||||||
version = "0.12.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
|
|
||||||
dependencies = [
|
|
||||||
"hmac",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hmac"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
|
||||||
dependencies = [
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "home"
|
|
||||||
version = "0.5.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.12"
|
version = "0.2.12"
|
||||||
|
@ -1348,6 +1195,25 @@ version = "1.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
|
checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
|
||||||
|
dependencies = [
|
||||||
|
"include_dir_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "include_dir_macros"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.2.6"
|
version = "2.2.6"
|
||||||
|
@ -1419,9 +1285,6 @@ name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
dependencies = [
|
|
||||||
"spin 0.5.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lebe"
|
name = "lebe"
|
||||||
|
@ -1468,15 +1331,15 @@ version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libsqlite3-sys"
|
name = "libsqlite3-sys"
|
||||||
version = "0.28.0"
|
version = "0.30.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
|
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
|
@ -1534,16 +1397,6 @@ dependencies = [
|
||||||
"rayon",
|
"rayon",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "md-5"
|
|
||||||
version = "0.10.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.4"
|
version = "2.7.4"
|
||||||
|
@ -1669,23 +1522,6 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-bigint-dig"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
"lazy_static",
|
|
||||||
"libm",
|
|
||||||
"num-integer",
|
|
||||||
"num-iter",
|
|
||||||
"num-traits",
|
|
||||||
"rand",
|
|
||||||
"smallvec",
|
|
||||||
"zeroize",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-complex"
|
name = "num-complex"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
|
@ -1799,12 +1635,6 @@ dependencies = [
|
||||||
"ttf-parser 0.24.1",
|
"ttf-parser 0.24.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
|
@ -1823,7 +1653,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.2",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.52.5",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
@ -1853,15 +1683,6 @@ dependencies = [
|
||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pem-rfc7468"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
|
|
||||||
dependencies = [
|
|
||||||
"base64ct",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
|
@ -1880,27 +1701,6 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pkcs1"
|
|
||||||
version = "0.7.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
|
|
||||||
dependencies = [
|
|
||||||
"der",
|
|
||||||
"pkcs8",
|
|
||||||
"spki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pkcs8"
|
|
||||||
version = "0.10.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
|
||||||
dependencies = [
|
|
||||||
"der",
|
|
||||||
"spki",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.30"
|
version = "0.3.30"
|
||||||
|
@ -2037,7 +1837,7 @@ version = "0.9.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
|
checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"memchr",
|
"memchr",
|
||||||
"unicase",
|
"unicase",
|
||||||
]
|
]
|
||||||
|
@ -2066,6 +1866,28 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r2d2"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"parking_lot",
|
||||||
|
"scheduled-thread-pool",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "r2d2_sqlite"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb14dba8247a6a15b7fdbc7d389e2e6f03ee9f184f87117706d509c092dfe846"
|
||||||
|
dependencies = [
|
||||||
|
"r2d2",
|
||||||
|
"rusqlite",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
|
@ -2182,22 +2004,13 @@ dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2303,29 +2116,35 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"libc",
|
"libc",
|
||||||
"spin 0.9.8",
|
"spin",
|
||||||
"untrusted",
|
"untrusted",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa"
|
name = "rusqlite"
|
||||||
version = "0.9.6"
|
version = "0.32.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
|
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid",
|
"bitflags 2.6.0",
|
||||||
"digest",
|
"chrono",
|
||||||
"num-bigint-dig",
|
"fallible-iterator",
|
||||||
"num-integer",
|
"fallible-streaming-iterator",
|
||||||
"num-traits",
|
"hashlink",
|
||||||
"pkcs1",
|
"libsqlite3-sys",
|
||||||
"pkcs8",
|
"smallvec",
|
||||||
"rand_core",
|
]
|
||||||
"signature",
|
|
||||||
"spki",
|
[[package]]
|
||||||
"subtle",
|
name = "rusqlite_migration"
|
||||||
"zeroize",
|
version = "1.3.0-alpha-without-tokio.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7756e1e9f1fb9574c499e66d9e3272e0440fc0ed109f425036e2a6e95e2ad818"
|
||||||
|
dependencies = [
|
||||||
|
"include_dir",
|
||||||
|
"log",
|
||||||
|
"rusqlite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2349,7 +2168,7 @@ version = "0.38.34"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
|
@ -2442,6 +2261,15 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scheduled-thread-pool"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
|
||||||
|
dependencies = [
|
||||||
|
"parking_lot",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -2547,7 +2375,7 @@ dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.6.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
|
@ -2582,17 +2410,6 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha2"
|
|
||||||
version = "0.10.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cpufeatures",
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shimmeringmoon"
|
name = "shimmeringmoon"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -2602,22 +2419,22 @@ dependencies = [
|
||||||
"hypertesseract",
|
"hypertesseract",
|
||||||
"image 0.25.2",
|
"image 0.25.2",
|
||||||
"imageproc",
|
"imageproc",
|
||||||
|
"include_dir",
|
||||||
"num",
|
"num",
|
||||||
"plotters",
|
"plotters",
|
||||||
"poise",
|
"poise",
|
||||||
"sqlx",
|
"r2d2",
|
||||||
|
"r2d2_sqlite",
|
||||||
|
"rusqlite",
|
||||||
|
"rusqlite_migration",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signature"
|
name = "shlex"
|
||||||
version = "2.2.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
dependencies = [
|
|
||||||
"digest",
|
|
||||||
"rand_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simba"
|
name = "simba"
|
||||||
|
@ -2676,9 +2493,6 @@ name = "smallvec"
|
||||||
version = "1.13.2"
|
version = "1.13.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
|
@ -2690,12 +2504,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spin"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
|
@ -2705,235 +2513,6 @@ dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spki"
|
|
||||||
version = "0.7.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
|
||||||
dependencies = [
|
|
||||||
"base64ct",
|
|
||||||
"der",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sqlformat"
|
|
||||||
version = "0.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f"
|
|
||||||
dependencies = [
|
|
||||||
"nom",
|
|
||||||
"unicode_categories",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sqlx"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "27144619c6e5802f1380337a209d2ac1c431002dd74c6e60aebff3c506dc4f0c"
|
|
||||||
dependencies = [
|
|
||||||
"sqlx-core",
|
|
||||||
"sqlx-macros",
|
|
||||||
"sqlx-mysql",
|
|
||||||
"sqlx-postgres",
|
|
||||||
"sqlx-sqlite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sqlx-core"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a999083c1af5b5d6c071d34a708a19ba3e02106ad82ef7bbd69f5e48266b613b"
|
|
||||||
dependencies = [
|
|
||||||
"atoi",
|
|
||||||
"byteorder",
|
|
||||||
"bytes",
|
|
||||||
"chrono",
|
|
||||||
"crc",
|
|
||||||
"crossbeam-queue",
|
|
||||||
"either",
|
|
||||||
"event-listener",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-intrusive",
|
|
||||||
"futures-io",
|
|
||||||
"futures-util",
|
|
||||||
"hashbrown",
|
|
||||||
"hashlink",
|
|
||||||
"hex",
|
|
||||||
"indexmap",
|
|
||||||
"log",
|
|
||||||
"memchr",
|
|
||||||
"once_cell",
|
|
||||||
"paste",
|
|
||||||
"percent-encoding",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sha2",
|
|
||||||
"smallvec",
|
|
||||||
"sqlformat",
|
|
||||||
"thiserror",
|
|
||||||
"tokio",
|
|
||||||
"tokio-stream",
|
|
||||||
"tracing",
|
|
||||||
"url",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sqlx-macros"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a23217eb7d86c584b8cbe0337b9eacf12ab76fe7673c513141ec42565698bb88"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"sqlx-core",
|
|
||||||
"sqlx-macros-core",
|
|
||||||
"syn 2.0.66",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sqlx-macros-core"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a099220ae541c5db479c6424bdf1b200987934033c2584f79a0e1693601e776"
|
|
||||||
dependencies = [
|
|
||||||
"dotenvy",
|
|
||||||
"either",
|
|
||||||
"heck",
|
|
||||||
"hex",
|
|
||||||
"once_cell",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sha2",
|
|
||||||
"sqlx-core",
|
|
||||||
"sqlx-mysql",
|
|
||||||
"sqlx-postgres",
|
|
||||||
"sqlx-sqlite",
|
|
||||||
"syn 2.0.66",
|
|
||||||
"tempfile",
|
|
||||||
"tokio",
|
|
||||||
"url",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sqlx-mysql"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5afe4c38a9b417b6a9a5eeffe7235d0a106716495536e7727d1c7f4b1ff3eba6"
|
|
||||||
dependencies = [
|
|
||||||
"atoi",
|
|
||||||
"base64 0.22.1",
|
|
||||||
"bitflags 2.5.0",
|
|
||||||
"byteorder",
|
|
||||||
"bytes",
|
|
||||||
"chrono",
|
|
||||||
"crc",
|
|
||||||
"digest",
|
|
||||||
"dotenvy",
|
|
||||||
"either",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-io",
|
|
||||||
"futures-util",
|
|
||||||
"generic-array",
|
|
||||||
"hex",
|
|
||||||
"hkdf",
|
|
||||||
"hmac",
|
|
||||||
"itoa",
|
|
||||||
"log",
|
|
||||||
"md-5",
|
|
||||||
"memchr",
|
|
||||||
"once_cell",
|
|
||||||
"percent-encoding",
|
|
||||||
"rand",
|
|
||||||
"rsa",
|
|
||||||
"serde",
|
|
||||||
"sha1",
|
|
||||||
"sha2",
|
|
||||||
"smallvec",
|
|
||||||
"sqlx-core",
|
|
||||||
"stringprep",
|
|
||||||
"thiserror",
|
|
||||||
"tracing",
|
|
||||||
"whoami",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sqlx-postgres"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b1dbb157e65f10dbe01f729339c06d239120221c9ad9fa0ba8408c4cc18ecf21"
|
|
||||||
dependencies = [
|
|
||||||
"atoi",
|
|
||||||
"base64 0.22.1",
|
|
||||||
"bitflags 2.5.0",
|
|
||||||
"byteorder",
|
|
||||||
"chrono",
|
|
||||||
"crc",
|
|
||||||
"dotenvy",
|
|
||||||
"etcetera",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-io",
|
|
||||||
"futures-util",
|
|
||||||
"hex",
|
|
||||||
"hkdf",
|
|
||||||
"hmac",
|
|
||||||
"home",
|
|
||||||
"itoa",
|
|
||||||
"log",
|
|
||||||
"md-5",
|
|
||||||
"memchr",
|
|
||||||
"once_cell",
|
|
||||||
"rand",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sha2",
|
|
||||||
"smallvec",
|
|
||||||
"sqlx-core",
|
|
||||||
"stringprep",
|
|
||||||
"thiserror",
|
|
||||||
"tracing",
|
|
||||||
"whoami",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sqlx-sqlite"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9b2cdd83c008a622d94499c0006d8ee5f821f36c89b7d625c900e5dc30b5c5ee"
|
|
||||||
dependencies = [
|
|
||||||
"atoi",
|
|
||||||
"chrono",
|
|
||||||
"flume",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-executor",
|
|
||||||
"futures-intrusive",
|
|
||||||
"futures-util",
|
|
||||||
"libsqlite3-sys",
|
|
||||||
"log",
|
|
||||||
"percent-encoding",
|
|
||||||
"serde",
|
|
||||||
"serde_urlencoded",
|
|
||||||
"sqlx-core",
|
|
||||||
"tracing",
|
|
||||||
"url",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "stringprep"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-bidi",
|
|
||||||
"unicode-normalization",
|
|
||||||
"unicode-properties",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
@ -3176,17 +2755,6 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-stream"
|
|
||||||
version = "0.1.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-tungstenite"
|
name = "tokio-tungstenite"
|
||||||
version = "0.21.0"
|
version = "0.21.0"
|
||||||
|
@ -3404,18 +2972,6 @@ dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-properties"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode_categories"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -3440,6 +2996,16 @@ version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "v_frame"
|
name = "v_frame"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
@ -3494,12 +3060,6 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasite"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.92"
|
version = "0.2.92"
|
||||||
|
@ -3610,16 +3170,6 @@ version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "whoami"
|
|
||||||
version = "1.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9"
|
|
||||||
dependencies = [
|
|
||||||
"redox_syscall 0.4.1",
|
|
||||||
"wasite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wide"
|
name = "wide"
|
||||||
version = "0.7.26"
|
version = "0.7.26"
|
||||||
|
|
|
@ -10,10 +10,14 @@ image = "0.25.2"
|
||||||
num = "0.4.3"
|
num = "0.4.3"
|
||||||
plotters = { git="https://github.com/starlitcanopy/plotters.git", rev="986cd959362a2dbec8d1b25670fd083b904d7b8c", features=["bitmap_backend"] }
|
plotters = { git="https://github.com/starlitcanopy/plotters.git", rev="986cd959362a2dbec8d1b25670fd083b904d7b8c", features=["bitmap_backend"] }
|
||||||
poise = "0.6.1"
|
poise = "0.6.1"
|
||||||
sqlx = { version = "0.8.0", features = ["sqlite", "runtime-tokio", "chrono"] }
|
|
||||||
hypertesseract = { features=["image"], git="https://github.com/BlueGhostGH/hypertesseract.git", rev="4e05063" }
|
hypertesseract = { features=["image"], git="https://github.com/BlueGhostGH/hypertesseract.git", rev="4e05063" }
|
||||||
tokio = {version="1.38.0", features=["rt-multi-thread"]}
|
tokio = {version="1.38.0", features=["rt-multi-thread"]}
|
||||||
imageproc = "0.25.0"
|
imageproc = "0.25.0"
|
||||||
|
rusqlite = { version = "0.32.1", features = ["bundled", "chrono"] }
|
||||||
|
r2d2_sqlite = "0.25.0"
|
||||||
|
r2d2 = "0.8.10"
|
||||||
|
rusqlite_migration = {version="1.3.0-alpha-without-tokio.1", features = ["from-directory"]}
|
||||||
|
include_dir = "0.7.4"
|
||||||
|
|
||||||
[profile.dev.package."*"]
|
[profile.dev.package."*"]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
36
migrations/01-create-users-table/up.sql
Normal file
36
migrations/01-create-users-table/up.sql
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
-- {{{ users
|
||||||
|
create table IF NOT EXISTS users (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
discord_id TEXT UNIQUE NOT NULL,
|
||||||
|
is_pookie BOOL NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
-- }}}
|
||||||
|
-- {{{ plays
|
||||||
|
CREATE TABLE IF NOT EXISTS plays (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
chart_id INTEGER NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
discord_attachment_id TEXT,
|
||||||
|
|
||||||
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
max_recall INTEGER,
|
||||||
|
far_notes INTEGER,
|
||||||
|
|
||||||
|
FOREIGN KEY (chart_id) REFERENCES charts(id),
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
);
|
||||||
|
-- }}}
|
||||||
|
-- {{{ scores
|
||||||
|
CREATE TABLE IF NOT EXISTS scores (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
play_id INTEGER NOT NULL,
|
||||||
|
|
||||||
|
score INTEGER NOT NULL,
|
||||||
|
creation_ptt INTEGER,
|
||||||
|
scoring_system TEXT NOT NULL CHECK (scoring_system IN ('standard', 'sdf', 'ex')),
|
||||||
|
|
||||||
|
FOREIGN KEY (play_id) REFERENCES plays(id),
|
||||||
|
UNIQUE(play_id, scoring_system)
|
||||||
|
)
|
||||||
|
-- }}}
|
29
migrations/02-create-charts-and-songs/up.sql
Normal file
29
migrations/02-create-charts-and-songs/up.sql
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
-- {{{ songs
|
||||||
|
CREATE TABLE IF NOT EXISTS songs (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
artist TEXT NOT NULL,
|
||||||
|
side TEXT NOT NULL CHECK (side IN ('light', 'conflict', 'silent')),
|
||||||
|
bpm TEXT NOT NULL,
|
||||||
|
pack TEXT,
|
||||||
|
|
||||||
|
UNIQUE(title, artist)
|
||||||
|
);
|
||||||
|
-- }}}
|
||||||
|
-- {{{ charts
|
||||||
|
CREATE TABLE IF NOT EXISTS charts (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
song_id INTEGER NOT NULL,
|
||||||
|
note_design TEXT,
|
||||||
|
shorthand TEXT,
|
||||||
|
|
||||||
|
difficulty TEXT NOT NULL CHECK (difficulty IN ('PST','PRS','FTR','ETR','BYD')),
|
||||||
|
level TEXT NOT NULL,
|
||||||
|
|
||||||
|
note_count INTEGER NOT NULL,
|
||||||
|
chart_constant INTEGER NOT NULL,
|
||||||
|
|
||||||
|
FOREIGN KEY (song_id) REFERENCES songs(id),
|
||||||
|
UNIQUE(song_id, difficulty)
|
||||||
|
);
|
||||||
|
-- }}}
|
29
migrations/03-create-plays-and-scores/up.sql
Normal file
29
migrations/03-create-plays-and-scores/up.sql
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
-- {{{ plays
|
||||||
|
CREATE TABLE IF NOT EXISTS plays (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
chart_id INTEGER NOT NULL,
|
||||||
|
user_id INTEGER NOT NULL,
|
||||||
|
discord_attachment_id TEXT,
|
||||||
|
|
||||||
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
max_recall INTEGER,
|
||||||
|
far_notes INTEGER,
|
||||||
|
|
||||||
|
FOREIGN KEY (chart_id) REFERENCES charts(id),
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||||
|
);
|
||||||
|
-- }}}
|
||||||
|
-- {{{ scores
|
||||||
|
CREATE TABLE IF NOT EXISTS scores (
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY,
|
||||||
|
play_id INTEGER NOT NULL,
|
||||||
|
|
||||||
|
score INTEGER NOT NULL,
|
||||||
|
creation_ptt INTEGER,
|
||||||
|
scoring_system TEXT NOT NULL CHECK (scoring_system IN ('standard', 'sdf', 'ex')),
|
||||||
|
|
||||||
|
FOREIGN KEY (play_id) REFERENCES plays(id),
|
||||||
|
UNIQUE(play_id, scoring_system)
|
||||||
|
)
|
||||||
|
-- }}}
|
67
schema.sql
67
schema.sql
|
@ -1,67 +0,0 @@
|
||||||
# {{{ users
|
|
||||||
create table IF NOT EXISTS users (
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
|
||||||
discord_id TEXT UNIQUE NOT NULL,
|
|
||||||
is_pookie BOOL NOT NULL DEFAULT 0
|
|
||||||
);
|
|
||||||
# }}}
|
|
||||||
# {{{ songs
|
|
||||||
CREATE TABLE IF NOT EXISTS songs (
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
|
||||||
title TEXT NOT NULL,
|
|
||||||
artist TEXT NOT NULL,
|
|
||||||
side TEXT NOT NULL CHECK (side IN ('light', 'conflict', 'silent')),
|
|
||||||
bpm TEXT NOT NULL,
|
|
||||||
pack TEXT,
|
|
||||||
|
|
||||||
UNIQUE(title, artist)
|
|
||||||
);
|
|
||||||
# }}}
|
|
||||||
# {{{ charts
|
|
||||||
CREATE TABLE IF NOT EXISTS charts (
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
|
||||||
song_id INTEGER NOT NULL,
|
|
||||||
note_design TEXT,
|
|
||||||
shorthand TEXT,
|
|
||||||
|
|
||||||
difficulty TEXT NOT NULL CHECK (difficulty IN ('PST','PRS','FTR','ETR','BYD')),
|
|
||||||
level TEXT NOT NULL,
|
|
||||||
|
|
||||||
note_count INTEGER NOT NULL,
|
|
||||||
chart_constant INTEGER NOT NULL,
|
|
||||||
|
|
||||||
FOREIGN KEY (song_id) REFERENCES songs(id),
|
|
||||||
UNIQUE(song_id, difficulty)
|
|
||||||
);
|
|
||||||
# }}}
|
|
||||||
# {{{ plays
|
|
||||||
CREATE TABLE IF NOT EXISTS plays (
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
|
||||||
chart_id INTEGER NOT NULL,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
discord_attachment_id TEXT,
|
|
||||||
|
|
||||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
|
|
||||||
max_recall INTEGER,
|
|
||||||
far_notes INTEGER,
|
|
||||||
|
|
||||||
FOREIGN KEY (chart_id) REFERENCES charts(id),
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
||||||
);
|
|
||||||
# }}}
|
|
||||||
# {{{ scores
|
|
||||||
CREATE TABLE IF NOT EXISTS scores (
|
|
||||||
id INTEGER NOT NULL PRIMARY KEY,
|
|
||||||
play_id INTEGER NOT NULL,
|
|
||||||
|
|
||||||
score INTEGER NOT NULL,
|
|
||||||
creation_ptt INTEGER,
|
|
||||||
scoring_system NOT NULL CHECK (scoring_system IN ('standard', 'sdf', 'ex')),
|
|
||||||
|
|
||||||
FOREIGN KEY (play_id) REFERENCES plays(id),
|
|
||||||
UNIQUE(play_id, scoring_system)
|
|
||||||
)
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
insert into users(discord_id) values (385759924917108740);
|
|
|
@ -1,5 +1,4 @@
|
||||||
use image::RgbaImage;
|
use image::RgbaImage;
|
||||||
use sqlx::query;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::get_data_dir,
|
assets::get_data_dir,
|
||||||
|
@ -122,16 +121,8 @@ impl GoalStats {
|
||||||
user: &User,
|
user: &User,
|
||||||
scoring_system: ScoringSystem,
|
scoring_system: ScoringSystem,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let plays = get_best_plays(
|
let plays = get_best_plays(ctx, user.id, scoring_system, 0, usize::MAX, None)??;
|
||||||
&ctx.db,
|
let conn = ctx.db.get()?;
|
||||||
&ctx.song_cache,
|
|
||||||
user.id,
|
|
||||||
scoring_system,
|
|
||||||
0,
|
|
||||||
usize::MAX,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
// {{{ PM count
|
// {{{ PM count
|
||||||
let pm_count = plays
|
let pm_count = plays
|
||||||
|
@ -142,30 +133,31 @@ impl GoalStats {
|
||||||
.count();
|
.count();
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Play count
|
// {{{ Play count
|
||||||
let play_count = query!(
|
let play_count = conn
|
||||||
"SELECT count() as count FROM plays WHERE user_id=?",
|
.prepare_cached("SELECT count() as count FROM plays WHERE user_id=?")?
|
||||||
user.id
|
.query_row([user.id], |row| row.get(0))?;
|
||||||
)
|
|
||||||
.fetch_one(&ctx.db)
|
|
||||||
.await?
|
|
||||||
.count as usize;
|
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Peak ptt
|
// {{{ Peak ptt
|
||||||
let peak_ptt = query!(
|
let peak_ptt = conn
|
||||||
"
|
.prepare_cached(
|
||||||
|
"
|
||||||
SELECT s.creation_ptt
|
SELECT s.creation_ptt
|
||||||
FROM plays p
|
FROM plays p
|
||||||
JOIN scores s ON s.play_id = p.id
|
JOIN scores s ON s.play_id = p.id
|
||||||
WHERE user_id = ?
|
WHERE user_id = ?
|
||||||
AND scoring_system = ?
|
AND scoring_system = ?
|
||||||
|
ORDER BY s.creation_ptt DESC
|
||||||
|
LIMIT 1
|
||||||
",
|
",
|
||||||
user.id,
|
)?
|
||||||
ScoringSystem::SCORING_SYSTEM_DB_STRINGS[scoring_system.to_index()]
|
.query_row(
|
||||||
)
|
(
|
||||||
.fetch_one(&ctx.db)
|
user.id,
|
||||||
.await?
|
ScoringSystem::SCORING_SYSTEM_DB_STRINGS[scoring_system.to_index()],
|
||||||
.creation_ptt
|
),
|
||||||
.ok_or_else(|| "No ptt history data found")? as u32;
|
|row| row.get(0),
|
||||||
|
)
|
||||||
|
.map_err(|_| "No ptt history data found")?;
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Peak PM relay
|
// {{{ Peak PM relay
|
||||||
let peak_pm_relay = {
|
let peak_pm_relay = {
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use std::{fmt::Display, num::NonZeroU16, path::PathBuf};
|
use std::{fmt::Display, num::NonZeroU16, path::PathBuf};
|
||||||
|
|
||||||
use image::{ImageBuffer, Rgb};
|
use image::{ImageBuffer, Rgb};
|
||||||
use sqlx::SqlitePool;
|
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ValueRef};
|
||||||
|
|
||||||
use crate::{bitmap::Color, context::Error};
|
use crate::{
|
||||||
|
bitmap::Color,
|
||||||
|
context::{DbConnection, Error},
|
||||||
|
};
|
||||||
|
|
||||||
// {{{ Difficuly
|
// {{{ Difficuly
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sqlx::Type)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum Difficulty {
|
pub enum Difficulty {
|
||||||
PST,
|
PST,
|
||||||
PRS,
|
PRS,
|
||||||
|
@ -29,17 +32,19 @@ impl Difficulty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for Difficulty {
|
impl FromSql for Difficulty {
|
||||||
type Error = String;
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
|
let str: String = rusqlite::types::FromSql::column_result(value)?;
|
||||||
|
|
||||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
|
||||||
for (i, s) in Self::DIFFICULTY_SHORTHANDS.iter().enumerate() {
|
for (i, s) in Self::DIFFICULTY_SHORTHANDS.iter().enumerate() {
|
||||||
if value == **s {
|
if str == **s {
|
||||||
return Ok(Self::DIFFICULTIES[i]);
|
return Ok(Self::DIFFICULTIES[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(format!("Cannot convert {} to difficulty", value))
|
FromSqlResult::Err(FromSqlError::Other(
|
||||||
|
format!("Cannot convert {} to difficulty", str).into(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,17 +125,19 @@ impl Display for Level {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for Level {
|
impl FromSql for Level {
|
||||||
type Error = String;
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
|
let str: String = rusqlite::types::FromSql::column_result(value)?;
|
||||||
|
|
||||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
|
||||||
for (i, s) in Self::LEVEL_STRINGS.iter().enumerate() {
|
for (i, s) in Self::LEVEL_STRINGS.iter().enumerate() {
|
||||||
if value == **s {
|
if str == **s {
|
||||||
return Ok(Self::LEVELS[i]);
|
return Ok(Self::LEVELS[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(format!("Cannot convert {} to a level", value))
|
FromSqlResult::Err(FromSqlError::Other(
|
||||||
|
format!("Cannot convert {} to level", str).into(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -152,17 +159,19 @@ impl Side {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for Side {
|
impl FromSql for Side {
|
||||||
type Error = String;
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
|
let str: String = rusqlite::types::FromSql::column_result(value)?;
|
||||||
|
|
||||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
|
||||||
for (i, s) in Self::SIDE_STRINGS.iter().enumerate() {
|
for (i, s) in Self::SIDE_STRINGS.iter().enumerate() {
|
||||||
if value == **s {
|
if str == **s {
|
||||||
return Ok(Self::SIDES[i]);
|
return Ok(Self::SIDES[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(format!("Cannot convert {} to difficulty", value))
|
FromSqlResult::Err(FromSqlError::Other(
|
||||||
|
format!("Cannot convert {} to side", str).into(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -304,44 +313,53 @@ impl SongCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{{ Populate cache
|
// {{{ Populate cache
|
||||||
pub async fn new(pool: &SqlitePool) -> Result<Self, Error> {
|
pub fn new(conn: &DbConnection) -> Result<Self, Error> {
|
||||||
|
let conn = conn.get()?;
|
||||||
let mut result = Self::default();
|
let mut result = Self::default();
|
||||||
|
|
||||||
// {{{ Songs
|
// {{{ Songs
|
||||||
let songs = sqlx::query!("SELECT * FROM songs").fetch_all(pool).await?;
|
let mut query = conn.prepare_cached("SELECT * FROM songs")?;
|
||||||
for song in songs {
|
let songs = query.query_map((), |row| {
|
||||||
let song = Song {
|
Ok(Song {
|
||||||
id: song.id as u32,
|
id: row.get("id")?,
|
||||||
lowercase_title: song.title.to_lowercase(),
|
lowercase_title: row.get::<_, String>("title")?.to_lowercase(),
|
||||||
title: song.title,
|
title: row.get("title")?,
|
||||||
artist: song.artist,
|
artist: row.get("artist")?,
|
||||||
pack: song.pack,
|
pack: row.get("pack")?,
|
||||||
bpm: song.bpm,
|
bpm: row.get("bpm")?,
|
||||||
side: Side::try_from(song.side)?,
|
side: row.get("side")?,
|
||||||
};
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for song in songs {
|
||||||
|
let song = song?;
|
||||||
let song_id = song.id as usize;
|
let song_id = song.id as usize;
|
||||||
|
|
||||||
if song_id >= result.songs.len() {
|
if song_id >= result.songs.len() {
|
||||||
result.songs.resize(song_id + 1, None);
|
result.songs.resize(song_id + 1, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
result.songs[song_id] = Some(CachedSong::new(song));
|
result.songs[song_id] = Some(CachedSong::new(song));
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Charts
|
// {{{ Charts
|
||||||
let charts = sqlx::query!("SELECT * FROM charts").fetch_all(pool).await?;
|
let mut query = conn.prepare_cached("SELECT * FROM charts")?;
|
||||||
for chart in charts {
|
let charts = query.query_map((), |row| {
|
||||||
let chart = Chart {
|
Ok(Chart {
|
||||||
id: chart.id as u32,
|
id: row.get("id")?,
|
||||||
song_id: chart.song_id as u32,
|
song_id: row.get("song_id")?,
|
||||||
shorthand: chart.shorthand,
|
shorthand: row.get("shorthand")?,
|
||||||
difficulty: Difficulty::try_from(chart.difficulty)?,
|
difficulty: row.get("difficulty")?,
|
||||||
level: Level::try_from(chart.level)?,
|
level: row.get("level")?,
|
||||||
chart_constant: chart.chart_constant as u32,
|
chart_constant: row.get("chart_constant")?,
|
||||||
note_count: chart.note_count as u32,
|
note_count: row.get("note_count")?,
|
||||||
|
note_design: row.get("note_design")?,
|
||||||
cached_jacket: None,
|
cached_jacket: None,
|
||||||
note_design: chart.note_design,
|
})
|
||||||
};
|
})?;
|
||||||
|
|
||||||
|
for chart in charts {
|
||||||
|
let chart = chart?;
|
||||||
|
|
||||||
// {{{ Tie chart to song
|
// {{{ Tie chart to song
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,14 +9,12 @@ use num::Zero;
|
||||||
use poise::serenity_prelude::{
|
use poise::serenity_prelude::{
|
||||||
Attachment, AttachmentId, CreateAttachment, CreateEmbed, CreateEmbedAuthor, Timestamp,
|
Attachment, AttachmentId, CreateAttachment, CreateEmbed, CreateEmbedAuthor, Timestamp,
|
||||||
};
|
};
|
||||||
use sqlx::query_as;
|
use rusqlite::Row;
|
||||||
use sqlx::{query, SqlitePool};
|
|
||||||
|
|
||||||
use crate::arcaea::chart::{Chart, Song};
|
use crate::arcaea::chart::{Chart, Song};
|
||||||
use crate::context::{Error, UserContext};
|
use crate::context::{Error, UserContext};
|
||||||
use crate::user::User;
|
use crate::user::User;
|
||||||
|
|
||||||
use super::chart::SongCache;
|
|
||||||
use super::rating::{rating_as_fixed, rating_as_float};
|
use super::rating::{rating_as_fixed, rating_as_float};
|
||||||
use super::score::{Score, ScoringSystem};
|
use super::score::{Score, ScoringSystem};
|
||||||
|
|
||||||
|
@ -61,12 +59,14 @@ impl CreatePlay {
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{{ Save
|
// {{{ Save
|
||||||
pub async fn save(self, ctx: &UserContext, user: &User, chart: &Chart) -> Result<Play, Error> {
|
pub fn save(self, ctx: &UserContext, user: &User, chart: &Chart) -> Result<Play, Error> {
|
||||||
|
let conn = ctx.db.get()?;
|
||||||
let attachment_id = self.discord_attachment_id.map(|i| i.get() as i64);
|
let attachment_id = self.discord_attachment_id.map(|i| i.get() as i64);
|
||||||
|
|
||||||
// {{{ Save current data to play
|
// {{{ Save current data to play
|
||||||
let play = sqlx::query!(
|
let (id, created_at) = conn
|
||||||
"
|
.prepare_cached(
|
||||||
|
"
|
||||||
INSERT INTO plays(
|
INSERT INTO plays(
|
||||||
user_id,chart_id,discord_attachment_id,
|
user_id,chart_id,discord_attachment_id,
|
||||||
max_recall,far_notes
|
max_recall,far_notes
|
||||||
|
@ -74,88 +74,55 @@ impl CreatePlay {
|
||||||
VALUES(?,?,?,?,?)
|
VALUES(?,?,?,?,?)
|
||||||
RETURNING id, created_at
|
RETURNING id, created_at
|
||||||
",
|
",
|
||||||
user.id,
|
)?
|
||||||
chart.id,
|
.query_row(
|
||||||
attachment_id,
|
(
|
||||||
self.max_recall,
|
user.id,
|
||||||
self.far_notes
|
chart.id,
|
||||||
)
|
attachment_id,
|
||||||
.fetch_one(&ctx.db)
|
self.max_recall,
|
||||||
.await?;
|
self.far_notes,
|
||||||
|
),
|
||||||
|
|row| Ok((row.get("id")?, row.get("created_at")?)),
|
||||||
|
)?;
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Update creation ptt data
|
// {{{ Update creation ptt data
|
||||||
let scores = ScoreCollection::from_standard_score(self.score, chart);
|
let scores = ScoreCollection::from_standard_score(self.score, chart);
|
||||||
|
|
||||||
for system in ScoringSystem::SCORING_SYSTEMS {
|
for system in ScoringSystem::SCORING_SYSTEMS {
|
||||||
let i = system.to_index();
|
let i = system.to_index();
|
||||||
let plays = get_best_plays(&ctx.db, &ctx.song_cache, user.id, system, 30, 30, None)
|
let plays = get_best_plays(ctx, user.id, system, 30, 30, None)?.ok();
|
||||||
.await?
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
let creation_ptt: Option<_> = try { rating_as_fixed(compute_b30_ptt(system, &plays?)) };
|
let creation_ptt: Option<_> = try { rating_as_fixed(compute_b30_ptt(system, &plays?)) };
|
||||||
|
|
||||||
query!(
|
conn.prepare_cached(
|
||||||
"
|
"
|
||||||
INSERT INTO scores(play_id, score, creation_ptt, scoring_system)
|
INSERT INTO scores(play_id, score, creation_ptt, scoring_system)
|
||||||
VALUES (?,?,?,?)
|
VALUES (?,?,?,?)
|
||||||
",
|
",
|
||||||
play.id,
|
)?
|
||||||
|
.execute((
|
||||||
|
id,
|
||||||
scores.0[i].0,
|
scores.0[i].0,
|
||||||
creation_ptt,
|
creation_ptt,
|
||||||
ScoringSystem::SCORING_SYSTEM_DB_STRINGS[i]
|
ScoringSystem::SCORING_SYSTEM_DB_STRINGS[i],
|
||||||
)
|
))?;
|
||||||
.execute(&ctx.db)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
Ok(Play {
|
Ok(Play {
|
||||||
id: play.id as u32,
|
id,
|
||||||
created_at: play.created_at,
|
created_at,
|
||||||
|
scores,
|
||||||
chart_id: chart.id,
|
chart_id: chart.id,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
scores,
|
|
||||||
max_recall: self.max_recall,
|
max_recall: self.max_recall,
|
||||||
far_notes: self.far_notes,
|
far_notes: self.far_notes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
}
|
}
|
||||||
// }}}
|
|
||||||
// {{{ DbPlay
|
|
||||||
/// Construct a `Play` from a sqlite return record.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! play_from_db_record {
|
|
||||||
($chart:expr, $record:expr) => {{
|
|
||||||
use crate::arcaea::play::{Play, ScoreCollection};
|
|
||||||
use crate::arcaea::score::Score;
|
|
||||||
Play {
|
|
||||||
id: $record.id as u32,
|
|
||||||
chart_id: $record.chart_id as u32,
|
|
||||||
user_id: $record.user_id as u32,
|
|
||||||
scores: ScoreCollection::from_standard_score(Score($record.score as u32), $chart),
|
|
||||||
max_recall: $record.max_recall.map(|r| r as u32),
|
|
||||||
far_notes: $record.far_notes.map(|r| r as u32),
|
|
||||||
created_at: $record.created_at,
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Typed version of the input to the macro above.
|
|
||||||
/// Useful when using the non-macro version of the sqlx functions.
|
|
||||||
#[derive(Debug, sqlx::FromRow)]
|
|
||||||
pub struct DbPlay {
|
|
||||||
pub id: i64,
|
|
||||||
pub chart_id: i64,
|
|
||||||
pub user_id: i64,
|
|
||||||
pub created_at: chrono::NaiveDateTime,
|
|
||||||
|
|
||||||
// Score details
|
|
||||||
pub max_recall: Option<i64>,
|
|
||||||
pub far_notes: Option<i64>,
|
|
||||||
pub score: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Score data
|
// {{{ Score data
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -185,6 +152,20 @@ pub struct Play {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Play {
|
impl Play {
|
||||||
|
// {{{ Row parsing
|
||||||
|
#[inline]
|
||||||
|
pub fn from_sql(chart: &Chart, row: &Row) -> Result<Self, rusqlite::Error> {
|
||||||
|
Ok(Play {
|
||||||
|
id: row.get("id")?,
|
||||||
|
chart_id: row.get("chart_id")?,
|
||||||
|
user_id: row.get("user_id")?,
|
||||||
|
created_at: row.get("created_at")?,
|
||||||
|
max_recall: row.get("max_recall")?,
|
||||||
|
far_notes: row.get("far_notes")?,
|
||||||
|
scores: ScoreCollection::from_standard_score(Score(row.get("score")?), chart),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
// {{{ Query the underlying score
|
// {{{ Query the underlying score
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn score(&self, system: ScoringSystem) -> Score {
|
pub fn score(&self, system: ScoringSystem) -> Score {
|
||||||
|
@ -272,9 +253,9 @@ impl Play {
|
||||||
/// Creates a discord embed for this play.
|
/// Creates a discord embed for this play.
|
||||||
///
|
///
|
||||||
/// The `index` variable is only used to create distinct filenames.
|
/// The `index` variable is only used to create distinct filenames.
|
||||||
pub async fn to_embed(
|
pub fn to_embed(
|
||||||
&self,
|
&self,
|
||||||
db: &SqlitePool,
|
ctx: &UserContext,
|
||||||
user: &User,
|
user: &User,
|
||||||
song: &Song,
|
song: &Song,
|
||||||
chart: &Chart,
|
chart: &Chart,
|
||||||
|
@ -282,33 +263,28 @@ impl Play {
|
||||||
author: Option<&poise::serenity_prelude::User>,
|
author: Option<&poise::serenity_prelude::User>,
|
||||||
) -> Result<(CreateEmbed, Option<CreateAttachment>), Error> {
|
) -> Result<(CreateEmbed, Option<CreateAttachment>), Error> {
|
||||||
// {{{ Get previously best score
|
// {{{ Get previously best score
|
||||||
let prev_play = query!(
|
let prev_play = ctx
|
||||||
"
|
.db
|
||||||
SELECT
|
.get()?
|
||||||
p.id, p.chart_id, p.user_id, p.created_at,
|
.prepare_cached(
|
||||||
p.max_recall, p.far_notes, s.score
|
"
|
||||||
FROM plays p
|
SELECT
|
||||||
JOIN scores s ON s.play_id = p.id
|
p.id, p.chart_id, p.user_id, p.created_at,
|
||||||
WHERE s.scoring_system='standard'
|
p.max_recall, p.far_notes, s.score
|
||||||
AND p.user_id=?
|
FROM plays p
|
||||||
AND p.chart_id=?
|
JOIN scores s ON s.play_id = p.id
|
||||||
AND p.created_at<?
|
WHERE s.scoring_system='standard'
|
||||||
ORDER BY s.score DESC
|
AND p.user_id=?
|
||||||
LIMIT 1
|
AND p.chart_id=?
|
||||||
",
|
AND p.created_at<?
|
||||||
user.id,
|
ORDER BY s.score DESC
|
||||||
chart.id,
|
LIMIT 1
|
||||||
self.created_at
|
",
|
||||||
)
|
)?
|
||||||
.fetch_optional(db)
|
.query_row((user.id, chart.id, self.created_at), |row| {
|
||||||
.await
|
Self::from_sql(chart, row)
|
||||||
.map_err(|_| {
|
})
|
||||||
format!(
|
.ok();
|
||||||
"Could not find any scores for {} [{:?}]",
|
|
||||||
song.title, chart.difficulty
|
|
||||||
)
|
|
||||||
})?
|
|
||||||
.map(|p| play_from_db_record!(chart, p));
|
|
||||||
|
|
||||||
let prev_score = prev_play.as_ref().map(|p| p.score(ScoringSystem::Standard));
|
let prev_score = prev_play.as_ref().map(|p| p.score(ScoringSystem::Standard));
|
||||||
let prev_zeta_score = prev_play.as_ref().map(|p| p.score(ScoringSystem::EX));
|
let prev_zeta_score = prev_play.as_ref().map(|p| p.score(ScoringSystem::EX));
|
||||||
|
@ -408,38 +384,47 @@ impl Play {
|
||||||
// {{{ General functions
|
// {{{ General functions
|
||||||
pub type PlayCollection<'a> = Vec<(Play, &'a Song, &'a Chart)>;
|
pub type PlayCollection<'a> = Vec<(Play, &'a Song, &'a Chart)>;
|
||||||
|
|
||||||
pub async fn get_best_plays<'a>(
|
pub fn get_best_plays<'a>(
|
||||||
db: &SqlitePool,
|
ctx: &'a UserContext,
|
||||||
song_cache: &'a SongCache,
|
|
||||||
user_id: u32,
|
user_id: u32,
|
||||||
scoring_system: ScoringSystem,
|
scoring_system: ScoringSystem,
|
||||||
min_amount: usize,
|
min_amount: usize,
|
||||||
max_amount: usize,
|
max_amount: usize,
|
||||||
before: Option<NaiveDateTime>,
|
before: Option<NaiveDateTime>,
|
||||||
) -> Result<Result<PlayCollection<'a>, String>, Error> {
|
) -> Result<Result<PlayCollection<'a>, String>, Error> {
|
||||||
|
let conn = ctx.db.get()?;
|
||||||
// {{{ DB data fetching
|
// {{{ DB data fetching
|
||||||
let plays: Vec<DbPlay> = query_as(
|
let mut plays = conn
|
||||||
"
|
.prepare_cached(
|
||||||
SELECT
|
"
|
||||||
p.id, p.chart_id, p.user_id, p.created_at,
|
SELECT
|
||||||
p.max_recall, p.far_notes, s.score,
|
p.id, p.chart_id, p.user_id, p.created_at,
|
||||||
MAX(s.score) as _cscore
|
p.max_recall, p.far_notes, s.score,
|
||||||
-- ^ This is only here to make sqlite pick the correct row for the bare columns
|
MAX(cs.score) as _cscore
|
||||||
FROM plays p
|
-- ^ This is only here to make sqlite pick the correct row for the bare columns
|
||||||
JOIN scores s ON s.play_id = p.id
|
FROM plays p
|
||||||
JOIN scores cs ON cs.play_id = p.id
|
JOIN scores s ON s.play_id = p.id
|
||||||
WHERE s.scoring_system='standard'
|
JOIN scores cs ON cs.play_id = p.id
|
||||||
AND cs.scoring_system=?
|
WHERE s.scoring_system='standard'
|
||||||
AND p.user_id=?
|
AND cs.scoring_system=?
|
||||||
AND p.created_at<=?
|
AND p.user_id=?
|
||||||
GROUP BY p.chart_id
|
AND p.created_at<=?
|
||||||
",
|
GROUP BY p.chart_id
|
||||||
)
|
",
|
||||||
.bind(ScoringSystem::SCORING_SYSTEM_DB_STRINGS[scoring_system.to_index()])
|
)?
|
||||||
.bind(user_id)
|
.query_and_then(
|
||||||
.bind(before.unwrap_or_else(|| Utc::now().naive_utc()))
|
(
|
||||||
.fetch_all(db)
|
ScoringSystem::SCORING_SYSTEM_DB_STRINGS[scoring_system.to_index()],
|
||||||
.await?;
|
user_id,
|
||||||
|
before.unwrap_or_else(|| Utc::now().naive_utc()),
|
||||||
|
),
|
||||||
|
|row| {
|
||||||
|
let (song, chart) = ctx.song_cache.lookup_chart(row.get("chart_id")?)?;
|
||||||
|
let play = Play::from_sql(chart, row)?;
|
||||||
|
Ok((play, song, chart))
|
||||||
|
},
|
||||||
|
)?
|
||||||
|
.collect::<Result<Vec<_>, Error>>()?;
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
if plays.len() < min_amount {
|
if plays.len() < min_amount {
|
||||||
|
@ -450,17 +435,6 @@ pub async fn get_best_plays<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// {{{ B30 computation
|
// {{{ B30 computation
|
||||||
// NOTE: we reallocate here, although we do not have much of a choice,
|
|
||||||
// unless we want to be lazy about things
|
|
||||||
let mut plays: Vec<(Play, &Song, &Chart)> = plays
|
|
||||||
.into_iter()
|
|
||||||
.map(|play| {
|
|
||||||
let (song, chart) = song_cache.lookup_chart(play.chart_id as u32)?;
|
|
||||||
let play = play_from_db_record!(chart, play);
|
|
||||||
Ok((play, song, chart))
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, Error>>()?;
|
|
||||||
|
|
||||||
plays.sort_by_key(|(play, _, chart)| -play.play_rating(scoring_system, chart.chart_constant));
|
plays.sort_by_key(|(play, _, chart)| -play.play_rating(scoring_system, chart.chart_constant));
|
||||||
plays.truncate(max_amount);
|
plays.truncate(max_amount);
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -480,7 +454,8 @@ pub fn compute_b30_ptt(scoring_system: ScoringSystem, plays: &PlayCollection<'_>
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Maintenance functions
|
// {{{ Maintenance functions
|
||||||
pub async fn generate_missing_scores(ctx: &UserContext) -> Result<(), Error> {
|
pub async fn generate_missing_scores(ctx: &UserContext) -> Result<(), Error> {
|
||||||
let plays = query!(
|
let conn = ctx.db.get()?;
|
||||||
|
let mut query = conn.prepare_cached(
|
||||||
"
|
"
|
||||||
SELECT
|
SELECT
|
||||||
p.id, p.chart_id, p.user_id, p.created_at,
|
p.id, p.chart_id, p.user_id, p.created_at,
|
||||||
|
@ -489,53 +464,44 @@ pub async fn generate_missing_scores(ctx: &UserContext) -> Result<(), Error> {
|
||||||
JOIN scores s ON s.play_id = p.id
|
JOIN scores s ON s.play_id = p.id
|
||||||
WHERE s.scoring_system='standard'
|
WHERE s.scoring_system='standard'
|
||||||
ORDER BY p.created_at ASC
|
ORDER BY p.created_at ASC
|
||||||
"
|
",
|
||||||
)
|
)?;
|
||||||
// Can't use the stream based version because of db locking...
|
|
||||||
.fetch_all(&ctx.db)
|
let plays = query.query_and_then((), |row| -> Result<_, Error> {
|
||||||
.await?;
|
let (_, chart) = ctx.song_cache.lookup_chart(row.get("chart_id")?)?;
|
||||||
|
let play = Play::from_sql(chart, row)?;
|
||||||
|
Ok(play)
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
|
||||||
for play in plays {
|
for play in plays {
|
||||||
let (_, chart) = ctx.song_cache.lookup_chart(play.chart_id as u32)?;
|
let play = play?;
|
||||||
let play = play_from_db_record!(chart, play);
|
|
||||||
|
|
||||||
for system in ScoringSystem::SCORING_SYSTEMS {
|
for system in ScoringSystem::SCORING_SYSTEMS {
|
||||||
let i = system.to_index();
|
let i = system.to_index();
|
||||||
let plays = get_best_plays(
|
let plays =
|
||||||
&ctx.db,
|
get_best_plays(&ctx, play.user_id, system, 30, 30, Some(play.created_at))?.ok();
|
||||||
&ctx.song_cache,
|
|
||||||
play.user_id,
|
|
||||||
system,
|
|
||||||
30,
|
|
||||||
30,
|
|
||||||
Some(play.created_at),
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
.ok();
|
|
||||||
|
|
||||||
let creation_ptt: Option<_> = try { rating_as_fixed(compute_b30_ptt(system, &plays?)) };
|
let creation_ptt: Option<_> = try { rating_as_fixed(compute_b30_ptt(system, &plays?)) };
|
||||||
let raw_score = play.scores.0[i].0;
|
let raw_score = play.scores.0[i].0;
|
||||||
|
|
||||||
query!(
|
conn.prepare_cached(
|
||||||
"
|
"
|
||||||
INSERT INTO scores(play_id, score, creation_ptt, scoring_system)
|
INSERT INTO scores(play_id, score, creation_ptt, scoring_system)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2, $3, $4)
|
||||||
ON CONFLICT(play_id, scoring_system)
|
ON CONFLICT(play_id, scoring_system)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
score=$2, creation_ptt=$3
|
score=$2, creation_ptt=$3
|
||||||
WHERE play_id = $1
|
WHERE play_id = $1
|
||||||
AND scoring_system = $4
|
AND scoring_system = $4
|
||||||
|
|
||||||
",
|
",
|
||||||
|
)?
|
||||||
|
.execute((
|
||||||
play.id,
|
play.id,
|
||||||
raw_score,
|
raw_score,
|
||||||
creation_ptt,
|
creation_ptt,
|
||||||
ScoringSystem::SCORING_SYSTEM_DB_STRINGS[i],
|
ScoringSystem::SCORING_SYSTEM_DB_STRINGS[i],
|
||||||
)
|
))?;
|
||||||
.execute(&ctx.db)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use poise::serenity_prelude::{CreateAttachment, CreateEmbed, CreateMessage};
|
use poise::serenity_prelude::{CreateAttachment, CreateEmbed, CreateMessage};
|
||||||
use sqlx::query;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arcaea::chart::Side,
|
arcaea::{chart::Side, play::Play},
|
||||||
context::{Context, Error},
|
context::{Context, Error},
|
||||||
get_user, play_from_db_record,
|
get_user,
|
||||||
recognition::fuzzy_song_name::guess_song_and_chart,
|
recognition::fuzzy_song_name::guess_song_and_chart,
|
||||||
};
|
};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
@ -23,7 +22,7 @@ use poise::CreateReply;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arcaea::score::{Score, ScoringSystem},
|
arcaea::score::{Score, ScoringSystem},
|
||||||
user::discord_it_to_discord_user,
|
user::discord_id_to_discord_user,
|
||||||
};
|
};
|
||||||
|
|
||||||
// {{{ Top command
|
// {{{ Top command
|
||||||
|
@ -55,16 +54,18 @@ async fn info(
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let play_count = query!(
|
let play_count: usize = ctx
|
||||||
"
|
.data()
|
||||||
|
.db
|
||||||
|
.get()?
|
||||||
|
.prepare_cached(
|
||||||
|
"
|
||||||
SELECT COUNT(*) as count
|
SELECT COUNT(*) as count
|
||||||
FROM plays
|
FROM plays
|
||||||
WHERE chart_id=?
|
WHERE chart_id=?
|
||||||
",
|
",
|
||||||
chart.id
|
)?
|
||||||
)
|
.query_row([chart.id], |row| row.get(0))?;
|
||||||
.fetch_one(&ctx.data().db)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut embed = CreateEmbed::default()
|
let mut embed = CreateEmbed::default()
|
||||||
.title(format!(
|
.title(format!(
|
||||||
|
@ -77,7 +78,7 @@ async fn info(
|
||||||
format!("{:.1}", chart.chart_constant as f32 / 100.0),
|
format!("{:.1}", chart.chart_constant as f32 / 100.0),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
.field("Total plays", format!("{}", play_count.count), true)
|
.field("Total plays", format!("{play_count}"), true)
|
||||||
.field("BPM", &song.bpm, true)
|
.field("BPM", &song.bpm, true)
|
||||||
.field("Side", Side::SIDE_STRINGS[song.side.to_index()], true)
|
.field("Side", Side::SIDE_STRINGS[song.side.to_index()], true)
|
||||||
.field("Artist", &song.title, true);
|
.field("Artist", &song.title, true);
|
||||||
|
@ -117,42 +118,40 @@ async fn best(
|
||||||
let user = get_user!(&ctx);
|
let user = get_user!(&ctx);
|
||||||
|
|
||||||
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
|
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
|
||||||
let play = query!(
|
let play = ctx
|
||||||
"
|
.data()
|
||||||
SELECT
|
.db
|
||||||
|
.get()?
|
||||||
|
.prepare_cached(
|
||||||
|
"
|
||||||
|
SELECT
|
||||||
p.id, p.chart_id, p.user_id, p.created_at,
|
p.id, p.chart_id, p.user_id, p.created_at,
|
||||||
p.max_recall, p.far_notes, s.score
|
p.max_recall, p.far_notes, s.score
|
||||||
FROM plays p
|
FROM plays p
|
||||||
JOIN scores s ON s.play_id = p.id
|
JOIN scores s ON s.play_id = p.id
|
||||||
WHERE s.scoring_system='standard'
|
WHERE s.scoring_system='standard'
|
||||||
AND p.user_id=?
|
AND p.user_id=?
|
||||||
AND p.chart_id=?
|
AND p.chart_id=?
|
||||||
ORDER BY s.score DESC
|
ORDER BY s.score DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
",
|
",
|
||||||
user.id,
|
)?
|
||||||
chart.id
|
.query_row((user.id, chart.id), |row| Play::from_sql(chart, row))
|
||||||
)
|
.map_err(|_| {
|
||||||
.fetch_one(&ctx.data().db)
|
format!(
|
||||||
.await
|
"Could not find any scores for {} [{:?}]",
|
||||||
.map_err(|_| {
|
song.title, chart.difficulty
|
||||||
format!(
|
)
|
||||||
"Could not find any scores for {} [{:?}]",
|
})?;
|
||||||
song.title, chart.difficulty
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let play = play_from_db_record!(chart, play);
|
|
||||||
|
|
||||||
let (embed, attachment) = play
|
let (embed, attachment) = play.to_embed(
|
||||||
.to_embed(
|
ctx.data(),
|
||||||
&ctx.data().db,
|
&user,
|
||||||
&user,
|
song,
|
||||||
&song,
|
chart,
|
||||||
&chart,
|
0,
|
||||||
0,
|
Some(&discord_id_to_discord_user(&ctx, &user.discord_id).await?),
|
||||||
Some(&discord_it_to_discord_user(&ctx, &user.discord_id).await?),
|
)?;
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
ctx.channel_id()
|
ctx.channel_id()
|
||||||
.send_files(ctx.http(), attachment, CreateMessage::new().embed(embed))
|
.send_files(ctx.http(), attachment, CreateMessage::new().embed(embed))
|
||||||
|
@ -177,8 +176,12 @@ async fn plot(
|
||||||
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
|
let (song, chart) = guess_song_and_chart(&ctx.data(), &name)?;
|
||||||
|
|
||||||
// SAFETY: we limit the amount of plotted plays to 1000.
|
// SAFETY: we limit the amount of plotted plays to 1000.
|
||||||
let plays = query!(
|
let plays = ctx
|
||||||
"
|
.data()
|
||||||
|
.db
|
||||||
|
.get()?
|
||||||
|
.prepare_cached(
|
||||||
|
"
|
||||||
SELECT
|
SELECT
|
||||||
p.id, p.chart_id, p.user_id, p.created_at,
|
p.id, p.chart_id, p.user_id, p.created_at,
|
||||||
p.max_recall, p.far_notes, s.score
|
p.max_recall, p.far_notes, s.score
|
||||||
|
@ -190,11 +193,9 @@ async fn plot(
|
||||||
ORDER BY s.score DESC
|
ORDER BY s.score DESC
|
||||||
LIMIT 1000
|
LIMIT 1000
|
||||||
",
|
",
|
||||||
user.id,
|
)?
|
||||||
chart.id
|
.query_map((user.id, chart.id), |row| Play::from_sql(chart, row))?
|
||||||
)
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
.fetch_all(&ctx.data().db)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if plays.len() == 0 {
|
if plays.len() == 0 {
|
||||||
ctx.reply(format!(
|
ctx.reply(format!(
|
||||||
|
@ -209,7 +210,7 @@ async fn plot(
|
||||||
let max_time = plays.iter().map(|p| p.created_at).max().unwrap();
|
let max_time = plays.iter().map(|p| p.created_at).max().unwrap();
|
||||||
let mut min_score = plays
|
let mut min_score = plays
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| play_from_db_record!(chart, p).score(scoring_system))
|
.map(|p| p.score(scoring_system))
|
||||||
.min()
|
.min()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0 as i64;
|
.0 as i64;
|
||||||
|
@ -266,7 +267,7 @@ async fn plot(
|
||||||
.map(|play| {
|
.map(|play| {
|
||||||
(
|
(
|
||||||
play.created_at.and_utc().timestamp_millis(),
|
play.created_at.and_utc().timestamp_millis(),
|
||||||
play_from_db_record!(chart, play).score(scoring_system),
|
play.score(scoring_system),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use crate::arcaea::play::CreatePlay;
|
use crate::arcaea::play::{CreatePlay, Play};
|
||||||
use crate::arcaea::score::Score;
|
use crate::arcaea::score::Score;
|
||||||
use crate::context::{Context, Error};
|
use crate::context::{Context, Error};
|
||||||
use crate::recognition::recognize::{ImageAnalyzer, ScoreKind};
|
use crate::recognition::recognize::{ImageAnalyzer, ScoreKind};
|
||||||
use crate::user::{discord_it_to_discord_user, User};
|
use crate::user::{discord_id_to_discord_user, User};
|
||||||
use crate::{edit_reply, get_user, play_from_db_record, timed};
|
use crate::{edit_reply, get_user, timed};
|
||||||
use image::DynamicImage;
|
use image::DynamicImage;
|
||||||
use poise::serenity_prelude::futures::future::join_all;
|
use poise::serenity_prelude::futures::future::join_all;
|
||||||
use poise::serenity_prelude::CreateMessage;
|
use poise::serenity_prelude::CreateMessage;
|
||||||
use poise::{serenity_prelude as serenity, CreateReply};
|
use poise::{serenity_prelude as serenity, CreateReply};
|
||||||
use sqlx::query;
|
|
||||||
|
|
||||||
// {{{ Score
|
// {{{ Score
|
||||||
/// Score management
|
/// Score management
|
||||||
|
@ -121,15 +120,13 @@ pub async fn magic(
|
||||||
.with_attachment(file)
|
.with_attachment(file)
|
||||||
.with_fars(maybe_fars)
|
.with_fars(maybe_fars)
|
||||||
.with_max_recall(max_recall)
|
.with_max_recall(max_recall)
|
||||||
.save(&ctx.data(), &user, &chart)
|
.save(&ctx.data(), &user, &chart)?;
|
||||||
.await?;
|
|
||||||
// }}}
|
// }}}
|
||||||
// }}}
|
// }}}
|
||||||
// {{{ Deliver embed
|
// {{{ Deliver embed
|
||||||
|
|
||||||
let (embed, attachment) = timed!("to embed", {
|
let (embed, attachment) = timed!("to embed", {
|
||||||
play.to_embed(&ctx.data().db, &user, &song, &chart, i, None)
|
play.to_embed(ctx.data(), &user, &song, &chart, i, None)?
|
||||||
.await?
|
|
||||||
});
|
});
|
||||||
|
|
||||||
embeds.push(embed);
|
embeds.push(embed);
|
||||||
|
@ -183,11 +180,14 @@ pub async fn delete(
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
||||||
for id in ids {
|
for id in ids {
|
||||||
let res = query!("DELETE FROM plays WHERE id=? AND user_id=?", id, user.id)
|
let res = ctx
|
||||||
.execute(&ctx.data().db)
|
.data()
|
||||||
.await?;
|
.db
|
||||||
|
.get()?
|
||||||
|
.prepare_cached("DELETE FROM plays WHERE id=? AND user_id=?")?
|
||||||
|
.execute((id, user.id))?;
|
||||||
|
|
||||||
if res.rows_affected() == 0 {
|
if res == 0 {
|
||||||
ctx.reply(format!("No play with id {} found", id)).await?;
|
ctx.reply(format!("No play with id {} found", id)).await?;
|
||||||
} else {
|
} else {
|
||||||
count += 1;
|
count += 1;
|
||||||
|
@ -216,36 +216,38 @@ pub async fn show(
|
||||||
|
|
||||||
let mut embeds = Vec::with_capacity(ids.len());
|
let mut embeds = Vec::with_capacity(ids.len());
|
||||||
let mut attachments = Vec::with_capacity(ids.len());
|
let mut attachments = Vec::with_capacity(ids.len());
|
||||||
|
let conn = ctx.data().db.get()?;
|
||||||
for (i, id) in ids.iter().enumerate() {
|
for (i, id) in ids.iter().enumerate() {
|
||||||
let res = query!(
|
let (song, chart, play, discord_id) = conn
|
||||||
"
|
.prepare_cached(
|
||||||
SELECT
|
"
|
||||||
p.id, p.chart_id, p.user_id, p.created_at,
|
SELECT
|
||||||
p.max_recall, p.far_notes, s.score,
|
p.id, p.chart_id, p.user_id, p.created_at,
|
||||||
u.discord_id
|
p.max_recall, p.far_notes, s.score,
|
||||||
FROM plays p
|
u.discord_id
|
||||||
JOIN scores s ON s.play_id = p.id
|
FROM plays p
|
||||||
JOIN users u ON p.user_id = u.id
|
JOIN scores s ON s.play_id = p.id
|
||||||
WHERE s.scoring_system='standard'
|
JOIN users u ON p.user_id = u.id
|
||||||
AND p.id=?
|
WHERE s.scoring_system='standard'
|
||||||
ORDER BY s.score DESC
|
AND p.id=?
|
||||||
LIMIT 1
|
ORDER BY s.score DESC
|
||||||
",
|
LIMIT 1
|
||||||
id
|
",
|
||||||
)
|
)?
|
||||||
.fetch_one(&ctx.data().db)
|
.query_and_then([id], |row| -> Result<_, Error> {
|
||||||
.await
|
let (song, chart) = ctx.data().song_cache.lookup_chart(row.get("chart_id")?)?;
|
||||||
.map_err(|_| format!("Could not find play with id {}", id))?;
|
let play = Play::from_sql(chart, row)?;
|
||||||
|
let discord_id = row.get::<_, String>("discord_id")?;
|
||||||
|
Ok((song, chart, play, discord_id))
|
||||||
|
})?
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| format!("Could not find play with id {}", id))??;
|
||||||
|
|
||||||
let (song, chart) = ctx.data().song_cache.lookup_chart(res.chart_id as u32)?;
|
let author = discord_id_to_discord_user(&ctx, &discord_id).await?;
|
||||||
let play = play_from_db_record!(chart, res);
|
let user = User::by_id(ctx.data(), play.user_id)?;
|
||||||
|
|
||||||
let author = discord_it_to_discord_user(&ctx, &res.discord_id).await?;
|
let (embed, attachment) =
|
||||||
let user = User::by_id(&ctx.data().db, play.user_id).await?;
|
play.to_embed(ctx.data(), &user, song, chart, i, Some(&author))?;
|
||||||
|
|
||||||
let (embed, attachment) = play
|
|
||||||
.to_embed(&ctx.data().db, &user, song, chart, i, Some(&author))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
embeds.push(embed);
|
embeds.push(embed);
|
||||||
attachments.extend(attachment);
|
attachments.extend(attachment);
|
||||||
|
|
|
@ -5,7 +5,6 @@ use poise::{
|
||||||
serenity_prelude::{CreateAttachment, CreateEmbed},
|
serenity_prelude::{CreateAttachment, CreateEmbed},
|
||||||
CreateReply,
|
CreateReply,
|
||||||
};
|
};
|
||||||
use sqlx::query;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arcaea::{
|
arcaea::{
|
||||||
|
@ -26,7 +25,7 @@ use crate::{
|
||||||
context::{Context, Error},
|
context::{Context, Error},
|
||||||
get_user,
|
get_user,
|
||||||
logs::debug_image_log,
|
logs::debug_image_log,
|
||||||
reply_errors,
|
reply_errors, timed,
|
||||||
user::User,
|
user::User,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,20 +52,20 @@ async fn best_plays(
|
||||||
let user_ctx = ctx.data();
|
let user_ctx = ctx.data();
|
||||||
let plays = reply_errors!(
|
let plays = reply_errors!(
|
||||||
ctx,
|
ctx,
|
||||||
get_best_plays(
|
timed!("get_best_plays", {
|
||||||
&user_ctx.db,
|
get_best_plays(
|
||||||
&user_ctx.song_cache,
|
user_ctx,
|
||||||
user.id,
|
user.id,
|
||||||
scoring_system,
|
scoring_system,
|
||||||
if require_full {
|
if require_full {
|
||||||
grid_size.0 * grid_size.1
|
grid_size.0 * grid_size.1
|
||||||
} else {
|
} else {
|
||||||
grid_size.0 * (grid_size.1.max(1) - 1) + 1
|
grid_size.0 * (grid_size.1.max(1) - 1) + 1
|
||||||
} as usize,
|
} as usize,
|
||||||
(grid_size.0 * grid_size.1) as usize,
|
(grid_size.0 * grid_size.1) as usize,
|
||||||
None
|
None,
|
||||||
)
|
)?
|
||||||
.await?
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// {{{ Layout
|
// {{{ Layout
|
||||||
|
@ -463,48 +462,42 @@ pub async fn bany(
|
||||||
#[poise::command(prefix_command, slash_command, user_cooldown = 1)]
|
#[poise::command(prefix_command, slash_command, user_cooldown = 1)]
|
||||||
async fn meta(ctx: Context<'_>) -> Result<(), Error> {
|
async fn meta(ctx: Context<'_>) -> Result<(), Error> {
|
||||||
let user = get_user!(&ctx);
|
let user = get_user!(&ctx);
|
||||||
let song_count = query!("SELECT count() as count FROM songs")
|
let conn = ctx.data().db.get()?;
|
||||||
.fetch_one(&ctx.data().db)
|
let song_count: usize = conn
|
||||||
.await?
|
.prepare_cached("SELECT count() as count FROM songs")?
|
||||||
.count;
|
.query_row((), |row| row.get(0))?;
|
||||||
|
|
||||||
let chart_count = query!("SELECT count() as count FROM charts")
|
let chart_count: usize = conn
|
||||||
.fetch_one(&ctx.data().db)
|
.prepare_cached("SELECT count() as count FROM charts")?
|
||||||
.await?
|
.query_row((), |row| row.get(0))?;
|
||||||
.count;
|
|
||||||
|
|
||||||
let users_count = query!("SELECT count() as count FROM users")
|
let users_count: usize = conn
|
||||||
.fetch_one(&ctx.data().db)
|
.prepare_cached("SELECT count() as count FROM users")?
|
||||||
.await?
|
.query_row((), |row| row.get(0))?;
|
||||||
.count;
|
|
||||||
|
|
||||||
let pookie_count = query!(
|
let pookie_count: usize = conn
|
||||||
"
|
.prepare_cached(
|
||||||
SELECT count() as count
|
"
|
||||||
FROM users
|
SELECT count() as count
|
||||||
WHERE is_pookie=1
|
FROM users
|
||||||
"
|
WHERE is_pookie=1
|
||||||
)
|
",
|
||||||
.fetch_one(&ctx.data().db)
|
)?
|
||||||
.await?
|
.query_row((), |row| row.get(0))?;
|
||||||
.count;
|
|
||||||
|
|
||||||
let play_count = query!("SELECT count() as count FROM plays")
|
let play_count: usize = conn
|
||||||
.fetch_one(&ctx.data().db)
|
.prepare_cached("SELECT count() as count FROM plays")?
|
||||||
.await?
|
.query_row((), |row| row.get(0))?;
|
||||||
.count;
|
|
||||||
|
|
||||||
let your_play_count = query!(
|
let your_play_count: usize = conn
|
||||||
"
|
.prepare_cached(
|
||||||
|
"
|
||||||
SELECT count() as count
|
SELECT count() as count
|
||||||
FROM plays
|
FROM plays
|
||||||
WHERE user_id=?
|
WHERE user_id=?
|
||||||
",
|
",
|
||||||
user.id
|
)?
|
||||||
)
|
.query_row([user.id], |row| row.get(0))?;
|
||||||
.fetch_one(&ctx.data().db)
|
|
||||||
.await?
|
|
||||||
.count;
|
|
||||||
|
|
||||||
let embed = CreateEmbed::default()
|
let embed = CreateEmbed::default()
|
||||||
.title("Bot statistics")
|
.title("Bot statistics")
|
||||||
|
|
|
@ -14,7 +14,7 @@ macro_rules! edit_reply {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! get_user {
|
macro_rules! get_user {
|
||||||
($ctx:expr) => {{
|
($ctx:expr) => {{
|
||||||
crate::reply_errors!($ctx, crate::user::User::from_context($ctx).await)
|
crate::reply_errors!($ctx, crate::user::User::from_context($ctx))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
use r2d2_sqlite::SqliteConnectionManager;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use sqlx::SqlitePool;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arcaea::{chart::SongCache, jacket::JacketCache},
|
arcaea::{chart::SongCache, jacket::JacketCache},
|
||||||
assets::{get_data_dir, EXO_FONT, GEOSANS_FONT, KAZESAWA_BOLD_FONT, KAZESAWA_FONT},
|
assets::{get_data_dir, EXO_FONT, GEOSANS_FONT, KAZESAWA_BOLD_FONT, KAZESAWA_FONT},
|
||||||
|
@ -13,9 +12,11 @@ use crate::{
|
||||||
pub type Error = Box<dyn std::error::Error + Send + Sync>;
|
pub type Error = Box<dyn std::error::Error + Send + Sync>;
|
||||||
pub type Context<'a> = poise::Context<'a, UserContext, Error>;
|
pub type Context<'a> = poise::Context<'a, UserContext, Error>;
|
||||||
|
|
||||||
|
pub type DbConnection = r2d2::Pool<SqliteConnectionManager>;
|
||||||
|
|
||||||
// Custom user data passed to all command functions
|
// Custom user data passed to all command functions
|
||||||
pub struct UserContext {
|
pub struct UserContext {
|
||||||
pub db: SqlitePool,
|
pub db: DbConnection,
|
||||||
pub song_cache: SongCache,
|
pub song_cache: SongCache,
|
||||||
pub jacket_cache: JacketCache,
|
pub jacket_cache: JacketCache,
|
||||||
pub ui_measurements: UIMeasurements,
|
pub ui_measurements: UIMeasurements,
|
||||||
|
@ -29,11 +30,11 @@ pub struct UserContext {
|
||||||
|
|
||||||
impl UserContext {
|
impl UserContext {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn new(db: SqlitePool) -> Result<Self, Error> {
|
pub async fn new(db: DbConnection) -> Result<Self, Error> {
|
||||||
timed!("create_context", {
|
timed!("create_context", {
|
||||||
fs::create_dir_all(get_data_dir())?;
|
fs::create_dir_all(get_data_dir())?;
|
||||||
|
|
||||||
let mut song_cache = timed!("make_song_cache", { SongCache::new(&db).await? });
|
let mut song_cache = timed!("make_song_cache", { SongCache::new(&db)? });
|
||||||
let jacket_cache = timed!("make_jacket_cache", { JacketCache::new(&mut song_cache)? });
|
let jacket_cache = timed!("make_jacket_cache", { JacketCache::new(&mut song_cache)? });
|
||||||
let ui_measurements = timed!("read_ui_measurements", { UIMeasurements::read()? });
|
let ui_measurements = timed!("read_ui_measurements", { UIMeasurements::read()? });
|
||||||
|
|
||||||
|
|
39
src/main.rs
39
src/main.rs
|
@ -22,9 +22,16 @@ mod user;
|
||||||
use arcaea::play::generate_missing_scores;
|
use arcaea::play::generate_missing_scores;
|
||||||
use assets::get_data_dir;
|
use assets::get_data_dir;
|
||||||
use context::{Error, UserContext};
|
use context::{Error, UserContext};
|
||||||
|
use include_dir::{include_dir, Dir};
|
||||||
use poise::serenity_prelude::{self as serenity};
|
use poise::serenity_prelude::{self as serenity};
|
||||||
use sqlx::sqlite::SqlitePoolOptions;
|
use r2d2::Pool;
|
||||||
use std::{env::var, sync::Arc, time::Duration};
|
use r2d2_sqlite::SqliteConnectionManager;
|
||||||
|
use rusqlite_migration::Migrations;
|
||||||
|
use std::{
|
||||||
|
env::var,
|
||||||
|
sync::{Arc, LazyLock},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
// {{{ Error handler
|
// {{{ Error handler
|
||||||
async fn on_error(error: poise::FrameworkError<'_, UserContext, Error>) {
|
async fn on_error(error: poise::FrameworkError<'_, UserContext, Error>) {
|
||||||
|
@ -40,13 +47,27 @@ async fn on_error(error: poise::FrameworkError<'_, UserContext, Error>) {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let pool = SqlitePoolOptions::new()
|
let pool = Pool::new(
|
||||||
.connect(&format!(
|
SqliteConnectionManager::file(&format!("{}/db.sqlite", get_data_dir().to_str().unwrap()))
|
||||||
"sqlite://{}/db.sqlite",
|
.with_init(|conn| {
|
||||||
get_data_dir().to_str().unwrap()
|
static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations");
|
||||||
))
|
static MIGRATIONS: LazyLock<Migrations> = LazyLock::new(|| {
|
||||||
.await
|
timed!("create_migration_structure", {
|
||||||
.unwrap();
|
Migrations::from_directory(&MIGRATIONS_DIR)
|
||||||
|
.expect("Could not load migrations")
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
timed!("run_migrations", {
|
||||||
|
MIGRATIONS
|
||||||
|
.to_latest(conn)
|
||||||
|
.expect("Could not run migrations");
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.expect("Could not open sqlite database.");
|
||||||
|
|
||||||
// {{{ Poise options
|
// {{{ Poise options
|
||||||
let options = poise::FrameworkOptions {
|
let options = poise::FrameworkOptions {
|
||||||
|
|
55
src/user.rs
55
src/user.rs
|
@ -1,9 +1,9 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use poise::serenity_prelude::UserId;
|
use poise::serenity_prelude::UserId;
|
||||||
use sqlx::SqlitePool;
|
use rusqlite::Row;
|
||||||
|
|
||||||
use crate::context::{Context, Error};
|
use crate::context::{Context, Error, UserContext};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
|
@ -13,35 +13,44 @@ pub struct User {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub async fn from_context(ctx: &Context<'_>) -> Result<Self, Error> {
|
#[inline]
|
||||||
let id = ctx.author().id.get().to_string();
|
fn from_row<'a, 'b>(row: &'a Row<'b>) -> Result<Self, rusqlite::Error> {
|
||||||
let user = sqlx::query!("SELECT * FROM users WHERE discord_id = ?", id)
|
Ok(Self {
|
||||||
.fetch_one(&ctx.data().db)
|
id: row.get("id")?,
|
||||||
.await
|
discord_id: row.get("discord_id")?,
|
||||||
.map_err(|_| "You are not an user in my database, sowwy ^~^")?;
|
is_pookie: row.get("is_pookie")?,
|
||||||
|
|
||||||
Ok(User {
|
|
||||||
id: user.id as u32,
|
|
||||||
discord_id: user.discord_id,
|
|
||||||
is_pookie: user.is_pookie,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn by_id(db: &SqlitePool, id: u32) -> Result<Self, Error> {
|
pub fn from_context(ctx: &Context<'_>) -> Result<Self, Error> {
|
||||||
let user = sqlx::query!("SELECT * FROM users WHERE id = ?", id)
|
let id = ctx.author().id.get().to_string();
|
||||||
.fetch_one(db)
|
let user = ctx
|
||||||
.await?;
|
.data()
|
||||||
|
.db
|
||||||
|
.get()?
|
||||||
|
.prepare_cached("SELECT * FROM users WHERE discord_id = ?")?
|
||||||
|
.query_map([id], Self::from_row)?
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| "You are not an user in my database, sowwy ^~^")??;
|
||||||
|
|
||||||
Ok(User {
|
Ok(user)
|
||||||
id: user.id as u32,
|
}
|
||||||
discord_id: user.discord_id,
|
|
||||||
is_pookie: user.is_pookie,
|
pub fn by_id(ctx: &UserContext, id: u32) -> Result<Self, Error> {
|
||||||
})
|
let user = ctx
|
||||||
|
.db
|
||||||
|
.get()?
|
||||||
|
.prepare_cached("SELECT * FROM users WHERE id = ?")?
|
||||||
|
.query_map([id], Self::from_row)?
|
||||||
|
.next()
|
||||||
|
.ok_or_else(|| "You are not an user in my database, sowwy ^~^")??;
|
||||||
|
|
||||||
|
Ok(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn discord_it_to_discord_user(
|
pub async fn discord_id_to_discord_user(
|
||||||
&ctx: &Context<'_>,
|
&ctx: &Context<'_>,
|
||||||
discord_id: &str,
|
discord_id: &str,
|
||||||
) -> Result<poise::serenity_prelude::User, Error> {
|
) -> Result<poise::serenity_prelude::User, Error> {
|
||||||
|
|
Loading…
Reference in a new issue