diff --git a/.gitignore b/.gitignore index 5bd05d6..443f1b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ target -.idea \ No newline at end of file +.idea +games.db +.env +diesel.toml +migrations/.* \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index fce1e62..0604f41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,7 +50,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom", + "getrandom 0.2.16", "instant", "pin-project-lite", "rand", @@ -78,6 +78,12 @@ version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "bytes" version = "1.10.1" @@ -99,6 +105,16 @@ dependencies = [ "rustversion", ] +[[package]] +name = "cc" +version = "1.2.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.3" @@ -146,6 +162,26 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "tiny-keccak", +] + [[package]] name = "convert_case" version = "0.7.1" @@ -214,14 +250,30 @@ dependencies = [ "winapi", ] +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "darling" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] @@ -238,13 +290,38 @@ dependencies = [ "syn", ] +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", "quote", "syn", ] @@ -262,6 +339,15 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "deranged" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_more" version = "2.0.1" @@ -283,6 +369,71 @@ dependencies = [ "syn", ] +[[package]] +name = "diesel" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8496eeb328dce26ee9d9b73275d396d9bddb433fa30106cf6056dd8c3c2764c" +dependencies = [ + "diesel_derives", + "downcast-rs", + "libsqlite3-sys", + "sqlite-wasm-rs", + "time", +] + +[[package]] +name = "diesel_derives" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09af0e983035368439f1383011cd87c46f41da81d0f21dc3727e2857d5a43c8e" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" +dependencies = [ + "syn", +] + +[[package]] +name = "directories" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.1", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + [[package]] name = "document-features" version = "0.2.11" @@ -292,6 +443,32 @@ dependencies = [ "litrs", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "downcast-rs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" + +[[package]] +name = "dsl_auto_type" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd122633e4bef06db27737f21d3738fb89c8f6d5360d6d9d7635dda142a7757e" +dependencies = [ + "darling 0.21.3", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "either" version = "1.15.0" @@ -324,6 +501,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" + [[package]] name = "fnv" version = "1.0.7" @@ -433,7 +616,19 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", ] [[package]] @@ -489,7 +684,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" dependencies = [ - "darling", + "darling 0.20.11", "indoc", "proc-macro2", "quote", @@ -531,6 +726,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "js-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -543,6 +748,27 @@ version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +[[package]] +name = "libredox" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -608,10 +834,16 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "object" version = "0.37.3" @@ -627,6 +859,22 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + [[package]] name = "owo-colors" version = "4.2.3" @@ -674,6 +922,18 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -701,6 +961,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -728,7 +994,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", ] [[package]] @@ -761,6 +1027,27 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror", +] + +[[package]] +name = "rust-ini" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -811,6 +1098,35 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -820,6 +1136,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "shutdown-async" version = "0.1.1" @@ -881,6 +1203,21 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "sqlite-wasm-rs" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aead1c279716985b981b7940ef9b652d3f93d70a7296853c633b7ce8fa8088a" +dependencies = [ + "js-sys", + "once_cell", + "thiserror", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -921,11 +1258,18 @@ version = "0.1.0" dependencies = [ "color-eyre", "crossterm 0.29.0", + "diesel", + "directories", + "dotenvy", "futures", + "lazy_static", + "libsqlite3-sys", "ratatui", + "rust-ini", "tokio", "tokio-util", "tokio-utils", + "uuid", ] [[package]] @@ -939,6 +1283,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.9" @@ -948,6 +1312,46 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tokio" version = "1.47.1" @@ -1090,18 +1494,135 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1212,6 +1733,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "zerocopy" version = "0.8.27" diff --git a/Cargo.toml b/Cargo.toml index d147e33..510c522 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,30 @@ edition = "2024" [dependencies] color-eyre = "0.6.3" ratatui = "0.29.0" - -crossterm = { version = "0.29.0", features = ["event-stream"] } futures = "0.3.28" -tokio = { version = "1.47.1", features = ["full"] } tokio-util = "0.7.9" tokio-utils = "0.1.2" +directories = "6.0.0" +lazy_static = "1.5.0" +dotenvy = "0.15" +rust-ini = "0.21.3" + +[dependencies.crossterm] +version = "0.29.0" +features = ["event-stream"] + +[dependencies.tokio] +version = "1.47.1" +features = ["full"] + +[dependencies.diesel] +version = "2.3.2" +features = ["sqlite"] + +[dependencies.libsqlite3-sys] +version = "0.35.0" +features = ["bundled"] + +[dependencies.uuid] +version = "1.18.1" +features = ["v4"] diff --git a/migrations/2025-10-06-111516-0000_dl_games/down.sql b/migrations/2025-10-06-111516-0000_dl_games/down.sql new file mode 100644 index 0000000..5ae38d5 --- /dev/null +++ b/migrations/2025-10-06-111516-0000_dl_games/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +DROP TABLE dl_games; \ No newline at end of file diff --git a/migrations/2025-10-06-111516-0000_dl_games/up.sql b/migrations/2025-10-06-111516-0000_dl_games/up.sql new file mode 100644 index 0000000..9159bbf --- /dev/null +++ b/migrations/2025-10-06-111516-0000_dl_games/up.sql @@ -0,0 +1,4 @@ +-- Your SQL goes here +CREATE TABLE dl_games ( + serial_number CHARACTER(10) NOT NULL PRIMARY KEY +) \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index a486ddc..3b431ba 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,27 +1,74 @@ +use std::env; +use std::path::PathBuf; use crate::event::{Event, EventHandler}; -use ratatui::widgets::Block; -use ratatui::{DefaultTerminal, Frame}; +use ratatui::widgets::{Block, Borders, Paragraph}; +use ratatui::{DefaultTerminal}; use std::time::Duration; use color_eyre::Result; +use crossterm::event; use crossterm::event::KeyCode::Char; -use ratatui::layout::Alignment; +use diesel::{AggregateExpressionMethods, Connection, SqliteConnection}; +use directories::BaseDirs; +use dotenvy::dotenv; +use lazy_static::lazy_static; +use ratatui::buffer::Buffer; +use ratatui::layout::{Constraint, Direction, Layout, Rect}; +use ratatui::prelude::{Widget}; +use ratatui::style::{Color, Style}; +use ratatui::text::{Line, Span, Text}; +use crate::config::types::ApplicationConfig; + +const APP_DIR_NAME: &str = "sus_manager"; +lazy_static! { + static ref BASE_DIRS: BaseDirs = BaseDirs::new().unwrap(); + static ref APP_CONFIG_DIR: PathBuf = BASE_DIRS.config_dir().to_path_buf() + .join(APP_DIR_NAME); + static ref APP_DATA_DIR: PathBuf = BASE_DIRS.data_dir().to_path_buf() + .join(APP_DIR_NAME); + static ref APP_CONIFG_FILE_PATH: PathBuf = APP_CONFIG_DIR.clone() + .join("config.ini"); +} pub(crate) struct App { running: bool, events: EventHandler, + db_connection: SqliteConnection, + app_config: ApplicationConfig, } impl App { pub fn new() -> App { + let app_conf = + if APP_CONIFG_FILE_PATH.exists() { ApplicationConfig::from_file(&APP_CONIFG_FILE_PATH) } + else { ApplicationConfig::new() }; + let db_conn = Self::establish_db_connection(app_conf.clone()); + Self::initialize(&db_conn); App { running: true, - events: EventHandler::new(Duration::from_millis(250)), + events: EventHandler::new(Duration::from_millis(app_conf.basic_config.tick_rate)), + db_connection: db_conn, + app_config: app_conf } } - pub async fn run(&mut self, mut terminal: DefaultTerminal) -> Result<()> { + fn initialize(db_conn: &SqliteConnection) { + if !APP_CONFIG_DIR.exists() { + std::fs::create_dir_all(APP_CONFIG_DIR.as_path()).unwrap(); + } + if !APP_DATA_DIR.exists() { + std::fs::create_dir_all(APP_DATA_DIR.as_path()).unwrap(); + } + } + + fn establish_db_connection(application_config: ApplicationConfig) -> SqliteConnection { + let database_url = application_config.basic_config.db_path; + SqliteConnection::establish(&database_url) + .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) + } + + pub async fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { loop { - terminal.draw(|frame| App::render(self, frame))?; + terminal.draw(|frame| frame.render_widget(&mut self, frame.area()))?; let event = self.events.next().await?; self.update(event)?; if !self.running { @@ -31,7 +78,7 @@ impl App { } fn update(&mut self, event: Event) -> Result<()> { - if let Event::Key(key) = event { + if let Event::Key(key) = event && event::KeyEventKind::is_press(&key.kind) { match key.code { Char('q') => self.quit(), _ => {} @@ -39,17 +86,66 @@ impl App { } Ok(()) } +} - fn quit(&mut self) { - self.running = false; - } +impl Widget for &mut App { + fn render(self, area: Rect, buf: &mut Buffer) + where Self: Sized + { + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(1), + Constraint::Min(1), + Constraint::Length(3), + ]) + .split(area); + + self.render_header(chunks[0], buf); + self.render_game_list(chunks[1], buf); + self.render_footer(chunks[2], buf); - fn render(&mut self, frame: &mut Frame) { - frame.render_widget( - Block::bordered() - .title("Sus Manager") - .title_alignment(Alignment::Center), - frame.area() - ); } } + +// render widgets +impl App { + fn render_game_list(&mut self, area: Rect, buf: &mut Buffer) { + let game_list = Block::new() + .title(Line::raw("Games")) + .borders(Borders::ALL) + .style(Style::default()); + + game_list.render(area, buf); + } + + fn render_header(&mut self, area: Rect, buf: &mut Buffer) { + let title = Paragraph::new( + Text::styled( + "SuS Manager", + Style::default().fg(Color::Green), + ) + ); + title.render(area, buf); + } + + fn render_footer(&mut self, area: Rect, buf: &mut Buffer) { + let navigation_text = vec![ + Span::styled("(q) quit / (a) add folders", Style::default().fg(Color::Green)), + ]; + let line = Line::from(navigation_text); + let footer = Paragraph::new(line); + footer.render(area, buf); + } +} + + +// event handlers +impl App { + fn quit(&mut self) { + self.running = false; + self.app_config + .clone() + .write_to_file(APP_CONIFG_FILE_PATH.to_path_buf()); + } +} \ No newline at end of file diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..8ca2ff0 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,36 @@ +use std::path::{PathBuf}; +use ini::Ini; +use crate::config::types::{ApplicationConfig, BasicConfig}; + +pub mod types; + +impl ApplicationConfig { + pub fn from_file(path: &PathBuf) -> Self { + let conf = Ini::load_from_file(path).unwrap(); + let basic_conf_section = conf.section(Some("Basic")).unwrap(); + let basic_conf = BasicConfig { + db_path: basic_conf_section.get("DBPath").unwrap().to_string(), + tick_rate: basic_conf_section.get("TickRate").unwrap().parse().unwrap(), + }; + Self { + basic_config: basic_conf + } + } + + pub fn new() -> Self { + Self { + basic_config: BasicConfig { + db_path: "games.db".to_string(), + tick_rate: 250 + } + } + } + + pub fn write_to_file(self, path: PathBuf) { + let mut conf = Ini::new(); + conf.with_section(Some("Basic")) + .set("DBPath", self.basic_config.db_path) + .set("TickRate", self.basic_config.tick_rate.to_string()); + conf.write_to_file(path).unwrap(); + } +} \ No newline at end of file diff --git a/src/config/types.rs b/src/config/types.rs new file mode 100644 index 0000000..168e1da --- /dev/null +++ b/src/config/types.rs @@ -0,0 +1,10 @@ +#[derive(Clone)] +pub(crate) struct ApplicationConfig { + pub(crate) basic_config: BasicConfig, +} + +#[derive(Clone)] +pub(crate) struct BasicConfig { + pub(crate) db_path: String, + pub(crate) tick_rate: u64 +} \ No newline at end of file diff --git a/src/event.rs b/src/event.rs index af3d04f..8c2dc42 100644 --- a/src/event.rs +++ b/src/event.rs @@ -22,7 +22,7 @@ pub(crate) struct EventHandler { impl EventHandler { pub fn new(tick_rate: Duration) -> Self { let mut interval = tokio::time::interval(tick_rate); - let mut event_reader = crossterm::event::EventStream::new(); + let mut event_reader = event::EventStream::new(); let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); let _tx = tx.clone(); @@ -37,8 +37,7 @@ impl EventHandler { tx.send(Event::Error).unwrap() } else if let Some(Ok(event)) = maybe_event && - let event::Event::Key(key) = event && - key.kind == event::KeyEventKind::Press + let event::Event::Key(key) = event { tx.send(Event::Key(key)).unwrap() } diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/main.rs b/src/main.rs index 54de0a2..9e16f07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,9 @@ mod app; mod event; +mod schema; +mod types; +mod config; +mod helpers; use color_eyre::Result; use tokio; @@ -8,7 +12,7 @@ use tokio; async fn main() -> Result<()> { color_eyre::install()?; let terminal = ratatui::init(); - let mut app = app::App::new(); + let app = app::App::new(); let result = app.run(terminal).await; ratatui::restore(); result diff --git a/src/schema.rs b/src/schema.rs new file mode 100644 index 0000000..8144187 --- /dev/null +++ b/src/schema.rs @@ -0,0 +1,7 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + dl_games (serial_number) { + serial_number -> Text, + } +} diff --git a/src/types/game.rs b/src/types/game.rs new file mode 100644 index 0000000..7dafe46 --- /dev/null +++ b/src/types/game.rs @@ -0,0 +1,14 @@ +use diesel::{Queryable, Selectable}; +use ratatui::widgets::ListState; + +pub(crate) struct GameList { + games: Vec, + state: ListState, +} + +#[derive(Queryable, Selectable)] +#[diesel(table_name = crate::schema::dl_games)] +#[diesel(check_for_backend(diesel::sqlite::Sqlite))] +pub(crate) struct DLSiteGame { + serial_number: String +} diff --git a/src/types/mod.rs b/src/types/mod.rs new file mode 100644 index 0000000..68c160d --- /dev/null +++ b/src/types/mod.rs @@ -0,0 +1 @@ +pub mod game; \ No newline at end of file