diff --git a/README.md b/README.md index ea2aaff..b3d5028 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ [Coursework](https://gitea.linerds.us/0x1D8/repo) paper, declared in [typst](https://typst.app/) v0.13, using [this template](https://gitea.linerds.us/pencelheimer/typst_nure_template). > [!CAUTION] -> The [template.typ](template.typ) file was included for reproducibility, it has it's own [license terms](https://gitea.linerds.us/pencelheimer/typst_nure_template/src/branch/main/LICENSE). +> The [template.typ](template.typ) file was included for reproducibility, it has it's own [license terms](https://gitea.linerds.us/pencelheimer/typst_nure_template/src/branch/main/LICENSE) (GPL-3.0 at the time of writing). diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..b59bb3c --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,2 @@ +/target/ +/**/scrapyard/ diff --git a/src/Cargo.lock b/src/Cargo.lock new file mode 100644 index 0000000..721c42b --- /dev/null +++ b/src/Cargo.lock @@ -0,0 +1,5381 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom 0.2.15", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.8.0", + "cc", + "cesu8", + "jni", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys 0.6.0+11769913", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +dependencies = [ + "serde", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "by_address" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" + +[[package]] +name = "bytemuck" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.8.0", + "log", + "polling", + "rustix", + "slab", + "thiserror 1.0.69", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.52.6", +] + +[[package]] +name = "clipboard-win" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +dependencies = [ + "error-code", +] + +[[package]] +name = "clipboard_macos" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "clipboard_wayland" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003f886bc4e2987729d10c1db3424e7f80809f3fc22dbc16c685738887cb37b8" +dependencies = [ + "smithay-clipboard", +] + +[[package]] +name = "clipboard_x11" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4274ea815e013e0f9f04a2633423e14194e408a0576c943ce3d14ca56c50031c" +dependencies = [ + "thiserror 1.0.69", + "x11rb", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "com" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" +dependencies = [ + "com_macros", +] + +[[package]] +name = "com_macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" +dependencies = [ + "com_macros_support", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "com_macros_support" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "compact_str" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[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]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.10.0", + "core-graphics-types 0.2.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.10.0", + "libc", +] + +[[package]] +name = "cosmic-text" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" +dependencies = [ + "bitflags 2.8.0", + "fontdb", + "log", + "rangemap", + "rayon", + "rustc-hash 1.1.0", + "rustybuzz", + "self_cell", + "swash", + "sys-locale", + "ttf-parser 0.21.1", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "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]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctor-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f791803201ab277ace03903de1594460708d2d54df6053f2d9e82f592b19e3b" + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + +[[package]] +name = "d3d12" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" +dependencies = [ + "bitflags 2.8.0", + "libloading 0.8.6", + "winapi", +] + +[[package]] +name = "dark-light" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a76fa97167fa740dcdbfe18e8895601e1bc36525f09b044e00916e717c03a3c" +dependencies = [ + "dconf_rs", + "detect-desktop-environment", + "dirs", + "objc", + "rust-ini", + "web-sys", + "winreg", + "zbus", +] + +[[package]] +name = "data" +version = "0.1.0" +dependencies = [ + "chrono", + "derive_more", + "futures", + "sqlx", +] + +[[package]] +name = "dconf_rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" + +[[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]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "detect-desktop-environment" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21d8ad60dd5b13a4ee6bd8fa2d5d88965c597c67bce32b5fc49c94f55cb50810" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.6", +] + +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + +[[package]] +name = "drm" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" +dependencies = [ + "bitflags 2.8.0", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "rustix", +] + +[[package]] +name = "drm-ffi" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97c98727e48b7ccb4f4aea8cfe881e5b07f702d17b7875991881b41af7278d53" +dependencies = [ + "drm-sys", + "rustix", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd39dde40b6e196c2e8763f23d119ddb1a8714534bf7d77fa97a65b0feda3986" +dependencies = [ + "libc", + "linux-raw-sys 0.6.5", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "error-code" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" + +[[package]] +name = "etagere" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc89bf99e5dc15954a60f707c1e09d7540e5cd9af85fa75caa0b510bc08c5342" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[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 = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fast-srgb8" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + +[[package]] +name = "font-types" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "fontconfig-parser" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser 0.20.0", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", + "num_cpus", +] + +[[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 0.12.3", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "garde" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a989bd2fd12136080f7825ff410d9239ce84a2a639487fc9d924ee42e2fb84f" +dependencies = [ + "compact_str", + "garde_derive", + "once_cell", + "regex", + "smallvec", + "url", +] + +[[package]] +name = "garde_derive" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f7f0545bbbba0a37d4d445890fa5759814e0716f02417b39f6fab292193df68" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.98", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" + +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.8.0", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.8.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "winapi", + "windows", +] + +[[package]] +name = "gpu-descriptor" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" +dependencies = [ + "bitflags 2.8.0", + "gpu-descriptor-types", + "hashbrown 0.14.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" +dependencies = [ + "bitflags 2.8.0", +] + +[[package]] +name = "guillotiere" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.2", +] + +[[package]] +name = "hassle-rs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" +dependencies = [ + "bitflags 2.8.0", + "com", + "libc", + "libloading 0.8.6", + "thiserror 1.0.69", + "widestring", + "winapi", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "iced" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88acfabc84ec077eaf9ede3457ffa3a104626d79022a9bf7f296093b1d60c73f" +dependencies = [ + "iced_core", + "iced_futures", + "iced_renderer", + "iced_widget", + "iced_winit", + "thiserror 1.0.69", +] + +[[package]] +name = "iced_core" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0013a238275494641bf8f1732a23a808196540dc67b22ff97099c044ae4c8a1c" +dependencies = [ + "bitflags 2.8.0", + "bytes", + "dark-light", + "glam", + "log", + "num-traits", + "once_cell", + "palette", + "rustc-hash 2.1.1", + "smol_str", + "thiserror 1.0.69", + "web-time", +] + +[[package]] +name = "iced_futures" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c04a6745ba2e80f32cf01e034fd00d853aa4f4cd8b91888099cb7aaee0d5d7c" +dependencies = [ + "futures", + "iced_core", + "log", + "rustc-hash 2.1.1", + "tokio", + "wasm-bindgen-futures", + "wasm-timer", +] + +[[package]] +name = "iced_glyphon" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41c3bb56f1820ca252bc1d0994ece33d233a55657c0c263ea7cb16895adbde82" +dependencies = [ + "cosmic-text", + "etagere", + "lru", + "rustc-hash 2.1.1", + "wgpu", +] + +[[package]] +name = "iced_graphics" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba25a18cfa6d5cc160aca7e1b34f73ccdff21680fa8702168c09739767b6c66f" +dependencies = [ + "bitflags 2.8.0", + "bytemuck", + "cosmic-text", + "half", + "iced_core", + "iced_futures", + "log", + "once_cell", + "raw-window-handle", + "rustc-hash 2.1.1", + "thiserror 1.0.69", + "unicode-segmentation", +] + +[[package]] +name = "iced_renderer" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73558208059f9e622df2bf434e044ee2f838ce75201a023cf0ca3e1244f46c2a" +dependencies = [ + "iced_graphics", + "iced_tiny_skia", + "iced_wgpu", + "log", + "thiserror 1.0.69", +] + +[[package]] +name = "iced_runtime" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348b5b2c61c934d88ca3b0ed1ed913291e923d086a66fa288ce9669da9ef62b5" +dependencies = [ + "bytes", + "iced_core", + "iced_futures", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "iced_tiny_skia" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c625d368284fcc43b0b36b176f76eff1abebe7959dd58bd8ce6897d641962a50" +dependencies = [ + "bytemuck", + "cosmic-text", + "iced_graphics", + "kurbo", + "log", + "rustc-hash 2.1.1", + "softbuffer", + "tiny-skia", +] + +[[package]] +name = "iced_wgpu" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15708887133671d2bcc6c1d01d1f176f43a64d6cdc3b2bf893396c3ee498295f" +dependencies = [ + "bitflags 2.8.0", + "bytemuck", + "futures", + "glam", + "guillotiere", + "iced_glyphon", + "iced_graphics", + "log", + "once_cell", + "rustc-hash 2.1.1", + "thiserror 1.0.69", + "wgpu", +] + +[[package]] +name = "iced_widget" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81429e1b950b0e4bca65be4c4278fea6678ea782030a411778f26fa9f8983e1d" +dependencies = [ + "iced_renderer", + "iced_runtime", + "num-traits", + "once_cell", + "ouroboros", + "rustc-hash 2.1.1", + "thiserror 1.0.69", + "unicode-segmentation", +] + +[[package]] +name = "iced_winit" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f44cd4e1c594b6334f409282937bf972ba14d31fedf03c23aa595d982a2fda28" +dependencies = [ + "iced_futures", + "iced_graphics", + "iced_runtime", + "log", + "rustc-hash 2.1.1", + "thiserror 1.0.69", + "tracing", + "wasm-bindgen-futures", + "web-sys", + "winapi", + "window_clipboard", + "winit", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading 0.8.6", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "kurbo" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1618d4ebd923e97d67e7cd363d80aef35fe961005cbbbb3d2dad8bdd1bc63440" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.8.0", + "libc", + "redox_syscall 0.5.8", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a385b1be4e5c3e362ad2ffa73c392e53f031eaa5b7d648e64cd87f27f6063d7" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[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]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" +dependencies = [ + "bitflags 2.8.0", + "block", + "core-graphics-types 0.1.3", + "foreign-types", + "log", + "objc", + "paste", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "naga" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" +dependencies = [ + "bit-set", + "bitflags 2.8.0", + "codespan-reporting", + "hexf-parse", + "indexmap", + "log", + "num-traits", + "rustc-hash 1.1.0", + "spirv", + "termcolor", + "thiserror 1.0.69", + "unicode-xid", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.8.0", + "jni-sys", + "log", + "ndk-sys 0.6.0+11769913", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.5.0+25.2.9519653" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.8.0", + "cfg-if", + "cfg_aliases 0.2.1", + "libc", + "memoffset", +] + +[[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]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.8.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.8.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "objc2-link-presentation", + "objc2-quartz-core", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] +name = "open" +version = "5.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "orbclient" +version = "0.3.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +dependencies = [ + "libredox", +] + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown 0.12.3", +] + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "ouroboros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +dependencies = [ + "ttf-parser 0.25.1", +] + +[[package]] +name = "palette" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +dependencies = [ + "approx", + "fast-srgb8", + "palette_derive", + "phf", +] + +[[package]] +name = "palette_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +dependencies = [ + "by_address", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.8", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[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]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "version_check", + "yansi", +] + +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" + +[[package]] +name = "quick-xml" +version = "0.37.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "range-alloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" + +[[package]] +name = "rangemap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "read-fonts" +version = "0.22.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f" +dependencies = [ + "bytemuck", + "font-types", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[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]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.8.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "repo" +version = "0.1.0" +dependencies = [ + "data", + "iced", + "open", + "service", + "strum", +] + +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + +[[package]] +name = "rsa" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.8.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "rustybuzz" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +dependencies = [ + "bitflags 2.8.0", + "bytemuck", + "libm", + "smallvec", + "ttf-parser 0.21.1", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] + +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "self_cell" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "service" +version = "0.1.0" +dependencies = [ + "argon2", + "data", + "derive_more", + "garde", + "thiserror 2.0.11", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "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]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "skrifa" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe" +dependencies = [ + "bytemuck", + "read-fonts", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.8.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" +dependencies = [ + "libc", + "smithay-client-toolkit", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "softbuffer" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18051cdd562e792cad055119e0cdb2cfc137e44e3987532e0f9659a77931bb08" +dependencies = [ + "as-raw-xcb-connection", + "bytemuck", + "cfg_aliases 0.2.1", + "core-graphics 0.24.0", + "drm", + "fastrand", + "foreign-types", + "js-sys", + "log", + "memmap2", + "objc2", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall 0.5.8", + "rustix", + "tiny-xlib", + "wasm-bindgen", + "wayland-backend", + "wayland-client", + "wayland-sys", + "web-sys", + "windows-sys 0.59.0", + "x11rb", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spirv" +version = "0.3.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +dependencies = [ + "bitflags 2.8.0", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlx" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" +dependencies = [ + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.2", + "hashlink", + "indexmap", + "log", + "memchr", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "thiserror 2.0.11", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.98", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" +dependencies = [ + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.98", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" +dependencies = [ + "atoi", + "base64", + "bitflags 2.8.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 2.0.11", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" +dependencies = [ + "atoi", + "base64", + "bitflags 2.8.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror 2.0.11", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" +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 = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[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]] +name = "strum" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1475c515a4f03a8a7129bb5228b81a781a86cb0b3fbbc19e1c556d491a401f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9688894b43459159c82bfa5a5fa0435c19cbe3c9b427fa1dd7b1ce0c279b18a7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.98", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svg_fmt" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa" + +[[package]] +name = "swash" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2" +dependencies = [ + "skrifa", + "yazi", + "zeno", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "tempfile" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom 0.3.1", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tiny-xlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" +dependencies = [ + "as-raw-xcb-connection", + "ctor-lite", + "libloading 0.8.6", + "pkg-config", + "tracing", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "ttf-parser" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" + +[[package]] +name = "unicode-ccc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" + +[[package]] +name = "unicode-script" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.98", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wayland-backend" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +dependencies = [ + "bitflags 2.8.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.8.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +dependencies = [ + "bitflags 2.8.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" +dependencies = [ + "bitflags 2.8.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" +dependencies = [ + "bitflags 2.8.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wgpu" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01" +dependencies = [ + "arrayvec", + "cfg-if", + "cfg_aliases 0.1.1", + "js-sys", + "log", + "naga", + "parking_lot 0.12.3", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 2.8.0", + "cfg_aliases 0.1.1", + "codespan-reporting", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot 0.12.3", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.19.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfabcfc55fd86611a855816326b2d54c3b2fd7972c27ce414291562650552703" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.8.0", + "block", + "cfg_aliases 0.1.1", + "core-graphics-types 0.1.3", + "d3d12", + "glow", + "glutin_wgl_sys", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hassle-rs", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.6", + "log", + "metal", + "naga", + "ndk-sys 0.5.0+25.2.9519653", + "objc", + "once_cell", + "parking_lot 0.12.3", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "rustc-hash 1.1.0", + "smallvec", + "thiserror 1.0.69", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" +dependencies = [ + "bitflags 2.8.0", + "js-sys", + "web-sys", +] + +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall 0.5.8", + "wasite", +] + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window_clipboard" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d692d46038c433f9daee7ad8757e002a4248c20b0a3fbc991d99521d3bcb6d" +dependencies = [ + "clipboard-win", + "clipboard_macos", + "clipboard_wayland", + "clipboard_x11", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winit" +version = "0.30.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" +dependencies = [ + "ahash 0.8.11", + "android-activity", + "atomic-waker", + "bitflags 2.8.0", + "block2", + "bytemuck", + "calloop", + "cfg_aliases 0.2.1", + "concurrent-queue", + "core-foundation 0.9.4", + "core-graphics 0.23.2", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading 0.8.6", + "once_cell", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" + +[[package]] +name = "xcursor" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" + +[[package]] +name = "xdg-home" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.8.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "xml-rs" +version = "0.8.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yazi" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "synstructure", +] + +[[package]] +name = "zbus" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.98", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] + +[[package]] +name = "zeno" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "zvariant" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive", +] + +[[package]] +name = "zvariant_derive" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.98", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] diff --git a/src/Cargo.toml b/src/Cargo.toml new file mode 100644 index 0000000..27bd69f --- /dev/null +++ b/src/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "repo" +version = "0.1.0" +edition = "2024" + +[dependencies] +data = { path = "data" } +service = { path = "service" } + +iced = { version = "0.13.1", features = ["tokio", "lazy"] } +strum = { version = "0.27.0", features = ["derive"] } +open = "5.3.2" + +[workspace] +resolver = "2" +members = ["data", "service"] diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..8376556 --- /dev/null +++ b/src/README.md @@ -0,0 +1,20 @@ +# Stuff that helped + +* Architecture: + - [How to apply hexagonal architecture to Rust](https://www.barrage.net/blog/technology/how-to-apply-hexagonal-architecture-to-rust) + - [Implementing onion architecture using Rust](https://mathias-vandaele.dev/implementing-onion-architecture-using-rust) +* Design: + - [Rust Data Modelling Without Classes](https://www.youtube.com/watch?v=z-0-bbc80JM) + - ["Making Impossible States Impossible" by Richard Feldman](https://www.youtube.com/watch?v=IcgmSRJHu_8) + - [Pretty State Machine Patterns in Rust](https://hoverbear.org/blog/rust-state-machine-pattern/) +* How to Iced: + - [Building a simple text editor with iced, a cross-platform GUI library for Rust](https://www.youtube.com/watch?v=gcBJ7cPSALo) + - [Unofficial Iced Guide](https://jl710.github.io/iced-guide/) + - [icebreaker](https://github.com/hecrj/icebreaker) + - [Halloy](https://github.com/squidowl/halloy) + +--- + +> _The purpose of abstraction is not to be vague, but to create a new semantic level in which one can be absolutely precise._ + +— _Edsger W. Dijkstra_ diff --git a/src/assets/compose.yaml b/src/assets/compose.yaml new file mode 100644 index 0000000..3faafd3 --- /dev/null +++ b/src/assets/compose.yaml @@ -0,0 +1,11 @@ +services: + database: + image: mysql:9.0 + restart: unless-stopped + ports: + - "127.0.0.1:3306:3306" + volumes: + - ../../repo-database:/var/lib/mysql + - ./init:/docker-entrypoint-initdb.d/:ro + environment: + MYSQL_ROOT_PASSWORD: password # yes, I know diff --git a/src/assets/init/0-init.sql b/src/assets/init/0-init.sql new file mode 100644 index 0000000..73da14c --- /dev/null +++ b/src/assets/init/0-init.sql @@ -0,0 +1,191 @@ +-- DROP DATABASE IF EXISTS repository; +CREATE DATABASE repository; +USE repository; + +-- Required info for an account +CREATE TABLE Users ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(31) UNIQUE NOT NULL, + email VARCHAR(255) UNIQUE NOT NULL, + password VARCHAR(255) NOT NULL, + + last_used TIMESTAMP NULL, + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +-- Enables multiple packages to have the same base yet different components +CREATE TABLE PackageBases ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(127) UNIQUE NOT NULL, + description VARCHAR(510) NULL, + + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +); + +-- User roles for working on packages: flagger, packager, submitter, maintainer, etc. +CREATE TABLE PackageBaseRoles ( + id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(31) UNIQUE NOT NULL, + description VARCHAR(255) NULL +); +INSERT INTO PackageBaseRoles (id, name) VALUES +(1, 'submitter'), +(2, 'packager'), +(3, 'maintainer'), +(4, 'flagger'); + +-- Roles that a user has for a package +CREATE TABLE PackageBaseUserRoles ( + base INT UNSIGNED, + user INT UNSIGNED, + role TINYINT UNSIGNED, + + comment VARCHAR(255) NULL, + + PRIMARY KEY (base, user, role), -- composite key + FOREIGN KEY (base) REFERENCES PackageBases(id) ON DELETE CASCADE, + FOREIGN KEY (user) REFERENCES Users(id) ON DELETE CASCADE, + FOREIGN KEY (role) REFERENCES PackageBaseRoles(id) ON DELETE CASCADE +); + +-- Information about the actual packages +CREATE TABLE Packages ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + base INT UNSIGNED NOT NULL, + name VARCHAR(127) UNIQUE NOT NULL, + version VARCHAR(127) NOT NULL, + description VARCHAR(255) NULL, + url VARCHAR(510) NULL, + + flagged_at TIMESTAMP NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + + FOREIGN KEY (base) REFERENCES PackageBases (id) ON DELETE CASCADE +); + +-- depends, makedepends, optdepends, etc. +CREATE TABLE DependencyTypes ( + id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(31) UNIQUE NOT NULL +); +INSERT INTO DependencyTypes (id, name) VALUES +(1, 'depends'), +(2, 'makedepends'), +(3, 'checkdepends'), +(4, 'optdepends'); + +-- Track which dependencies a package has +CREATE TABLE PackageDependencies ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + arch VARCHAR(63) NULL, + requirement VARCHAR(255) NULL, + description VARCHAR(127) NULL, + + package INT UNSIGNED NOT NULL, + dependency_type TINYINT UNSIGNED NOT NULL, + dependency_package_name VARCHAR(127) NOT NULL, -- Not an actual package, but an an alias. Allows for package substitution. + + FOREIGN KEY (package) REFERENCES Packages (id) ON DELETE CASCADE, + FOREIGN KEY (dependency_type) REFERENCES DependencyTypes (id) +); + +-- conflicts, provides, replaces, etc. +CREATE TABLE RelationTypes ( + id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(31) UNIQUE NOT NULL +); +INSERT INTO RelationTypes (id, name) VALUES +(1, 'conflicts'), +(2, 'provides'), +(3, 'replaces'); + +-- Track which conflicts, provides and replaces a package has +CREATE TABLE PackageRelations ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + arch VARCHAR(63) NULL, + requirement VARCHAR(255) NULL, + + package INT UNSIGNED NOT NULL, + relation_type TINYINT UNSIGNED NOT NULL, + relation_package_name VARCHAR(127) NOT NULL, + + FOREIGN KEY (package) REFERENCES Packages (id) ON DELETE CASCADE, + FOREIGN KEY (relation_type) REFERENCES RelationTypes (id) +); + +-- Public user profile +/* CREATE TABLE UserProfiles ( user_id INT UNSIGNED PRIMARY KEY, + real_name VARCHAR(63) NULL, + homepage TEXT NULL, -- bio / description / whatever + irc_nick VARCHAR(31) NULL, + pgp_key CHAR(40) NULL, + language VARCHAR(31) NULL, -- only for display + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, + + FOREIGN KEY (user_id) REFERENCES Users(id) ON DELETE CASCADE +); */ + +-- Settings for the User +/* CREATE TABLE UserPreferences ( user_id INT UNSIGNED PRIMARY KEY, + inactive BOOLEAN DEFAULT 0 NOT NULL, -- user is no longer active + show_email BOOLEAN DEFAULT 0 NOT NULL, -- on public profile page + utc_timezone TINYINT DEFAULT 0 NOT NULL, -- adjust timestamps shown + backup_email VARCHAR(127) NULL, -- to restore the account + + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, + + FOREIGN KEY (user_id) REFERENCES Users(id) ON DELETE CASCADE +); */ + +-- Levels of access to the repository +/* CREATE TABLE AccessRoles ( id TINYINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(31) UNIQUE NOT NULL, + description VARCHAR(255) NULL +); */ +-- Roles that a user has +/* CREATE TABLE UserAccessRoles ( + user_id INT UNSIGNED, + role_id TINYINT UNSIGNED, + + PRIMARY KEY (user_id, role_id), -- composite key + FOREIGN KEY (user_id) REFERENCES Users(id) ON DELETE CASCADE, + FOREIGN KEY (role_id) REFERENCES AccessRoles(id) ON DELETE CASCADE +); */ + +-- Votes +/* CREATE TABLE PackageBaseUserVotes ( + package_base INT UNSIGNED, + user INT UNSIGNED, + score TINYINT UNSIGNED DEFAULT 0 NOT NULL CHECK (score <= 10), + + comment VARCHAR(255) NULL, + log TEXT NULL, -- error logs, etc. + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, + + PRIMARY KEY (package_base, user), -- composite key + FOREIGN KEY (package_base) REFERENCES PackageBases (id) ON DELETE CASCADE, + FOREIGN KEY (user) REFERENCES Users (id) ON DELETE CASCADE +); */ + +-- Information about licenses +/* CREATE TABLE Licenses ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(127) UNIQUE NOT NULL, + description TEXT NULL +); */ +-- Information about licenses +/* CREATE TABLE PackageLicenses ( + package INT UNSIGNED, + license INT UNSIGNED, + + PRIMARY KEY (package, license), -- composite key + FOREIGN KEY (package) REFERENCES Packages (id) ON DELETE CASCADE, + FOREIGN KEY (license) REFERENCES Licenses (id) ON DELETE CASCADE +); */ diff --git a/src/assets/init/1-data.sql b/src/assets/init/1-data.sql new file mode 100644 index 0000000..e7ea4b4 --- /dev/null +++ b/src/assets/init/1-data.sql @@ -0,0 +1,103 @@ +-- Insert Users +INSERT INTO Users (name, email, password, last_used) VALUES +('alice', 'alice@example.com', 'password123', NOW()), +('bob', 'bob@example.com', 'securepass', NOW()), +('charlie', 'charlie@example.com', 'charliepwd', NOW()), +('dave', 'dave@example.com', 'davepass', NOW()), +('eve', 'eve@example.com', 'evepwd', NOW()), +('frank', 'frank@example.com', 'frankpass', NOW()), +('grace', 'grace@example.com', 'gracepwd', NOW()), +('heidi', 'heidi@example.com', 'heidipwd', NOW()), +('ivan', 'ivan@example.com', 'ivanpass', NOW()), +('judy', 'judy@example.com', 'judypass', NOW()), +('mallory', 'mallory@example.com', 'mallorypwd', NOW()), +('oscar', 'oscar@example.com', 'oscarpass', NOW()), +('peggy', 'peggy@example.com', 'peggypwd', NOW()), +('trent', 'trent@example.com', 'trentpass', NOW()), +('victor', 'victor@example.com', 'victorpwd', NOW()); + +-- Insert PackageBases +INSERT INTO PackageBases (name, description) VALUES +('libcore', 'Core system libraries'), +('webframework', 'A modern web framework'), +('dataproc', 'Data processing toolkit'), +('authmodule', 'Authentication and authorization module'), +('networkstack', 'Networking utilities and stack'), +('uikit', 'UI Kit for building interfaces'), +('cryptoengine', 'Cryptographic library'), +('dbconnector', 'Database connectivity drivers'), +('imageproc', 'Image processing library'), +('audiokit', 'Audio toolkit'), +('videokit', 'Video processing toolkit'), +('mlcore', 'Machine Learning core library'), +('analyticspro', 'Advanced analytics toolkit'), +('monitoragent', 'System monitoring agent'), +('filesystem', 'Filesystem utilities'); + +-- Assign Roles to Users for PackageBases +INSERT INTO PackageBaseUserRoles (base, user, role, comment) VALUES +(1, 1, 1, 'Original submitter'), +(1, 2, 2, 'Packager for latest release'), +(2, 3, 3, 'Maintains stability'), +(2, 4, 4, 'Flags issues'), +(3, 5, 1, 'Initial submission'), +(3, 6, 3, 'Lead maintainer'), +(4, 7, 2, 'Core packager'), +(5, 8, 1, 'Submitted new version'), +(6, 9, 4, 'Flagged for performance issues'), +(7, 10, 3, 'Maintainer for security fixes'), +(8, 11, 2, 'Driver package manager'), +(9, 12, 1, 'Original contributor'), +(10, 13, 3, 'Maintains core features'), +(11, 14, 4, 'Reported critical bug'), +(12, 15, 2, 'Optimized build process'); + +-- Insert Packages +INSERT INTO Packages (base, name, version, description, url) VALUES +(1, 'libcore-utils', '1.0.0', 'Utilities for libcore', 'http://example.com/libcore-utils'), +(1, 'libcore-extended', '1.1.0', 'Extended functionalities', 'http://example.com/libcore-extended'), +(2, 'webframework-api', '2.0.0', 'REST API module', 'http://example.com/webframework-api'), +(2, 'webframework-cli', '2.1.0', 'Command-line tools', 'http://example.com/webframework-cli'), +(3, 'dataproc-engine', '3.0.1', 'Data processing engine', 'http://example.com/dataproc-engine'), +(4, 'authmodule-oauth', '4.2.0', 'OAuth module', 'http://example.com/authmodule-oauth'), +(5, 'networkstack-core', '5.5.0', 'Core network stack', 'http://example.com/networkstack-core'), +(6, 'uikit-designer', '6.0.3', 'UI designer toolkit', 'http://example.com/uikit-designer'), +(7, 'cryptoengine-hash', '7.1.1', 'Hash algorithms', 'http://example.com/cryptoengine-hash'), +(8, 'dbconnector-mysql', '8.0.0', 'MySQL connector', 'http://example.com/dbconnector-mysql'), +(9, 'imageproc-filters', '9.3.0', 'Image filters library', 'http://example.com/imageproc-filters'), +(10, 'audiokit-mixer', '10.2.1', 'Audio mixing toolkit', 'http://example.com/audiokit-mixer'), +(11, 'videokit-stream', '11.4.0', 'Video streaming tools', 'http://example.com/videokit-stream'), +(12, 'mlcore-algo', '12.0.2', 'ML algorithms', 'http://example.com/mlcore-algo'), +(13, 'analyticspro-dashboard', '13.5.1', 'Analytics dashboard', 'http://example.com/analyticspro-dashboard'); + +-- Insert PackageDependencies +INSERT INTO PackageDependencies (arch, requirement, description, package, dependency_type, dependency_package_name) VALUES +('x86_64', '>=1.0.0', 'Core dependency', 3, 1, 'libcore-utils'), +('x86_64', '>=2.0.0', 'Required for API', 4, 2, 'webframework-api'), +('arm64', '>=3.0.1', 'Optional analytics', 5, 4, 'analyticspro-dashboard'), +('x86_64', '>=5.5.0', 'Network stack dependency', 6, 1, 'networkstack-core'), +('x86_64', '>=4.2.0', 'Authentication module', 7, 1, 'authmodule-oauth'), +('x86_64', NULL, 'Database driver', 8, 1, 'dbconnector-mysql'), +('arm64', NULL, 'Machine learning algorithms', 9, 3, 'mlcore-algo'), +('x86_64', '>=6.0.3', 'UI designer toolkit', 10, 1, 'uikit-designer'), +('x86_64', NULL, 'Audio toolkit dependency', 11, 2, 'audiokit-mixer'), +('x86_64', '>=7.1.1', 'Hash functions', 12, 1, 'cryptoengine-hash'), +('arm64', NULL, 'Video streaming tools', 13, 4, 'videokit-stream'), +('x86_64', '>=9.3.0', 'Image filters', 14, 1, 'imageproc-filters'), +('x86_64', NULL, 'System monitoring agent', 15, 2, 'monitoragent'); + +-- Insert PackageRelations +INSERT INTO PackageRelations (arch, requirement, package, relation_type, relation_package_name) VALUES +('x86_64', '>=1.0.0', 3, 1, 'legacy-web-api'), -- conflicts +('x86_64', NULL, 4, 2, 'web-cli-tools'), -- provides +('arm64', NULL, 5, 3, 'old-dataproc'), -- replaces +('x86_64', '>=5.0.0', 6, 1, 'net-tools-legacy'), +('x86_64', NULL, 7, 2, 'crypto-lib'), +('x86_64', '>=4.0.0', 8, 3, 'db-driver-old'), +('arm64', NULL, 9, 1, 'imgproc-v1'), +('x86_64', NULL, 10, 2, 'audio-tools'), +('x86_64', '>=7.0.0', 11, 3, 'video-kit-old'), +('x86_64', NULL, 12, 1, 'ml-core-legacy'), +('x86_64', '>=6.0.0', 13, 2, 'analytics-pro-tools'), +('x86_64', NULL, 14, 3, 'monitor-agent-v1'), +('x86_64', '>=9.0.0', 15, 1, 'filesystem-old'); diff --git a/src/data/.sqlx/query-014cf2ec55142a17047ad7c469685df75ae8e3c95a1a7c6c21be7b5624a82ae1.json b/src/data/.sqlx/query-014cf2ec55142a17047ad7c469685df75ae8e3c95a1a7c6c21be7b5624a82ae1.json new file mode 100644 index 0000000..6635f37 --- /dev/null +++ b/src/data/.sqlx/query-014cf2ec55142a17047ad7c469685df75ae8e3c95a1a7c6c21be7b5624a82ae1.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "INSERT INTO PackageBases (name, description, created_at, updated_at) VALUES (?, ?, ?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 4 + }, + "nullable": [] + }, + "hash": "014cf2ec55142a17047ad7c469685df75ae8e3c95a1a7c6c21be7b5624a82ae1" +} diff --git a/src/data/.sqlx/query-063059de083c42956506d991bc04472929e6b2618ab13eb90e772ad9bd9c1984.json b/src/data/.sqlx/query-063059de083c42956506d991bc04472929e6b2618ab13eb90e772ad9bd9c1984.json new file mode 100644 index 0000000..5e754bf --- /dev/null +++ b/src/data/.sqlx/query-063059de083c42956506d991bc04472929e6b2618ab13eb90e772ad9bd9c1984.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Packages SET created_at = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "063059de083c42956506d991bc04472929e6b2618ab13eb90e772ad9bd9c1984" +} diff --git a/src/data/.sqlx/query-0af939868e37bad5eb9097badeaefca62c247c4b2265a9667c4b33885126c771.json b/src/data/.sqlx/query-0af939868e37bad5eb9097badeaefca62c247c4b2265a9667c4b33885126c771.json new file mode 100644 index 0000000..97263b5 --- /dev/null +++ b/src/data/.sqlx/query-0af939868e37bad5eb9097badeaefca62c247c4b2265a9667c4b33885126c771.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE PackageBases SET name = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "0af939868e37bad5eb9097badeaefca62c247c4b2265a9667c4b33885126c771" +} diff --git a/src/data/.sqlx/query-0bb7353d64231dc12416f5504d94513493670e3f2ae017d87a2f0c3eca045f60.json b/src/data/.sqlx/query-0bb7353d64231dc12416f5504d94513493670e3f2ae017d87a2f0c3eca045f60.json new file mode 100644 index 0000000..0f4639e --- /dev/null +++ b/src/data/.sqlx/query-0bb7353d64231dc12416f5504d94513493670e3f2ae017d87a2f0c3eca045f60.json @@ -0,0 +1,84 @@ +{ + "db_name": "MySQL", + "query": "SELECT * FROM Users WHERE email = ?", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | PRIMARY_KEY | UNSIGNED | AUTO_INCREMENT", + "max_size": 10 + } + }, + { + "ordinal": 1, + "name": "name", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE", + "max_size": 124 + } + }, + { + "ordinal": 2, + "name": "email", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE", + "max_size": 1020 + } + }, + { + "ordinal": 3, + "name": "password", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | NO_DEFAULT_VALUE", + "max_size": 1020 + } + }, + { + "ordinal": 4, + "name": "last_used", + "type_info": { + "type": "Timestamp", + "flags": "BINARY", + "max_size": 19 + } + }, + { + "ordinal": 5, + "name": "created_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "max_size": 19 + } + }, + { + "ordinal": 6, + "name": "updated_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "max_size": 19 + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + true, + false, + false + ] + }, + "hash": "0bb7353d64231dc12416f5504d94513493670e3f2ae017d87a2f0c3eca045f60" +} diff --git a/src/data/.sqlx/query-346beb83d6351740a503b72133a190ac327ae79f6e555def8fec89fcc75fb015.json b/src/data/.sqlx/query-346beb83d6351740a503b72133a190ac327ae79f6e555def8fec89fcc75fb015.json new file mode 100644 index 0000000..ae86705 --- /dev/null +++ b/src/data/.sqlx/query-346beb83d6351740a503b72133a190ac327ae79f6e555def8fec89fcc75fb015.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Packages SET version = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "346beb83d6351740a503b72133a190ac327ae79f6e555def8fec89fcc75fb015" +} diff --git a/src/data/.sqlx/query-389e38e7e0a0b7d9ba667ac148a0a468da889a3455c47325938b819ab41ef4c8.json b/src/data/.sqlx/query-389e38e7e0a0b7d9ba667ac148a0a468da889a3455c47325938b819ab41ef4c8.json new file mode 100644 index 0000000..2e79b5c --- /dev/null +++ b/src/data/.sqlx/query-389e38e7e0a0b7d9ba667ac148a0a468da889a3455c47325938b819ab41ef4c8.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "DELETE FROM PackageBases WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "389e38e7e0a0b7d9ba667ac148a0a468da889a3455c47325938b819ab41ef4c8" +} diff --git a/src/data/.sqlx/query-3ffe9168c1eb30bb54c65055314655c21f03d04973e4291e6d4b6c7847364659.json b/src/data/.sqlx/query-3ffe9168c1eb30bb54c65055314655c21f03d04973e4291e6d4b6c7847364659.json new file mode 100644 index 0000000..9d27eba --- /dev/null +++ b/src/data/.sqlx/query-3ffe9168c1eb30bb54c65055314655c21f03d04973e4291e6d4b6c7847364659.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "INSERT INTO Packages (base, name, version, description, url, flagged_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 8 + }, + "nullable": [] + }, + "hash": "3ffe9168c1eb30bb54c65055314655c21f03d04973e4291e6d4b6c7847364659" +} diff --git a/src/data/.sqlx/query-404747d44ee859e8c967695c29963594bb8273e66c053934ec20d5fc3db9d41e.json b/src/data/.sqlx/query-404747d44ee859e8c967695c29963594bb8273e66c053934ec20d5fc3db9d41e.json new file mode 100644 index 0000000..6a26eca --- /dev/null +++ b/src/data/.sqlx/query-404747d44ee859e8c967695c29963594bb8273e66c053934ec20d5fc3db9d41e.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "DELETE FROM Users WHERE name = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "404747d44ee859e8c967695c29963594bb8273e66c053934ec20d5fc3db9d41e" +} diff --git a/src/data/.sqlx/query-68ed36ae997fff190b4b15b80bf24b553d8ac922da251d9e8b8f4e897bab46b0.json b/src/data/.sqlx/query-68ed36ae997fff190b4b15b80bf24b553d8ac922da251d9e8b8f4e897bab46b0.json new file mode 100644 index 0000000..688e561 --- /dev/null +++ b/src/data/.sqlx/query-68ed36ae997fff190b4b15b80bf24b553d8ac922da251d9e8b8f4e897bab46b0.json @@ -0,0 +1,84 @@ +{ + "db_name": "MySQL", + "query": "SELECT * FROM Users WHERE name = ?", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | PRIMARY_KEY | UNSIGNED | AUTO_INCREMENT", + "max_size": 10 + } + }, + { + "ordinal": 1, + "name": "name", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE", + "max_size": 124 + } + }, + { + "ordinal": 2, + "name": "email", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE", + "max_size": 1020 + } + }, + { + "ordinal": 3, + "name": "password", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | NO_DEFAULT_VALUE", + "max_size": 1020 + } + }, + { + "ordinal": 4, + "name": "last_used", + "type_info": { + "type": "Timestamp", + "flags": "BINARY", + "max_size": 19 + } + }, + { + "ordinal": 5, + "name": "created_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "max_size": 19 + } + }, + { + "ordinal": 6, + "name": "updated_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "max_size": 19 + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + true, + false, + false + ] + }, + "hash": "68ed36ae997fff190b4b15b80bf24b553d8ac922da251d9e8b8f4e897bab46b0" +} diff --git a/src/data/.sqlx/query-695f4b0a4286cf625dc60dc3dfc4a9cd92aaea3ea58ef8702903983cfc32ab47.json b/src/data/.sqlx/query-695f4b0a4286cf625dc60dc3dfc4a9cd92aaea3ea58ef8702903983cfc32ab47.json new file mode 100644 index 0000000..07c6a04 --- /dev/null +++ b/src/data/.sqlx/query-695f4b0a4286cf625dc60dc3dfc4a9cd92aaea3ea58ef8702903983cfc32ab47.json @@ -0,0 +1,104 @@ +{ + "db_name": "MySQL", + "query": "SELECT * FROM Packages WHERE id = ?", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | PRIMARY_KEY | UNSIGNED | AUTO_INCREMENT", + "max_size": 10 + } + }, + { + "ordinal": 1, + "name": "base", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | MULTIPLE_KEY | UNSIGNED | NO_DEFAULT_VALUE", + "max_size": 10 + } + }, + { + "ordinal": 2, + "name": "name", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE", + "max_size": 508 + } + }, + { + "ordinal": 3, + "name": "version", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | NO_DEFAULT_VALUE", + "max_size": 508 + } + }, + { + "ordinal": 4, + "name": "description", + "type_info": { + "type": "VarString", + "flags": "", + "max_size": 1020 + } + }, + { + "ordinal": 5, + "name": "url", + "type_info": { + "type": "VarString", + "flags": "", + "max_size": 2040 + } + }, + { + "ordinal": 6, + "name": "flagged_at", + "type_info": { + "type": "Timestamp", + "flags": "BINARY", + "max_size": 19 + } + }, + { + "ordinal": 7, + "name": "created_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "max_size": 19 + } + }, + { + "ordinal": 8, + "name": "updated_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW", + "max_size": 19 + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + true, + false, + false + ] + }, + "hash": "695f4b0a4286cf625dc60dc3dfc4a9cd92aaea3ea58ef8702903983cfc32ab47" +} diff --git a/src/data/.sqlx/query-6b6f842d169e2a9628474edcd6dad10878bb9aaa612f62080d40ba24682b0c3b.json b/src/data/.sqlx/query-6b6f842d169e2a9628474edcd6dad10878bb9aaa612f62080d40ba24682b0c3b.json new file mode 100644 index 0000000..320b56f --- /dev/null +++ b/src/data/.sqlx/query-6b6f842d169e2a9628474edcd6dad10878bb9aaa612f62080d40ba24682b0c3b.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Packages SET base = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "6b6f842d169e2a9628474edcd6dad10878bb9aaa612f62080d40ba24682b0c3b" +} diff --git a/src/data/.sqlx/query-7cc4cf73572c0830d1da7b8e621a79a09f3e3d8cfd42d3946bd1fac93838b913.json b/src/data/.sqlx/query-7cc4cf73572c0830d1da7b8e621a79a09f3e3d8cfd42d3946bd1fac93838b913.json new file mode 100644 index 0000000..27f3c2e --- /dev/null +++ b/src/data/.sqlx/query-7cc4cf73572c0830d1da7b8e621a79a09f3e3d8cfd42d3946bd1fac93838b913.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Packages SET updated_at = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "7cc4cf73572c0830d1da7b8e621a79a09f3e3d8cfd42d3946bd1fac93838b913" +} diff --git a/src/data/.sqlx/query-7f06016e9892486c938a5e94c9e5f70903a38ed314235712c28ac5e14d9ac20f.json b/src/data/.sqlx/query-7f06016e9892486c938a5e94c9e5f70903a38ed314235712c28ac5e14d9ac20f.json new file mode 100644 index 0000000..db397d0 --- /dev/null +++ b/src/data/.sqlx/query-7f06016e9892486c938a5e94c9e5f70903a38ed314235712c28ac5e14d9ac20f.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE PackageBases SET description = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "7f06016e9892486c938a5e94c9e5f70903a38ed314235712c28ac5e14d9ac20f" +} diff --git a/src/data/.sqlx/query-839cea68f9de889f35a0d0ad0b48b4a0dc1af49f0f0e7bb12238d22a9c37fbbc.json b/src/data/.sqlx/query-839cea68f9de889f35a0d0ad0b48b4a0dc1af49f0f0e7bb12238d22a9c37fbbc.json new file mode 100644 index 0000000..85c33af --- /dev/null +++ b/src/data/.sqlx/query-839cea68f9de889f35a0d0ad0b48b4a0dc1af49f0f0e7bb12238d22a9c37fbbc.json @@ -0,0 +1,64 @@ +{ + "db_name": "MySQL", + "query": "SELECT * FROM PackageBases WHERE id = ?", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | PRIMARY_KEY | UNSIGNED | AUTO_INCREMENT", + "max_size": 10 + } + }, + { + "ordinal": 1, + "name": "name", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE", + "max_size": 508 + } + }, + { + "ordinal": 2, + "name": "description", + "type_info": { + "type": "VarString", + "flags": "", + "max_size": 2040 + } + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "max_size": 19 + } + }, + { + "ordinal": 4, + "name": "updated_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW", + "max_size": 19 + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + true, + false, + false + ] + }, + "hash": "839cea68f9de889f35a0d0ad0b48b4a0dc1af49f0f0e7bb12238d22a9c37fbbc" +} diff --git a/src/data/.sqlx/query-8af7a0169e934cb82997a1cab04e921f719ed9466c13713fda8736c540d0fa78.json b/src/data/.sqlx/query-8af7a0169e934cb82997a1cab04e921f719ed9466c13713fda8736c540d0fa78.json new file mode 100644 index 0000000..0a0d0ad --- /dev/null +++ b/src/data/.sqlx/query-8af7a0169e934cb82997a1cab04e921f719ed9466c13713fda8736c540d0fa78.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Users SET updated_at = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "8af7a0169e934cb82997a1cab04e921f719ed9466c13713fda8736c540d0fa78" +} diff --git a/src/data/.sqlx/query-8be76176b46f645095dce3bcbed11134ec0f43504d3a820698282848fd67dbad.json b/src/data/.sqlx/query-8be76176b46f645095dce3bcbed11134ec0f43504d3a820698282848fd67dbad.json new file mode 100644 index 0000000..8b24831 --- /dev/null +++ b/src/data/.sqlx/query-8be76176b46f645095dce3bcbed11134ec0f43504d3a820698282848fd67dbad.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Users SET password = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "8be76176b46f645095dce3bcbed11134ec0f43504d3a820698282848fd67dbad" +} diff --git a/src/data/.sqlx/query-8bfaca937858ed1060da5a650f749849d29af3d6345d0e02474abf4d8c78b89d.json b/src/data/.sqlx/query-8bfaca937858ed1060da5a650f749849d29af3d6345d0e02474abf4d8c78b89d.json new file mode 100644 index 0000000..ecb0a98 --- /dev/null +++ b/src/data/.sqlx/query-8bfaca937858ed1060da5a650f749849d29af3d6345d0e02474abf4d8c78b89d.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Packages SET flagged_at = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "8bfaca937858ed1060da5a650f749849d29af3d6345d0e02474abf4d8c78b89d" +} diff --git a/src/data/.sqlx/query-8e3ffe0d11d3eb38cd805771cd133588c0679404a68a8041f414553226abeeb2.json b/src/data/.sqlx/query-8e3ffe0d11d3eb38cd805771cd133588c0679404a68a8041f414553226abeeb2.json new file mode 100644 index 0000000..514a1f9 --- /dev/null +++ b/src/data/.sqlx/query-8e3ffe0d11d3eb38cd805771cd133588c0679404a68a8041f414553226abeeb2.json @@ -0,0 +1,84 @@ +{ + "db_name": "MySQL", + "query": "SELECT * FROM Users WHERE id = ?", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | PRIMARY_KEY | UNSIGNED | AUTO_INCREMENT", + "max_size": 10 + } + }, + { + "ordinal": 1, + "name": "name", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE", + "max_size": 124 + } + }, + { + "ordinal": 2, + "name": "email", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE", + "max_size": 1020 + } + }, + { + "ordinal": 3, + "name": "password", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | NO_DEFAULT_VALUE", + "max_size": 1020 + } + }, + { + "ordinal": 4, + "name": "last_used", + "type_info": { + "type": "Timestamp", + "flags": "BINARY", + "max_size": 19 + } + }, + { + "ordinal": 5, + "name": "created_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "max_size": 19 + } + }, + { + "ordinal": 6, + "name": "updated_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "max_size": 19 + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + true, + false, + false + ] + }, + "hash": "8e3ffe0d11d3eb38cd805771cd133588c0679404a68a8041f414553226abeeb2" +} diff --git a/src/data/.sqlx/query-93ec7d124c9bfa7329478d975614db874788ed297fe2d95275592becb186f942.json b/src/data/.sqlx/query-93ec7d124c9bfa7329478d975614db874788ed297fe2d95275592becb186f942.json new file mode 100644 index 0000000..a0b5b1c --- /dev/null +++ b/src/data/.sqlx/query-93ec7d124c9bfa7329478d975614db874788ed297fe2d95275592becb186f942.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE PackageBases SET created_at = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "93ec7d124c9bfa7329478d975614db874788ed297fe2d95275592becb186f942" +} diff --git a/src/data/.sqlx/query-944eb40633e943a75244dee639fe6efb16919aff7172189c81240cb12462ae58.json b/src/data/.sqlx/query-944eb40633e943a75244dee639fe6efb16919aff7172189c81240cb12462ae58.json new file mode 100644 index 0000000..336a2f1 --- /dev/null +++ b/src/data/.sqlx/query-944eb40633e943a75244dee639fe6efb16919aff7172189c81240cb12462ae58.json @@ -0,0 +1,104 @@ +{ + "db_name": "MySQL", + "query": "SELECT * FROM Packages WHERE name = ?", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | PRIMARY_KEY | UNSIGNED | AUTO_INCREMENT", + "max_size": 10 + } + }, + { + "ordinal": 1, + "name": "base", + "type_info": { + "type": "Long", + "flags": "NOT_NULL | MULTIPLE_KEY | UNSIGNED | NO_DEFAULT_VALUE", + "max_size": 10 + } + }, + { + "ordinal": 2, + "name": "name", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | UNIQUE_KEY | NO_DEFAULT_VALUE", + "max_size": 508 + } + }, + { + "ordinal": 3, + "name": "version", + "type_info": { + "type": "VarString", + "flags": "NOT_NULL | NO_DEFAULT_VALUE", + "max_size": 508 + } + }, + { + "ordinal": 4, + "name": "description", + "type_info": { + "type": "VarString", + "flags": "", + "max_size": 1020 + } + }, + { + "ordinal": 5, + "name": "url", + "type_info": { + "type": "VarString", + "flags": "", + "max_size": 2040 + } + }, + { + "ordinal": 6, + "name": "flagged_at", + "type_info": { + "type": "Timestamp", + "flags": "BINARY", + "max_size": 19 + } + }, + { + "ordinal": 7, + "name": "created_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP", + "max_size": 19 + } + }, + { + "ordinal": 8, + "name": "updated_at", + "type_info": { + "type": "Timestamp", + "flags": "NOT_NULL | BINARY | TIMESTAMP | ON_UPDATE_NOW", + "max_size": 19 + } + } + ], + "parameters": { + "Right": 1 + }, + "nullable": [ + false, + false, + false, + false, + true, + true, + true, + false, + false + ] + }, + "hash": "944eb40633e943a75244dee639fe6efb16919aff7172189c81240cb12462ae58" +} diff --git a/src/data/.sqlx/query-9be7f66630e64787e55946dff428d28035747b66e57260bc9cd4634a71a037a6.json b/src/data/.sqlx/query-9be7f66630e64787e55946dff428d28035747b66e57260bc9cd4634a71a037a6.json new file mode 100644 index 0000000..083bb37 --- /dev/null +++ b/src/data/.sqlx/query-9be7f66630e64787e55946dff428d28035747b66e57260bc9cd4634a71a037a6.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Users SET email = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "9be7f66630e64787e55946dff428d28035747b66e57260bc9cd4634a71a037a6" +} diff --git a/src/data/.sqlx/query-9fa86328c40ce0469f755efb4876010092b7bc9f240a5d43dc69f9d0b1b5b7ce.json b/src/data/.sqlx/query-9fa86328c40ce0469f755efb4876010092b7bc9f240a5d43dc69f9d0b1b5b7ce.json new file mode 100644 index 0000000..7c34a5b --- /dev/null +++ b/src/data/.sqlx/query-9fa86328c40ce0469f755efb4876010092b7bc9f240a5d43dc69f9d0b1b5b7ce.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "DELETE FROM Users WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "9fa86328c40ce0469f755efb4876010092b7bc9f240a5d43dc69f9d0b1b5b7ce" +} diff --git a/src/data/.sqlx/query-b5814b93236d587957a103e61726b0b9ae811ba6bff0617871e76de3ef0ff662.json b/src/data/.sqlx/query-b5814b93236d587957a103e61726b0b9ae811ba6bff0617871e76de3ef0ff662.json new file mode 100644 index 0000000..db15194 --- /dev/null +++ b/src/data/.sqlx/query-b5814b93236d587957a103e61726b0b9ae811ba6bff0617871e76de3ef0ff662.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Packages SET url = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "b5814b93236d587957a103e61726b0b9ae811ba6bff0617871e76de3ef0ff662" +} diff --git a/src/data/.sqlx/query-c1abf048d65d421717f20343bb0ef4fcd78f8571cfe2347c147124763bd17491.json b/src/data/.sqlx/query-c1abf048d65d421717f20343bb0ef4fcd78f8571cfe2347c147124763bd17491.json new file mode 100644 index 0000000..3d9e1ec --- /dev/null +++ b/src/data/.sqlx/query-c1abf048d65d421717f20343bb0ef4fcd78f8571cfe2347c147124763bd17491.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Packages SET description = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "c1abf048d65d421717f20343bb0ef4fcd78f8571cfe2347c147124763bd17491" +} diff --git a/src/data/.sqlx/query-c2b00adbcb3c35a6ffa6b2bce08a738a9b3cd1ca4aa4c843909c7e14f7ef3e06.json b/src/data/.sqlx/query-c2b00adbcb3c35a6ffa6b2bce08a738a9b3cd1ca4aa4c843909c7e14f7ef3e06.json new file mode 100644 index 0000000..5e9d5c5 --- /dev/null +++ b/src/data/.sqlx/query-c2b00adbcb3c35a6ffa6b2bce08a738a9b3cd1ca4aa4c843909c7e14f7ef3e06.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "DELETE FROM Users WHERE email = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "c2b00adbcb3c35a6ffa6b2bce08a738a9b3cd1ca4aa4c843909c7e14f7ef3e06" +} diff --git a/src/data/.sqlx/query-cc8f7e13c6aedf6aa4d6d4fc39db7aa98b84baf911e7f779641c1dc514c676cd.json b/src/data/.sqlx/query-cc8f7e13c6aedf6aa4d6d4fc39db7aa98b84baf911e7f779641c1dc514c676cd.json new file mode 100644 index 0000000..efa1af3 --- /dev/null +++ b/src/data/.sqlx/query-cc8f7e13c6aedf6aa4d6d4fc39db7aa98b84baf911e7f779641c1dc514c676cd.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Users SET last_used = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "cc8f7e13c6aedf6aa4d6d4fc39db7aa98b84baf911e7f779641c1dc514c676cd" +} diff --git a/src/data/.sqlx/query-cf79e2f6038dddd055d535d2c41dd8dccd1a4e6a763963590c904c25abf33137.json b/src/data/.sqlx/query-cf79e2f6038dddd055d535d2c41dd8dccd1a4e6a763963590c904c25abf33137.json new file mode 100644 index 0000000..d27cc6d --- /dev/null +++ b/src/data/.sqlx/query-cf79e2f6038dddd055d535d2c41dd8dccd1a4e6a763963590c904c25abf33137.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Users SET created_at = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "cf79e2f6038dddd055d535d2c41dd8dccd1a4e6a763963590c904c25abf33137" +} diff --git a/src/data/.sqlx/query-d289747c7c7fba86e2b66174e2d1546f10e8213d36b6b3cd25016f829e9d731b.json b/src/data/.sqlx/query-d289747c7c7fba86e2b66174e2d1546f10e8213d36b6b3cd25016f829e9d731b.json new file mode 100644 index 0000000..60dd430 --- /dev/null +++ b/src/data/.sqlx/query-d289747c7c7fba86e2b66174e2d1546f10e8213d36b6b3cd25016f829e9d731b.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE PackageBases SET updated_at = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "d289747c7c7fba86e2b66174e2d1546f10e8213d36b6b3cd25016f829e9d731b" +} diff --git a/src/data/.sqlx/query-d474dd848d0ef8832afd4d1302fa562a3c4a4569032e8636d664043b5dc96661.json b/src/data/.sqlx/query-d474dd848d0ef8832afd4d1302fa562a3c4a4569032e8636d664043b5dc96661.json new file mode 100644 index 0000000..5a21f41 --- /dev/null +++ b/src/data/.sqlx/query-d474dd848d0ef8832afd4d1302fa562a3c4a4569032e8636d664043b5dc96661.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "DELETE FROM Packages WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "d474dd848d0ef8832afd4d1302fa562a3c4a4569032e8636d664043b5dc96661" +} diff --git a/src/data/.sqlx/query-daf98e6f1013c4993f7329f6fa690e92bccd89d1ff90131719c40626088dabd1.json b/src/data/.sqlx/query-daf98e6f1013c4993f7329f6fa690e92bccd89d1ff90131719c40626088dabd1.json new file mode 100644 index 0000000..6108db8 --- /dev/null +++ b/src/data/.sqlx/query-daf98e6f1013c4993f7329f6fa690e92bccd89d1ff90131719c40626088dabd1.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "INSERT INTO Users (name, email, password, last_used, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 6 + }, + "nullable": [] + }, + "hash": "daf98e6f1013c4993f7329f6fa690e92bccd89d1ff90131719c40626088dabd1" +} diff --git a/src/data/.sqlx/query-e8ee44281a87c6e7147332dd5548971cb804a1ab1edcdae8bf009ac39059c2bb.json b/src/data/.sqlx/query-e8ee44281a87c6e7147332dd5548971cb804a1ab1edcdae8bf009ac39059c2bb.json new file mode 100644 index 0000000..9841c3d --- /dev/null +++ b/src/data/.sqlx/query-e8ee44281a87c6e7147332dd5548971cb804a1ab1edcdae8bf009ac39059c2bb.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Packages SET name = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "e8ee44281a87c6e7147332dd5548971cb804a1ab1edcdae8bf009ac39059c2bb" +} diff --git a/src/data/.sqlx/query-f4963ad77bcbc0af4fc929f1f66b7ee842c26c44da32ae9bbbc06c466a908ccf.json b/src/data/.sqlx/query-f4963ad77bcbc0af4fc929f1f66b7ee842c26c44da32ae9bbbc06c466a908ccf.json new file mode 100644 index 0000000..722ae18 --- /dev/null +++ b/src/data/.sqlx/query-f4963ad77bcbc0af4fc929f1f66b7ee842c26c44da32ae9bbbc06c466a908ccf.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "DELETE FROM Packages WHERE name = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 1 + }, + "nullable": [] + }, + "hash": "f4963ad77bcbc0af4fc929f1f66b7ee842c26c44da32ae9bbbc06c466a908ccf" +} diff --git a/src/data/.sqlx/query-f656bd1abb82c10af4e0e21b4a04a364988f5329356282f2ae0098dbfcaec671.json b/src/data/.sqlx/query-f656bd1abb82c10af4e0e21b4a04a364988f5329356282f2ae0098dbfcaec671.json new file mode 100644 index 0000000..e818217 --- /dev/null +++ b/src/data/.sqlx/query-f656bd1abb82c10af4e0e21b4a04a364988f5329356282f2ae0098dbfcaec671.json @@ -0,0 +1,12 @@ +{ + "db_name": "MySQL", + "query": "UPDATE Users SET name = ? WHERE id = ?", + "describe": { + "columns": [], + "parameters": { + "Right": 2 + }, + "nullable": [] + }, + "hash": "f656bd1abb82c10af4e0e21b4a04a364988f5329356282f2ae0098dbfcaec671" +} diff --git a/src/data/Cargo.toml b/src/data/Cargo.toml new file mode 100644 index 0000000..83a4d2c --- /dev/null +++ b/src/data/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "data" +version = "0.1.0" +edition = "2024" + +[dependencies] +derive_more = { version = "2.0.1", features = ["deref", "into"] } + +futures = "0.3.31" +chrono = { version = "0.4.39", default-features = false, features = [ + "std", + "now", +] } +sqlx = { version = "0.8.3", default-features = false, features = ["mysql", "macros", "chrono", "runtime-tokio"] } + +# thiserror = "2.0.11" +# garde = { version = "0.22.0", features = ["email", "url", "derive"] } diff --git a/src/data/src/adapter.rs b/src/data/src/adapter.rs new file mode 100644 index 0000000..37f698b --- /dev/null +++ b/src/data/src/adapter.rs @@ -0,0 +1,2 @@ +//! Specific implementations of [`crate::port`]s to plug into other parts of the application. +pub mod mysql; diff --git a/src/data/src/adapter/mysql.rs b/src/data/src/adapter/mysql.rs new file mode 100644 index 0000000..67d439e --- /dev/null +++ b/src/data/src/adapter/mysql.rs @@ -0,0 +1,5 @@ +//! `MySQL` adapters. +pub mod base; +pub mod package; +pub mod search; +pub mod user; diff --git a/src/data/src/adapter/mysql/base.rs b/src/data/src/adapter/mysql/base.rs new file mode 100644 index 0000000..51c0517 --- /dev/null +++ b/src/data/src/adapter/mysql/base.rs @@ -0,0 +1,105 @@ +use crate::Result; +use crate::port::base::{Base, BaseRepository, Field, New}; + +use chrono::Utc; +use sqlx::{Executor, MySql}; + +pub struct BaseAdapter; + +impl BaseRepository for BaseAdapter +where + E: Send, + for<'a> &'a E: Executor<'a, Database = MySql>, +{ +} +impl crate::port::Crud for BaseAdapter +where + E: Send, + for<'a> &'a E: Executor<'a, Database = MySql>, +{ + type New = New; + type Unique = u64; + type Update = Field; + type Existing = Base; + + async fn create(connection: &mut E, data: Self::New) -> Result { + let created_at = Utc::now(); + let id = sqlx::query!( + "INSERT INTO PackageBases (name, description, created_at, updated_at) VALUES (?, ?, ?, ?)", + data.name.as_str(), + data.description.as_ref(), + created_at, created_at, + ) + .execute(&*connection) + .await? + .last_insert_id(); + + Ok(Self::Existing { + id, + name: data.name.into(), + description: data.description.into(), + created_at, + updated_at: created_at, + }) + } + + async fn read(connection: &E, data: Self::Unique) -> Result> { + Ok( + sqlx::query_as!(Base, "SELECT * FROM PackageBases WHERE id = ?", data) + .fetch_optional(connection) + .await?, + ) + } + + async fn update( + connection: &mut E, + existing: &mut Self::Existing, + data: Self::Update, + ) -> Result { + match &data { + Field::Name(name) => { + sqlx::query!( + "UPDATE PackageBases SET name = ? WHERE id = ?", + name.as_str(), + existing.id + ) + } + Field::Description(description) => { + sqlx::query!( + "UPDATE PackageBases SET description = ? WHERE id = ?", + description.as_ref(), + existing.id + ) + } + Field::CreatedAt(date_time) => sqlx::query!( + "UPDATE PackageBases SET created_at = ? WHERE id = ?", + date_time, + existing.id + ), + Field::UpdatedAt(date_time) => sqlx::query!( + "UPDATE PackageBases SET updated_at = ? WHERE id = ?", + date_time, + existing.id + ), + } + .execute(&*connection) + .await?; + + match data { + Field::Name(s) => existing.name = s.into(), + Field::Description(o) => existing.description = o.into(), + Field::CreatedAt(date_time) => existing.created_at = date_time, + Field::UpdatedAt(date_time) => existing.updated_at = date_time, + } + + Ok(()) + } + + async fn delete(connection: &mut E, data: Self::Unique) -> Result { + sqlx::query!("DELETE FROM PackageBases WHERE id = ?", data) + .execute(&*connection) + .await?; + + Ok(()) + } +} diff --git a/src/data/src/adapter/mysql/package.rs b/src/data/src/adapter/mysql/package.rs new file mode 100644 index 0000000..96dbf5e --- /dev/null +++ b/src/data/src/adapter/mysql/package.rs @@ -0,0 +1,162 @@ +use crate::Result; +use crate::port::package::{Field, New, Package, PackageRepository, Unique}; + +use chrono::Utc; +use sqlx::{Executor, MySql}; + +pub struct PackageAdapter; + +impl PackageRepository for PackageAdapter +where + E: Send, + for<'a> &'a E: Executor<'a, Database = MySql>, +{ +} +impl crate::port::Crud for PackageAdapter +where + E: Send, + for<'a> &'a E: Executor<'a, Database = MySql>, +{ + type New = New; + type Update = Field; + type Unique = Unique; + type Existing = Package; + + async fn create(connection: &mut E, data: Self::New) -> Result { + let created_at = Utc::now(); + let id = sqlx::query!( + "INSERT INTO Packages \ + (base, name, version, description, url, flagged_at, created_at, updated_at) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + data.package_base.id, + data.name.as_str(), + data.version.as_str(), + data.description.as_ref(), + data.url.as_ref(), + data.flagged_at, + created_at, + created_at, + ) + .execute(&*connection) + .await? + .last_insert_id(); + + Ok(Self::Existing { + id, + base: data.package_base.id, + name: data.name.into(), + version: data.version.into(), + description: data.description.into(), + url: data.url.into(), + flagged_at: data.flagged_at, + created_at, + updated_at: created_at, + }) + } + + async fn read(connection: &E, data: Self::Unique) -> Result> { + Ok(match data { + Unique::Id(id) => { + sqlx::query_as!(Package, "SELECT * FROM Packages WHERE id = ?", id) + .fetch_optional(connection) + .await + } + Unique::Name(name) => { + sqlx::query_as!( + Package, + "SELECT * FROM Packages WHERE name = ?", + name.as_str() + ) + .fetch_optional(connection) + .await + } + }?) + } + + async fn update( + connection: &mut E, + existing: &mut Self::Existing, + data: Self::Update, + ) -> Result { + match &data { + Field::Name(name) => { + sqlx::query!( + "UPDATE Packages SET name = ? WHERE id = ?", + name.as_str(), + existing.id + ) + } + Field::PackageBase(package_base) => { + sqlx::query!( + "UPDATE Packages SET base = ? WHERE id = ?", + package_base.id, + existing.id + ) + } + Field::Version(version) => { + sqlx::query!( + "UPDATE Packages SET version = ? WHERE id = ?", + version.as_str(), + existing.id + ) + } + Field::Description(description) => { + sqlx::query!( + "UPDATE Packages SET description = ? WHERE id = ?", + description.as_ref(), + existing.id + ) + } + Field::Url(url) => { + sqlx::query!( + "UPDATE Packages SET url = ? WHERE id = ?", + url.as_ref(), + existing.id + ) + } + Field::FlaggedAt(date_time) => sqlx::query!( + "UPDATE Packages SET flagged_at = ? WHERE id = ?", + date_time, + existing.id + ), + Field::CreatedAt(date_time) => sqlx::query!( + "UPDATE Packages SET created_at = ? WHERE id = ?", + date_time, + existing.id + ), + Field::UpdatedAt(date_time) => sqlx::query!( + "UPDATE Packages SET updated_at = ? WHERE id = ?", + date_time, + existing.id + ), + } + .execute(&*connection) + .await?; + + match data { + Field::Name(s) => existing.name = s.into(), + Field::PackageBase(s) => existing.base = s.id, + Field::Version(s) => existing.version = s.into(), + Field::Description(o) => existing.description = o.into(), + Field::Url(o) => existing.url = o.into(), + Field::FlaggedAt(date_time) => existing.flagged_at = date_time, + Field::CreatedAt(date_time) => existing.created_at = date_time, + Field::UpdatedAt(date_time) => existing.updated_at = date_time, + } + + Ok(()) + } + + async fn delete(connection: &mut E, data: Self::Unique) -> Result { + match data { + Unique::Id(id) => sqlx::query!("DELETE FROM Packages WHERE id = ?", id), + Unique::Name(name) => { + sqlx::query!("DELETE FROM Packages WHERE name = ?", name.as_str()) + } + } + .execute(&*connection) + .await?; + + Ok(()) + } +} diff --git a/src/data/src/adapter/mysql/search.rs b/src/data/src/adapter/mysql/search.rs new file mode 100644 index 0000000..03ce524 --- /dev/null +++ b/src/data/src/adapter/mysql/search.rs @@ -0,0 +1,152 @@ +use crate::Result; +use crate::port::search::{Data, Entry, Mode, Order, SearchRepository}; + +// use chrono::Utc; +use futures::TryStreamExt; +use sqlx::{Executor, MySql, QueryBuilder, Row}; + +pub struct SearchAdapter; + +impl SearchRepository for SearchAdapter +where + E: Send, + for<'a> &'a E: Executor<'a, Database = MySql>, +{ + async fn search(connection: &E, data: Data) -> Result> { + let mut builder = QueryBuilder::new( + "SELECT \ + p.id, p.name, p.version, p.url, p.description, \ + p.updated_at, p.created_at, \ + pb.id AS base_id, pb.name AS base_name, \ + ( \ + SELECT COUNT(DISTINCT pbur.user) \ + FROM PackageBaseUserRoles pbur \ + WHERE pbur.base = pb.id AND pbur.role = 3 \ + ) AS maintainers_num \ + FROM \ + Packages p \ + JOIN \ + PackageBases pb ON p.base = pb.id ", + ); + + let mut push_search = |cond, param| { + builder.push(format_args!( + " {cond} {param} {} ", + if data.exact { "=" } else { "LIKE" } + )); + builder.push_bind(if data.exact { + data.search.to_string() + } else { + format!("%{}%", data.search.as_str()) + }); + }; + + let join_user = " JOIN PackageBaseUserRoles pbur ON pb.id = pbur.base \ + JOIN Users u ON pbur.user = u.id WHERE "; + + match data.mode { + Mode::Url => push_search("WHERE", "p.url"), + Mode::Name => push_search("WHERE", "p.name"), + Mode::PackageBase => push_search("WHERE", "pb.name"), + Mode::Description => push_search("WHERE", "p.description"), + Mode::BaseDescription => push_search("WHERE", "pb.description"), + Mode::NameAndDescription => { + // WHERE (p.name LIKE '%search_term%' OR p.description LIKE '%search_term%') + builder.push(" WHERE p.name LIKE "); + builder.push_bind(format!("%{}%", data.search.as_str())); + builder.push(" OR p.description LIKE "); + builder.push_bind(format!("%{}%", data.search.as_str())); + } + Mode::User => { + push_search( + "WHERE EXISTS ( \ + SELECT 1 \ + FROM PackageBaseUserRoles pbur \ + JOIN Users u ON pbur.user = u.id \ + WHERE pbur.base = pb.id AND", + "u.name", + ); + builder.push(" ) "); + } + Mode::Flagger => { + push_search(join_user, "u.name"); + builder.push(" AND pbur.role = 4 "); + } // 4 + Mode::Packager => { + push_search(join_user, "u.name"); + builder.push(" AND pbur.role = 2 "); + } // 2 + Mode::Submitter => { + push_search(join_user, "u.name"); + builder.push(" AND pbur.role = 1 "); + } // 1 + Mode::Maintainer => { + push_search(join_user, "u.name"); + builder.push(" AND pbur.role = 3 "); + } // 3 + } + + builder.push(format_args!( + " ORDER BY {} {} LIMIT {};", + match data.order { + Order::Name => "p.name", + Order::Version => "p.version", + Order::BaseName => "pb.name", + Order::UpdatedAt => "p.updated_at", + Order::CreatedAt => "p.created_at", + }, + if data.ascending { "ASC" } else { "DESC" }, + data.limit + )); + + let mut entries = Vec::new(); + + let mut rows = builder.build().fetch(connection); + while let Some(row) = rows.try_next().await? { + entries.push(Entry { + id: row.try_get("id")?, + name: row.try_get("name")?, + version: row.try_get("version")?, + base_id: row.try_get("base_id")?, + base_name: row.try_get("base_name")?, + url: row.try_get("url")?, + description: row.try_get("description")?, + // submitter_id: row.try_get("submitter_id")?, + // submitter_name: row.try_get("submitter_name")?, + updated_at: row.try_get("updated_at")?, + created_at: row.try_get("created_at")?, + }); + } + + Ok(entries) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::Validation; + use crate::port::search::Search; + use sqlx::MySqlPool; + + #[sqlx::test] + async fn search() -> crate::Result { + let pool = MySqlPool::connect_lazy( + &std::env::var("DATABASE_URL") + .expect("environment variable `DATABASE_URL` should be set"), + )?; + + let data = Data { + mode: Mode::NameAndDescription, + order: Order::UpdatedAt, + search: Search::new("f").map_err(|e| e.1)?, + limit: 50, + exact: true, + ascending: false, + }; + + SearchAdapter::search(&pool, data).await?; + + Ok(()) + } +} diff --git a/src/data/src/adapter/mysql/user.rs b/src/data/src/adapter/mysql/user.rs new file mode 100644 index 0000000..c7490ab --- /dev/null +++ b/src/data/src/adapter/mysql/user.rs @@ -0,0 +1,146 @@ +use crate::Result; +use crate::port::user::{Field, New, Unique, User, UserRepository}; + +use chrono::Utc; +use sqlx::{Executor, MySql}; + +pub struct UserAdapter; + +impl UserRepository for UserAdapter +where + E: Send, + for<'a> &'a E: Executor<'a, Database = MySql>, +{ +} +impl crate::port::Crud for UserAdapter +where + E: Send, + for<'a> &'a E: Executor<'a, Database = MySql>, +{ + type New = New; + type Update = Field; + type Unique = Unique; + type Existing = User; + + async fn create(connection: &mut E, data: Self::New) -> Result { + let created_at = Utc::now(); + let id = sqlx::query!( + "INSERT INTO Users (name, email, password, last_used, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", + data.name.as_str(), + data.email.as_str(), + data.password.as_str(), + data.last_used, + created_at, + created_at, + ) + .execute(&*connection) + .await? + .last_insert_id(); + + Ok(Self::Existing { + id, + name: data.name.into(), + email: data.email.into(), + password: data.password.into(), + last_used: data.last_used, + created_at, + updated_at: created_at, + }) + } + + async fn read(connection: &E, data: Self::Unique) -> Result> { + Ok(match data { + Unique::Id(id) => { + sqlx::query_as!(User, "SELECT * FROM Users WHERE id = ?", id) + .fetch_optional(connection) + .await + } + Unique::Name(name) => { + sqlx::query_as!(User, "SELECT * FROM Users WHERE name = ?", name.as_str()) + .fetch_optional(connection) + .await + } + Unique::Email(email) => { + sqlx::query_as!(User, "SELECT * FROM Users WHERE email = ?", email.as_str()) + .fetch_optional(connection) + .await + } + }?) + } + + async fn update( + connection: &mut E, + existing: &mut Self::Existing, + data: Self::Update, + ) -> Result { + match &data { + Field::Name(name) => { + sqlx::query!( + "UPDATE Users SET name = ? WHERE id = ?", + name.as_str(), + existing.id + ) + } + Field::Email(email) => { + sqlx::query!( + "UPDATE Users SET email = ? WHERE id = ?", + email.as_str(), + existing.id + ) + } + Field::Password(password) => { + sqlx::query!( + "UPDATE Users SET password = ? WHERE id = ?", + password.as_str(), + existing.id + ) + } + Field::LastUsed(date_time) => { + sqlx::query!( + "UPDATE Users SET last_used = ? WHERE id = ?", + date_time, + existing.id + ) + } + Field::CreatedAt(date_time) => sqlx::query!( + "UPDATE Users SET created_at = ? WHERE id = ?", + date_time, + existing.id + ), + Field::UpdatedAt(date_time) => sqlx::query!( + "UPDATE Users SET updated_at = ? WHERE id = ?", + date_time, + existing.id + ), + } + .execute(&*connection) + .await?; + + match data { + Field::Name(valid) => existing.name = valid.into(), + Field::Email(valid) => existing.email = valid.into(), + Field::Password(valid) => existing.password = valid.into(), + Field::LastUsed(date_time) => existing.last_used = date_time, + Field::CreatedAt(date_time) => existing.created_at = date_time, + Field::UpdatedAt(date_time) => existing.updated_at = date_time, + } + + Ok(()) + } + + async fn delete(connection: &mut E, data: Self::Unique) -> Result { + match data { + Unique::Id(id) => sqlx::query!("DELETE FROM Users WHERE id = ?", id), + Unique::Name(name) => { + sqlx::query!("DELETE FROM Users WHERE name = ?", name.as_str()) + } + Unique::Email(email) => { + sqlx::query!("DELETE FROM Users WHERE email = ?", email.as_str()) + } + } + .execute(&*connection) + .await?; + + Ok(()) + } +} diff --git a/src/data/src/atomic.rs b/src/data/src/atomic.rs new file mode 100644 index 0000000..1141e7d --- /dev/null +++ b/src/data/src/atomic.rs @@ -0,0 +1,43 @@ +//! Unify transaction management for established connections. +use crate::Result; + +pub trait Atomic { + type Transaction<'a>; + + fn start_transaction(&mut self) -> impl Future>> + Send; + fn abort_transaction(transaction: Self::Transaction<'_>) + -> impl Future + Send; + fn commit_transaction( + transaction: Self::Transaction<'_>, + ) -> impl Future + Send; +} + +use sqlx::Connection; + +impl Atomic for sqlx::MySqlPool { + type Transaction<'a> = sqlx::MySqlTransaction<'a>; + + async fn start_transaction(&mut self) -> Result> { + self.begin().await.map_err(Box::from) + } + async fn abort_transaction(transaction: Self::Transaction<'_>) -> Result { + transaction.rollback().await.map_err(Box::from) + } + async fn commit_transaction(transaction: Self::Transaction<'_>) -> Result { + transaction.commit().await.map_err(Box::from) + } +} + +impl Atomic for sqlx::MySqlConnection { + type Transaction<'a> = sqlx::MySqlTransaction<'a>; + + async fn start_transaction(&mut self) -> Result> { + self.begin().await.map_err(Box::from) + } + async fn abort_transaction(transaction: Self::Transaction<'_>) -> Result { + transaction.rollback().await.map_err(Box::from) + } + async fn commit_transaction(transaction: Self::Transaction<'_>) -> Result { + transaction.commit().await.map_err(Box::from) + } +} diff --git a/src/data/src/connect.rs b/src/data/src/connect.rs new file mode 100644 index 0000000..f8e054b --- /dev/null +++ b/src/data/src/connect.rs @@ -0,0 +1,53 @@ +//! Driver to manage a connection which is passed to adapters. +use crate::Result; + +pub trait Connect { + type Connection; + + fn open_connection(&self) -> impl Future> + Send; + fn close_connection(connection: Self::Connection) -> impl Future + Send; +} + +use sqlx::Connection; +pub use sqlx::MySqlConnection as SqlxConnection; +pub use sqlx::MySqlPool as SqlxPool; + +#[derive(Clone)] +pub struct MySqlPool { + pool: SqlxPool, +} +impl MySqlPool { + pub const fn new(pool: SqlxPool) -> Self { + Self { pool } + } +} +impl Connect for MySqlPool { + type Connection = SqlxPool; + + async fn open_connection(&self) -> Result { + Ok(self.pool.clone()) + } + async fn close_connection(_: Self::Connection) -> Result { + Ok(()) + } +} + +pub struct MySqlConnection { + link: String, +} +impl MySqlConnection { + pub const fn new(link: String) -> Self { + Self { link } + } +} +impl Connect for MySqlConnection { + type Connection = SqlxConnection; + + async fn open_connection(&self) -> Result { + SqlxConnection::connect(&self.link).await.map_err(Box::from) + } + async fn close_connection(connection: Self::Connection) -> Result { + connection.close().await?; + Ok(()) + } +} diff --git a/src/data/src/lib.rs b/src/data/src/lib.rs new file mode 100644 index 0000000..0713e21 --- /dev/null +++ b/src/data/src/lib.rs @@ -0,0 +1,23 @@ +//! Data access for the application. +pub mod adapter; +pub mod atomic; +pub mod connect; +pub mod port; + +// Don't want to handle errors for dynamic mess. +pub type BoxDynError = Box; +pub type Result = std::result::Result; + +pub use chrono::Utc; + +pub use adapter::mysql::base::BaseAdapter as MySqlBaseAdapter; +pub use adapter::mysql::package::PackageAdapter as MySqlPackageAdapter; +pub use adapter::mysql::user::UserAdapter as MySqlUserAdapter; +pub use adapter::mysql::search::SearchAdapter as MySqlSearchAdapter; +pub use atomic::Atomic; +pub use connect::*; +pub use port::base::{Base, BaseRepository}; +pub use port::package::{Package, PackageRepository}; +pub use port::search::{Search, SearchRepository}; +pub use port::user::{User, UserRepository}; +pub use port::*; diff --git a/src/data/src/port.rs b/src/data/src/port.rs new file mode 100644 index 0000000..e1d3dcb --- /dev/null +++ b/src/data/src/port.rs @@ -0,0 +1,91 @@ +//! Low-level repository traits for unified data access. +//! +//! Very mild argument validation. +use crate::{BoxDynError, Result}; + +pub mod base; +pub mod package; +pub mod search; +pub mod user; + +pub trait Crud { + type New; + type Unique; + type Update; + type Existing; + + fn create( + connection: &mut C, + data: Self::New, + ) -> impl Future> + Send; + fn read( + connection: &C, + data: Self::Unique, + ) -> impl Future>> + Send; + fn update( + connection: &mut C, + existing: &mut Self::Existing, + data: Self::Update, + ) -> impl Future + Send; + fn delete(connection: &mut C, data: Self::Unique) -> impl Future + Send; +} + +pub trait CharLength { + fn length(&self) -> usize; +} +impl CharLength for &str { + fn length(&self) -> usize { + self.chars().count() + } +} +impl CharLength for String { + fn length(&self) -> usize { + self.chars().count() + } +} +impl CharLength for Option { + fn length(&self) -> usize { + self.as_ref().map_or(0, CharLength::length) + } +} + +trait Validatable { + type Inner: CharLength; + const MAX_LENGTH: usize; + fn encapsulate(value: Self::Inner) -> Self; +} + +#[allow(private_bounds)] // don't expose the impl details +pub trait Validation: Validatable +where + T: CharLength + Into, +{ + fn valid(value: &T) -> Result<(), String> { + if value.length() > Self::MAX_LENGTH { + Err(format!( + "too long (length: {}, max length: {})", + value.length(), + Self::MAX_LENGTH + )) + } else { + Ok(()) + } + } + + fn new(value: T) -> Result + where + Self: Sized, + { + match Self::valid(&value) { + Ok(()) => Ok(Self::encapsulate(value.into())), + Err(e) => Err((value, e.into())), + } + } +} + +impl Validation for T +where + T: Validatable, + U: CharLength + Into, +{ +} diff --git a/src/data/src/port/base.rs b/src/data/src/port/base.rs new file mode 100644 index 0000000..a6e3a96 --- /dev/null +++ b/src/data/src/port/base.rs @@ -0,0 +1,70 @@ +use super::Validatable; + +use chrono::{DateTime, Utc}; +use derive_more::{Deref, Into}; + +pub trait BaseRepository: + super::Crud +{ +} + +// #[derive(Deref, Into, Clone, Copy)] +// pub struct Id(pub(crate) u64); + +#[derive(Clone, Deref, Into)] +pub struct Name(String); +impl Validatable for Name { + type Inner = String; + const MAX_LENGTH: usize = 127; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +#[derive(Clone, Deref, Into)] +pub struct Description(Option); +impl Validatable for Description { + type Inner = Option; + const MAX_LENGTH: usize = 510; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +pub enum Field { + Name(Name), + Description(Description), + CreatedAt(DateTime), + UpdatedAt(DateTime), +} + +pub struct New { + pub name: Name, + pub description: Description, +} + +pub struct Base { + pub(crate) id: u64, + pub(crate) name: String, + pub(crate) description: Option, + pub(crate) created_at: DateTime, + pub(crate) updated_at: DateTime, +} + +impl Base { + pub const fn id(&self) -> u64 { + self.id + } + pub const fn name(&self) -> &String { + &self.name + } + pub const fn description(&self) -> Option<&String> { + self.description.as_ref() + } + pub const fn created_at(&self) -> DateTime { + self.created_at + } + pub const fn updated_at(&self) -> DateTime { + self.updated_at + } +} diff --git a/src/data/src/port/package.rs b/src/data/src/port/package.rs new file mode 100644 index 0000000..2f35392 --- /dev/null +++ b/src/data/src/port/package.rs @@ -0,0 +1,117 @@ +use super::Validatable; +use crate::Base; + +use chrono::{DateTime, Utc}; +use derive_more::{Deref, Into}; + +pub trait PackageRepository: + super::Crud +{ +} + +#[derive(Clone, Deref, Into)] +pub struct Name(String); +impl Validatable for Name { + type Inner = String; + const MAX_LENGTH: usize = 127; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +#[derive(Clone, Deref, Into)] +pub struct Version(String); +impl Validatable for Version { + type Inner = String; + const MAX_LENGTH: usize = 127; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +#[derive(Clone, Deref, Into)] +pub struct Description(Option); +impl Validatable for Description { + type Inner = Option; + const MAX_LENGTH: usize = 255; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +#[derive(Clone, Deref, Into)] +pub struct Url(Option); +impl Validatable for Url { + type Inner = Option; + const MAX_LENGTH: usize = 510; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +pub enum Unique { + Id(u64), + Name(Name), +} + +pub enum Field { + PackageBase(Base), + Name(Name), + Version(Version), + Description(Description), + Url(Url), + FlaggedAt(Option>), + CreatedAt(DateTime), + UpdatedAt(DateTime), +} + +pub struct New { + pub package_base: Base, + pub name: Name, + pub version: Version, + pub description: Description, + pub url: Url, + pub flagged_at: Option>, +} + +pub struct Package { + pub(crate) id: u64, + pub(crate) base: u64, + pub(crate) name: String, + pub(crate) version: String, + pub(crate) description: Option, + pub(crate) url: Option, + pub(crate) flagged_at: Option>, + pub(crate) created_at: DateTime, + pub(crate) updated_at: DateTime, +} + +impl Package { + pub const fn id(&self) -> u64 { + self.id + } + pub const fn package_base(&self) -> u64 { + self.base + } + pub const fn name(&self) -> &String { + &self.name + } + pub const fn version(&self) -> &String { + &self.version + } + pub const fn description(&self) -> Option<&String> { + self.description.as_ref() + } + pub const fn url(&self) -> Option<&String> { + self.url.as_ref() + } + pub const fn flagged_at(&self) -> Option> { + self.flagged_at + } + pub const fn created_at(&self) -> DateTime { + self.created_at + } + pub const fn updated_at(&self) -> DateTime { + self.updated_at + } +} diff --git a/src/data/src/port/search.rs b/src/data/src/port/search.rs new file mode 100644 index 0000000..2af96f3 --- /dev/null +++ b/src/data/src/port/search.rs @@ -0,0 +1,69 @@ +use super::Validatable; +use crate::Result; + +use chrono::{DateTime, Utc}; +use derive_more::{Deref, Into}; + +pub trait SearchRepository { + fn search(connection: &C, data: Data) -> impl Future>> + Send; +} + +#[derive(Clone, Deref, Into)] +pub struct Search(String); +impl Validatable for Search { + type Inner = String; + const MAX_LENGTH: usize = 255; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +pub struct Data { + pub mode: Mode, + pub order: Order, + pub search: Search, + + pub limit: u16, + pub exact: bool, + pub ascending: bool, +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct Entry { + pub id: u64, + pub name: Box, + pub version: Box, + pub base_id: u64, + pub base_name: Box, + pub url: Option>, + pub description: Box, + // pub submitter_id: u64, + // pub submitter_name: Box, + pub updated_at: DateTime, + pub created_at: DateTime, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Mode { + Url, + Name, + PackageBase, + Description, + BaseDescription, + NameAndDescription, + User, + Flagger, + Packager, + Submitter, + Maintainer, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Order { + Name, + Version, + BaseName, + // Submitter, + UpdatedAt, + CreatedAt, +} diff --git a/src/data/src/port/user.rs b/src/data/src/port/user.rs new file mode 100644 index 0000000..f1085a0 --- /dev/null +++ b/src/data/src/port/user.rs @@ -0,0 +1,96 @@ +use super::Validatable; + +use chrono::{DateTime, Utc}; +use derive_more::{Deref, Into}; + +pub trait UserRepository: + super::Crud +{ +} + +#[derive(Clone, Deref, Into)] +pub struct Name(String); +impl Validatable for Name { + type Inner = String; + const MAX_LENGTH: usize = 31; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +#[derive(Clone, Deref, Into)] +pub struct Email(String); +impl Validatable for Email { + type Inner = String; + const MAX_LENGTH: usize = 255; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +#[derive(Clone, Deref, Into)] +pub struct Password(String); +impl Validatable for Password { + type Inner = String; + const MAX_LENGTH: usize = 255; + fn encapsulate(value: Self::Inner) -> Self { + Self(value) + } +} + +pub enum Unique { + Id(u64), + Name(Name), + Email(Email), +} + +pub enum Field { + Name(Name), + Email(Email), + Password(Password), + LastUsed(Option>), + CreatedAt(DateTime), + UpdatedAt(DateTime), +} + +pub struct New { + pub name: Name, + pub email: Email, + pub password: Password, + pub last_used: Option>, +} + +#[derive(Debug, Clone)] +pub struct User { + pub(crate) id: u64, + pub(crate) name: String, + pub(crate) email: String, + pub(crate) password: String, + pub(crate) last_used: Option>, + pub(crate) created_at: DateTime, + pub(crate) updated_at: DateTime, +} + +impl User { + pub const fn id(&self) -> u64 { + self.id + } + pub const fn name(&self) -> &String { + &self.name + } + pub const fn email(&self) -> &String { + &self.email + } + pub const fn password(&self) -> &String { + &self.password + } + pub const fn last_used(&self) -> Option> { + self.last_used + } + pub const fn created_at(&self) -> DateTime { + self.created_at + } + pub const fn updated_at(&self) -> DateTime { + self.updated_at + } +} diff --git a/src/service/Cargo.toml b/src/service/Cargo.toml new file mode 100644 index 0000000..c7270b6 --- /dev/null +++ b/src/service/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "service" +version = "0.1.0" +edition = "2024" + +[dependencies] +thiserror = "2.0.11" +argon2 = { version = "0.5.3", features = ["std"] } +garde = { version = "0.22.0", features = ["email", "url", "derive"] } +derive_more = { version = "2.0.1", features = ["deref", "deref_mut", "into"] } + +[dependencies.data] +path = "../data" diff --git a/src/service/src/authentication.rs b/src/service/src/authentication.rs new file mode 100644 index 0000000..142e065 --- /dev/null +++ b/src/service/src/authentication.rs @@ -0,0 +1,9 @@ +pub mod adapter; +pub mod contract; +pub mod repository; +pub mod service; + +pub use adapter::*; +pub use contract::*; +pub use repository::*; +pub use service::*; diff --git a/src/service/src/authentication/adapter.rs b/src/service/src/authentication/adapter.rs new file mode 100644 index 0000000..98ac5d0 --- /dev/null +++ b/src/service/src/authentication/adapter.rs @@ -0,0 +1,71 @@ +use super::{Authenticated, AuthenticationRepository, Get}; +use data::user::{Field, New, User, UserRepository}; +use data::{Connect, Result}; + +use std::marker::PhantomData; + +pub struct AuthenticationAdapter +where + C: Send, + D: Connect + Sync, + UR: UserRepository + Sync, +{ + driver: D, + // connection: Option, + _user_repository: PhantomData, +} + +impl AuthenticationAdapter +where + C: Send, + D: Connect + Sync, + UR: UserRepository + Sync, +{ + pub const fn new(driver: D) -> Self { + Self { + driver, + // connection: None, + _user_repository: PhantomData, + } + } + + // async fn connection(&mut self) -> Result<&C> { + // if let Some(ref c) = self.connection { + // Ok(c) + // } else { + // self.connection = Some(self.driver.open_connection().await?); + // self.connection().await + // } + // } +} + +impl AuthenticationRepository for AuthenticationAdapter +where + C: Send, //+ Sync, + D: Connect + Sync, + UR: UserRepository + Sync, +{ + async fn get_user(&self, get: Get) -> Result> { + let c = self.driver.open_connection().await?; + let user = UR::read(&c, get.into()).await?; + D::close_connection(c).await?; + + Ok(user) + } + + async fn create_user(&self, new: New) -> Result { + let mut c = self.driver.open_connection().await?; + let user = UR::create(&mut c, new).await?; + D::close_connection(c).await?; + + Ok(user) + } + + async fn start_session(&self, mut user: User) -> Result { + let mut c = self.driver.open_connection().await?; + UR::update(&mut c, &mut user, Field::LastUsed(Some(data::Utc::now()))).await?; + D::close_connection(c).await?; + + Ok(Authenticated(user)) + } +} diff --git a/src/service/src/authentication/contract.rs b/src/service/src/authentication/contract.rs new file mode 100644 index 0000000..42b3d06 --- /dev/null +++ b/src/service/src/authentication/contract.rs @@ -0,0 +1,155 @@ +use super::Authenticated; +pub use data::Validation; +use data::{BoxDynError, user}; + +use derive_more::{Deref, Into}; +use garde::Validate; + +pub type Result = std::result::Result; + +pub trait AuthenticationContract: Send { + fn name_available(&self, name: Name) -> impl Future + Send; + fn email_available(&self, email: Email) -> impl Future + Send; + + fn login(&self, data: LoginData) -> impl Future> + Send; + fn register( + &mut self, + data: RegisterData, + ) -> impl Future> + Send; +} + +pub struct LoginData { + pub login: Login, + pub password: Password, +} + +pub struct RegisterData { + pub name: Name, + pub email: Email, + pub password: Password, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + // Login + #[error("login was not found")] + LoginNotFound, + #[error("incorrect password")] + IncorrectPassword, + // Register + #[error("username is taken")] + NameExists, + #[error("email is already in use")] + EmailExists, + // Shared + #[error("invalid password: {0}")] + InvalidPassword(data::BoxDynError), + #[error("data source error: {0}")] + Repository(data::BoxDynError), + + #[error(transparent)] + Other(data::BoxDynError), +} + +pub type ReturnError = (T, BoxDynError); + +#[derive(Clone)] +pub enum Login { + Name(Name), + Email(Email), +} +impl AsRef for Login { + fn as_ref(&self) -> &str { + match self { + Self::Name(name) => name.as_ref(), + Self::Email(email) => email.as_ref(), + } + } +} +impl TryFrom for Login { + type Error = ReturnError; + + fn try_from(value: String) -> Result { + let value = match Email::try_from(value) { + Ok(x) => return Ok(Self::Email(x)), + Err((v, _)) => v, + }; + match Name::try_from(value) { + Ok(x) => Ok(Self::Name(x)), + Err((v, _)) => Err((v, "login is invalid".into())), + } + } +} +impl From for String { + fn from(val: Login) -> Self { + match val { + Login::Name(name) => name.0.into(), + Login::Email(email) => email.0.into(), + } + } +} + +#[derive(Clone, Deref, Into)] +pub struct Name(user::Name); +impl AsRef for Name { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} +impl TryFrom for Name { + type Error = ReturnError; + + fn try_from(value: String) -> Result { + #[derive(Validate)] + #[garde(transparent)] + struct Username<'a>(#[garde(alphanumeric, length(chars, min = 2, max = 31))] &'a str); + + match Username(value.as_str()).validate() { + Ok(()) => (), + Err(e) => return Err((value, e.into())), + } + Ok(Self(user::Name::new(value)?)) + } +} + +#[derive(Clone, Deref, Into)] +pub struct Email(user::Email); +impl AsRef for Email { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} +impl TryFrom for Email { + type Error = ReturnError; + + fn try_from(value: String) -> Result { + #[derive(Validate)] + #[garde(transparent)] + pub struct Email<'a>(#[garde(email, length(chars, max = 255))] &'a str); + + match Email(value.as_str()).validate() { + Ok(()) => (), + Err(e) => return Err((value, e.into())), + } + Ok(Self(user::Email::new(value)?)) + } +} + +#[derive(Clone, Deref, Into)] +pub struct Password(String); +impl AsRef for Password { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} +impl TryFrom for Password { + type Error = ReturnError; + + fn try_from(value: String) -> Result { + if value.chars().count() > 7 { + Ok(Self(value)) + } else { + Err((value, "password must be longer than 7 characters".into())) + } + } +} diff --git a/src/service/src/authentication/repository.rs b/src/service/src/authentication/repository.rs new file mode 100644 index 0000000..39eaefa --- /dev/null +++ b/src/service/src/authentication/repository.rs @@ -0,0 +1,26 @@ +use data::Result; +use data::user::{Email, Name, New, Unique, User}; + +use derive_more::{Deref, DerefMut}; + +#[derive(Debug, Clone, Deref, DerefMut)] +pub struct Authenticated(pub(super) User); + +pub trait AuthenticationRepository { + fn get_user(&self, get: Get) -> impl Future>> + Send; + fn create_user(&self, new: New) -> impl Future> + Send; + fn start_session(&self, user: User) -> impl Future> + Send; +} + +pub enum Get { + Name(Name), + Email(Email), +} +impl From for Unique { + fn from(value: Get) -> Self { + match value { + Get::Name(s) => Self::Name(s), + Get::Email(s) => Self::Email(s), + } + } +} diff --git a/src/service/src/authentication/service.rs b/src/service/src/authentication/service.rs new file mode 100644 index 0000000..e044d3a --- /dev/null +++ b/src/service/src/authentication/service.rs @@ -0,0 +1,114 @@ +use super::{ + Authenticated, AuthenticationContract, AuthenticationRepository, Email, Error, Get, Login, + LoginData, Name, RegisterData, Result, Validation, +}; + +use argon2::{ + Argon2, + password_hash::{ + self, PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng, + }, +}; + +impl From for Error { + fn from(error: password_hash::Error) -> Self { + match error { + password_hash::Error::Password => Self::IncorrectPassword, + _ => Self::InvalidPassword(error.into()), + } + } +} + +pub struct AuthenticationService +where + R: AuthenticationRepository, +{ + pub(crate) repository: R, +} + +impl AuthenticationService +where + R: AuthenticationRepository, +{ + pub const fn new(repository: R) -> Self { + Self { repository } + } +} + +impl AuthenticationContract for AuthenticationService +where + R: AuthenticationRepository + Send + Sync, +{ + async fn name_available(&self, name: Name) -> Result { + if self + .repository + .get_user(Get::Name(name.into())) + .await + .map_err(Error::Repository)? + .is_some() + { + return Err(Error::NameExists); + } + Ok(()) + } + async fn email_available(&self, email: Email) -> Result { + if self + .repository + .get_user(Get::Email(email.into())) + .await + .map_err(Error::Repository)? + .is_some() + { + return Err(Error::EmailExists); + } + Ok(()) + } + + async fn login(&self, data: LoginData) -> Result { + let user = match data.login { + Login::Name(name) => self.repository.get_user(Get::Name(name.into())), + Login::Email(email) => self.repository.get_user(Get::Email(email.into())), + } + .await + .map_err(Error::Repository)? + .ok_or(Error::LoginNotFound)?; + + Argon2::default().verify_password( + data.password.as_bytes(), + &PasswordHash::new(user.password())?, + )?; + + self.repository + .start_session(user) + .await + .map_err(Error::Repository) + } + + async fn register(&mut self, data: RegisterData) -> Result { + self.name_available(data.name.clone()).await?; + self.email_available(data.email.clone()).await?; + + // Get PHC string ($argon2id$v=19$...) + let phc = Argon2::default() + .hash_password(data.password.as_bytes(), &SaltString::generate(&mut OsRng))? + .to_string(); + let password = data::user::Password::new(phc) + .map_err(|(_, e)| Error::InvalidPassword(e))?; + + let user = self + .repository + .create_user(data::user::New { + name: data.name.into(), + email: data.email.into(), + password, + last_used: None, + }) + .await + .map_err(Error::Repository)?; + + self.repository + .start_session(user) + .await + .map_err(Error::Repository) + } +} diff --git a/src/service/src/lib.rs b/src/service/src/lib.rs new file mode 100644 index 0000000..60c03cb --- /dev/null +++ b/src/service/src/lib.rs @@ -0,0 +1,8 @@ +pub mod authentication; +pub mod search; + +pub use authentication::{ + Authenticated, AuthenticationAdapter, AuthenticationContract, AuthenticationRepository, + AuthenticationService, +}; +pub use search::{Search, SearchAdapter, SearchContract, SearchRepository, SearchService}; diff --git a/src/service/src/search.rs b/src/service/src/search.rs new file mode 100644 index 0000000..142e065 --- /dev/null +++ b/src/service/src/search.rs @@ -0,0 +1,9 @@ +pub mod adapter; +pub mod contract; +pub mod repository; +pub mod service; + +pub use adapter::*; +pub use contract::*; +pub use repository::*; +pub use service::*; diff --git a/src/service/src/search/adapter.rs b/src/service/src/search/adapter.rs new file mode 100644 index 0000000..faa766c --- /dev/null +++ b/src/service/src/search/adapter.rs @@ -0,0 +1,43 @@ +use data::search::*; +use data::{Connect, Result}; + +use std::marker::PhantomData; + +pub struct SearchAdapter +where + C: Send, + D: Connect + Sync, + UR: SearchRepository + Sync, +{ + driver: D, + _search_repository: PhantomData, +} + +impl SearchAdapter +where + C: Send, + D: Connect + Sync, + UR: SearchRepository + Sync, +{ + pub const fn new(driver: D) -> Self { + Self { + driver, + _search_repository: PhantomData, + } + } +} + +impl super::SearchRepository for SearchAdapter +where + C: Send, //+ Sync, + D: Connect + Sync, + SR: SearchRepository + Sync, +{ + async fn search(&self, data: Data) -> Result> { + let c = self.driver.open_connection().await?; + let result = SR::search(&c, data).await?; + D::close_connection(c).await?; + + Ok(result) + } +} diff --git a/src/service/src/search/contract.rs b/src/service/src/search/contract.rs new file mode 100644 index 0000000..305b5e3 --- /dev/null +++ b/src/service/src/search/contract.rs @@ -0,0 +1,60 @@ +use data::{BoxDynError, search}; +pub use data::{ + Result, Validation, + search::{Mode, Order, Entry}, +}; + +use derive_more::{Deref, Into}; +use garde::Validate; + +pub trait SearchContract: Send { + fn search(&self, data: Data) -> impl Future>> + Send; +} + +pub struct Data { + pub mode: Mode, + pub order: Order, + pub search: Search, + + pub limit: u16, + pub exact: bool, + pub ascending: bool, +} + +impl From for search::Data { + fn from(value: Data) -> Self { + Self { + mode: value.mode, + order: value.order, + search: value.search.into(), + limit: value.limit, + exact: value.exact, + ascending: value.ascending, + } + } +} + +pub type ReturnError = (T, BoxDynError); + +#[derive(Clone, Deref, Into)] +pub struct Search(search::Search); +impl AsRef for Search { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} +impl TryFrom for Search { + type Error = ReturnError; + + fn try_from(value: String) -> Result { + #[derive(Validate)] + #[garde(transparent)] + struct Check<'a>(#[garde(ascii, length(chars, min = 1, max = 255))] &'a str); + + match Check(value.as_str()).validate() { + Ok(()) => (), + Err(e) => return Err((value, e.into())), + } + Ok(Self(search::Search::new(value)?)) + } +} diff --git a/src/service/src/search/repository.rs b/src/service/src/search/repository.rs new file mode 100644 index 0000000..0626718 --- /dev/null +++ b/src/service/src/search/repository.rs @@ -0,0 +1,6 @@ +use data::Result; +use data::search::{Data, Entry}; + +pub trait SearchRepository { + fn search(&self, data: Data) -> impl Future>> + Send; +} diff --git a/src/service/src/search/service.rs b/src/service/src/search/service.rs new file mode 100644 index 0000000..48a9e90 --- /dev/null +++ b/src/service/src/search/service.rs @@ -0,0 +1,27 @@ +use super::{Data, Result, SearchContract, SearchRepository}; +use data::search; + +pub struct SearchService +where + R: SearchRepository, +{ + pub(crate) repository: R, +} + +impl SearchService +where + R: SearchRepository, +{ + pub const fn new(repository: R) -> Self { + Self { repository } + } +} + +impl SearchContract for SearchService +where + R: SearchRepository + Send + Sync, +{ + async fn search(&self, data: Data) -> Result> { + self.repository.search(data.into()).await + } +} diff --git a/src/src/authentication.rs b/src/src/authentication.rs new file mode 100644 index 0000000..433bc8e --- /dev/null +++ b/src/src/authentication.rs @@ -0,0 +1,83 @@ +mod login; +mod register; + +use login::Login; +use register::Register; + +use service::{Authenticated, AuthenticationContract}; + +use iced::{Element, Task, futures::lock::Mutex}; +use std::sync::Arc; + +pub struct Authentication { + login: Login, + register: Register, + screen: Screen, +} + +enum Screen { + Login, + Register, +} + +#[derive(Debug)] +pub enum Message { + Login(login::Message), + Register(register::Message), +} + +pub enum Event { + Task(Task), + Authenticated(Authenticated), +} +impl From> for Event { + fn from(value: Task) -> Self { + Self::Task(value) + } +} + +impl Authentication { + pub fn new(service: Arc>) -> Self { + Self { + login: Login::new(service.clone()), + register: Register::new(service), + screen: Screen::Login, + } + } + + pub fn update(&mut self, message: Message) -> Option { + Some(match message { + Message::Login(message) => match self.login.update(message)? { + login::Event::SwitchToRegister => { + self.screen = Screen::Register; + return None; + } + + login::Event::Task(task) => task.map(Message::Login).into(), + login::Event::Authenticated(x) => Event::Authenticated(x), + }, + Message::Register(message) => match self.register.update(message)? { + register::Event::SwitchToLogin => { + self.screen = Screen::Login; + return None; + } + register::Event::Task(task) => task.map(Message::Register).into(), + register::Event::Authenticated(x) => Event::Authenticated(x), + }, + }) + } + + pub fn view(&self) -> Element { + match self.screen { + Screen::Login => self.login.view().map(Message::Login), + Screen::Register => self.register.view().map(Message::Register), + } + } + + pub fn title(&self) -> String { + match self.screen { + Screen::Login => self.login.title(), + Screen::Register => self.register.title(), + } + } +} diff --git a/src/src/authentication/login.rs b/src/src/authentication/login.rs new file mode 100644 index 0000000..df0f216 --- /dev/null +++ b/src/src/authentication/login.rs @@ -0,0 +1,181 @@ +use crate::input::Input; +use crate::widget::centerbox; +use service::{ + Authenticated, AuthenticationContract, + authentication::{self, Error, LoginData, Result}, +}; + +use iced::futures::lock::Mutex; +use iced::widget::{Space, button, checkbox, column, container, row, text}; +use iced::{Length, Task, padding}; +use std::sync::Arc; + +pub struct Login { + login: Input, + password: Input, + show_password: bool, + + state: State, + service: Arc>, +} +enum State { + None, + Requesting, + Success, + Error(String), +} + +pub enum Event { + SwitchToRegister, + Task(Task), + Authenticated(Authenticated), +} +impl From> for Event { + fn from(value: Task) -> Self { + Self::Task(value) + } +} + +#[derive(Debug, Clone)] +pub enum Message { + LoginChanged(String), + PasswordChanged(String), + ShowPasswordToggled(bool), + + LoginSubmitted, + PasswordSubmitted, + + LoginPressed, + RegisterPressed, + + RequestResult(Arc>), +} + +impl Login { + pub fn new(service: Arc>) -> Self { + Self { + login: Input::new("login_name"), + password: Input::new("login_password"), + show_password: false, + state: State::None, + service, + } + } + + pub fn update(&mut self, message: Message) -> Option { + match message { + Message::LoginChanged(s) => self.login.update(s), + Message::PasswordChanged(s) => self.password.update(s), + Message::ShowPasswordToggled(b) => self.show_password = b, + + Message::LoginSubmitted if self.login.critical() => (), + Message::LoginSubmitted => return Some(self.password.focus().into()), + + Message::RegisterPressed => return Some(Event::SwitchToRegister), + + Message::LoginPressed | Message::PasswordSubmitted => { + let login_data = LoginData { + login: match self.login.submit() { + Ok(x) => x, + Err(t) => return Some(t.into()), + }, + password: match self.password.submit() { + Ok(x) => x, + Err(t) => return Some(t.into()), + }, + }; + + self.state = State::Requesting; + let arc = self.service.clone(); + + return Some( + Task::perform( + async move { + let Some(service) = arc.try_lock() else { + return Err(Error::Other( + "other authentication request is being performed".into(), + )); + }; + service.login(login_data).await + }, + |r| Message::RequestResult(Arc::new(r)), + ) + .into(), + ); + } + Message::RequestResult(r) => match &*r { + Ok(a) => { + self.state = State::Success; + return Some(Event::Authenticated(a.clone())); + } + + Err(e) => { + self.state = State::None; + match e { + Error::LoginNotFound => self.login.set_warning(e), + Error::IncorrectPassword => self.password.set_warning(e), + Error::InvalidPassword(_) => self.password.set_error(e), + + _ => self.state = State::Error(e.to_string()), + } + } + }, + } + + None + } + + pub fn view(&self) -> iced::Element { + centerbox( + column![ + container(text(self.title()).size(20)) + .center_x(Length::Fill) + .padding(padding::bottom(10)), + self.login + .view("Email or Username") + .on_input(Message::LoginChanged) + .on_submit(Message::LoginSubmitted), + self.password + .view("Password") + .on_input(Message::PasswordChanged) + .on_submit(Message::PasswordSubmitted) + .secure(!self.show_password), + checkbox("Show password", self.show_password) + .on_toggle(Message::ShowPasswordToggled), + row![ + button(text("Register").center().size(18)) + .on_press(Message::RegisterPressed) + .style(button::secondary) + .width(Length::FillPortion(3)) + .padding(10), + Space::with_width(Length::FillPortion(2)), + button(text("Login").center().size(18)) + .on_press(Message::LoginPressed) + .style(button::primary) + .width(Length::FillPortion(3)) + .padding(10) + ] + .padding(padding::top(15)), + ] + .width(Length::Fixed(250.)) + .spacing(20), + ) + } + + pub fn title(&self) -> String { + let errors = [ + self.login.error(), + self.password.error(), + self.login.warning(), + self.password.warning(), + ]; + let error = errors.into_iter().flatten().next(); + + match &self.state { + State::None => error.map_or_else(|| "Login".into(), Into::into), + State::Success => "Success".into(), + State::Requesting => "Requesting...".into(), + State::Error(e) => e.into(), + } + } +} diff --git a/src/src/authentication/register.rs b/src/src/authentication/register.rs new file mode 100644 index 0000000..6615775 --- /dev/null +++ b/src/src/authentication/register.rs @@ -0,0 +1,234 @@ +use crate::input::{self, Input, Value}; +use crate::widget::centerbox; +use service::authentication::{self, Email, Name, Password, RegisterData}; +use service::{ + Authenticated, AuthenticationContract, + authentication::{Error, LoginData, Result}, +}; + +use iced::futures::lock::Mutex; +use iced::widget::{Space, button, checkbox, column, container, row, text}; +use iced::{Length, Task, padding}; +use std::sync::Arc; + +pub struct Register { + name: Input, + email: Input, + password: Input, + repeat: Input, + show_password: bool, + + state: State, + service: Arc>, +} +enum State { + None, + Success, + Requesting, + Error(String), +} + +pub enum Event { + SwitchToLogin, + Task(Task), + Authenticated(Authenticated), +} +impl From> for Event { + fn from(value: Task) -> Self { + Self::Task(value) + } +} + +#[derive(Debug, Clone)] +pub enum Message { + NameChanged(String), + EmailChanged(String), + PasswordChanged(String), + RepeatChanged(String), + ShowPasswordToggled(bool), + + EmailSubmitted, + NameSubmitted, + PasswordSubmitted, + RepeatSubmitted, + + RegisterPressed, + LoginPressed, + + RequestResult(Arc>), +} + +impl Register { + pub fn new(service: Arc>) -> Self { + Self { + name: Input::new("register_name"), + email: Input::new("register_email"), + password: Input::new("register_password"), + repeat: Input::new("register_repeat"), + show_password: false, + + state: State::None, + service, + } + } + + fn check_passwords(&mut self) { + if self.password.as_ref() == self.repeat.as_ref() { + self.repeat + .set_value(Value::Valid(self.repeat.as_ref().to_string())); + } else { + self.repeat.set_error(&"passwords are different"); + } + } + + pub fn update(&mut self, message: Message) -> Option { + match message { + Message::NameChanged(s) => self.name.update(s), + Message::EmailChanged(s) => self.email.update(s), + Message::PasswordChanged(s) => { + self.password.update(s); + self.check_passwords(); + } + Message::RepeatChanged(s) => { + self.repeat.set_value(Value::Valid(s)); + self.check_passwords(); + } + Message::ShowPasswordToggled(b) => self.show_password = b, + + Message::NameSubmitted if self.name.critical() => (), + Message::NameSubmitted => return Some(self.email.focus().into()), + Message::EmailSubmitted if self.email.critical() => (), + Message::EmailSubmitted => return Some(self.password.focus().into()), + Message::PasswordSubmitted if self.password.critical() => (), + Message::PasswordSubmitted => return Some(self.repeat.focus().into()), + Message::RegisterPressed | Message::RepeatSubmitted + if self.repeat.error().is_some() => + { + return Some(self.repeat.focus().into()); + } + Message::RegisterPressed | Message::RepeatSubmitted => { + let register_data = RegisterData { + name: match self.name.submit() { + Ok(x) => x, + Err(t) => return Some(t.into()), + }, + email: match self.email.submit() { + Ok(x) => x, + Err(t) => return Some(t.into()), + }, + password: match self.password.submit() { + Ok(x) => x, + Err(t) => return Some(t.into()), + }, + }; + + self.state = State::Requesting; + let arc = self.service.clone(); + + return Some( + Task::perform( + async move { + let Some(mut service) = arc.try_lock() else { + return Err(Error::Other( + "other authentication request is being performed".into(), + )); + }; + service.register(register_data).await + }, + |r| Message::RequestResult(Arc::new(r)), + ) + .into(), + ); + } + + Message::LoginPressed => return Some(Event::SwitchToLogin), + Message::RequestResult(r) => match &*r { + Ok(a) => { + self.state = State::Success; + return Some(Event::Authenticated(a.clone())); + } + + Err(e) => { + self.state = State::None; + match e { + Error::NameExists => self.name.set_warning(e), + Error::EmailExists => self.email.set_warning(e), + Error::IncorrectPassword => self.password.set_warning(e), + Error::InvalidPassword(_) => self.password.set_error(e), + + _ => self.state = State::Error(e.to_string()), + } + } + }, + } + + None + } + + pub fn view(&self) -> iced::Element { + centerbox( + column![ + container(text(self.title()).size(20)) + .center_x(Length::Fill) + .padding(padding::bottom(10)), + self.name + .view("Username") + .on_input(Message::NameChanged) + .on_submit(Message::NameSubmitted), + self.email + .view("Email") + .on_input(Message::EmailChanged) + .on_submit(Message::EmailSubmitted), + self.password + .view("Password") + .on_input(Message::PasswordChanged) + .on_submit(Message::PasswordSubmitted) + .secure(!self.show_password), + self.repeat + .view("Repeat Password") + .on_input(Message::RepeatChanged) + .on_submit(Message::RepeatSubmitted) + .secure(!self.show_password), + checkbox("Show password", self.show_password) + .on_toggle(Message::ShowPasswordToggled), + row![ + button(text("Login").center().size(18)) + .on_press(Message::LoginPressed) + .style(button::secondary) + .width(Length::FillPortion(3)) + .padding(10), + Space::with_width(Length::FillPortion(2)), + button(text("Register").center().size(18)) + .on_press(Message::RegisterPressed) + .style(button::primary) + .width(Length::FillPortion(3)) + .padding(10), + ] + .padding(padding::top(15)), + ] + .width(Length::Fixed(250.)) + .spacing(20), + ) + } + + pub fn title(&self) -> String { + let errors = [ + self.name.error(), + self.email.error(), + self.password.error(), + self.repeat.error(), + self.name.warning(), + self.email.warning(), + self.password.warning(), + self.repeat.warning(), + ]; + let error = errors.into_iter().flatten().next(); + + match &self.state { + State::None => error.map_or_else(|| "Register".into(), Into::into), + State::Success => "Success".into(), + State::Requesting => "Requesting...".into(), + State::Error(e) => e.into(), + } + } +} diff --git a/src/src/input.rs b/src/src/input.rs new file mode 100644 index 0000000..d50ebfd --- /dev/null +++ b/src/src/input.rs @@ -0,0 +1,171 @@ +use crate::widget::text_input::{error, success, warning}; + +use iced::widget::{TextInput, text_input, text_input::default}; + +/// A smarter [`text_input`].to avoid boilerplate. +pub struct Input { + id: &'static str, + value: Value, + warning: Option, +} + +// use std::ops::Deref; +// impl Deref for Input { +// type Target = Value; +// fn deref(&self) -> &Self::Target { +// &self.value +// } +// } + +impl> AsRef for Input { + fn as_ref(&self) -> &str { + self.value.as_ref() + } +} + +impl Input { + pub const fn new(id: &'static str) -> Self { + Self { + id, + value: Value::None, + warning: None, + } + } + + pub fn focus(&self) -> iced::Task { + iced::widget::text_input::focus(self.id) + } + + pub fn warning(&self) -> Option<&str> { + self.warning.as_ref().map(AsRef::as_ref) + } + pub fn error(&self) -> Option<&str> { + match &self.value { + Value::Invalid { error, .. } => Some(error.as_ref()), + _ => None, + } + } + + pub fn set_value(&mut self, value: Value) { + self.value = value; + } + pub fn set_warning(&mut self, value: &impl ToString) { + self.warning = Some(value.to_string()); + } +} + +impl> Input { + pub fn set_error(&mut self, value: &impl ToString) { + self.value.set_error(value.to_string()); + } + + pub fn view(&self, placeholder: &str) -> TextInput { + text_input(placeholder, self.value.as_ref()) + .id(self.id) + .padding(12) + .style(match self.value { + Value::Invalid { .. } => error, + Value::None | Value::Valid(_) if self.warning.is_some() => warning, + Value::Valid(_) => success, + Value::None => default, + }) + } +} + +impl Input +where + E: ToString, + T: TryFrom, +{ + pub fn update(&mut self, value: String) { + self.value.update(value); + self.warning = None; + } + + pub fn value(&mut self) -> Result<&T, &str> { + match self.value { + Value::None => { + self.value.update(String::new()); + self.value() + } + Value::Valid(ref x) => Ok(x), + Value::Invalid { ref error, .. } => Err(error), + } + } + pub fn submittable(&mut self) -> bool { + self.value().is_ok() + } + pub fn critical(&mut self) -> bool { + self.value().is_err() + } +} + +impl Input +where + E: ToString, + T: TryFrom + Clone, +{ + pub fn submit(&mut self) -> Result> { + match self.value() { + Ok(x) => Ok(x.clone()), + Err(_) => Err(self.focus()), + } + } +} + +#[derive(Default)] +pub enum Value { + #[default] + None, + Valid(T), + Invalid { + value: String, + error: String, + }, +} + +impl> AsRef for Value { + fn as_ref(&self) -> &str { + match self { + Self::None => "", + Self::Valid(x) => x.as_ref(), + Self::Invalid { value, .. } => value.as_ref(), + } + } +} + +impl> Value { + fn set_error(&mut self, error: String) { + match self { + Self::None => { + *self = Self::Invalid { + value: String::new(), + error, + } + } + Self::Valid(x) => { + *self = Self::Invalid { + value: x.as_ref().to_string(), + error, + } + } + Self::Invalid { error: e, .. } => *e = error, + } + } +} + +impl Value +where + E: ToString, + T: TryFrom, +{ + fn update(&mut self, value: String) { + *self = match T::try_from(value) { + Ok(x) => Self::Valid(x), + Err((s, e)) => Self::Invalid { + value: s, + error: e.to_string(), + }, + }; + } +} diff --git a/src/src/main.rs b/src/src/main.rs new file mode 100644 index 0000000..b286455 --- /dev/null +++ b/src/src/main.rs @@ -0,0 +1,208 @@ +mod authentication; +mod input; +mod search; +//mod statistics; +mod widget; + +use std::sync::Arc; + +use crate::authentication::Authentication; +use crate::search::Search; +//use crate::statistics::Statistics; + +use data::{MySqlPool, MySqlSearchAdapter, MySqlUserAdapter, SqlxPool}; +use iced::{ + Element, Subscription, Task, Theme, + futures::lock::Mutex, + widget::{center, row}, + window, +}; +use service::{ + Authenticated, AuthenticationAdapter, AuthenticationService, SearchAdapter, SearchService, +}; + +struct Repository { + scale_factor: f64, + main_id: window::Id, + screen: Screen, + + authenticated: Option, + + search: Search>>, + authentication: Authentication< + AuthenticationService>, + >, +} + +#[derive(Default)] +enum Screen { + Search, + // Statistics, + #[default] + Authentication, +} + +#[derive(Debug)] +enum Message { + ScaleUp, + ScaleDown, + WindowOpened(window::Id), + WindowClosed(window::Id), + + Search(search::Message), + Authentecation(authentication::Message), +} + +impl Repository { + fn new() -> (Self, Task) { + let (main_id, open_task) = window::open(window::Settings::default()); + // let (main_window, main_window_task) = MainWindow::new(); + + let pool = MySqlPool::new( + SqlxPool::connect_lazy( + &std::env::var("DATABASE_URL") + .expect("environment variable `DATABASE_URL` should be set"), + ) + .unwrap(), + ); + + let auth_service = Arc::new(Mutex::new(AuthenticationService::new( + AuthenticationAdapter::new(pool.clone()), + ))); + + let search_service = Arc::new(Mutex::new(SearchService::new(SearchAdapter::new( + pool.clone(), + )))); + + ( + Self { + main_id, + scale_factor: 1.4, + screen: Screen::default(), + + authenticated: None, + + search: Search::new(search_service), + authentication: Authentication::new(auth_service), + }, + Task::batch([ + open_task.map(Message::WindowOpened), + // main_window_task.map(Message::MainWindow), + ]), + ) + } + + fn update(&mut self, message: Message) -> Task { + match message { + Message::ScaleUp => self.scale_factor = (self.scale_factor + 0.2).min(5.0), + Message::ScaleDown => self.scale_factor = (self.scale_factor - 0.2).max(0.2), + Message::WindowOpened(id) => { + log!("Window opened: {id}"); + return iced::widget::focus_next(); + } + Message::WindowClosed(id) => { + log!("Window closed: {id}"); + if id == self.main_id { + return iced::exit(); + } + } + Message::Authentecation(message) => { + if let Some(event) = self.authentication.update(message) { + match event { + authentication::Event::Task(task) => { + return task.map(Message::Authentecation); + } + authentication::Event::Authenticated(authenticated) => { + log!("authenticated as {:#?}", *authenticated); + self.authenticated = Some(authenticated); + self.screen = Screen::Search; + } + } + } + } + Message::Search(m) => { + if let Some(event) = self.search.update(m) { + match event { + search::Event::Task(task) => { + return task.map(Message::Search); + } + search::Event::OpenPackage(id) => { + log!("opening package {id}"); + } + search::Event::OpenBase(id) => { + log!("opening package base {id}"); + } + search::Event::OpenURL(url) => { + log!("opening url {url}"); + match open::that(url.as_ref()) { + Ok(()) => { + log!("opened url {url}"); + } + Err(e) => { + log!("can't open url \"{url}\": {e}"); + } + } + } + } + } + } + } + + Task::none() + } + + fn view(&self, id: window::Id) -> Element { + if self.main_id == id { + match self.screen { + Screen::Search => self.search.view().map(Message::Search), + Screen::Authentication => self.authentication.view().map(Message::Authentecation), + } + } else { + center(row!["This window is unknown.", "It may be closed."]).into() + } + } + + fn title(&self, _: window::Id) -> String { + // "Repository".into() + match self.screen { + Screen::Search => self.search.title(), + Screen::Authentication => self.authentication.title(), + } + } + + fn subscription(&self) -> Subscription { + use iced::keyboard::{self, Key, Modifiers}; + + let hotkeys = keyboard::on_key_press(|key, modifiers| match (modifiers, key) { + (Modifiers::CTRL, Key::Character(c)) if c == "-" => Some(Message::ScaleDown), + (Modifiers::CTRL, Key::Character(c)) if c == "=" || c == "+" => Some(Message::ScaleUp), + _ => None, + }); + + Subscription::batch([hotkeys, window::close_events().map(Message::WindowClosed)]) + } + + const fn scale_factor(&self, _: window::Id) -> f64 { + self.scale_factor + } + + const fn theme(_: &Self, _: window::Id) -> Theme { + Theme::TokyoNight + } +} + +#[macro_export] +macro_rules! log { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + println!($($arg)*) + }; +} + +fn main() -> iced::Result { + iced::daemon(Repository::title, Repository::update, Repository::view) + .subscription(Repository::subscription) + .scale_factor(Repository::scale_factor) + .theme(Repository::theme) + .run_with(Repository::new) +} diff --git a/src/src/search.rs b/src/src/search.rs new file mode 100644 index 0000000..3ddaa7f --- /dev/null +++ b/src/src/search.rs @@ -0,0 +1,341 @@ +use crate::input::Input; +use crate::widget::{scroll, tip, url}; + +use iced::Length::Shrink; +use service::search::Data; +use service::{ + SearchContract, + search::{self, Entry, Result}, +}; + +use iced::widget::{Column, button, checkbox, column, container, lazy, pick_list, row, text}; +use iced::{Element, Length::Fill, Task, futures::lock::Mutex}; +use std::sync::Arc; +use strum::{Display, VariantArray}; + +pub struct Search { + input: Input, + mode: Mode, + order: Order, + ascending: bool, + exact: bool, + limit: u8, + + state: State, + service: Arc>, +} + +#[derive(Default)] +enum State { + #[default] + None, + Searching, + // Aborted, + Table(Table), + Error(String), +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Display, VariantArray)] +pub enum Mode { + Url, + Name, + #[strum(to_string = "Package Base")] + PackageBase, + Description, + #[strum(to_string = "Base description")] + BaseDescription, + #[default] + #[strum(to_string = "Name and Description")] + NameAndDescription, + User, + Flagger, + Packager, + Submitter, + Maintainer, +} +impl From for search::Mode { + fn from(value: Mode) -> Self { + match value { + Mode::Url => Self::Url, + Mode::Name => Self::Name, + Mode::PackageBase => Self::PackageBase, + Mode::Description => Self::Description, + Mode::BaseDescription => Self::BaseDescription, + Mode::NameAndDescription => Self::NameAndDescription, + Mode::User => Self::User, + Mode::Flagger => Self::Flagger, + Mode::Packager => Self::Packager, + Mode::Submitter => Self::Submitter, + Mode::Maintainer => Self::Maintainer, + } + } +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Display, VariantArray)] +pub enum Order { + Name, + Version, + #[strum(to_string = "Base Name")] + BaseName, + // Submitter, + #[default] + #[strum(to_string = "Last update")] + UpdatedAt, + #[strum(to_string = "Created time")] + CreatedAt, +} +impl From for search::Order { + fn from(value: Order) -> Self { + match value { + Order::Name => Self::Name, + Order::Version => Self::Version, + Order::BaseName => Self::BaseName, + Order::UpdatedAt => Self::UpdatedAt, + Order::CreatedAt => Self::CreatedAt, + } + } +} + +#[derive(Debug, Clone)] +pub enum Message { + // Search bar + Reset, + Search, + SearchChanged(String), + ModePicked(Mode), + OrderPicked(Order), + AscendingToggled(bool), + ExactToggled(bool), + ShowEntriesPicked(u8), + // Table + PackagePressed(u64), + BasePressed(u64), + URLPressed(Box), + + RequestResult(Arc>>), +} + +pub enum Event { + Task(Task), + OpenPackage(u64), + OpenBase(u64), + OpenURL(Box), +} +impl From> for Event { + fn from(value: Task) -> Self { + Self::Task(value) + } +} + +#[derive(Debug, Hash)] +struct Table(Vec); + +impl Table { + pub fn view(&self) -> Element<'static, Message> { + let mut table: Vec<_> = [ + "Package", // 0 + "Version", // 1 + "Base", // 2 + "URL", // 3 + "Description", // 4 + "Last Updated", // 5 + "Created", // 6 + ] + .into_iter() + .map(|s| { + let mut v = Vec::with_capacity(self.0.len()); + v.push(s.into()); + v.push("".into()); + v + }) + .collect(); + + for entry in &self.0 { + table[0].push(url(&entry.name, Message::PackagePressed(entry.id))); + table[1].push(text(entry.version.to_string()).into()); + table[2].push(url(&entry.base_name, Message::BasePressed(entry.base_id))); + table[3].push( + entry + .url + .as_ref() + .map_or("-".into(), |s| + + tip( + url(&"link", Message::URLPressed(s.clone())), + s.clone(), + tip::Position::Bottom, + ), + + ), + ); + table[4].push(text(entry.description.to_string()).into()); + table[5].push(text(entry.updated_at.to_string()).into()); + table[6].push(text(entry.created_at.to_string()).into()); + // table[5].push(Element::from(column( entry .maintainers .iter() .map(|(id, s)| url(s, Message::UserPressed(*id))),))); + } + + scroll( + row(table + .into_iter() + .map(|v| Column::from_vec(v).spacing(5).into())) + .spacing(20) + .padding(30), + ) + } +} + +impl Search { + pub fn new(service: Arc>) -> Self { + Self { + input: Input::new("search_input"), + mode: Mode::NameAndDescription, + order: Order::UpdatedAt, + ascending: false, + exact: false, + limit: 25, + state: State::default(), + service, + } + } + + pub fn view(&self) -> Element { + let search_bar = container(scroll( + column![ + row![ + self.input + .view("Search") + .on_input(Message::SearchChanged) + .on_submit(Message::Search), + tip( + button("Go").on_press(Message::Search), + "Perform the search", + tip::Position::Bottom, + ), + ] + .spacing(10), + row![ + tip( + button("Reset").on_press(Message::Reset), + "Reset the search bar", + tip::Position::Bottom, + ), + tip( + pick_list(Mode::VARIANTS, Some(&self.mode), Message::ModePicked), + "Search mode", + tip::Position::Bottom, + ), + tip( + pick_list(Order::VARIANTS, Some(&self.order), Message::OrderPicked), + "Field used to sort the results", + tip::Position::Bottom, + ), + tip( + checkbox("Exact", self.exact).on_toggle(Message::ExactToggled), + "Exact search", + tip::Position::Bottom, + ), + tip( + checkbox("Ascending", self.ascending).on_toggle(Message::AscendingToggled), + "Sort order of results", + tip::Position::Bottom, + ), + tip( + pick_list( + [25, 50, 75, 100], + Some(self.limit), + Message::ShowEntriesPicked + ), + "Number of results to show", + tip::Position::Bottom, + ), + ] + .spacing(10) + ] + .padding(20) + .width(750) + .spacing(10), + )) + .center_x(Fill); + + column![ + search_bar, + match &self.state { + State::None => Element::from(""), + State::Searching => "Searching...".into(), + // State::Aborted => "Aborted".into(), + State::Error(e) => text(e).into(), + State::Table(table) => container(lazy(table, |t| t.view())).center_x(Fill).into(), + } + ] + .into() + } + + pub fn update(&mut self, message: Message) -> Option { + match message { + Message::SearchChanged(s) => self.input.update(s), + Message::ModePicked(mode) => self.mode = mode, + Message::OrderPicked(order) => self.order = order, + Message::AscendingToggled(b) => self.ascending = b, + Message::ExactToggled(b) => self.exact = b, + Message::ShowEntriesPicked(x) => self.limit = x, + Message::Reset => { + let state = std::mem::take(&mut self.state); + *self = Self::new(self.service.clone()); + self.state = state; + } + + Message::PackagePressed(id) => return Some(Event::OpenPackage(id)), + Message::BasePressed(id) => return Some(Event::OpenBase(id)), + Message::URLPressed(url) => return Some(Event::OpenURL(url)), + + Message::Search => { + let search_data = Data { + mode: self.mode.into(), + order: self.order.into(), + search: match self.input.submit() { + Ok(x) => x, + Err(t) => return Some(t.into()), + }, + limit: self.limit.into(), + exact: self.exact, + ascending: self.ascending, + }; + + self.state = State::Searching; + let arc = self.service.clone(); + + return Some( + Task::perform( + async move { + let Some(service) = arc.try_lock() else { + return Err("other search request is being performed".into()); + }; + service.search(search_data).await + }, + |r| Message::RequestResult(Arc::new(r)), + ) + .into(), + ); + } + + Message::RequestResult(r) => match &*r { + Ok(v) => self.state = State::Table(Table(v.clone())), + Err(e) => self.state = State::Error(e.to_string()), + }, + } + + None + } + + pub fn title(&self) -> String { + let errors = [self.input.error(), self.input.warning()]; + let error = errors.into_iter().flatten().next(); + + match &self.state { + State::None => error.map_or_else(|| "Search".into(), Into::into), + State::Searching => "Searching...".into(), + State::Table(_) => "Displaying search results".into(), + State::Error(e) => e.into(), + } + } +} diff --git a/src/src/widget.rs b/src/src/widget.rs new file mode 100644 index 0000000..f064317 --- /dev/null +++ b/src/src/widget.rs @@ -0,0 +1,74 @@ +use iced::widget::{Scrollable, center, container, mouse_area, scrollable, text, tooltip}; +use iced::{Element, color}; + +/// Put content into a dark container at the center of the screen +/// which can be scrolled in multiple dirrections +pub fn centerbox<'a, Message: 'a>( + content: impl Into>, +) -> Element<'a, Message> { + center(scroll( + container(content).style(container::dark).padding(20), + )) + .into() +} + +/// Scrollable but in both vertical and horizontal directions +pub fn scroll<'a, Message: 'a>(content: impl Into>) -> Element<'a, Message> { + Scrollable::with_direction( + content, + scrollable::Direction::Both { + vertical: scrollable::Scrollbar::default(), + horizontal: scrollable::Scrollbar::default(), + }, + ) + .into() +} + +/// Clickable url +pub fn url<'a, Message: Clone + 'a>(txt: &impl ToString, msg: Message) -> Element<'a, Message> { + Element::from(mouse_area(text(txt.to_string()).color(color!(0x00_BB_FF))).on_press(msg)) +} + +pub mod tip { + pub use iced::widget::tooltip::Position; +} + +/// Tooltip with some styling applied +pub fn tip<'a, Message: 'a>( + content: impl Into>, + tip: impl ToString, + position: tip::Position, +) -> Element<'a, Message> { + tooltip( + content, + container(text(tip.to_string()).size(14)) + .padding(5) + .style(container::dark), + position, + ) + .into() +} + +pub mod text_input { + use iced::widget::text_input::{Status, Style, default}; + use iced::{Theme, color}; + + pub fn success(theme: &Theme, status: Status) -> Style { + Style { + background: color!(0x00_33_00).into(), + ..default(theme, status) + } + } + pub fn warning(theme: &Theme, status: Status) -> Style { + Style { + background: color!(0x33_33_00).into(), + ..default(theme, status) + } + } + pub fn error(theme: &Theme, status: Status) -> Style { + Style { + background: color!(0x33_00_00).into(), + ..default(theme, status) + } + } +}