diff --git a/Cargo.lock b/Cargo.lock index cc5095b..774dc2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,23 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -26,6 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -81,7 +80,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -91,7 +90,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -101,10 +100,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] -name = "arrayvec" -version = "0.7.4" +name = "atoi" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] [[package]] name = "autocfg" @@ -112,6 +114,21 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.21.7" @@ -119,43 +136,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] -name = "base64" -version = "0.22.0" +name = "base64ct" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" - -[[package]] -name = "bigdecimal" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9324c8014cd04590682b34f1e9448d38f0674d0f7b2dc553331016ef0e4e9ebc" -dependencies = [ - "autocfg", - "libm", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "bindgen" -version = "0.69.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" -dependencies = [ - "bitflags 2.5.0", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.60", -] +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bitflags" @@ -168,17 +152,8 @@ name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty", - "radium", - "tap", - "wyz", + "serde", ] [[package]] @@ -190,67 +165,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "borsh" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0901fc8eb0aca4c83be0106d6f2db17d86a08dfc2c25f0e84464bf381158add6" -dependencies = [ - "borsh-derive", - "cfg_aliases", -] - -[[package]] -name = "borsh-derive" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51670c3aa053938b0ee3bd67c3817e471e626151131b934038e83c5bf8de48f5" -dependencies = [ - "once_cell", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.60", - "syn_derive", -] - -[[package]] -name = "btoi" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" -dependencies = [ - "num-traits", -] - -[[package]] -name = "bufstream" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" - -[[package]] -name = "bytecheck" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "byteorder" version = "1.5.0" @@ -265,22 +179,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" -dependencies = [ - "jobserver", - "libc", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cfg-if" @@ -294,17 +195,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" -[[package]] -name = "clang-sys" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.5.4" @@ -324,7 +214,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] @@ -345,15 +235,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - [[package]] name = "colorchoice" version = "1.0.0" @@ -361,20 +242,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] -name = "core-foundation" -version = "0.9.4" +name = "const-oid" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cpufeatures" @@ -386,54 +257,19 @@ dependencies = [ ] [[package]] -name = "crc32fast" -version = "1.4.0" +name = "crc" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ - "cfg-if", + "crc-catalog", ] [[package]] -name = "crossbeam" -version = "0.8.4" +name = "crc-catalog" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -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", -] +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crossbeam-queue" @@ -461,58 +297,35 @@ dependencies = [ ] [[package]] -name = "darling" -version = "0.20.8" +name = "csv" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" dependencies = [ - "darling_core", - "darling_macro", + "csv-core", + "itoa", + "ryu", + "serde", ] [[package]] -name = "darling_core" -version = "0.20.8" +name = "csv-core" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 2.0.60", + "memchr", ] [[package]] -name = "darling_macro" -version = "0.20.8" +name = "der" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ - "darling_core", - "quote", - "syn 2.0.60", -] - -[[package]] -name = "deranged" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_utils" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61bb5a1014ce6dfc2a378578509abe775a5aa06bff584a547555d9efdb81b926" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", + "const-oid", + "pem-rfc7468", + "zeroize", ] [[package]] @@ -522,7 +335,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", + "subtle", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "edit" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f364860e764787163c8c8f58231003839be31276e821e2ad2092ddf496b1aa09" +dependencies = [ + "tempfile", + "which", ] [[package]] @@ -530,6 +382,38 @@ name = "either" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +dependencies = [ + "serde", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] [[package]] name = "equivalent" @@ -544,9 +428,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fastrand" version = "2.0.2" @@ -554,37 +455,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] -name = "flate2" -version = "1.0.28" +name = "finl_unicode" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ - "crc32fast", - "libz-sys", - "miniz_oxide", + "futures-core", + "futures-sink", + "spin 0.9.8", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -595,62 +481,76 @@ dependencies = [ ] [[package]] -name = "frunk" -version = "0.4.2" +name = "futures-channel" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a351b59e12f97b4176ee78497dff72e4276fb1ceb13e19056aca7fa0206287" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ - "frunk_core", - "frunk_derives", - "frunk_proc_macros", + "futures-core", + "futures-sink", ] [[package]] -name = "frunk_core" -version = "0.4.2" +name = "futures-core" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2469fab0bd07e64ccf0ad57a1438f63160c69b2e57f04a439653d68eb558d6" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] -name = "frunk_derives" -version = "0.4.2" +name = "futures-executor" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ - "frunk_proc_macro_helpers", - "quote", - "syn 2.0.60", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "frunk_proc_macro_helpers" -version = "0.1.2" +name = "futures-intrusive" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b54add839292b743aeda6ebedbd8b11e93404f902c56223e51b9ec18a13d2c" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ - "frunk_core", - "proc-macro2", - "quote", - "syn 2.0.60", + "futures-core", + "lock_api", + "parking_lot", ] [[package]] -name = "frunk_proc_macros" -version = "0.1.2" +name = "futures-io" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b85a1d4a9a6b300b41c05e8e13ef2feca03e0334127f29eca9506a7fe13a93" -dependencies = [ - "frunk_core", - "frunk_proc_macro_helpers", - "quote", - "syn 2.0.60", -] +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] -name = "funty" -version = "2.0.0" +name = "futures-sink" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] [[package]] name = "generic-array" @@ -674,19 +574,10 @@ dependencies = [ ] [[package]] -name = "glob" -version = "0.3.1" +name = "gimli" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "hashbrown" @@ -694,15 +585,27 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.11", + "ahash", "allocator-api2", ] +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown", +] + [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] [[package]] name = "heck" @@ -711,10 +614,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "ident_case" -version = "1.0.1" +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "idna" @@ -733,16 +675,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", ] [[package]] -name = "io-enum" -version = "1.1.3" +name = "indoc" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b53d712d99a73eec59ee5e4fe6057f8052142d38eeafbbffcb06b36d738a6e" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "derive_utils", + "hermit-abi", + "libc", + "windows-sys 0.52.0", ] [[package]] @@ -760,26 +710,14 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "jobserver" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685a7d121ee3f65ae4fddd72b25a04bb36b6af81bc0828f7d5434c0fe60fa3a2" -dependencies = [ - "libc", -] - [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +dependencies = [ + "spin 0.5.2", +] [[package]] name = "libc" @@ -787,16 +725,6 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" -[[package]] -name = "libloading" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" -dependencies = [ - "cfg-if", - "windows-targets", -] - [[package]] name = "libm" version = "0.2.8" @@ -804,10 +732,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] -name = "libz-sys" -version = "1.1.16" +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", + "libc", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" dependencies = [ "cc", "pkg-config", @@ -820,6 +758,16 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.21" @@ -827,12 +775,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] -name = "lru" -version = "0.12.3" +name = "md-5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ - "hashbrown 0.14.3", + "cfg-if", + "digest", ] [[package]] @@ -857,86 +806,14 @@ dependencies = [ ] [[package]] -name = "mysql" -version = "25.0.0" +name = "mio" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4cc09a8118051e4617886c9c6e693c61444c2eeb5f9a792dc5d631501706565" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ - "bufstream", - "bytes", - "crossbeam", - "flate2", - "io-enum", "libc", - "lru", - "mysql_common", - "named_pipe", - "native-tls", - "once_cell", - "pem", - "percent-encoding", - "serde", - "serde_json", - "socket2", - "twox-hash", - "url", -] - -[[package]] -name = "mysql-common-derive" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe0450cc9344afff34915f8328600ab5ae19260802a334d0f72d2d5bdda3bfe" -dependencies = [ - "darling", - "heck 0.4.1", - "num-bigint", - "proc-macro-crate", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.60", - "termcolor", - "thiserror", -] - -[[package]] -name = "mysql_common" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ccdc1fe2bb3ef97e07ba4397327ed45509a1e2e499e2f8265243879cbc7313c" -dependencies = [ - "base64 0.21.7", - "bigdecimal", - "bindgen", - "bitflags 2.5.0", - "bitvec", - "btoi", - "byteorder", - "bytes", - "cc", - "cmake", - "crc32fast", - "flate2", - "frunk", - "lazy_static", - "mysql-common-derive", - "num-bigint", - "num-traits", - "rand", - "regex", - "rust_decimal", - "saturating", - "serde", - "serde_json", - "sha1", - "sha2", - "smallvec", - "subprocess", - "thiserror", - "time", - "uuid", - "zstd", + "wasi", + "windows-sys 0.48.0", ] [[package]] @@ -945,36 +822,31 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "mysql", + "edit", + "env_logger", + "indoc", + "itertools", + "log", + "nix", + "prettytable", + "rpassword", "serde", + "serde_json", + "sqlx", + "tokio", "toml", ] [[package]] -name = "named_pipe" -version = "0.4.1" +name = "nix" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9c443cce91fc3e12f017290db75dde490d685cdaaf508d7159d7cf41f0eb2b" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "winapi", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", + "bitflags 2.5.0", + "cfg-if", + "cfg_aliases", "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", ] [[package]] @@ -988,22 +860,22 @@ dependencies = [ ] [[package]] -name = "num-bigint" -version = "0.4.4" +name = "num-bigint-dig" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" dependencies = [ - "autocfg", + "byteorder", + "lazy_static", + "libm", "num-integer", + "num-iter", "num-traits", + "rand", + "smallvec", + "zeroize", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "num-integer" version = "0.1.46" @@ -1013,6 +885,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.18" @@ -1020,6 +903,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" 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", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", ] [[package]] @@ -1029,57 +932,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "openssl" -version = "0.10.64" +name = "parking_lot" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ - "bitflags 2.5.0", "cfg-if", - "foreign-types", "libc", - "once_cell", - "openssl-macros", - "openssl-sys", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", ] [[package]] -name = "openssl-macros" -version = "0.1.1" +name = "paste" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.60", -] +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] -name = "openssl-probe" -version = "0.1.5" +name = "pem-rfc7468" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "pem" -version = "3.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" -dependencies = [ - "base64 0.22.0", - "serde", + "base64ct", ] [[package]] @@ -1088,18 +975,45 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1107,36 +1021,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] -name = "proc-macro-crate" -version = "3.1.0" +name = "prettytable" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +checksum = "46480520d1b77c9a3482d39939fcf96831537a250ec62d4fd8fbdf8e0302e781" dependencies = [ - "toml_edit 0.21.1", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", + "csv", + "encode_unicode", + "is-terminal", + "lazy_static", + "term", + "unicode-width", ] [[package]] @@ -1148,26 +1043,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "quote" version = "1.0.36" @@ -1177,12 +1052,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" version = "0.8.5" @@ -1213,6 +1082,26 @@ dependencies = [ "getrandom", ] +[[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_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.10.4" @@ -1243,64 +1132,66 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] -name = "rend" -version = "0.4.2" +name = "ring" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ - "bytecheck", + "cc", + "cfg-if", + "getrandom", + "libc", + "spin 0.9.8", + "untrusted", + "windows-sys 0.52.0", ] [[package]] -name = "rkyv" -version = "0.7.44" +name = "rpassword" +version = "7.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", + "libc", + "rtoolbox", + "windows-sys 0.48.0", ] [[package]] -name = "rkyv_derive" -version = "0.7.44" +name = "rsa" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "rust_decimal" -version = "1.35.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" -dependencies = [ - "arrayvec", - "borsh", - "bytes", + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", "num-traits", - "rand", - "rkyv", - "serde", - "serde_json", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", ] [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "rtoolbox" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" @@ -1312,9 +1203,45 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.21.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +dependencies = [ + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" + [[package]] name = "ryu" version = "1.0.17" @@ -1322,47 +1249,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] -name = "saturating" -version = "0.1.0" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece8e78b2f38ec51c51f5d475df0a7187ba5111b2a28bdc761ee05b075d40a71" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "schannel" -version = "0.1.23" +name = "sct" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "windows-sys", -] - -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - -[[package]] -name = "security-framework" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" -dependencies = [ - "core-foundation-sys", - "libc", + "ring", + "untrusted", ] [[package]] @@ -1428,16 +1327,23 @@ dependencies = [ ] [[package]] -name = "shlex" -version = "1.3.0" +name = "signature" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] [[package]] -name = "simdutf8" -version = "0.1.4" +name = "slab" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" @@ -1452,20 +1358,251 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "spin" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "strsim" -version = "0.10.0" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" +dependencies = [ + "ahash", + "atoi", + "byteorder", + "bytes", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", + "webpki-roots", +] + +[[package]] +name = "sqlx-macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 1.0.109", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.1", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-sqlite", + "syn 1.0.109", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" +dependencies = [ + "atoi", + "base64", + "bitflags 2.5.0", + "byteorder", + "bytes", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" +dependencies = [ + "atoi", + "base64", + "bitflags 2.5.0", + "byteorder", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" +dependencies = [ + "atoi", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "sqlx-core", + "tracing", + "url", + "urlencoding", +] + +[[package]] +name = "stringprep" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +dependencies = [ + "finl_unicode", + "unicode-bidi", + "unicode-normalization", +] [[package]] name = "strsim" @@ -1474,14 +1611,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "subprocess" -version = "0.2.9" +name = "subtle" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" -dependencies = [ - "libc", - "winapi", -] +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -1505,24 +1638,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "syn_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.60", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tempfile" version = "3.10.1" @@ -1532,16 +1647,18 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] -name = "termcolor" -version = "1.4.1" +name = "term" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" dependencies = [ - "winapi-util", + "dirs-next", + "rustversion", + "winapi", ] [[package]] @@ -1564,35 +1681,6 @@ dependencies = [ "syn 2.0.60", ] -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -1608,6 +1696,45 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.12" @@ -1617,7 +1744,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.12", + "toml_edit", ] [[package]] @@ -1629,17 +1756,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.12" @@ -1650,18 +1766,39 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.6", + "winnow", ] [[package]] -name = "twox-hash" -version = "1.6.3" +name = "tracing" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", - "rand", - "static_assertions", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", ] [[package]] @@ -1691,6 +1828,30 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -1702,18 +1863,18 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "uuid" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" - [[package]] name = "vcpkg" version = "0.2.15" @@ -1732,6 +1893,40 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "whoami" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" +dependencies = [ + "redox_syscall", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1748,28 +1943,43 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 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", + "windows-targets 0.52.5", +] + +[[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]] @@ -1778,28 +1988,46 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] +[[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +[[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +[[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.5" @@ -1812,39 +2040,54 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +[[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +[[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +[[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +[[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.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.6.6" @@ -1854,15 +2097,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "zerocopy" version = "0.7.32" @@ -1884,29 +2118,7 @@ dependencies = [ ] [[package]] -name = "zstd" -version = "0.13.1" +name = "zeroize" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" -dependencies = [ - "cc", - "pkg-config", -] +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index eafefd4..f23da8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,26 @@ edition = "2021" [dependencies] anyhow = "1.0.82" clap = { version = "4.5.4", features = ["derive"] } -mysql = "25.0.0" +edit = "0.1.5" +env_logger = "0.11.3" +indoc = "2.0.5" +itertools = "0.12.1" +log = "0.4.21" +nix = { version = "0.28.0", features = ["user"] } +prettytable = "0.10.0" +rpassword = "7.3.1" serde = "1.0.198" +serde_json = "1.0.116" +sqlx = { version = "0.7.4", features = ["runtime-tokio", "mysql", "tls-rustls"] } +tokio = { version = "1.37.0", features = ["rt-multi-thread", "macros"] } toml = "0.8.12" [[bin]] name = "mysqladm" bench = false path = "src/main.rs" + +[profile.release] +strip = true +lto = true +codegen-units = 1 diff --git a/example-config.toml b/example-config.toml new file mode 100644 index 0000000..5eb1888 --- /dev/null +++ b/example-config.toml @@ -0,0 +1,7 @@ +# This should go to `/etc/mysqladm/config.toml` + +[mysql] +host = "localhost" +posrt = 3306 +username = "root" +password = "secret" \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..e96a85e --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,2 @@ +pub mod database_command; +pub mod user_command; diff --git a/src/cli/database_command.rs b/src/cli/database_command.rs new file mode 100644 index 0000000..51bb8f6 --- /dev/null +++ b/src/cli/database_command.rs @@ -0,0 +1,343 @@ +use anyhow::Context; +use clap::Parser; +use prettytable::{Cell, Row, Table}; +use sqlx::MySqlConnection; + +use crate::core::{self, database_operations::DatabasePrivileges}; + +#[derive(Parser)] +pub struct DatabaseArgs { + #[clap(subcommand)] + subcmd: DatabaseCommand, +} + +// TODO: Support batch creation/dropping,showing of databases, +// using a comma-separated list of database names. + +#[derive(Parser)] +enum DatabaseCommand { + /// Create the DATABASE(S). + #[command(alias = "add", alias = "c")] + Create(DatabaseCreateArgs), + + /// Delete the DATABASE(S). + #[command(alias = "remove", alias = "delete", alias = "rm", alias = "d")] + Drop(DatabaseDropArgs), + + /// List the DATABASE(S) you own. + #[command()] + List(DatabaseListArgs), + + /// Give information about the DATABASE(S), or if none are given, all the ones you own. + /// + /// In particular, this will show the permissions for the database(s) owned by the current user. + #[command(alias = "s")] + ShowPerm(DatabaseShowPermArgs), + + /// Change permissions for the DATABASE(S). Run `edit-perm --help` for more information. + /// + /// TODO: fix this help message. + /// + /// This command has two modes of operation: + /// 1. Interactive mode: If the `-t` flag is used, the user will be prompted to edit the permissions using a text editor. + /// 2. Non-interactive mode: If the `-t` flag is not used, the user can specify the permissions to change using the `-p` flag. + /// + /// In non-interactive mode, the `-p` flag should be followed by strings, each representing a single permission change. + /// + /// The permission arguments should be a string, formatted as `db:user:privileges` + /// where privs are a string of characters, each representing a single permissions, + /// with the exception of `A` which represents all permissions. + /// + /// The permission to character mapping is as follows: + /// + /// - `s` - SELECT + /// - `i` - INSERT + /// - `u` - UPDATE + /// - `d` - DELETE + /// - `c` - CREATE + /// - `D` - DROP + /// - `a` - ALTER + /// - `I` - INDEX + /// - `t` - CREATE TEMPORARY TABLES + /// - `l` - LOCK TABLES + /// - `r` - REFERENCES + /// - `A` - ALL PRIVILEGES + /// + #[command(display_name = "edit-perm", alias = "e", verbatim_doc_comment)] + EditPerm(DatabaseEditPermArgs), +} + +#[derive(Parser)] +struct DatabaseCreateArgs { + /// The name of the database(s) to create. + #[arg(num_args = 1..)] + name: Vec, +} + +#[derive(Parser)] +struct DatabaseDropArgs { + /// The name of the database(s) to drop. + #[arg(num_args = 1..)] + name: Vec, +} + +#[derive(Parser)] +struct DatabaseListArgs { + /// Whether to output the information in JSON format. + #[arg(short, long)] + json: bool, +} + +#[derive(Parser)] +struct DatabaseShowPermArgs { + /// The name of the database(s) to show. + #[arg(num_args = 0..)] + name: Vec, + + /// Whether to output the information in JSON format. + #[arg(short, long)] + json: bool, +} + +#[derive(Parser)] +struct DatabaseEditPermArgs { + /// The name of the database to edit permissions for. + name: Option, + + #[arg(short, long, value_name = "[DATABASE:]USER:PERMISSIONS", num_args = 0..)] + perm: Vec, + + /// Whether to output the information in JSON format. + #[arg(short, long)] + json: bool, + + /// Whether to edit the permissions using a text editor. + #[arg(short, long)] + text: bool, + + /// Specify the text editor to use for editing permissions. + #[arg(short, long)] + editor: Option, + + /// Disable confirmation before saving changes. + #[arg(short, long)] + yes: bool, +} + +pub async fn handle_command(args: DatabaseArgs, conn: MySqlConnection) -> anyhow::Result<()> { + match args.subcmd { + DatabaseCommand::Create(args) => create_databases(args, conn).await, + DatabaseCommand::Drop(args) => drop_databases(args, conn).await, + DatabaseCommand::List(args) => list_databases(args, conn).await, + DatabaseCommand::ShowPerm(args) => show_databases(args, conn).await, + DatabaseCommand::EditPerm(args) => edit_permissions(args, conn).await, + } +} + +async fn create_databases( + args: DatabaseCreateArgs, + mut conn: MySqlConnection, +) -> anyhow::Result<()> { + if args.name.is_empty() { + anyhow::bail!("No database names provided"); + } + + for name in args.name { + // TODO: This can be optimized by fetching all the database privileges in one query. + if let Err(e) = core::database_operations::create_database(&name, &mut conn).await { + eprintln!("Failed to create database '{}': {}", name, e); + eprintln!("Skipping..."); + } + } + + Ok(()) +} + +async fn drop_databases(args: DatabaseDropArgs, mut conn: MySqlConnection) -> anyhow::Result<()> { + if args.name.is_empty() { + anyhow::bail!("No database names provided"); + } + + for name in args.name { + // TODO: This can be optimized by fetching all the database privileges in one query. + if let Err(e) = core::database_operations::drop_database(&name, &mut conn).await { + eprintln!("Failed to drop database '{}': {}", name, e); + eprintln!("Skipping..."); + } + } + + Ok(()) +} + +async fn list_databases(args: DatabaseListArgs, mut conn: MySqlConnection) -> anyhow::Result<()> { + let databases = core::database_operations::get_database_list(&mut conn).await?; + + if databases.is_empty() { + println!("No databases to show."); + return Ok(()); + } + + if args.json { + println!("{}", serde_json::to_string_pretty(&databases)?); + } else { + for db in databases { + println!("{}", db); + } + } + + Ok(()) +} + +async fn show_databases(args: DatabaseShowPermArgs, mut conn: MySqlConnection) -> anyhow::Result<()> { + let database_users_to_show = if args.name.is_empty() { + core::database_operations::get_all_database_privileges(&mut conn).await? + } else { + // TODO: This can be optimized by fetching all the database privileges in one query. + let mut result = Vec::with_capacity(args.name.len()); + for name in args.name { + match core::database_operations::get_database_privileges(&name, &mut conn).await { + Ok(db) => result.extend(db), + Err(e) => { + eprintln!("Failed to show database '{}': {}", name, e); + eprintln!("Skipping..."); + } + } + } + result + }; + + if database_users_to_show.is_empty() { + println!("No database users to show."); + return Ok(()); + } + + if args.json { + println!("{}", serde_json::to_string_pretty(&database_users_to_show)?); + } else { + let mut table = Table::new(); + table.add_row(Row::new( + core::database_operations::HUMAN_READABLE_DATABASE_PRIVILEGE_NAMES + .iter() + .map(|(name, _)| Cell::new(name)) + .collect(), + )); + + for row in database_users_to_show { + table.add_row(row![ + row.db, + row.user, + row.select_priv, + row.insert_priv, + row.update_priv, + row.delete_priv, + row.create_priv, + row.drop_priv, + row.alter_priv, + row.index_priv, + row.create_tmp_table_priv, + row.lock_tables_priv, + row.references_priv + ]); + } + table.printstd(); + } + + Ok(()) +} + +/// See documentation for `DatabaseCommand::EditPerm`. +fn parse_permission_table_cli_arg(arg: &str) -> anyhow::Result { + let parts: Vec<&str> = arg.split(':').collect(); + if parts.len() != 3 { + anyhow::bail!("Invalid argument format. See `edit-perm --help` for more information."); + } + + let db = parts[0].to_string(); + let user = parts[1].to_string(); + let privs = parts[2].to_string(); + + let mut result = DatabasePrivileges { + db, + user, + select_priv: "N".to_string(), + insert_priv: "N".to_string(), + update_priv: "N".to_string(), + delete_priv: "N".to_string(), + create_priv: "N".to_string(), + drop_priv: "N".to_string(), + alter_priv: "N".to_string(), + index_priv: "N".to_string(), + create_tmp_table_priv: "N".to_string(), + lock_tables_priv: "N".to_string(), + references_priv: "N".to_string(), + }; + + for char in privs.chars() { + match char { + 's' => result.select_priv = "Y".to_string(), + 'i' => result.insert_priv = "Y".to_string(), + 'u' => result.update_priv = "Y".to_string(), + 'd' => result.delete_priv = "Y".to_string(), + 'c' => result.create_priv = "Y".to_string(), + 'D' => result.drop_priv = "Y".to_string(), + 'a' => result.alter_priv = "Y".to_string(), + 'I' => result.index_priv = "Y".to_string(), + 't' => result.create_tmp_table_priv = "Y".to_string(), + 'l' => result.lock_tables_priv = "Y".to_string(), + 'r' => result.references_priv = "Y".to_string(), + 'A' => { + result.select_priv = "Y".to_string(); + result.insert_priv = "Y".to_string(); + result.update_priv = "Y".to_string(); + result.delete_priv = "Y".to_string(); + result.create_priv = "Y".to_string(); + result.drop_priv = "Y".to_string(); + result.alter_priv = "Y".to_string(); + result.index_priv = "Y".to_string(); + result.create_tmp_table_priv = "Y".to_string(); + result.lock_tables_priv = "Y".to_string(); + result.references_priv = "Y".to_string(); + } + _ => anyhow::bail!("Invalid permission character: {}", char), + } + } + + Ok(result) +} + +async fn edit_permissions(args: DatabaseEditPermArgs, mut conn: MySqlConnection) -> anyhow::Result<()> { + let _data = if let Some(name) = &args.name { + core::database_operations::get_database_privileges(name, &mut conn).await? + } else { + core::database_operations::get_all_database_privileges(&mut conn).await? + }; + + if !args.text { + let permissions_to_change: Vec = if let Some(name) = args.name { + args.perm + .iter() + .map(|perm| { + parse_permission_table_cli_arg(&format!("{}:{}", name, &perm)) + .context(format!("Failed parsing database permissions: `{}`", &perm)) + }) + .collect::>>()? + } else { + args.perm + .iter() + .map(|perm| { + parse_permission_table_cli_arg(perm) + .context(format!("Failed parsing database permissions: `{}`", &perm)) + }) + .collect::>>()? + }; + + println!("{:#?}", permissions_to_change); + } else { + // TODO: debug assert that -p is not used with -t + } + + // TODO: find the difference between the two vectors, and ask for confirmation before applying the changes. + + // TODO: apply the changes to the database. + unimplemented!(); +} diff --git a/src/cli/user_command.rs b/src/cli/user_command.rs new file mode 100644 index 0000000..4a500aa --- /dev/null +++ b/src/cli/user_command.rs @@ -0,0 +1,180 @@ +use std::vec; + +use anyhow::Context; +use clap::Parser; +use sqlx::MySqlConnection; + +use crate::core::user_operations::validate_ownership_of_user_name; + +#[derive(Parser)] +pub struct UserArgs { + #[clap(subcommand)] + subcmd: UserCommand, +} + +#[derive(Parser)] +enum UserCommand { + /// Create the USER(s). + #[command(alias = "add", alias = "c")] + Create(UserCreateArgs), + + /// Delete the USER(s). + #[command(alias = "remove", alias = "delete", alias = "rm", alias = "d")] + Drop(UserDeleteArgs), + + /// Change the MySQL password for the USER. + #[command(alias = "password", alias = "p")] + Passwd(UserPasswdArgs), + + /// Give information about the USER(s), or if no USER is given, all USERs you have access to. + #[command(alias = "list", alias = "ls", alias = "s")] + Show(UserShowArgs), +} + +#[derive(Parser)] +struct UserCreateArgs { + #[arg(num_args = 1..)] + username: Vec, +} + +#[derive(Parser)] +struct UserDeleteArgs { + #[arg(num_args = 1..)] + username: Vec, +} + +#[derive(Parser)] +struct UserPasswdArgs { + username: String, + + #[clap(short, long)] + password_file: Option, +} + +#[derive(Parser)] +struct UserShowArgs { + #[arg(num_args = 0..)] + username: Vec, +} + +pub async fn handle_command(args: UserArgs, conn: MySqlConnection) -> anyhow::Result<()> { + match args.subcmd { + UserCommand::Create(args) => create_users(args, conn).await, + UserCommand::Drop(args) => drop_users(args, conn).await, + UserCommand::Passwd(args) => change_password_for_user(args, conn).await, + UserCommand::Show(args) => show_users(args, conn).await, + } +} + +// TODO: provide a better error message when the user already exists +async fn create_users(args: UserCreateArgs, mut conn: MySqlConnection) -> anyhow::Result<()> { + if args.username.is_empty() { + anyhow::bail!("No usernames provided"); + } + + for username in args.username { + if let Err(e) = + crate::core::user_operations::create_database_user(&username, &mut conn).await + { + eprintln!("{}", e); + eprintln!("Skipping..."); + } + } + Ok(()) +} + +// TODO: provide a better error message when the user does not exist +async fn drop_users(args: UserDeleteArgs, mut conn: MySqlConnection) -> anyhow::Result<()> { + if args.username.is_empty() { + anyhow::bail!("No usernames provided"); + } + + for username in args.username { + if let Err(e) = + crate::core::user_operations::delete_database_user(&username, &mut conn).await + { + eprintln!("{}", e); + eprintln!("Skipping..."); + } + } + Ok(()) +} + +async fn change_password_for_user( + args: UserPasswdArgs, + mut conn: MySqlConnection, +) -> anyhow::Result<()> { + // NOTE: although this also is checked in `set_password_for_database_user`, we check it here + // to provide a more natural order of error messages. + let unix_user = crate::core::common::get_current_unix_user()?; + validate_ownership_of_user_name(&args.username, &unix_user)?; + + let password = if let Some(password_file) = args.password_file { + std::fs::read_to_string(password_file) + .context("Failed to read password file")? + .trim() + .to_string() + } else { + let pass1 = rpassword::prompt_password("Enter new password: ") + .context("Failed to read password")?; + + let pass2 = rpassword::prompt_password("Re-enter new password: ") + .context("Failed to read password")?; + + if pass1 != pass2 { + anyhow::bail!("Passwords do not match"); + } + + pass1 + }; + + crate::core::user_operations::set_password_for_database_user( + &args.username, + &password, + &mut conn, + ) + .await?; + + Ok(()) +} + +async fn show_users(args: UserShowArgs, mut conn: MySqlConnection) -> anyhow::Result<()> { + let user = crate::core::common::get_current_unix_user()?; + + let users = if args.username.is_empty() { + crate::core::user_operations::get_all_database_users_for_user(&user, &mut conn).await? + } else { + let mut result = vec![]; + for username in args.username { + if let Err(e) = validate_ownership_of_user_name(&username, &user) { + eprintln!("{}", e); + eprintln!("Skipping..."); + continue; + } + + let user = + crate::core::user_operations::get_database_user_for_user(&username, &mut conn) + .await?; + if let Some(user) = user { + result.push(user); + } else { + eprintln!("User not found: {}", username); + } + } + result + }; + + for user in users { + println!( + "User '{}': {}", + &user.user, + if !(user.authentication_string.is_empty() && user.password.is_empty()) { + "password set." + } else { + "no password set." + } + ); + } + + Ok(()) +} diff --git a/src/core.rs b/src/core.rs new file mode 100644 index 0000000..b83db7b --- /dev/null +++ b/src/core.rs @@ -0,0 +1,4 @@ +pub mod common; +pub mod config; +pub mod database_operations; +pub mod user_operations; diff --git a/src/core/common.rs b/src/core/common.rs new file mode 100644 index 0000000..813aa6c --- /dev/null +++ b/src/core/common.rs @@ -0,0 +1,91 @@ +use anyhow::Context; +use indoc::indoc; +use itertools::Itertools; +use nix::unistd::{getuid, Group, User}; +use std::ffi::CString; + +pub fn get_current_unix_user() -> anyhow::Result { + User::from_uid(getuid()) + .context("Failed to look up your UNIX username") + .and_then(|u| u.ok_or(anyhow::anyhow!("Failed to look up your UNIX username"))) +} + +pub fn get_unix_groups(user: &User) -> anyhow::Result> { + let user_cstr = + CString::new(user.name.as_bytes()).context("Failed to convert username to CStr")?; + let groups = nix::unistd::getgrouplist(&user_cstr, user.gid)? + .iter() + .filter_map(|gid| { + match Group::from_gid(*gid).map_err(|e| { + log::trace!( + "Failed to look up group with GID {}: {}\nIgnoring...", + gid, + e + ); + e + }) { + Ok(Some(group)) => Some(group), + _ => None, + } + }) + .collect::>(); + + Ok(groups) +} + +pub fn validate_prefix_for_user<'a>(name: &'a str, user: &User) -> anyhow::Result<&'a str> { + let user_groups = get_unix_groups(user)?; + + let mut split_name = name.split('_'); + + let prefix = split_name + .next() + .ok_or(anyhow::anyhow!(indoc! {r#" + Failed to find prefix. + "#},)) + .and_then(|prefix| { + if user.name == prefix || user_groups.iter().any(|g| g.name == prefix) { + Ok(prefix) + } else { + anyhow::bail!( + indoc! {r#" + Invalid prefix: '{}' does not match your username or any of your groups. + Are you sure you are allowed to create databases or users with this prefix? + + Allowed prefixes: + - {} + {} + "#}, + prefix, + user.name, + user_groups + .iter() + .filter(|g| g.name != user.name) + .map(|g| format!(" - {}", g.name)) + .sorted() + .join("\n"), + ); + } + })?; + + if !split_name.next().is_some_and(|s| !s.is_empty()) { + anyhow::bail!( + indoc! {r#" + Missing the rest of the name after the user/group prefix. + + The name should be in the format: '{}_' + "#}, + prefix + ); + } + + Ok(prefix) +} + +pub fn quote_literal(s: &str) -> String { + format!("'{}'", s.replace('\'', r"\'")) +} + +pub fn quote_identifier(s: &str) -> String { + format!("`{}`", s.replace('`', r"\`")) +} \ No newline at end of file diff --git a/src/core/config.rs b/src/core/config.rs new file mode 100644 index 0000000..2122185 --- /dev/null +++ b/src/core/config.rs @@ -0,0 +1,122 @@ +use std::{fs, path::PathBuf}; + +use anyhow::Context; +use clap::Parser; +use serde::{Deserialize, Serialize}; +use sqlx::{mysql::MySqlConnectOptions, ConnectOptions, MySqlConnection}; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Config { + pub mysql: MysqlConfig, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename = "mysql")] +pub struct MysqlConfig { + pub host: String, + pub port: Option, + pub username: String, + pub password: String, +} + +#[derive(Parser)] +pub struct ConfigOverrideArgs { + #[arg( + long, + value_name = "PATH", + global = true, + help_heading = Some("Configuration overrides"), + hide_short_help = true, + alias = "config", + alias = "conf", + )] + config_file: Option, + + #[arg( + long, + value_name = "HOST", + global = true, + help_heading = Some("Configuration overrides"), + hide_short_help = true, + )] + mysql_host: Option, + + #[arg( + long, + value_name = "PORT", + global = true, + help_heading = Some("Configuration overrides"), + hide_short_help = true, + )] + mysql_port: Option, + + #[arg( + long, + value_name = "USER", + global = true, + help_heading = Some("Configuration overrides"), + hide_short_help = true, + )] + mysql_user: Option, + + #[arg( + long, + value_name = "PATH", + global = true, + help_heading = Some("Configuration overrides"), + hide_short_help = true, + )] + mysql_password_file: Option, +} + +pub fn get_config(args: ConfigOverrideArgs) -> anyhow::Result { + let config_path = args + .config_file + .unwrap_or("/etc/mysqladm/config.toml".to_string()); + let config_path = PathBuf::from(config_path); + + let config: Config = fs::read_to_string(&config_path) + .context(format!( + "Failed to read config file from {:?}", + &config_path + )) + .and_then(|c| toml::from_str(&c).context("Failed to parse config file")) + .context(format!( + "Failed to parse config file from {:?}", + &config_path + ))?; + + let mysql = &config.mysql; + + let password = if let Some(path) = args.mysql_password_file { + fs::read_to_string(path) + .context("Failed to read MySQL password file") + .map(|s| s.trim().to_owned())? + } else { + mysql.password.to_owned() + }; + + let mysql_config = MysqlConfig { + host: args.mysql_host.unwrap_or(mysql.host.to_owned()), + port: args.mysql_port.or(mysql.port), + username: args.mysql_user.unwrap_or(mysql.username.to_owned()), + password, + }; + + Ok(Config { + mysql: mysql_config, + }) +} + +/// TODO: Add timeout. +pub async fn mysql_connection_from_config(config: Config) -> anyhow::Result { + MySqlConnectOptions::new() + .host(&config.mysql.host) + .username(&config.mysql.username) + .password(&config.mysql.password) + .port(config.mysql.port.unwrap_or(3306)) + .database("mysql") + .connect() + .await + .context("Failed to connect to MySQL") +} diff --git a/src/core/database_operations.rs b/src/core/database_operations.rs new file mode 100644 index 0000000..0bc869f --- /dev/null +++ b/src/core/database_operations.rs @@ -0,0 +1,201 @@ +use anyhow::Context; +use indoc::indoc; +use itertools::Itertools; +use nix::unistd::User; +use serde::{Deserialize, Serialize}; +use sqlx::{prelude::*, MySqlConnection}; + +use super::common::{ + get_current_unix_user, get_unix_groups, quote_identifier, validate_prefix_for_user, +}; + +pub async fn create_database(name: &str, conn: &mut MySqlConnection) -> anyhow::Result<()> { + let user = get_current_unix_user()?; + validate_ownership_of_database_name(name, &user)?; + + // NOTE: see the note about SQL injections in `validate_owner_of_database_name` + sqlx::query(&format!("CREATE DATABASE {}", quote_identifier(name))) + .execute(conn) + .await + .map_err(|e| { + if e.to_string().contains("database exists") { + anyhow::anyhow!("Database '{}' already exists", name) + } else { + e.into() + } + })?; + + Ok(()) +} + +pub async fn drop_database(name: &str, conn: &mut MySqlConnection) -> anyhow::Result<()> { + let user = get_current_unix_user()?; + validate_ownership_of_database_name(name, &user)?; + + // NOTE: see the note about SQL injections in `validate_owner_of_database_name` + sqlx::query(&format!("DROP DATABASE {}", quote_identifier(name))) + .execute(conn) + .await + .map_err(|e| { + if e.to_string().contains("doesn't exist") { + anyhow::anyhow!("Database '{}' does not exist", name) + } else { + e.into() + } + })?; + + Ok(()) +} + +#[derive(Debug, Clone, FromRow, Serialize, Deserialize)] +struct DatabaseName { + database: String, +} + +pub async fn get_database_list(conn: &mut MySqlConnection) -> anyhow::Result> { + let unix_user = get_current_unix_user()?; + let unix_groups = get_unix_groups(&unix_user)? + .into_iter() + .map(|g| g.name) + .collect::>(); + + let databases = sqlx::query_as::<_, DatabaseName>( + r#" + SELECT `SCHEMA_NAME` AS `database` + FROM `information_schema`.`SCHEMATA` + WHERE `SCHEMA_NAME` NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys') + AND `SCHEMA_NAME` REGEXP ? + "#, + ) + .bind(format!( + "({}|{})_.+", + unix_user.name, + unix_groups.iter().map(|g| g.to_string()).join("|") + )) + .fetch_all(conn) + .await + .context(format!( + "Failed to get databases for user '{}'", + unix_user.name + ))?; + + Ok(databases.into_iter().map(|d| d.database).collect()) +} + +#[derive(Debug, Clone, FromRow, Serialize, Deserialize)] +pub struct DatabasePrivileges { + pub db: String, + pub user: String, + pub select_priv: String, + pub insert_priv: String, + pub update_priv: String, + pub delete_priv: String, + pub create_priv: String, + pub drop_priv: String, + pub alter_priv: String, + pub index_priv: String, + pub create_tmp_table_priv: String, + pub lock_tables_priv: String, + pub references_priv: String, +} + +pub const HUMAN_READABLE_DATABASE_PRIVILEGE_NAMES: [(&str, &str); 13] = [ + ("Database", "db"), + ("User", "user"), + ("Select", "select_priv"), + ("Insert", "insert_priv"), + ("Update", "update_priv"), + ("Delete", "delete_priv"), + ("Create", "create_priv"), + ("Drop", "drop_priv"), + ("Alter", "alter_priv"), + ("Index", "index_priv"), + ("Temp", "create_tmp_table_priv"), + ("Lock", "lock_tables_priv"), + ("References", "references_priv"), +]; + +pub async fn get_database_privileges( + database_name: &str, + conn: &mut MySqlConnection, +) -> anyhow::Result> { + let unix_user = get_current_unix_user()?; + validate_ownership_of_database_name(database_name, &unix_user)?; + + let result = sqlx::query_as::<_, DatabasePrivileges>(&format!( + "SELECT {} FROM `db` WHERE `db` = ?", + HUMAN_READABLE_DATABASE_PRIVILEGE_NAMES + .iter() + .map(|(_, prop)| quote_identifier(prop)) + .join(","), + )) + .bind(database_name) + .fetch_all(conn) + .await + .context("Failed to show database")?; + + Ok(result) +} + +pub async fn get_all_database_privileges( + conn: &mut MySqlConnection, +) -> anyhow::Result> { + let unix_user = get_current_unix_user()?; + let unix_groups = get_unix_groups(&unix_user)? + .into_iter() + .map(|g| g.name) + .collect::>(); + + let result = sqlx::query_as::<_, DatabasePrivileges>(&format!( + indoc! {r#" + SELECT {} FROM `db` WHERE `db` IN + (SELECT DISTINCT `SCHEMA_NAME` AS `database` + FROM `information_schema`.`SCHEMATA` + WHERE `SCHEMA_NAME` NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys') + AND `SCHEMA_NAME` REGEXP ?) + "#}, + HUMAN_READABLE_DATABASE_PRIVILEGE_NAMES + .iter() + .map(|(_, prop)| format!("`{}`", prop)) + .join(","), + )) + .bind(format!( + "({}|{})_.+", + unix_user.name, + unix_groups.iter().map(|g| g.to_string()).join("|") + )) + .fetch_all(conn) + .await + .context("Failed to show databases")?; + Ok(result) +} + +/// NOTE: It is very critical that this function validates the database name +/// properly. MySQL does not seem to allow for prepared statements, binding +/// the database name as a parameter to the query. This means that we have +/// to validate the database name ourselves to prevent SQL injection. +pub fn validate_ownership_of_database_name(name: &str, user: &User) -> anyhow::Result<()> { + if name.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_' && c != '-') { + anyhow::bail!( + indoc! {r#" + Database name '{}' contains invalid characters. + Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. + "#}, + name + ); + } + + if name.len() > 64 { + anyhow::bail!( + indoc! {r#" + Database name '{}' is too long. + Maximum length is 64 characters. + "#}, + name + ); + } + + validate_prefix_for_user(name, user).context("Invalid database name")?; + + Ok(()) +} diff --git a/src/core/user_operations.rs b/src/core/user_operations.rs new file mode 100644 index 0000000..468ed85 --- /dev/null +++ b/src/core/user_operations.rs @@ -0,0 +1,151 @@ +use anyhow::Context; +use indoc::indoc; +use nix::unistd::User; +use serde::{Deserialize, Serialize}; +use sqlx::{prelude::*, MySqlConnection}; + +use crate::core::common::quote_literal; + +use super::common::{get_current_unix_user, get_unix_groups, validate_prefix_for_user}; + +pub async fn create_database_user(db_user: &str, conn: &mut MySqlConnection) -> anyhow::Result<()> { + let unix_user = get_current_unix_user()?; + + validate_ownership_of_user_name(db_user, &unix_user)?; + + // NOTE: see the note about SQL injections in `validate_ownershipt_of_user_name` + sqlx::query(format!("CREATE USER {}@'%'", quote_literal(db_user),).as_str()) + .execute(conn) + .await?; + + Ok(()) +} + +pub async fn delete_database_user(db_user: &str, conn: &mut MySqlConnection) -> anyhow::Result<()> { + let unix_user = get_current_unix_user()?; + + validate_ownership_of_user_name(db_user, &unix_user)?; + + // NOTE: see the note about SQL injections in `validate_ownershipt_of_user_name` + sqlx::query(format!("DROP USER {}@'%'", quote_literal(db_user),).as_str()) + .execute(conn) + .await?; + + Ok(()) +} + +pub async fn set_password_for_database_user( + db_user: &str, + password: &str, + conn: &mut MySqlConnection, +) -> anyhow::Result<()> { + let unix_user = crate::core::common::get_current_unix_user()?; + validate_ownership_of_user_name(db_user, &unix_user)?; + + // NOTE: see the note about SQL injections in `validate_ownershipt_of_user_name` + sqlx::query( + format!( + "ALTER USER {}@'%' IDENTIFIED BY {}", + quote_literal(db_user), + quote_literal(password).as_str() + ) + .as_str(), + ) + .execute(conn) + .await?; + + Ok(()) +} + +#[derive(Debug, Clone, FromRow, Serialize, Deserialize)] +pub struct DatabaseUser { + #[sqlx(rename = "User")] + pub user: String, + + #[sqlx(rename = "Host")] + pub host: String, + + #[sqlx(rename = "Password")] + pub password: String, + + pub authentication_string: String, +} + +pub async fn get_all_database_users_for_user( + user: &User, + conn: &mut MySqlConnection, +) -> anyhow::Result> { + let groups = get_unix_groups(user)?; + + let regex = format!( + "({}|{})(_.+)?", + user.name, + groups + .iter() + .map(|g| g.name.as_str()) + .collect::>() + .join("|") + ); + + let users = sqlx::query_as::<_, DatabaseUser>( + r#" + SELECT `User`, `Host`, `Password`, `authentication_string` + FROM `mysql`.`user` + WHERE `User` REGEXP ? + "#, + ) + .bind(regex) + .fetch_all(conn) + .await?; + + Ok(users) +} + +pub async fn get_database_user_for_user( + username: &str, + conn: &mut MySqlConnection, +) -> anyhow::Result> { + let user = sqlx::query_as::<_, DatabaseUser>( + r#" + SELECT `User`, `Host`, `Password`, `authentication_string` + FROM `mysql`.`user` + WHERE `User` = ? + "#, + ) + .bind(username) + .fetch_optional(conn) + .await?; + + Ok(user) +} + +/// NOTE: It is very critical that this function validates the database name +/// properly. MySQL does not seem to allow for prepared statements, binding +/// the database name as a parameter to the query. This means that we have +/// to validate the database name ourselves to prevent SQL injection. +pub fn validate_ownership_of_user_name(name: &str, user: &User) -> anyhow::Result<()> { + if name.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_' && c != '-') { + anyhow::bail!( + indoc! {r#" + Username '{}' contains invalid characters. + Only A-Z, a-z, 0-9, _ (underscore) and - (dash) permitted. + "#}, + name + ); + } + + // TODO: does the name have a length limit? + // if name.len() > 48 { + // anyhow::bail!( + // indoc! {r#" + // Username '{}' is too long. + // Maximum length is 48 characters. Skipping. + // "#}, + // name + // ); + // } + + validate_prefix_for_user(name, user).context(format!("Invalid username: '{}'", name))?; + + Ok(()) +} diff --git a/src/database_command.rs b/src/database_command.rs deleted file mode 100644 index f114abc..0000000 --- a/src/database_command.rs +++ /dev/null @@ -1,33 +0,0 @@ -use clap::Parser; - -#[derive(Parser)] -pub struct DatabaseArgs { - #[clap(subcommand)] - subcmd: DatabaseCommand, -} - -#[derive(Parser)] -enum DatabaseCommand { - /// Create the DATABASE(S). - Create, - - /// Delete the DATABASE(S). - Drop, - - /// Give information about the DATABASE(S), or, if none are given, all the ones you own. - Show, - - /// Change permissions for the DATABASE(S). - /// Your favorite editor will be started, allowing you to make changes to the permission table. - /// Run `mysql-dbadm --help-editperm` for more information. - EditPerm, -} - -pub fn handle_command(args: DatabaseArgs) { - match args.subcmd { - DatabaseCommand::Create => println!("Creating database"), - DatabaseCommand::Drop => println!("Dropping database"), - DatabaseCommand::Show => println!("Showing database"), - DatabaseCommand::EditPerm => println!("Editing permissions"), - } -} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 1e6bff3..1fe3a63 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,61 +1,48 @@ +#[macro_use] +extern crate prettytable; + use clap::Parser; -mod database_command; -mod user_command; +mod cli; +mod core; #[derive(Parser)] struct Args { - #[clap(subcommand)] + #[command(subcommand)] command: Command, + + #[command(flatten)] + config_overrides: core::config::ConfigOverrideArgs, } +/// Database administration tool designed for non-admin users to manage their own MySQL databases and users. +/// Use `--help` for advanced usage. +/// +/// This tool allows you to manage users and databases in MySQL that are prefixed with your username. #[derive(Parser)] +#[command(version, about, disable_help_subcommand = true)] enum Command { - /// Create, drop or edit permission for the DATABASE(s), - #[clap(name = "db")] - Database(database_command::DatabaseArgs), + /// Create, drop or show/edit permissions for DATABASE(s), + #[command(alias = "database")] + Db(cli::database_command::DatabaseArgs), - /// Create, delete or change password for your USER, - #[clap(name = "user")] - User(user_command::UserArgs), + // Database(cli::database_command::DatabaseArgs), + /// Create, drop, change password for, or show your USER(s), + #[command(name = "user")] + User(cli::user_command::UserArgs), } -fn main() { +#[tokio::main] +async fn main() -> anyhow::Result<()> { + env_logger::init(); let args: Args = Args::parse(); + let config = core::config::get_config(args.config_overrides)?; + let connection = core::config::mysql_connection_from_config(config).await?; + match args.command { - Command::Database(database_args) => database_command::handle_command(database_args), - Command::User(user_args) => user_command::handle_command(user_args), + Command::Db(database_args) => { + cli::database_command::handle_command(database_args, connection).await + } + Command::User(user_args) => cli::user_command::handle_command(user_args, connection).await, } -} - -// loginstud03% mysql-dbadm --help - -// Usage: mysql-dbadm COMMAND [DATABASE]... -// Create, drop og edit permission for the DATABASE(s), -// as determined by the COMMAND. Valid COMMANDs: - -// create create the DATABASE(s). -// drop delete the DATABASE(s). -// show give information about the DATABASE(s), or, if -// none are given, all the ones you own. -// editperm change permissions for the DATABASE(s). Your -// favorite editor will be started, allowing you -// to make changes to the permission table. -// Run 'mysql-dbadm --help-editperm' for more -// information. - -// Report bugs to orakel@ntnu.no - -// loginstud03% mysql-useradm --help - -// Usage: mysql-useradm COMMAND [USER]... -// Create, delete or change password for the USER(s), -// as determined by the COMMAND. Valid COMMANDs: - -// create create the USER(s). -// delete delete the USER(s). -// passwd change the MySQL password for the USER(s). -// show give information about the USERS(s), or, if -// none are given, all the users you have. - -// Report bugs to orakel@ntnu.no +} \ No newline at end of file diff --git a/src/user_command.rs b/src/user_command.rs deleted file mode 100644 index ed3cd2a..0000000 --- a/src/user_command.rs +++ /dev/null @@ -1,24 +0,0 @@ -use clap::Parser; - -#[derive(Parser)] -pub struct UserArgs { - #[clap(subcommand)] - subcmd: UserCommand, -} - -#[derive(Parser)] -enum UserCommand { - Create, - Delete, - Passwd, - Show, -} - -pub fn handle_command(args: UserArgs) { - match args.subcmd { - UserCommand::Create => println!("Creating user"), - UserCommand::Delete => println!("Deleting user"), - UserCommand::Passwd => println!("Changing password"), - UserCommand::Show => println!("Showing user"), - } -} \ No newline at end of file