diff --git a/Cargo.lock b/Cargo.lock index 2de78fb..528b17b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,9 +39,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "628a8f9bd1e24b4e0db2b4bc2d000b001e7dd032d54afa60a68836aeec5aa54a" dependencies = [ "anstyle", "anstyle-parse", @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "async-channel" @@ -109,7 +109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" dependencies = [ "concurrent-queue", - "event-listener 4.0.0", + "event-listener 4.0.3", "event-listener-strategy", "futures-core", "pin-project-lite", @@ -121,26 +121,26 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ - "async-lock 3.1.2", + "async-lock 3.3.0", "async-task", "concurrent-queue", "fastrand 2.0.1", - "futures-lite 2.0.1", + "futures-lite 2.2.0", "slab", ] [[package]] name = "async-global-executor" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4353121d5644cdf2beb5726ab752e79a8db1ebb52031770ec47db31d245526" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.1.1", "async-executor", - "async-io 2.2.1", - "async-lock 3.1.2", + "async-io 2.3.0", + "async-lock 3.3.0", "blocking", - "futures-lite 2.0.1", + "futures-lite 2.2.0", "once_cell", ] @@ -166,18 +166,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff" +checksum = "fb41eb19024a91746eba0773aa5e16036045bbf45733766661099e182ea6a744" dependencies = [ - "async-lock 3.1.2", + "async-lock 3.3.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.0.1", + "futures-lite 2.2.0", "parking", - "polling 3.3.1", - "rustix 0.38.25", + "polling 3.3.2", + "rustix 0.38.30", "slab", "tracing", "windows-sys 0.52.0", @@ -194,11 +194,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.1.2" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea8b3453dd7cc96711834b75400d671b73e3656975fa68d9f277163b7f7e316" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ - "event-listener 4.0.0", + "event-listener 4.0.3", "event-listener-strategy", "pin-project-lite", ] @@ -253,15 +253,15 @@ dependencies = [ [[package]] name = "async-task" -version = "4.5.0" +version = "4.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.76" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", @@ -348,9 +348,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -360,9 +360,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "block-buffer" @@ -380,11 +380,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ "async-channel 2.1.1", - "async-lock 3.1.2", + "async-lock 3.3.0", "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite 2.0.1", + "futures-lite 2.2.0", "piper", "tracing", ] @@ -446,9 +446,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.11" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", @@ -456,9 +456,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", @@ -514,9 +514,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] @@ -542,31 +542,27 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-common" @@ -647,9 +643,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "4.0.0" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" dependencies = [ "concurrent-queue", "parking", @@ -662,7 +658,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 4.0.0", + "event-listener 4.0.3", "pin-project-lite", ] @@ -704,9 +700,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -719,9 +715,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -729,15 +725,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -746,9 +742,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-lite" @@ -767,23 +763,22 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.0.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" dependencies = [ "fastrand 2.0.1", "futures-core", "futures-io", - "memchr", "parking", "pin-project-lite", ] [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", @@ -792,15 +787,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" @@ -810,9 +805,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -838,9 +833,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -867,9 +862,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -907,9 +902,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "home" @@ -933,9 +928,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -962,9 +957,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -977,7 +972,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2 0.5.5", "tokio", "tower-service", "tracing", @@ -1067,13 +1062,13 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi", - "rustix 0.38.25", - "windows-sys 0.48.0", + "rustix 0.38.30", + "windows-sys 0.52.0", ] [[package]] @@ -1087,15 +1082,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] @@ -1128,9 +1123,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "linked-hash-map" @@ -1146,9 +1141,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" @@ -1167,9 +1162,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -1194,9 +1189,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", @@ -1215,7 +1210,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cfg-if", "libc", ] @@ -1242,33 +1237,33 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl-src" -version = "300.1.6+3.1.4" +version = "300.2.1+3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085" +checksum = "3fe476c29791a5ca0d1273c697e96085bbabbbea2ef7afd5617e78a4b40332d3" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.96" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ "cc", "libc", @@ -1333,9 +1328,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" dependencies = [ "memchr", "thiserror", @@ -1344,9 +1339,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" dependencies = [ "pest", "pest_generator", @@ -1354,9 +1349,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" dependencies = [ "pest", "pest_meta", @@ -1367,9 +1362,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" dependencies = [ "once_cell", "pest", @@ -1431,9 +1426,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" [[package]] name = "polling" @@ -1453,14 +1448,14 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.1" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.25", + "rustix 0.38.30", "tracing", "windows-sys 0.52.0", ] @@ -1473,9 +1468,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", "syn", @@ -1483,9 +1478,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -1576,6 +1571,7 @@ dependencies = [ "config", "env_logger", "futures", + "include_dir", "log", "paho-mqtt", "proc-macros", @@ -1593,9 +1589,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1711,15 +1707,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", - "linux-raw-sys 0.4.11", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", ] [[package]] @@ -1730,9 +1726,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "sample-mqtt-connector" @@ -1778,18 +1774,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", @@ -1798,9 +1794,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.109" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -1900,9 +1896,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -1917,40 +1913,40 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall", - "rustix 0.38.25", - "windows-sys 0.48.0", + "rustix 0.38.30", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", @@ -2053,7 +2049,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.5", + "base64 0.21.7", "bytes", "h2", "http", @@ -2149,9 +2145,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typenum" @@ -2167,9 +2163,9 @@ checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -2227,9 +2223,9 @@ dependencies = [ [[package]] name = "value-bag" -version = "1.4.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe" +checksum = "7cdbaf5e132e593e9fc1de6a15bbec912395b11fb9719e061cf64f804524c503" [[package]] name = "vcpkg" @@ -2266,9 +2262,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2276,9 +2272,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", @@ -2291,9 +2287,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" dependencies = [ "cfg-if", "js-sys", @@ -2303,9 +2299,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2313,9 +2309,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", @@ -2326,15 +2322,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", @@ -2349,7 +2345,7 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.25", + "rustix 0.38.30", ] [[package]] diff --git a/common/src/config_utils.rs b/common/src/config_utils.rs index 3f7b47c..baadb7b 100644 --- a/common/src/config_utils.rs +++ b/common/src/config_utils.rs @@ -2,83 +2,466 @@ // Licensed under the MIT license. // SPDX-License-Identifier: MIT -use std::{env, path::Path}; +use std::{ + env, + path::{Path, PathBuf}, +}; -use config::{File, FileFormat, Source}; +use config::{Config, File, FileFormat, FileStoredFormat, Source}; use home::home_dir; -use include_dir::{include_dir, Dir}; +use include_dir::Dir; use serde::Deserialize; -pub const YAML_EXT: &str = "yaml"; +pub const FILE_SEPARATOR: &str = "."; -const CONFIG_DIR: &str = "config"; -const DEFAULT: &str = "default"; -const DOT_AGEMO_DIR: &str = ".agemo"; -const AGEMO_HOME: &str = "AGEMO_HOME"; +/// Attempts to convert an extension in str format into a FileFormat enum. +/// Throws an error if the extension is unknown. +/// +/// # Arguments +/// * `ext` - extension str to convert. +fn try_ext_into_file_format( + ext: &str, +) -> Result> { + match ext { + ext if FileFormat::Ini.file_extensions().contains(&ext) => Ok(FileFormat::Ini), + ext if FileFormat::Json.file_extensions().contains(&ext) => Ok(FileFormat::Json), + ext if FileFormat::Json5.file_extensions().contains(&ext) => Ok(FileFormat::Json5), + ext if FileFormat::Ron.file_extensions().contains(&ext) => Ok(FileFormat::Ron), + ext if FileFormat::Toml.file_extensions().contains(&ext) => Ok(FileFormat::Toml), + ext if FileFormat::Yaml.file_extensions().contains(&ext) => Ok(FileFormat::Yaml), + _ => Err(Box::new(std::io::Error::new( + std::io::ErrorKind::NotFound, + "No supported format found.", + ))), + } +} + +/// Service's home directory metadata. +pub struct SvcConfigHomeMetadata { + /// Name of the environment variable used to set the service's HOME dir. + pub home_env_var: String, + /// Default name for the service's HOME dir, used if `home_env_var` is not set. + pub home_dir: String, + /// Name of the config directory where configuration files should live. + pub config_dir: String, +} + +/// Metadata for a config file. +#[derive(Debug, PartialEq, Eq)] +pub struct ConfigFileMetadata { + /// Config file name with extension. + pub name: String, + /// File extension. + pub ext: FileFormat, +} + +impl ConfigFileMetadata { + /// Create a new instance of ConfigFileMetadata. + /// Will result in an error if provided file does not have a valid extension. + /// + /// # Arguments + /// * `file_name` - Name of the file including the extension. + pub fn new(file_name: &str) -> Result> { + let name = file_name.to_string(); + let mut split_name: Vec<&str> = file_name.split(FILE_SEPARATOR).collect(); + + if split_name.len() <= 1 { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + format!("Invalid file name format. Expected an extension in name '{file_name}'."), + ))); + } + + let parsed_ext = split_name.pop().unwrap(); + let ext = try_ext_into_file_format(parsed_ext)?; + + if split_name.join(FILE_SEPARATOR).is_empty() { + return Err(Box::new(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + format!( + "Invalid file name format. File cannot have an empty file_stem '{file_name}'." + ), + ))); + } -const DEFAULT_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/../config"); + Ok(ConfigFileMetadata { name, ext }) + } +} -/// Read config from layered configuration files. -/// Searches for `{config_file_name}.default.{config_file_ext}` as the base configuration, -/// then searches for overrides named `{config_file_name}.{config_file_ext}` in `$AGEMO_HOME`. -/// If `$AGEMO_HOME` is not set, it defaults to `$HOME/.agemo`. +/// Loads default config source for the given configuration file. +/// Extracts configuration parameters from the provided directory object which pulled in default +/// configuration files in at build time. /// /// # Arguments -/// * `config_file_name` - The config file name. This is used to construct the file names to search for. -/// * `config_file_ext` - The config file extension. This is used to construct the file names to search for. -/// * `args` - Optional commandline arguments. Any values set will override values gathered from config files. -pub fn read_from_files( - config_file_name: &str, - config_file_ext: &str, - args: Option, -) -> Result> -where - T: for<'a> Deserialize<'a>, - A: Source + Send + Sync + 'static + Clone, -{ - // Get default config. - let default_config_filename = format!("{config_file_name}.{DEFAULT}.{config_file_ext}"); - let default_config_file = DEFAULT_DIR.get_file(default_config_filename).unwrap(); - let default_config_contents_str = default_config_file.contents_utf8().unwrap(); - - // Get override_files - let overrides_file = format!("{config_file_name}.{config_file_ext}"); - - let config_path = match env::var(AGEMO_HOME) { - Ok(agemo_home) => { - // The path below resolves to $AGEMO_HOME/config/ - Path::new(&agemo_home).join(CONFIG_DIR) +/// * `config_file` - The default config file to load. +/// * `default_dir` - Object that represents directory to pull default config file from. Generated +/// by the `include_dir!` macro. +pub fn load_default_config_from_file( + config_file: &ConfigFileMetadata, + default_dir: &Dir, +) -> Result, Box> { + // Get appropriate default config. + let file = default_dir + .get_file(&config_file.name) + .ok_or(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("Unable to find default file '{}'.", &config_file.name), + ))?; + + let file_contents = file.contents_utf8().ok_or(std::io::Error::new( + std::io::ErrorKind::InvalidData, + format!( + "Unable to parse default file '{}' contents.", + &config_file.name + ), + ))?; + + Ok(File::from_str(file_contents, config_file.ext).clone_into_box()) +} + +/// Retrieve configuration home path from the service's HOME dir environment variable. +/// Attempts to construct a path from the provided service HOME env var. If the given env var is +/// not set, it defaults a path under the $HOME directory. +/// +/// # Arguments +/// * `svc_home_metadata` - Metadata related to the service's home and config directories. +pub fn get_config_home_path_from_env( + svc_home_metadata: &SvcConfigHomeMetadata, +) -> Result> { + let config_path = match env::var(&svc_home_metadata.home_env_var) { + Ok(svc_home) => { + // The path below resolves to $SVC_HOME/{config_dir_name}/ + Path::new(&svc_home).join(&svc_home_metadata.config_dir) } Err(_) => { - // The path below resolves to $HOME/.agemo/config/ + // The path below resolves to $HOME/{svc_home_dir_name}/{config_dir_name}/ home_dir() .ok_or_else(|| { std::io::Error::new( std::io::ErrorKind::Other, - "Could not retrieve home directory", + "Could not retrieve home directory.", ) })? - .join(DOT_AGEMO_DIR) - .join(CONFIG_DIR) + .join(&svc_home_metadata.home_dir) + .join(&svc_home_metadata.config_dir) } }; - // The path below resolves to {config_path}/{overrides_file} - let overrides_config_file_path = config_path.join(overrides_file); + Ok(config_path) +} - let mut config_sources = config::Config::builder() - .add_source(File::from_str( - default_config_contents_str, - FileFormat::Yaml, - )) - .add_source(File::from(overrides_config_file_path).required(false)); +/// Read config from a configuration file located at the given config path at runtime. +/// +/// # Arguments +/// * `config_file` - The config file to read. +/// * `config_path` - The path to the directory containing the config. +pub fn read_config_from_file( + config_file: &ConfigFileMetadata, + config_path: TPath, +) -> Result, Box> +where + TPath: AsRef, +{ + let config_file_path = config_path.as_ref().join(&config_file.name); - // Adds command line arguments if there are any. - if let Some(args) = args { - config_sources = config_sources.add_source(args); + Ok(File::from(config_file_path) + .required(false) + .clone_into_box()) +} + +/// Builds unified config from provided configuration sources. +/// +/// # Arguments +/// * `sources` - List of sources to build configuration from. Sources towards the end of the list +/// take higher precedence over sources at the beginning of the list. +pub fn build_config_from_sources( + sources: Vec>, +) -> Result> +where + TConfig: for<'a> Deserialize<'a>, +{ + Config::builder() + .add_source(sources) + .build()? + .try_deserialize() + .map_err(|e| e.into()) +} + +/// Load a unified configuration given a file and commandline arguments. +/// Config will be compiled in the following order, with values from sources near the end of the +/// list taking higher precedence: +/// +/// - default config file +/// - config file +/// - commandline args +/// +/// Since the configuration is layered, config can be partially defined. Any unspecified +/// configuration will use the default value from the default config source. +/// +/// # Arguments +/// * `config_file` - The config file to load configuration from. +/// * `default_config_file` - The default config file to load default config from. +/// * `default_dir` - Object that represents directory to pull default config file from. Generated +/// by the `include_dir!` macro. +/// * `svc_home_metadata` - Metadata related to the service's home and config directories. Used to +/// get path to provided config file. +/// * `cmdline_args` - Optional commandline config arguments. +pub fn load_config( + config_file: &ConfigFileMetadata, + default_config_file: &ConfigFileMetadata, + default_dir: &Dir, + svc_home_metadata: &SvcConfigHomeMetadata, + cmdline_args: Option, +) -> Result> +where + TConfig: for<'de> serde::Deserialize<'de>, + TArgs: Source + Send + Sync, +{ + let default_source = load_default_config_from_file(default_config_file, default_dir)?; + + // Find and read configuration file for any overrides. + let config_path = get_config_home_path_from_env(svc_home_metadata)?; + let file_source = read_config_from_file(config_file, config_path)?; + + // Create source list from lowest to highest priority. + let mut sources = vec![default_source, file_source]; + + // If commandline args are present, add them to the source list. + // Commandline args will override any config from file sources. + if let Some(args) = cmdline_args { + sources.push(args.clone_into_box()); } - let config_store = config_sources.build()?; + build_config_from_sources(sources) +} + +#[cfg(test)] +mod config_utils_tests { + use config::Value; + use include_dir::{DirEntry, File}; + + use super::*; + + #[test] + fn try_into_format_success() { + // Ini format + let ini_ext = "ini"; + let ini_format = try_ext_into_file_format(ini_ext).unwrap(); + assert_eq!(ini_format, FileFormat::Ini); + + // Json format + let json_ext = "json"; + let json_format = try_ext_into_file_format(json_ext).unwrap(); + assert_eq!(json_format, FileFormat::Json); - config_store.try_deserialize().map_err(|e| e.into()) + // Json5 format + let json5_ext = "json5"; + let json5_format = try_ext_into_file_format(json5_ext).unwrap(); + assert_eq!(json5_format, FileFormat::Json5); + + // Ron format + let ron_ext = "ron"; + let ron_format = try_ext_into_file_format(ron_ext).unwrap(); + assert_eq!(ron_format, FileFormat::Ron); + + // Toml format + let toml_ext = "toml"; + let toml_format = try_ext_into_file_format(toml_ext).unwrap(); + assert_eq!(toml_format, FileFormat::Toml); + + // Yaml format + let yaml_ext = "yaml"; + let yaml_format = try_ext_into_file_format(yaml_ext).unwrap(); + assert_eq!(yaml_format, FileFormat::Yaml); + + let yaml_ext_2 = "yml"; + let yaml_format_2 = try_ext_into_file_format(yaml_ext_2).unwrap(); + assert_eq!(yaml_format_2, FileFormat::Yaml); + } + + #[test] + fn try_into_format_invalid_err() { + let ext_1 = "invalid"; + let result_1 = try_ext_into_file_format(ext_1); + assert!(result_1.is_err()); + + let ext_2 = ""; + let result_2 = try_ext_into_file_format(ext_2); + assert!(result_2.is_err()); + + let ext_3 = "123@"; + let result_3 = try_ext_into_file_format(ext_3); + assert!(result_3.is_err()); + } + + #[test] + fn new_config_metadata_from_file_name() { + let expected_name = "test.yaml"; + let expected_metadata = ConfigFileMetadata { + name: expected_name.to_string(), + ext: FileFormat::Yaml, + }; + + let metadata = ConfigFileMetadata::new(expected_name).unwrap(); + assert_eq!(metadata, expected_metadata); + + let expected_name_2 = "test.default.json"; + let expected_metadata_2 = ConfigFileMetadata { + name: expected_name_2.to_string(), + ext: FileFormat::Json, + }; + + let metadata_2 = ConfigFileMetadata::new(expected_name_2).unwrap(); + assert_eq!(metadata_2, expected_metadata_2); + } + + #[test] + fn new_config_metadata_from_invalid_str_err() { + let result = ConfigFileMetadata::new("no_extension"); + let err = result.err().unwrap().downcast::().unwrap(); + assert_eq!(err.kind(), std::io::ErrorKind::InvalidInput); + + let result_2 = ConfigFileMetadata::new(""); + let err_2 = result_2 + .err() + .unwrap() + .downcast::() + .unwrap(); + assert_eq!(err_2.kind(), std::io::ErrorKind::InvalidInput); + + let result_3 = ConfigFileMetadata::new(".yaml"); + let err_3 = result_3 + .err() + .unwrap() + .downcast::() + .unwrap(); + assert_eq!(err_3.kind(), std::io::ErrorKind::InvalidInput); + + let result_4 = ConfigFileMetadata::new("test.bad_extension"); + let err_4 = result_4 + .err() + .unwrap() + .downcast::() + .unwrap(); + assert_eq!(err_4.kind(), std::io::ErrorKind::NotFound); + } + + #[test] + fn load_default_config_from_file_success() { + let file_name = "config.yaml"; + let config_file = ConfigFileMetadata { + name: file_name.to_string(), + ext: FileFormat::Yaml, + }; + + // Expected property to be returned. + let expected_property_name = "test"; + let expected_property_value = Value::new(None, config::ValueKind::I64(1)); + let expected_properties_list_len = 1; + + // u8 representation of the expected property in yaml: "test: 1". + let contents: &[u8] = &[116, 101, 115, 116, 58, 32, 49]; + + // Create directory object. + let expected_file = File::new(file_name, contents); + let entry = DirEntry::File(expected_file); + let entries = &[entry]; + let dir = Dir::new("", entries); + + let source = load_default_config_from_file(&config_file, &dir).unwrap(); + let properties = source.collect().unwrap(); + + assert_eq!(properties.len(), expected_properties_list_len); + assert!(properties.contains_key(expected_property_name)); + assert_eq!( + properties.get(expected_property_name).unwrap(), + &expected_property_value + ); + } + + #[test] + fn load_default_config_from_file_non_existent() { + let non_existent_file = ConfigFileMetadata { + name: "non_existent.yaml".to_string(), + ext: FileFormat::Yaml, + }; + + let dir = Dir::new("", &[]); + + let result = load_default_config_from_file(&non_existent_file, &dir); + let err = result.err().unwrap().downcast::().unwrap(); + assert_eq!(err.kind(), std::io::ErrorKind::NotFound); + } + + #[test] + fn load_default_config_from_file_malformed_contents() { + let file_name = "config.yaml"; + let config_file = ConfigFileMetadata { + name: file_name.to_string(), + ext: FileFormat::Yaml, + }; + + // Malformed bytes. + let contents: &[u8] = &[0, 159, 146, 150]; + + // Create directory object. + let expected_file = File::new(file_name, contents); + let entry = DirEntry::File(expected_file); + let entries = &[entry]; + let dir = Dir::new("", entries); + + let result = load_default_config_from_file(&config_file, &dir); + let err = result.err().unwrap().downcast::().unwrap(); + assert_eq!(err.kind(), std::io::ErrorKind::InvalidData); + } + + #[test] + fn get_config_home_path_from_env_success() { + let env_var_key = "TEST_ENV_VAR"; + let env_var_value = "test_dir"; + let svc_home_dir = ".svc"; + let config_dir = "config"; + + let expected_path = Path::new(env_var_value).join(config_dir); + + // Set the test environment variable. + env::set_var(env_var_key, env_var_value); + + let svc_home_metadata = SvcConfigHomeMetadata { + home_env_var: env_var_key.to_string(), + home_dir: svc_home_dir.to_string(), + config_dir: config_dir.to_string(), + }; + + let path = get_config_home_path_from_env(&svc_home_metadata); + + // Unset the environment variable. + env::remove_var(env_var_key); + + assert_eq!(path.unwrap(), expected_path); + } + + #[test] + fn get_config_home_path_from_env_no_svc_home() { + let env_var_key = "TEST_ENV_VAR"; + let home_dir = home_dir(); + let svc_home_dir = ".svc"; + let config_dir = "config"; + + // If the environment variable happens to be set, we want to unset it. + if env::var(env_var_key).is_ok() { + env::remove_var(env_var_key); + } + + let expected_path = home_dir.unwrap().join(svc_home_dir).join(config_dir); + + let svc_home_metadata = SvcConfigHomeMetadata { + home_env_var: env_var_key.to_string(), + home_dir: svc_home_dir.to_string(), + config_dir: config_dir.to_string(), + }; + + let path = get_config_home_path_from_env(&svc_home_metadata).unwrap(); + assert_eq!(path, expected_path); + } } diff --git a/pub-sub-service/Cargo.toml b/pub-sub-service/Cargo.toml index 88b8ddd..f2410fc 100644 --- a/pub-sub-service/Cargo.toml +++ b/pub-sub-service/Cargo.toml @@ -16,6 +16,7 @@ common = { path = "../common" } config = { workspace = true } env_logger = { workspace = true } futures = { workspace = true } +include_dir = { workspace = true } log = { workspace = true } paho-mqtt = { workspace = true } proc-macros = { path = "../proc-macros"} diff --git a/pub-sub-service/src/load_config.rs b/pub-sub-service/src/load_config.rs index b9ec89a..71e3b7d 100644 --- a/pub-sub-service/src/load_config.rs +++ b/pub-sub-service/src/load_config.rs @@ -7,13 +7,29 @@ use std::env; use clap::Parser; -use common::config_utils; +use common::config_utils::{self, ConfigFileMetadata, SvcConfigHomeMetadata}; +use include_dir::{include_dir, Dir}; use log::{debug, error}; use proc_macros::ConfigSource; use serde_derive::{Deserialize, Serialize}; -const CONFIG_FILE_NAME: &str = "pub_sub_service_settings"; -const CONSTANTS_FILE_NAME: &str = "constants"; +// Config file stems +const CONFIG_FILE_STEM: &str = "pub_sub_service_settings"; +const CONSTANTS_FILE_STEM: &str = "constants"; + +// Config file extensions +const YAML_EXT: &str = "yaml"; + +// Default config file marker +const DEFAULT: &str = "default"; + +// Config directory consts +const CONFIG_DIR: &str = "config"; +const DOT_AGEMO_DIR: &str = ".agemo"; +const AGEMO_HOME_ENV_VAR: &str = "AGEMO_HOME"; + +// Default directory struct +const DEFAULT_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/../config"); /// Object containing commandline config options for the Pub Sub service. /// Non-optional fields must be passed in via the commandline and will override any values from @@ -78,19 +94,38 @@ pub struct Settings { pub version: Option, } -/// Load a configuration file. +/// Load configuration given a file and commandline arguments. /// /// # Arguments -/// * `config_file_name` - Name of the config file to load settings from. +/// * `config_file_name` - Name of the config file to load override settings from. +/// * `default_file_name` - Name of default config file to load settings from. /// * `args` - Optional commandline config arguments. pub fn load_config( config_file_name: &str, + default_file_name: &str, args: Option, ) -> Result> where T: for<'de> serde::Deserialize<'de>, { - config_utils::read_from_files(config_file_name, config_utils::YAML_EXT, args) + let config_file = ConfigFileMetadata::new(config_file_name)?; + let default_config_file = ConfigFileMetadata::new(default_file_name)?; + + let default_dir = DEFAULT_DIR; + + let svc_home_metadata = SvcConfigHomeMetadata { + home_env_var: AGEMO_HOME_ENV_VAR.to_string(), + home_dir: DOT_AGEMO_DIR.to_string(), + config_dir: CONFIG_DIR.to_string(), + }; + + config_utils::load_config( + &config_file, + &default_config_file, + &default_dir, + &svc_home_metadata, + args, + ) } /// Load the settings. @@ -103,7 +138,10 @@ where pub fn load_settings( args: CmdConfigOptions, ) -> Result> { - let mut settings: Settings = load_config(CONFIG_FILE_NAME, Some(args)) + let file_name = format!("{CONFIG_FILE_STEM}.{YAML_EXT}"); + let default_file_name = format!("{CONFIG_FILE_STEM}.{DEFAULT}.{YAML_EXT}"); + + let mut settings: Settings = load_config(&file_name, &default_file_name, Some(args)) .map_err(|e| { format!( "Failed to load required configuration settings due to error: {e}. See --help for more details." @@ -145,5 +183,8 @@ pub fn load_constants() -> Result where T: for<'de> serde::Deserialize<'de>, { - load_config(CONSTANTS_FILE_NAME, None) + let file_name = format!("{CONSTANTS_FILE_STEM}.{YAML_EXT}"); + let default_file_name = format!("{CONSTANTS_FILE_STEM}.{DEFAULT}.{YAML_EXT}"); + + load_config(&file_name, &default_file_name, None) }