config-0.15.9/.cargo_vcs_info.json0000644000000001360000000000100123750ustar { "git": { "sha1": "2ee2c6356bdea1ae0e21c5a117bc7c9585bd0c49" }, "path_in_vcs": "" }config-0.15.9/Cargo.lock0000644000001746160000000000100103670ustar # This file is automatically @generated by Cargo. # 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 = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anstream" version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", "windows-sys 0.59.0", ] [[package]] name = "arraydeque" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" [[package]] name = "async-trait" version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "base64" version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" dependencies = [ "serde", ] [[package]] name = "block-buffer" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", "windows-targets 0.52.6", ] [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "config" version = "0.15.9" dependencies = [ "async-trait", "chrono", "convert_case", "float-cmp", "futures", "glob", "indexmap 2.2.2", "json5", "log", "notify", "pathdiff", "reqwest", "ron", "rust-ini", "serde", "serde_derive", "serde_json", "snapbox", "temp-env", "tokio", "toml", "warp", "winnow 0.7.0", "yaml-rust2", ] [[package]] name = "const-random" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e" dependencies = [ "const-random-macro", "proc-macro-hack", ] [[package]] name = "const-random-macro" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" dependencies = [ "getrandom", "once_cell", "proc-macro-hack", "tiny-keccak", ] [[package]] name = "convert_case" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" dependencies = [ "unicode-segmentation", ] [[package]] name = "core-foundation" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "core-foundation-sys" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" dependencies = [ "libc", ] [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", ] [[package]] name = "data-encoding" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "digest" version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", ] [[package]] name = "dlv-list" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aead04dc46b5f263c25721cf25c9e595951d15055f8063f92392fa0d7f64cf4" dependencies = [ "const-random", ] [[package]] name = "encoding_rs" version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ "libc", "windows-sys 0.48.0", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "filetime" version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", "redox_syscall 0.3.5", "windows-sys 0.48.0", ] [[package]] name = "float-cmp" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" dependencies = [ "num-traits", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] [[package]] name = "fsevent-sys" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" dependencies = [ "libc", ] [[package]] name = "futures" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" 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-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", ] [[package]] name = "getrandom" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", "http 0.2.9", "indexmap 1.9.3", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "h2" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", "http 1.1.0", "indexmap 2.2.2", "slab", "tokio", "tokio-util", "tracing", ] [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ "foldhash", ] [[package]] name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ "hashbrown 0.15.2", ] [[package]] name = "headers" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ "base64 0.21.5", "bytes", "headers-core", "http 0.2.9", "httpdate", "mime", "sha1", ] [[package]] name = "headers-core" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ "http 0.2.9", ] [[package]] name = "hermit-abi" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "http" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", "itoa", ] [[package]] name = "http-body" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http 0.2.9", "pin-project-lite", ] [[package]] name = "http-body" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http 1.1.0", ] [[package]] name = "http-body-util" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", "http 1.1.0", "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "h2 0.3.21", "http 0.2.9", "http-body 0.4.5", "httparse", "httpdate", "itoa", "pin-project-lite", "socket2 0.4.10", "tokio", "tower-service", "tracing", "want", ] [[package]] name = "hyper" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", "futures-util", "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", "smallvec", "tokio", "want", ] [[package]] name = "hyper-rustls" version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", "hyper 1.5.0", "hyper-util", "rustls", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", ] [[package]] name = "hyper-tls" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", "hyper 1.5.0", "hyper-util", "native-tls", "tokio", "tokio-native-tls", "tower-service", ] [[package]] name = "hyper-util" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", "http-body 1.0.1", "hyper 1.5.0", "pin-project-lite", "socket2 0.5.5", "tokio", "tower-service", "tracing", ] [[package]] name = "iana-time-zone" version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "idna" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", ] [[package]] name = "indexmap" version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" dependencies = [ "equivalent", "hashbrown 0.14.2", "serde", ] [[package]] name = "inotify" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" dependencies = [ "bitflags 1.3.2", "inotify-sys", "libc", ] [[package]] name = "inotify-sys" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" dependencies = [ "libc", ] [[package]] name = "instant" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", ] [[package]] name = "ipnet" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] [[package]] name = "json5" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" dependencies = [ "pest", "pest_derive", "serde", ] [[package]] name = "kqueue" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" dependencies = [ "kqueue-sys", "libc", ] [[package]] name = "kqueue-sys" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" dependencies = [ "bitflags 1.3.2", "libc", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" [[package]] name = "linux-raw-sys" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[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.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" dependencies = [ "serde", ] [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" dependencies = [ "mime", "unicase", ] [[package]] name = "miniz_oxide" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", "windows-sys 0.48.0", ] [[package]] name = "mio" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "log", "wasi", "windows-sys 0.52.0", ] [[package]] name = "multer" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" dependencies = [ "bytes", "encoding_rs", "futures-util", "http 0.2.9", "httparse", "log", "memchr", "mime", "spin", "version_check", ] [[package]] name = "native-tls" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", "log", "openssl", "openssl-probe", "openssl-sys", "schannel", "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "notify" version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" dependencies = [ "bitflags 2.4.1", "filetime", "fsevent-sys", "inotify", "kqueue", "libc", "log", "mio 1.0.3", "notify-types", "walkdir", "windows-sys 0.52.0", ] [[package]] name = "notify-types" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" dependencies = [ "instant", ] [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", "once_cell", "openssl-macros", "openssl-sys", ] [[package]] name = "openssl-macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", "pkg-config", "vcpkg", ] [[package]] name = "ordered-multimap" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4d6a8c22fc714f0c2373e6091bf6f5e9b37b1bc0b1184874b7e0a4e303d318f" dependencies = [ "dlv-list", "hashbrown 0.14.2", ] [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ "cfg-if", "libc", "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] [[package]] name = "pathdiff" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "percent-encoding" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" dependencies = [ "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" dependencies = [ "pest", "pest_generator", ] [[package]] name = "pest_generator" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", "syn", ] [[package]] name = "pest_meta" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" dependencies = [ "once_cell", "pest", "sha2", ] [[package]] name = "pin-project" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-hack" version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "reqwest" version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", "h2 0.4.6", "http 1.1.0", "http-body 1.0.1", "http-body-util", "hyper 1.5.0", "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", "native-tls", "once_cell", "percent-encoding", "pin-project-lite", "rustls-pemfile 2.2.0", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", "windows-registry", ] [[package]] name = "ring" version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", "getrandom", "libc", "spin", "untrusted", "windows-sys 0.52.0", ] [[package]] name = "ron" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64 0.21.5", "bitflags 2.4.1", "indexmap 2.2.2", "serde", "serde_derive", ] [[package]] name = "rust-ini" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" dependencies = [ "cfg-if", "ordered-multimap", "trim-in-place", ] [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" version = "0.38.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", "windows-sys 0.48.0", ] [[package]] name = "rustls" version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ "once_cell", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-pemfile" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ "base64 0.21.5", ] [[package]] name = "rustls-pemfile" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ "rustls-pki-types", ] [[package]] name = "rustls-pki-types" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-webpki" version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "schannel" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ "windows-sys 0.48.0", ] [[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "serde" version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "indexmap 2.2.2", "itoa", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] [[package]] name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", "itoa", "ryu", "serde", ] [[package]] name = "sha1" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "sha2" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", "digest", ] [[package]] name = "similar" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snapbox" version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96dcfc4581e3355d70ac2ee14cfdf81dce3d85c85f1ed9e2c1d3013f53b3436b" dependencies = [ "anstream", "anstyle", "normalize-line-endings", "similar", "snapbox-macros", ] [[package]] name = "snapbox-macros" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16569f53ca23a41bb6f62e0a5084aa1661f4814a67fa33696a79073e03a664af" dependencies = [ "anstream", ] [[package]] name = "socket2" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", ] [[package]] name = "socket2" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", ] [[package]] name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" dependencies = [ "futures-core", ] [[package]] name = "system-configuration" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.4.1", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", ] [[package]] name = "temp-env" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96374855068f47402c3121c6eed88d29cb1de8f3ab27090e273e420bdabcf050" dependencies = [ "parking_lot", ] [[package]] name = "tempfile" version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", "rustix", "windows-sys 0.48.0", ] [[package]] name = "thiserror" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tiny-keccak" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" dependencies = [ "crunchy", ] [[package]] name = "tinyvec" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", "libc", "mio 0.8.9", "num_cpus", "pin-project-lite", "socket2 0.5.5", "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", ] [[package]] name = "tokio-native-tls" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", ] [[package]] name = "tokio-rustls" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ "rustls", "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", "tokio", ] [[package]] name = "tokio-tungstenite" version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", "tokio", "tungstenite", ] [[package]] name = "tokio-util" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", "tracing", ] [[package]] name = "toml" version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af06656561d28735e9c1cd63dfd57132c8155426aa6af24f36a00a351f88c48e" dependencies = [ "indexmap 2.2.2", "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18769cd1cec395d70860ceb4d932812a0b4d06b1a4bb336745a4d21b9496e992" dependencies = [ "indexmap 2.2.2", "serde", "serde_spanned", "toml_datetime", "winnow 0.6.26", ] [[package]] name = "tower-service" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "log", "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "trim-in-place" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" [[package]] name = "try-lock" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ "byteorder", "bytes", "data-encoding", "http 0.2.9", "httparse", "log", "rand", "sha1", "thiserror", "url", "utf-8", ] [[package]] name = "typenum" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicase" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] [[package]] name = "unicode-bidi" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "want" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ "try-lock", ] [[package]] name = "warp" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" dependencies = [ "bytes", "futures-channel", "futures-util", "headers", "http 0.2.9", "hyper 0.14.27", "log", "mime", "mime_guess", "multer", "percent-encoding", "pin-project", "rustls-pemfile 1.0.3", "scoped-tls", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-stream", "tokio-tungstenite", "tokio-util", "tower-service", "tracing", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.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-core" version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-registry" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ "windows-result", "windows-strings", "windows-targets 0.52.6", ] [[package]] name = "windows-result" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result", "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" dependencies = [ "memchr", ] [[package]] name = "winnow" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e49d2d35d3fad69b39b94139037ecfb4f359f08958b9c11e7315ce770462419" dependencies = [ "memchr", ] [[package]] name = "yaml-rust2" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "232bdb534d65520716bef0bbb205ff8f2db72d807b19c0bc3020853b92a0cd4b" dependencies = [ "arraydeque", "encoding_rs", "hashlink", ] [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" config-0.15.9/Cargo.toml0000644000000145350000000000100104030ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.75.0" name = "config" version = "0.15.9" build = false include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Layered configuration system for Rust applications." readme = "README.md" keywords = [ "config", "configuration", "settings", "env", "environment", ] categories = ["config"] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-cli/config-rs" resolver = "2" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", "--generate-link-to-definition", ] [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" min = 1 replace = "{{version}}" search = "Unreleased" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = "...{{tag_name}}" search = '\.\.\.HEAD' [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" min = 1 replace = "{{date}}" search = "ReleaseDate" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ ## [Unreleased] - ReleaseDate """ search = "" [[package.metadata.release.pre-release-replacements]] exactly = 1 file = "CHANGELOG.md" replace = """ [Unreleased]: https://github.com/rust-cli/config-rs/compare/{{tag_name}}...HEAD""" search = "" [features] async = ["async-trait"] convert-case = ["convert_case"] default = [ "toml", "json", "yaml", "ini", "ron", "json5", "convert-case", "async", ] ini = ["rust-ini"] json = ["serde_json"] json5 = [ "json5_rs", "serde/derive", ] preserve_order = [ "indexmap", "toml?/preserve_order", "serde_json?/preserve_order", "ron?/indexmap", ] toml = ["dep:toml"] yaml = ["yaml-rust2"] [lib] name = "config" path = "src/lib.rs" [[example]] name = "async_source" path = "examples/async_source/main.rs" required-features = [ "json", "async", ] [[example]] name = "custom_file_format" path = "examples/custom_file_format/main.rs" [[example]] name = "custom_str_format" path = "examples/custom_str_format/main.rs" [[example]] name = "env-list" path = "examples/env-list/main.rs" [[example]] name = "glob" path = "examples/glob/main.rs" [[example]] name = "hierarchical-env" path = "examples/hierarchical-env/main.rs" [[example]] name = "simple" path = "examples/simple/main.rs" [[example]] name = "static_env" path = "examples/static_env.rs" [[example]] name = "watch" path = "examples/watch/main.rs" [dependencies.async-trait] version = "0.1" optional = true [dependencies.convert_case] version = "0.6" optional = true [dependencies.indexmap] version = "2.2" features = ["serde"] optional = true [dependencies.json5_rs] version = "0.4" optional = true package = "json5" [dependencies.pathdiff] version = "0.2" [dependencies.ron] version = "0.8" optional = true [dependencies.rust-ini] version = "0.21" optional = true [dependencies.serde] version = "1.0" [dependencies.serde_json] version = "1.0" optional = true [dependencies.toml] version = "0.8" features = ["parse"] optional = true default-features = false [dependencies.winnow] version = "0.7.0" [dependencies.yaml-rust2] version = "0.10" optional = true [dev-dependencies.chrono] version = "0.4" features = ["serde"] [dev-dependencies.float-cmp] version = "0.10" [dev-dependencies.futures] version = "0.3" [dev-dependencies.glob] version = "0.3" [dev-dependencies.log] version = "0.4" features = ["serde"] [dev-dependencies.notify] version = "7.0" [dev-dependencies.reqwest] version = "0.12" [dev-dependencies.serde_derive] version = "1.0" [dev-dependencies.snapbox] version = "0.6.21" [dev-dependencies.temp-env] version = "0.3" [dev-dependencies.tokio] version = "1" features = [ "rt-multi-thread", "macros", "fs", "io-util", "time", ] [dev-dependencies.warp] version = "0.3" [lints.clippy] bool_assert_comparison = "allow" branches_sharing_code = "allow" checked_conversions = "warn" collapsible_else_if = "allow" create_dir = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" doc_markdown = "warn" empty_enum = "warn" enum_glob_use = "warn" expl_impl_clone_on_copy = "warn" explicit_deref_methods = "warn" explicit_into_iter_loop = "warn" fallible_impl_from = "warn" filter_map_next = "warn" flat_map_option = "warn" float_cmp_const = "warn" fn_params_excessive_bools = "warn" from_iter_instead_of_collect = "warn" if_same_then_else = "allow" implicit_clone = "warn" imprecise_flops = "warn" inconsistent_struct_constructor = "warn" inefficient_to_string = "warn" infinite_loop = "warn" invalid_upcast_comparisons = "warn" large_digit_groups = "warn" large_stack_arrays = "warn" large_types_passed_by_value = "warn" let_and_return = "allow" linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" mem_forget = "warn" mutex_integer = "warn" needless_continue = "warn" needless_for_each = "warn" negative_feature_names = "warn" path_buf_push_overwrite = "warn" ptr_as_ptr = "warn" rc_mutex = "warn" redundant_feature_names = "warn" ref_option_ref = "warn" rest_pat_in_fully_bound_structs = "warn" same_functions_in_if_condition = "warn" self_named_module_files = "warn" semicolon_if_nothing_returned = "warn" str_to_string = "warn" string_add = "warn" string_add_assign = "warn" string_lit_as_bytes = "warn" string_to_string = "warn" todo = "warn" trait_duplication_in_bounds = "warn" uninlined_format_args = "warn" verbose_file_reads = "warn" wildcard_imports = "warn" zero_sized_map_values = "warn" [lints.rust] unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" [lints.rust.rust_2018_idioms] level = "warn" priority = -1 config-0.15.9/Cargo.toml.orig000064400000000000000000000112041046102023000140520ustar 00000000000000[workspace] resolver = "2" [workspace.package] repository = "https://github.com/rust-cli/config-rs" license = "MIT OR Apache-2.0" edition = "2018" rust-version = "1.75.0" # MSRV include = [ "build.rs", "src/**/*", "Cargo.toml", "Cargo.lock", "LICENSE*", "README.md", "benches/**/*", "examples/**/*" ] [workspace.lints.rust] rust_2018_idioms = { level = "warn", priority = -1 } unreachable_pub = "warn" unsafe_op_in_unsafe_fn = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" unused_qualifications = "warn" [workspace.lints.clippy] bool_assert_comparison = "allow" branches_sharing_code = "allow" checked_conversions = "warn" collapsible_else_if = "allow" create_dir = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" doc_markdown = "warn" empty_enum = "warn" enum_glob_use = "warn" expl_impl_clone_on_copy = "warn" explicit_deref_methods = "warn" explicit_into_iter_loop = "warn" fallible_impl_from = "warn" filter_map_next = "warn" flat_map_option = "warn" float_cmp_const = "warn" fn_params_excessive_bools = "warn" from_iter_instead_of_collect = "warn" if_same_then_else = "allow" implicit_clone = "warn" imprecise_flops = "warn" inconsistent_struct_constructor = "warn" inefficient_to_string = "warn" infinite_loop = "warn" invalid_upcast_comparisons = "warn" large_digit_groups = "warn" large_stack_arrays = "warn" large_types_passed_by_value = "warn" let_and_return = "allow" # sometimes good to name what you are returning linkedlist = "warn" lossy_float_literal = "warn" macro_use_imports = "warn" mem_forget = "warn" mutex_integer = "warn" needless_continue = "warn" needless_for_each = "warn" negative_feature_names = "warn" path_buf_push_overwrite = "warn" ptr_as_ptr = "warn" rc_mutex = "warn" redundant_feature_names = "warn" ref_option_ref = "warn" rest_pat_in_fully_bound_structs = "warn" same_functions_in_if_condition = "warn" self_named_module_files = "warn" semicolon_if_nothing_returned = "warn" str_to_string = "warn" string_add = "warn" string_add_assign = "warn" string_lit_as_bytes = "warn" string_to_string = "warn" todo = "warn" trait_duplication_in_bounds = "warn" uninlined_format_args = "warn" verbose_file_reads = "warn" wildcard_imports = "warn" zero_sized_map_values = "warn" [package] name = "config" version = "0.15.9" description = "Layered configuration system for Rust applications." categories = ["config"] keywords = ["config", "configuration", "settings", "env", "environment"] repository.workspace = true license.workspace = true edition.workspace = true rust-version.workspace = true include.workspace = true [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] [package.metadata.release] pre-release-replacements = [ {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/rust-cli/config-rs/compare/{{tag_name}}...HEAD", exactly=1}, ] [features] default = ["toml", "json", "yaml", "ini", "ron", "json5", "convert-case", "async"] json = ["serde_json"] yaml = ["yaml-rust2"] ini = ["rust-ini"] json5 = ["json5_rs", "serde/derive"] convert-case = ["convert_case"] preserve_order = ["indexmap", "toml?/preserve_order", "serde_json?/preserve_order", "ron?/indexmap"] async = ["async-trait"] toml = ["dep:toml"] [dependencies] serde = "1.0" async-trait = { version = "0.1", optional = true } toml = { version = "0.8", optional = true, default-features = false, features = ["parse"] } serde_json = { version = "1.0", optional = true } yaml-rust2 = { version = "0.10", optional = true } rust-ini = { version = "0.21", optional = true } ron = { version = "0.8", optional = true } json5_rs = { version = "0.4", optional = true, package = "json5" } indexmap = { version = "2.2", features = ["serde"], optional = true } convert_case = { version = "0.6", optional = true } pathdiff = "0.2" winnow = "0.7.0" [dev-dependencies] serde_derive = "1.0" float-cmp = "0.10" chrono = { version = "0.4", features = ["serde"] } tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util", "time"]} warp = "0.3" futures = "0.3" reqwest = "0.12" glob = "0.3" notify = "7.0" temp-env = "0.3" log = { version = "0.4", features = ["serde"] } snapbox = "0.6.21" [[example]] name = "async_source" required-features = ["json", "async"] [lints] workspace = true config-0.15.9/LICENSE-APACHE000064400000000000000000000261361046102023000131210ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. config-0.15.9/LICENSE-MIT000064400000000000000000000020461046102023000126230ustar 00000000000000Copyright (c) Individual contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. config-0.15.9/README.md000064400000000000000000000047461046102023000124570ustar 00000000000000# config-rs ![Rust](https://img.shields.io/badge/rust-stable-brightgreen.svg) [![Build Status](https://travis-ci.org/mehcode/config-rs.svg?branch=master)](https://travis-ci.org/mehcode/config-rs) [![Crates.io](https://img.shields.io/crates/d/config.svg)](https://crates.io/crates/config) [![Docs.rs](https://docs.rs/config/badge.svg)](https://docs.rs/config) > Layered configuration system for Rust applications (with strong support for [12-factor] applications). [12-factor]: https://12factor.net/config - Set defaults - Set explicit values (to programmatically override) - Read from [JSON], [TOML], [YAML], [INI], [RON], [JSON5] files - Read from environment - Loosely typed — Configuration values may be read in any supported type, as long as there exists a reasonable conversion - Access nested fields using a formatted path — Uses a subset of JSONPath; currently supports the child ( `redis.port` ) and subscript operators ( `databases[0].name` ) [JSON]: https://github.com/serde-rs/json [TOML]: https://github.com/toml-lang/toml [YAML]: https://github.com/Ethiraric/yaml-rust2 [INI]: https://github.com/zonyitoo/rust-ini [RON]: https://github.com/ron-rs/ron [JSON5]: https://github.com/callum-oakley/json5-rs Please note that this library can not be used to write changed configuration values back to the configuration file(s)! ## Usage ### Feature flags - `ini` - Adds support for reading INI files - `json` - Adds support for reading JSON files - `yaml` - Adds support for reading YAML files - `toml` - Adds support for reading TOML files - `ron` - Adds support for reading RON files - `json5` - Adds support for reading JSON5 files ### Support for custom formats Library provides out of the box support for most renowned data formats such as JSON or Yaml. Nonetheless, it contains an extensibility point - a `Format` trait that, once implemented, allows seamless integration with library's APIs using custom, less popular or proprietary data formats. See [custom_file_format](https://github.com/mehcode/config-rs/tree/master/examples/custom_file_format) example for more information. ### More See the [documentation](https://docs.rs/config) or [examples](https://github.com/mehcode/config-rs/tree/master/examples) for more usage information. ## License Licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. config-0.15.9/examples/async_source/main.rs000064400000000000000000000041151046102023000167730ustar 00000000000000use std::{error::Error, fmt::Debug}; use async_trait::async_trait; use config::{ builder::AsyncState, AsyncSource, ConfigBuilder, ConfigError, FileFormat, Format, Map, }; use futures::{select, FutureExt}; use warp::Filter; // Example below presents sample configuration server and client. // // Server serves simple configuration on HTTP endpoint. // Client consumes it using custom HTTP AsyncSource built on top of reqwest. #[tokio::main] async fn main() -> Result<(), Box> { select! { r = run_server().fuse() => r, r = run_client().fuse() => r } } async fn run_server() -> Result<(), Box> { let service = warp::path("configuration").map(|| r#"{ "value" : 123 }"#); println!("Running server on localhost:5001"); warp::serve(service).bind(([127, 0, 0, 1], 5001)).await; Ok(()) } async fn run_client() -> Result<(), Box> { // Good enough for an example to allow server to start tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; let config = ConfigBuilder::::default() .add_async_source(HttpSource { uri: "http://localhost:5001/configuration".into(), format: FileFormat::Json, }) .build() .await?; println!("Config value is {}", config.get::("value")?); Ok(()) } // Actual implementation of AsyncSource can be found below #[derive(Debug)] struct HttpSource { uri: String, format: F, } #[async_trait] impl AsyncSource for HttpSource { async fn collect(&self) -> Result, ConfigError> { reqwest::get(&self.uri) .await .map_err(|e| ConfigError::Foreign(Box::new(e)))? // error conversion is possible from custom AsyncSource impls .text() .await .map_err(|e| ConfigError::Foreign(Box::new(e))) .and_then(|text| { self.format .parse(Some(&self.uri), &text) .map_err(ConfigError::Foreign) }) } } config-0.15.9/examples/custom_file_format/files/private.pem000064400000000000000000000032171046102023000221460ustar 00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA7P+5Ow3YfQJJ0W4DhwdJRUWi1cYOen7qQ+XPAtFOdbJcvIZe T+D+fEENDpkDM+lOE1KtpehW4JOZ13ePLM0phktEf9hT1aB0Zc5LXB3M4YuW+lAW iXF9moHWxa2DlpyGck7cSlVVKbdljP9AQOzMdTXi3JJUWlnqjeUSINnBqCW21nOh frUZZbqBKOx7/iEgjdAsR7K5WAyVIXgbIipCAgWWjP3ejUjrl20QpXu06dGQF8O8 S7ztwLwtmdUJ5SBrhiub1Ocjr+DSUvIg8kOzUp9gQiMtV8RGTr4W0Bfr75ZwgAfC oDNZ+D9S/rlZX0eJJd5rMobiQRE8qBk2oxWcxwIDAQABAoIBAQCtP3MEvGZZW+bi de2WM7lYLkOOyi2jVkuiPshJYwBcAXrRRdiDxBHEezk0Rp6UwCQW9AWEloeLu9pm LDw5n/CO/06ftl/ydk0gbuGgARjYd9ZyPUF8T75lyCxcbS8YVmvh+8wFesO6rxpJ K/6od3Iu7KleXInVUo2oFKBf608pvp/80oSvCeNCK9vh64UUZnm2PfhzD47jYE+u QEM/Ceb4LZ/6jf3SqXi/PpZu/IfDqc7JaBkuGIh4Zv+EQuSh6MYvkkn/51PmqOvf KM+bCo8U6sGzvQkMJKDUXFPfTeKCaRFdYgYDm94CCGgaMtMUBt+lm2vG2tNuKH0b a/J+x1ZBAoGBAPelu0V2T0Wg8WKjI6nbIWRbuPImAp35WyPikchqmi3hpnP68VxG D9z0TNmfr5TAajKQ11SSReIEiwJPOwq1/5v0xmqYdhyWX2alAQq5xUAfJzMDN2Rg ftO4qMcNoVeH4wAMwXc1gdRHjqWNZrz381y3Z4K/VWOm+BbG7JrejeE/AoGBAPT+ DLc///zfBEA94m6/I/78jL/+SsLM7LflPByO7JNrsQTm6mo1DvluGYmE34TP8aTb dvt5KXb8gpsKS3Z9vD6FJTB0dNrSpTWEPKFTTp/VWTvwHuh8mF/r1KyngDW3IU3q 7mKkVHMrfnU23qYHODJDnS6WmL3X3tJJAUXDqlp5AoGAR/VlRBrLj/zjBvlGbJ2a x1GLnPkEe6iwHe5A1A59vGU7+6loJprJEzf9eKLY3w1GDmld2FokajdNuR8Sldsq acOnP+QLNeVP1UCO2/H86dPjjQQbPVR4pcabbDN+tTNr92C9eokWr3sXbO14c+JM WZ2FO02jXzBuGBg3Ogz/BvsCgYEA69lCfotTMam0mu+4c2r5CTkxeocgi6Xh4SsC km+ZGlabJJ/0XWhU0RUH6paK432YIF/SjEbY/x4Z0Y24lgp3VSyyX5JNCHeu6fUy tQ/Q6hfmfsgryR5hRj5vEAN0bsGsgyk+cqHGVtUxOUAoWWcr11+2CqqZwnD1pjT3 z6SM8+kCgYEA3GPFdb/ILXwPSEFfHE5RGWa2jlns+xVvQTaymR6ZAtLPv2RkBKvw Hwy8maCmWgw0+U/f8nMUDPVYYa/5Tyk5UzEVhtbAXGYzyY+Nk4IBFZZ+8P95RJBL 8jqfXxr2ZpYf9mEgZI8v8Pr013R3Vqkpy+B8jlfpvxFdOwSzkY42ur4= -----END RSA PRIVATE KEY----- config-0.15.9/examples/custom_file_format/files/public.pem000064400000000000000000000007031046102023000217470ustar 00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7P+5Ow3YfQJJ0W4DhwdJ RUWi1cYOen7qQ+XPAtFOdbJcvIZeT+D+fEENDpkDM+lOE1KtpehW4JOZ13ePLM0p hktEf9hT1aB0Zc5LXB3M4YuW+lAWiXF9moHWxa2DlpyGck7cSlVVKbdljP9AQOzM dTXi3JJUWlnqjeUSINnBqCW21nOhfrUZZbqBKOx7/iEgjdAsR7K5WAyVIXgbIipC AgWWjP3ejUjrl20QpXu06dGQF8O8S7ztwLwtmdUJ5SBrhiub1Ocjr+DSUvIg8kOz Up9gQiMtV8RGTr4W0Bfr75ZwgAfCoDNZ+D9S/rlZX0eJJd5rMobiQRE8qBk2oxWc xwIDAQAB -----END PUBLIC KEY----- config-0.15.9/examples/custom_file_format/main.rs000064400000000000000000000050671046102023000201660ustar 00000000000000use std::io::{Error, ErrorKind}; use config::{Config, File, FileStoredFormat, Format, Map, Value, ValueKind}; /// The private and public key sources will be read into their associated variable: #[derive(serde_derive::Deserialize, Clone, Debug)] pub struct Settings { pub private_key: Option, pub public_key: Option, } fn main() { // Sourcing from two separate files for the `Settings` struct,: let file_public_key = File::new("examples/custom_file_format/files/public.pem", PemFile); let file_private_key = File::new("examples/custom_file_format/files/private.pem", PemFile); // Provide the sources and build the config object: // Both are marked as optional to avoid failure if the file doesn't exist. let settings = Config::builder() .add_source(file_public_key.required(false)) .add_source(file_private_key.required(false)) .build() .unwrap(); // Deserialize the config object into your Settings struct: let settings: Settings = settings.try_deserialize().unwrap(); println!("{settings:#?}"); } #[derive(Debug, Clone)] pub struct PemFile; impl Format for PemFile { fn parse( &self, uri: Option<&String>, text: &str, ) -> Result, Box> { // Store any valid keys into this map, they'll be merged with other sources into the final config map: let mut result = Map::new(); // Identify the PEM encoded data type by the first occurrence found: // NOTE: This example is kept simple, multiple or other encoded types are not handled. let key_type = vec!["PUBLIC", "PRIVATE"] .into_iter() .find(|s| text.contains(s)); let key = match key_type { Some("PRIVATE") => "private_key", Some("PUBLIC") => "public_key", // Otherwise fail with an error message (the filename is implicitly appended): _ => { return Err(Box::new(Error::new( ErrorKind::InvalidData, "PEM file did not contain a Private or Public key", ))) } }; result.insert( key.to_owned(), Value::new(uri, ValueKind::String(text.into())), ); Ok(result) } } // A slice of extensions associated to this format, when an extension // is omitted from a file source, these will be tried implicitly: impl FileStoredFormat for PemFile { fn file_extensions(&self) -> &'static [&'static str] { &["pem"] } } config-0.15.9/examples/custom_str_format/main.rs000064400000000000000000000030171046102023000200500ustar 00000000000000use config::{Config, File, FileStoredFormat, Format, Map, Value, ValueKind}; fn main() { let config = Config::builder() .add_source(File::from_str("bad", MyFormat)) .add_source(File::from_str("good", MyFormat)) .build(); match config { Ok(cfg) => println!("A config: {cfg:#?}"), Err(e) => println!("An error: {e}"), } } #[derive(Debug, Clone)] pub struct MyFormat; impl Format for MyFormat { fn parse( &self, uri: Option<&String>, text: &str, ) -> Result, Box> { // Let's assume our format is somewhat malformed, but this is fine // In real life anything can be used here - nom, serde or other. // // For some more real-life examples refer to format implementation within the library code let mut result = Map::new(); if text == "good" { result.insert( "key".to_owned(), Value::new(uri, ValueKind::String(text.into())), ); } else { println!("Something went wrong in {uri:?}"); } Ok(result) } } // As strange as it seems for config sourced from a string, legacy demands its sacrifice // It is only required for File source, custom sources can use Format without caring for extensions static MY_FORMAT_EXT: Vec<&'static str> = vec![]; impl FileStoredFormat for MyFormat { fn file_extensions(&self) -> &'static [&'static str] { &MY_FORMAT_EXT } } config-0.15.9/examples/env-list/main.rs000064400000000000000000000011741046102023000160410ustar 00000000000000use config::Config; #[derive(Debug, Default, serde_derive::Deserialize, PartialEq, Eq)] struct AppConfig { list: Vec, } fn main() { std::env::set_var("APP_LIST", "Hello World"); let config = Config::builder() .add_source( config::Environment::with_prefix("APP") .try_parsing(true) .separator("_") .list_separator(" "), ) .build() .unwrap(); let app: AppConfig = config.try_deserialize().unwrap(); assert_eq!(app.list, vec![String::from("Hello"), String::from("World")]); std::env::remove_var("APP_LIST"); } config-0.15.9/examples/glob/conf/00-default.toml000064400000000000000000000000161046102023000174060ustar 00000000000000debug = false config-0.15.9/examples/glob/conf/05-some.yml000064400000000000000000000000431046102023000165600ustar 00000000000000secret: THIS IS SECRET debug: true config-0.15.9/examples/glob/conf/99-extra.json000064400000000000000000000000741046102023000171310ustar 00000000000000{ "that": 3, "this": 1230, "key": "sdgnjklsdjklgds" } config-0.15.9/examples/glob/main.rs000064400000000000000000000036531046102023000152270ustar 00000000000000use std::collections::HashMap; use std::path::Path; use config::{Config, File}; use glob::glob; fn main() { // Option 1 // -------- // Gather all conf files from conf/ manually let settings = Config::builder() // File::with_name(..) is shorthand for File::from(Path::new(..)) .add_source(File::with_name("examples/glob/conf/00-default.toml")) .add_source(File::from(Path::new("examples/glob/conf/05-some.yml"))) .add_source(File::from(Path::new("examples/glob/conf/99-extra.json"))) .build() .unwrap(); // Print out our settings (as a HashMap) println!( "\n{:?} \n\n-----------", settings .try_deserialize::>() .unwrap() ); // Option 2 // -------- // Gather all conf files from conf/ manually, but put in 1 merge call. let settings = Config::builder() .add_source(vec![ File::with_name("examples/glob/conf/00-default.toml"), File::from(Path::new("examples/glob/conf/05-some.yml")), File::from(Path::new("examples/glob/conf/99-extra.json")), ]) .build() .unwrap(); // Print out our settings (as a HashMap) println!( "\n{:?} \n\n-----------", settings .try_deserialize::>() .unwrap() ); // Option 3 // -------- // Gather all conf files from conf/ using glob and put in 1 merge call. let settings = Config::builder() .add_source( glob("examples/glob/conf/*") .unwrap() .map(|path| File::from(path.unwrap())) .collect::>(), ) .build() .unwrap(); // Print out our settings (as a HashMap) println!( "\n{:?} \n\n-----------", settings .try_deserialize::>() .unwrap() ); } config-0.15.9/examples/hierarchical-env/config/default.toml000064400000000000000000000006201046102023000217730ustar 00000000000000[database] url = "postgres://postgres@localhost" [sparkpost] key = "sparkpost-dev-key" token = "sparkpost-dev-token" url = "https://api.sparkpost.com" version = 1 [twitter] consumer_token = "twitter-dev-consumer-key" consumer_secret = "twitter-dev-consumer-secret" [braintree] merchant_id = "braintree-merchant-id" public_key = "braintree-dev-public-key" private_key = "braintree-dev-private-key" config-0.15.9/examples/hierarchical-env/config/development.toml000064400000000000000000000000451046102023000226720ustar 00000000000000debug = true [database] echo = true config-0.15.9/examples/hierarchical-env/config/production.toml000064400000000000000000000004371046102023000225430ustar 00000000000000debug = false [sparkpost] key = "sparkpost-prod-key" token = "sparkpost-prod-token" [twitter] consumer_token = "twitter-prod-consumer-key" consumer_secret = "twitter-prod-consumer-secret" [braintree] public_key = "braintree-prod-public-key" private_key = "braintree-prod-private-key" config-0.15.9/examples/hierarchical-env/main.rs000064400000000000000000000002271046102023000175020ustar 00000000000000mod settings; use settings::Settings; fn main() { let settings = Settings::new(); // Print out our settings println!("{settings:?}"); } config-0.15.9/examples/hierarchical-env/settings.rs000064400000000000000000000043711046102023000204220ustar 00000000000000use std::env; use config::{Config, ConfigError, Environment, File}; use serde_derive::Deserialize; #[derive(Debug, Deserialize)] #[allow(unused)] struct Database { url: String, } #[derive(Debug, Deserialize)] #[allow(unused)] struct Sparkpost { key: String, token: String, url: String, version: u8, } #[derive(Debug, Deserialize)] #[allow(unused)] struct Twitter { consumer_token: String, consumer_secret: String, } #[derive(Debug, Deserialize)] #[allow(unused)] struct Braintree { merchant_id: String, public_key: String, private_key: String, } #[derive(Debug, Deserialize)] #[allow(unused)] pub(crate) struct Settings { debug: bool, database: Database, sparkpost: Sparkpost, twitter: Twitter, braintree: Braintree, } impl Settings { pub(crate) fn new() -> Result { let run_mode = env::var("RUN_MODE").unwrap_or_else(|_| "development".into()); let s = Config::builder() // Start off by merging in the "default" configuration file .add_source(File::with_name("examples/hierarchical-env/config/default")) // Add in the current environment file // Default to 'development' env // Note that this file is _optional_ .add_source( File::with_name(&format!("examples/hierarchical-env/config/{run_mode}")) .required(false), ) // Add in a local configuration file // This file shouldn't be checked in to git .add_source(File::with_name("examples/hierarchical-env/config/local").required(false)) // Add in settings from the environment (with a prefix of APP) // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key .add_source(Environment::with_prefix("app")) // You may also programmatically change settings .set_override("database.url", "postgres://")? .build()?; // Now that we're done, let's access our configuration println!("debug: {:?}", s.get_bool("debug")); println!("database: {:?}", s.get::("database.url")); // You can deserialize (and thus freeze) the entire configuration as s.try_deserialize() } } config-0.15.9/examples/simple/Settings.toml000064400000000000000000000000721046102023000167700ustar 00000000000000debug = false priority = 32 key = "189rjfadoisfj8923fjio" config-0.15.9/examples/simple/main.rs000064400000000000000000000011661046102023000155720ustar 00000000000000use std::collections::HashMap; use config::Config; fn main() { let settings = Config::builder() // Add in `./Settings.toml` .add_source(config::File::with_name("examples/simple/Settings")) // Add in settings from the environment (with a prefix of APP) // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key .add_source(config::Environment::with_prefix("APP")) .build() .unwrap(); // Print out our settings (as a HashMap) println!( "{:?}", settings .try_deserialize::>() .unwrap() ); } config-0.15.9/examples/static_env.rs000064400000000000000000000013131046102023000155060ustar 00000000000000use std::sync::OnceLock; use config::Config; fn config() -> &'static Config { static CONFIG: OnceLock = OnceLock::new(); CONFIG.get_or_init(|| { Config::builder() .add_source(config::Environment::with_prefix("APP_NAME").separator("_")) .build() .unwrap() }) } /// Get a configuration value from the static configuration object pub fn get<'a, T: serde::Deserialize<'a>>(key: &str) -> T { // You shouldn't probably do it like that and actually handle that error that might happen // here, but for the sake of simplicity, we do it like this here config().get::(key).unwrap() } fn main() { println!("{:?}", get::("foo")); } config-0.15.9/examples/watch/Settings.toml000064400000000000000000000000531046102023000166040ustar 00000000000000debug = false port = 3223 host = "0.0.0.0" config-0.15.9/examples/watch/main.rs000064400000000000000000000045021046102023000154040ustar 00000000000000use std::collections::HashMap; use std::path::Path; use std::sync::mpsc::channel; use std::sync::OnceLock; use std::sync::RwLock; use std::time::Duration; use config::{Config, File}; use notify::{Event, RecommendedWatcher, RecursiveMode, Watcher}; fn settings() -> &'static RwLock { static CONFIG: OnceLock> = OnceLock::new(); CONFIG.get_or_init(|| { let settings = load(); RwLock::new(settings) }) } fn refresh() { *settings().write().unwrap() = load(); } fn load() -> Config { Config::builder() .add_source(File::with_name("examples/watch/Settings.toml")) .build() .unwrap() } fn show() { println!( " * Settings :: \n\x1b[31m{:?}\x1b[0m", settings() .read() .unwrap() .clone() .try_deserialize::>() .unwrap() ); } fn watch() -> ! { // Create a channel to receive the events. let (tx, rx) = channel(); // Automatically select the best implementation for your platform. // You can also access each implementation directly e.g. INotifyWatcher. let mut watcher: RecommendedWatcher = Watcher::new( tx, notify::Config::default().with_poll_interval(Duration::from_secs(2)), ) .unwrap(); // Add a path to be watched. All files and directories at that path and // below will be monitored for changes. watcher .watch( Path::new("examples/watch/Settings.toml"), RecursiveMode::NonRecursive, ) .unwrap(); // This is a simple loop, but you may want to use more complex logic here, // for example to handle I/O. loop { match rx.recv() { Ok(Ok(Event { kind: notify::event::EventKind::Modify(_), .. })) => { println!(" * Settings.toml written; refreshing configuration ..."); refresh(); show(); } Err(e) => println!("watch error: {e:?}"), _ => { // Ignore event } } } } fn main() { // This is just an example of what could be done, today // We do want this to be built-in to config-rs at some point // Feel free to take a crack at a PR show(); watch(); } config-0.15.9/src/builder.rs000064400000000000000000000272111046102023000137530ustar 00000000000000use std::str::FromStr; use crate::error::Result; use crate::map::Map; #[cfg(feature = "async")] use crate::source::AsyncSource; use crate::{config::Config, path::Expression, source::Source, value::Value}; /// A configuration builder /// /// It registers ordered sources of configuration to later build consistent [`Config`] from them. /// Configuration sources it defines are defaults, [`Source`]s and overrides. /// /// Defaults are always loaded first and can be overwritten by any of two other sources. /// Overrides are always loaded last, thus cannot be overridden. /// Both can be only set explicitly key by key in code /// using [`set_default`](Self::set_default) or [`set_override`](Self::set_override). /// /// An intermediate category, [`Source`], set groups of keys at once implicitly using data coming from external sources /// like files, environment variables or others that one implements. Defining a [`Source`] is as simple as implementing /// a trait for a struct. /// /// Adding sources, setting defaults and overrides does not invoke any I/O nor builds a config. /// It happens on demand when [`build`](Self::build) (or its alternative) is called. /// Therefore all errors, related to any of the [`Source`] will only show up then. /// /// # Sync and async builder /// /// [`ConfigBuilder`] uses type parameter to keep track of builder state. /// /// In [`DefaultState`] builder only supports [`Source`]s /// /// In [`AsyncState`] it supports both [`Source`]s and [`AsyncSource`]s at the price of building using `async fn`. /// /// # Examples /// /// ```rust /// # use config::*; /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// # #[cfg(feature = "json")] /// # { /// let mut builder = Config::builder() /// .set_default("default", "1")? /// .add_source(File::new("config/settings", FileFormat::Json)) /// // .add_async_source(...) /// .set_override("override", "1")?; /// /// match builder.build() { /// Ok(config) => { /// // use your config /// }, /// Err(e) => { /// // something went wrong /// } /// } /// # } /// # Ok(()) /// # } /// ``` /// /// If any [`AsyncSource`] is used, the builder will transition to [`AsyncState`]. /// In such case, it is required to _await_ calls to [`build`](Self::build) and its non-consuming sibling. /// /// Calls can be not chained as well /// ```rust /// # use std::error::Error; /// # use config::*; /// # fn main() -> Result<(), Box> { /// # #[cfg(feature = "json")] /// # { /// let mut builder = Config::builder(); /// builder = builder.set_default("default", "1")?; /// builder = builder.add_source(File::new("config/settings", FileFormat::Json)); /// builder = builder.add_source(File::new("config/settings.prod", FileFormat::Json)); /// builder = builder.set_override("override", "1")?; /// # } /// # Ok(()) /// # } /// ``` /// /// Calling [`Config::builder`](Config::builder) yields builder in the default state. /// If having an asynchronous state as the initial state is desired, _turbofish_ notation needs to be used. /// ```rust /// # use config::{*, builder::AsyncState}; /// let mut builder = ConfigBuilder::::default(); /// ``` /// /// If for some reason acquiring builder in default state is required without calling [`Config::builder`](Config::builder) /// it can also be achieved. /// ```rust /// # use config::{*, builder::DefaultState}; /// let mut builder = ConfigBuilder::::default(); /// ``` #[derive(Debug, Clone, Default)] #[must_use] pub struct ConfigBuilder { defaults: Map, overrides: Map, state: St, } /// Represents [`ConfigBuilder`] state. pub trait BuilderState {} /// Represents data specific to builder in default, synchronous state, without support for async. #[derive(Debug, Default, Clone)] pub struct DefaultState { sources: Vec>, } /// Represents data specific to builder in asynchronous state, with support for async. #[derive(Debug, Default, Clone)] pub struct AsyncState { sources: Vec, } #[derive(Debug, Clone)] enum SourceType { Sync(Box), #[cfg(feature = "async")] Async(Box), } impl BuilderState for DefaultState {} impl BuilderState for AsyncState {} /// Operations allowed in any state impl ConfigBuilder { /// Set a default `value` at `key` /// /// This value can be overwritten by any [`Source`], [`AsyncSource`] or override. /// /// # Errors /// /// Fails if `Expression::from_str(key)` fails. pub fn set_default(mut self, key: S, value: T) -> Result where S: AsRef, T: Into, { self.defaults .insert(Expression::from_str(key.as_ref())?, value.into()); Ok(self) } /// Set an override /// /// This function sets an overwrite value. It will not be altered by any default, [`Source`] nor [`AsyncSource`] /// /// # Errors /// /// Fails if `Expression::from_str(key)` fails. pub fn set_override(mut self, key: S, value: T) -> Result where S: AsRef, T: Into, { self.overrides .insert(Expression::from_str(key.as_ref())?, value.into()); Ok(self) } /// Sets an override if value is Some(_) /// /// This function sets an overwrite value if Some(_) is passed. If None is passed, this function does nothing. /// It will not be altered by any default, [`Source`] nor [`AsyncSource`] /// /// # Errors /// /// Fails if `Expression::from_str(key)` fails. pub fn set_override_option(mut self, key: S, value: Option) -> Result where S: AsRef, T: Into, { if let Some(value) = value { self.overrides .insert(Expression::from_str(key.as_ref())?, value.into()); } Ok(self) } } /// Operations allowed in sync state impl ConfigBuilder { /// Registers new [`Source`] in this builder. /// /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use. pub fn add_source(mut self, source: T) -> Self where T: Source + Send + Sync + 'static, { self.state.sources.push(Box::new(source)); self } /// Registers new [`AsyncSource`] in this builder and forces transition to [`AsyncState`]. /// /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use. #[cfg(feature = "async")] pub fn add_async_source(self, source: T) -> ConfigBuilder where T: AsyncSource + Send + Sync + 'static, { let async_state = ConfigBuilder { state: AsyncState { sources: self .state .sources .into_iter() .map(SourceType::Sync) .collect(), }, defaults: self.defaults, overrides: self.overrides, }; async_state.add_async_source(source) } /// Reads all registered [`Source`]s. /// /// This is the method that invokes all I/O operations. /// For a non consuming alternative see [`build_cloned`](Self::build_cloned) /// /// # Errors /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons, /// this method returns error. pub fn build(self) -> Result { Self::build_internal(self.defaults, self.overrides, &self.state.sources) } /// Reads all registered [`Source`]s. /// /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse. /// Internally it clones data to achieve it. /// /// # Errors /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons, /// this method returns error. pub fn build_cloned(&self) -> Result { Self::build_internal( self.defaults.clone(), self.overrides.clone(), &self.state.sources, ) } fn build_internal( defaults: Map, overrides: Map, sources: &[Box], ) -> Result { let mut cache: Value = Map::::new().into(); // Add defaults for (key, val) in defaults { key.set(&mut cache, val); } // Add sources sources.collect_to(&mut cache)?; // Add overrides for (key, val) in overrides { key.set(&mut cache, val); } Ok(Config::new(cache)) } } /// Operations allowed in async state impl ConfigBuilder { /// Registers new [`Source`] in this builder. /// /// Calling this method does not invoke any I/O. [`Source`] is only saved in internal register for later use. pub fn add_source(mut self, source: T) -> Self where T: Source + Send + Sync + 'static, { self.state.sources.push(SourceType::Sync(Box::new(source))); self } /// Registers new [`AsyncSource`] in this builder. /// /// Calling this method does not invoke any I/O. [`AsyncSource`] is only saved in internal register for later use. #[cfg(feature = "async")] pub fn add_async_source(mut self, source: T) -> Self where T: AsyncSource + Send + Sync + 'static, { self.state.sources.push(SourceType::Async(Box::new(source))); self } /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides. /// /// This is the method that invokes all I/O operations. /// For a non consuming alternative see [`build_cloned`](Self::build_cloned) /// /// # Errors /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons, /// this method returns error. pub async fn build(self) -> Result { Self::build_internal(self.defaults, self.overrides, &self.state.sources).await } /// Reads all registered defaults, [`Source`]s, [`AsyncSource`]s and overrides. /// /// Similar to [`build`](Self::build), but it does not take ownership of `ConfigBuilder` to allow later reuse. /// Internally it clones data to achieve it. /// /// # Errors /// If source collection fails, be it technical reasons or related to inability to read data as `Config` for different reasons, /// this method returns error. pub async fn build_cloned(&self) -> Result { Self::build_internal( self.defaults.clone(), self.overrides.clone(), &self.state.sources, ) .await } async fn build_internal( defaults: Map, overrides: Map, sources: &[SourceType], ) -> Result { let mut cache: Value = Map::::new().into(); // Add defaults for (key, val) in defaults { key.set(&mut cache, val); } for source in sources.iter() { match source { SourceType::Sync(source) => source.collect_to(&mut cache)?, #[cfg(feature = "async")] SourceType::Async(source) => source.collect_to(&mut cache).await?, } } // Add overrides for (key, val) in overrides { key.set(&mut cache, val); } Ok(Config::new(cache)) } } config-0.15.9/src/config.rs000064400000000000000000000115151046102023000135720ustar 00000000000000use std::fmt::Debug; use serde::de::Deserialize; use serde::ser::Serialize; use crate::builder::{ConfigBuilder, DefaultState}; use crate::error::{ConfigError, Result}; use crate::map::Map; use crate::path; use crate::ser::ConfigSerializer; use crate::source::Source; use crate::value::{Table, Value}; /// A prioritized configuration repository. /// /// It maintains a set of configuration sources, fetches values to populate those, and provides /// them according to the source's priority. #[derive(Clone, Debug)] pub struct Config { defaults: Map, overrides: Map, sources: Vec>, /// Root of the cached configuration. pub cache: Value, } impl Default for Config { fn default() -> Self { Self { defaults: Default::default(), overrides: Default::default(), sources: Default::default(), cache: Value::new(None, Table::new()), } } } impl Config { pub(crate) fn new(value: Value) -> Self { Self { cache: value, ..Self::default() } } /// Creates new [`ConfigBuilder`] instance pub fn builder() -> ConfigBuilder { ConfigBuilder::::default() } /// Refresh the configuration cache with fresh /// data from added sources. /// /// Configuration is automatically refreshed after a mutation /// operation (`set`, `merge`, `set_default`, etc.). fn refresh(&mut self) -> Result<&mut Self> { self.cache = { let mut cache: Value = Map::::new().into(); // Add defaults for (key, val) in &self.defaults { key.set(&mut cache, val.clone()); } // Add sources self.sources.collect_to(&mut cache)?; // Add overrides for (key, val) in &self.overrides { key.set(&mut cache, val.clone()); } cache }; Ok(self) } /// Set an overwrite /// /// This function sets an overwrite value. /// The overwrite `value` is written to the `key` location on every `refresh()` /// /// # Warning /// /// Errors if config is frozen pub(crate) fn set(&mut self, key: &str, value: T) -> Result<&mut Self> where T: Into, { self.overrides.insert(key.parse()?, value.into()); self.refresh() } fn get_value(&self, key: &str) -> Result { // Parse the key into a path expression let expr: path::Expression = key.parse()?; // Traverse the cache using the path to (possibly) retrieve a value let value = expr.get(&self.cache).cloned(); value.ok_or_else(|| ConfigError::NotFound(key.into())) } pub fn get<'de, T: Deserialize<'de>>(&self, key: &str) -> Result { self.get_value(key).and_then(|value| { // Deserialize the received value into the requested type T::deserialize(value).map_err(|e| e.extend_with_key(key)) }) } pub fn get_string(&self, key: &str) -> Result { self.get_value(key) .and_then(|value| value.into_string().map_err(|e| e.extend_with_key(key))) } pub fn get_int(&self, key: &str) -> Result { self.get_value(key) .and_then(|value| value.into_int().map_err(|e| e.extend_with_key(key))) } pub fn get_float(&self, key: &str) -> Result { self.get_value(key) .and_then(|value| value.into_float().map_err(|e| e.extend_with_key(key))) } pub fn get_bool(&self, key: &str) -> Result { self.get_value(key) .and_then(|value| value.into_bool().map_err(|e| e.extend_with_key(key))) } pub fn get_table(&self, key: &str) -> Result> { self.get_value(key) .and_then(|value| value.into_table().map_err(|e| e.extend_with_key(key))) } pub fn get_array(&self, key: &str) -> Result> { self.get_value(key) .and_then(|value| value.into_array().map_err(|e| e.extend_with_key(key))) } /// Attempt to deserialize the entire configuration into the requested type. pub fn try_deserialize<'de, T: Deserialize<'de>>(self) -> Result { T::deserialize(self) } /// Attempt to serialize the entire configuration from the given type. pub fn try_from(from: &T) -> Result { let mut serializer = ConfigSerializer::default(); from.serialize(&mut serializer)?; Ok(serializer.output) } } impl Source for Config { fn clone_into_box(&self) -> Box { Box::new((*self).clone()) } fn collect(&self) -> Result> { self.cache.clone().into_table() } } config-0.15.9/src/de.rs000064400000000000000000000273151046102023000127220ustar 00000000000000use std::collections::VecDeque; use std::convert::TryInto; use std::iter::Enumerate; use serde::de; use crate::config::Config; use crate::error::{ConfigError, Result, Unexpected}; use crate::map::Map; use crate::value::{Table, Value, ValueKind}; macro_rules! try_convert_number { (signed, $self:expr, $size:literal) => {{ let num = $self.into_int()?; num.try_into().map_err(|_| { ConfigError::invalid_type( None, Unexpected::I64(num), concat!("an signed ", $size, " bit integer"), ) })? }}; (unsigned, $self:expr, $size:literal) => {{ let num = $self.into_uint()?; num.try_into().map_err(|_| { ConfigError::invalid_type( None, Unexpected::U64(num), concat!("an unsigned ", $size, " bit integer"), ) })? }}; } impl<'de> de::Deserializer<'de> for Value { type Error = ConfigError; #[inline] fn deserialize_any(self, visitor: V) -> Result where V: de::Visitor<'de>, { // Deserialize based on the underlying type match self.kind { ValueKind::Nil => visitor.visit_unit(), ValueKind::I64(i) => visitor.visit_i64(i), ValueKind::I128(i) => visitor.visit_i128(i), ValueKind::U64(i) => visitor.visit_u64(i), ValueKind::U128(i) => visitor.visit_u128(i), ValueKind::Boolean(b) => visitor.visit_bool(b), ValueKind::Float(f) => visitor.visit_f64(f), ValueKind::String(s) => visitor.visit_string(s), ValueKind::Array(values) => visitor.visit_seq(SeqAccess::new(values)), ValueKind::Table(map) => visitor.visit_map(MapAccess::new(map)), } } #[inline] fn deserialize_bool>(self, visitor: V) -> Result { visitor.visit_bool(self.into_bool()?) } #[inline] fn deserialize_i8>(self, visitor: V) -> Result { let num = try_convert_number!(signed, self, "8"); visitor.visit_i8(num) } #[inline] fn deserialize_i16>(self, visitor: V) -> Result { let num = try_convert_number!(signed, self, "16"); visitor.visit_i16(num) } #[inline] fn deserialize_i32>(self, visitor: V) -> Result { let num = try_convert_number!(signed, self, "32"); visitor.visit_i32(num) } #[inline] fn deserialize_i64>(self, visitor: V) -> Result { let num = try_convert_number!(signed, self, "64"); visitor.visit_i64(num) } #[inline] fn deserialize_u8>(self, visitor: V) -> Result { let num = try_convert_number!(unsigned, self, "8"); visitor.visit_u8(num) } #[inline] fn deserialize_u16>(self, visitor: V) -> Result { let num = try_convert_number!(unsigned, self, "16"); visitor.visit_u16(num) } #[inline] fn deserialize_u32>(self, visitor: V) -> Result { let num = try_convert_number!(unsigned, self, "32"); visitor.visit_u32(num) } #[inline] fn deserialize_u64>(self, visitor: V) -> Result { let num = try_convert_number!(unsigned, self, "u64"); visitor.visit_u64(num) } #[inline] fn deserialize_f32>(self, visitor: V) -> Result { visitor.visit_f32(self.into_float()? as f32) } #[inline] fn deserialize_f64>(self, visitor: V) -> Result { visitor.visit_f64(self.into_float()?) } #[inline] fn deserialize_str>(self, visitor: V) -> Result { visitor.visit_string(self.into_string()?) } #[inline] fn deserialize_string>(self, visitor: V) -> Result { visitor.visit_string(self.into_string()?) } #[inline] fn deserialize_option(self, visitor: V) -> Result where V: de::Visitor<'de>, { // Match an explicit nil as None and everything else as Some match self.kind { ValueKind::Nil => visitor.visit_none(), _ => visitor.visit_some(self), } } fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: de::Visitor<'de>, { visitor.visit_newtype_struct(self) } fn deserialize_enum( self, name: &'static str, variants: &'static [&'static str], visitor: V, ) -> Result where V: de::Visitor<'de>, { visitor.visit_enum(EnumAccess { value: self, name, variants, }) } serde::forward_to_deserialize_any! { char seq bytes byte_buf map struct unit identifier ignored_any unit_struct tuple_struct tuple } } struct StrDeserializer<'a>(&'a str); impl<'de> de::Deserializer<'de> for StrDeserializer<'_> { type Error = ConfigError; #[inline] fn deserialize_any>(self, visitor: V) -> Result { visitor.visit_str(self.0) } serde::forward_to_deserialize_any! { bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq bytes byte_buf map struct unit enum newtype_struct identifier ignored_any unit_struct tuple_struct tuple option } } struct SeqAccess { elements: Enumerate<::std::vec::IntoIter>, } impl SeqAccess { fn new(elements: Vec) -> Self { Self { elements: elements.into_iter().enumerate(), } } } impl<'de> de::SeqAccess<'de> for SeqAccess { type Error = ConfigError; fn next_element_seed(&mut self, seed: T) -> Result> where T: de::DeserializeSeed<'de>, { match self.elements.next() { Some((idx, value)) => seed .deserialize(value) .map(Some) .map_err(|e| e.prepend_index(idx)), None => Ok(None), } } fn size_hint(&self) -> Option { match self.elements.size_hint() { (lower, Some(upper)) if lower == upper => Some(upper), _ => None, } } } struct MapAccess { elements: VecDeque<(String, Value)>, } impl MapAccess { fn new(table: Map) -> Self { Self { elements: table.into_iter().collect(), } } } impl<'de> de::MapAccess<'de> for MapAccess { type Error = ConfigError; fn next_key_seed(&mut self, seed: K) -> Result> where K: de::DeserializeSeed<'de>, { if let Some((ref key_s, _)) = self.elements.front() { let key_de = Value::new(None, key_s as &str); let key = de::DeserializeSeed::deserialize(seed, key_de)?; Ok(Some(key)) } else { Ok(None) } } fn next_value_seed(&mut self, seed: V) -> Result where V: de::DeserializeSeed<'de>, { let (key, value) = self.elements.pop_front().unwrap(); de::DeserializeSeed::deserialize(seed, value).map_err(|e| e.prepend_key(&key)) } } struct EnumAccess { value: Value, name: &'static str, variants: &'static [&'static str], } impl EnumAccess { fn variant_deserializer(&self, name: &str) -> Result> { self.variants .iter() .find(|&&s| s == name) .map(|&s| StrDeserializer(s)) .ok_or_else(|| self.no_constructor_error(name)) } fn table_deserializer(&self, table: &Table) -> Result> { if table.len() == 1 { self.variant_deserializer(table.iter().next().unwrap().0) } else { Err(self.structural_error()) } } fn no_constructor_error(&self, supposed_variant: &str) -> ConfigError { ConfigError::Message(format!( "enum {} does not have variant constructor {}", self.name, supposed_variant )) } fn structural_error(&self) -> ConfigError { ConfigError::Message(format!( "value of enum {} should be represented by either string or table with exactly one key", self.name )) } } impl<'de> de::EnumAccess<'de> for EnumAccess { type Error = ConfigError; type Variant = Self; fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant)> where V: de::DeserializeSeed<'de>, { let value = { let deserializer = match self.value.kind { ValueKind::String(ref s) => self.variant_deserializer(s), ValueKind::Table(ref t) => self.table_deserializer(t), _ => Err(self.structural_error()), }?; seed.deserialize(deserializer)? }; Ok((value, self)) } } impl<'de> de::VariantAccess<'de> for EnumAccess { type Error = ConfigError; fn unit_variant(self) -> Result<()> { Ok(()) } fn newtype_variant_seed(self, seed: T) -> Result where T: de::DeserializeSeed<'de>, { match self.value.kind { ValueKind::Table(t) => seed.deserialize(t.into_iter().next().unwrap().1), _ => unreachable!(), } } fn tuple_variant(self, _len: usize, visitor: V) -> Result where V: de::Visitor<'de>, { match self.value.kind { ValueKind::Table(t) => { de::Deserializer::deserialize_seq(t.into_iter().next().unwrap().1, visitor) } _ => unreachable!(), } } fn struct_variant(self, _fields: &'static [&'static str], visitor: V) -> Result where V: de::Visitor<'de>, { match self.value.kind { ValueKind::Table(t) => { de::Deserializer::deserialize_map(t.into_iter().next().unwrap().1, visitor) } _ => unreachable!(), } } } /// Define `$method`s, `deserialize_foo`, by forwarding to `Value` /// /// `($arg: $argtype, ...)`, if supplied, are the formal arguments macro_rules! config_deserialize_via_value { { $( $method:ident $( ( $( $arg:ident: $argtype:ty ),* ) )? ; )* } => { $( #[inline] fn $method>( self, $( $( $arg: $argtype, )* )? visitor: V, ) -> Result { self.cache.$method( $( $( $arg, )* )? visitor) } )* } } impl<'de> de::Deserializer<'de> for Config { type Error = ConfigError; config_deserialize_via_value! { deserialize_any; deserialize_bool; deserialize_i8; deserialize_i16; deserialize_i32; deserialize_i64; deserialize_u8; deserialize_u16; deserialize_u32; deserialize_u64; deserialize_f32; deserialize_f64; deserialize_str; deserialize_string; deserialize_option; deserialize_char; deserialize_seq; deserialize_bytes; deserialize_byte_buf; deserialize_map; deserialize_unit; deserialize_identifier; deserialize_ignored_any; deserialize_enum(name: &'static str, variants: &'static [&'static str]); deserialize_unit_struct(name: &'static str); deserialize_newtype_struct(name: &'static str); deserialize_tuple(n: usize); deserialize_tuple_struct(name: &'static str, n: usize); deserialize_struct(name: &'static str, fields: &'static [&'static str]); } } config-0.15.9/src/env.rs000064400000000000000000000303361046102023000131170ustar 00000000000000use std::env; use std::ffi::OsString; #[cfg(feature = "convert-case")] use convert_case::{Case, Casing}; use crate::error::Result; use crate::map::Map; use crate::source::Source; use crate::value::{Value, ValueKind}; use crate::ConfigError; /// An environment source collects a dictionary of environment variables values into a hierarchical /// config Value type. We have to be aware how the config tree is created from the environment /// dictionary, therefore we are mindful about prefixes for the environment keys, level separators, /// encoding form (kebab, snake case) etc. #[must_use] #[derive(Clone, Debug, Default)] pub struct Environment { /// Optional prefix that will limit access to the environment to only keys that /// begin with the defined prefix. /// /// A prefix with a separator of `_` is tested to be present on each key before its considered /// to be part of the source environment. /// /// For example, the key `CONFIG_DEBUG` would become `DEBUG` with a prefix of `config`. prefix: Option, /// Optional character sequence that separates the prefix from the rest of the key prefix_separator: Option, /// Optional character sequence that separates each key segment in an environment key pattern. /// Consider a nested configuration such as `redis.password`, a separator of `_` would allow /// an environment key of `REDIS_PASSWORD` to match. separator: Option, /// Optional directive to translate collected keys into a form that matches what serializers /// that the configuration would expect. For example if you have the `kebab-case` attribute /// for your serde config types, you may want to pass `Case::Kebab` here. #[cfg(feature = "convert-case")] convert_case: Option, /// Optional character sequence that separates each env value into a vector. only works when `try_parsing` is set to true /// Once set, you cannot have type String on the same environment, unless you set `list_parse_keys`. list_separator: Option, /// A list of keys which should always be parsed as a list. If not set you can have only `Vec` or `String` (not both) in one environment. list_parse_keys: Option>, /// Ignore empty env values (treat as unset). ignore_empty: bool, /// Parses booleans, integers and floats if they're detected (can be safely parsed). try_parsing: bool, // Preserve the prefix while parsing keep_prefix: bool, /// Alternate source for the environment. This can be used when you want to test your own code /// using this source, without the need to change the actual system environment variables. /// /// ## Example /// /// ```rust /// # use config::{Environment, Config}; /// # use serde::Deserialize; /// # use std::collections::HashMap; /// # use std::convert::TryInto; /// # /// #[test] /// fn test_config() -> Result<(), config::ConfigError> { /// #[derive(Clone, Debug, Deserialize)] /// struct MyConfig { /// pub my_string: String, /// } /// /// let source = Environment::default() /// .source(Some({ /// let mut env = HashMap::new(); /// env.insert("MY_STRING".into(), "my-value".into()); /// env /// })); /// /// let config: MyConfig = Config::builder() /// .add_source(source) /// .build()? /// .try_into()?; /// assert_eq!(config.my_string, "my-value"); /// /// Ok(()) /// } /// ``` source: Option>, } impl Environment { /// Optional prefix that will limit access to the environment to only keys that /// begin with the defined prefix. /// /// A prefix with a separator of `_` is tested to be present on each key before its considered /// to be part of the source environment. /// /// For example, the key `CONFIG_DEBUG` would become `DEBUG` with a prefix of `config`. pub fn with_prefix(s: &str) -> Self { Self { prefix: Some(s.into()), ..Self::default() } } /// See [`Environment::with_prefix`] pub fn prefix(mut self, s: &str) -> Self { self.prefix = Some(s.into()); self } #[cfg(feature = "convert-case")] pub fn with_convert_case(tt: Case) -> Self { Self::default().convert_case(tt) } #[cfg(feature = "convert-case")] pub fn convert_case(mut self, tt: Case) -> Self { self.convert_case = Some(tt); self } /// Optional character sequence that separates the prefix from the rest of the key pub fn prefix_separator(mut self, s: &str) -> Self { self.prefix_separator = Some(s.into()); self } /// Optional character sequence that separates each key segment in an environment key pattern. /// Consider a nested configuration such as `redis.password`, a separator of `_` would allow /// an environment key of `REDIS_PASSWORD` to match. pub fn separator(mut self, s: &str) -> Self { self.separator = Some(s.into()); self } /// When set and `try_parsing` is true, then all environment variables will be parsed as [`Vec`] instead of [`String`]. /// See /// [`with_list_parse_key`](Self::with_list_parse_key) /// when you want to use [`Vec`] in combination with [`String`]. pub fn list_separator(mut self, s: &str) -> Self { self.list_separator = Some(s.into()); self } /// Add a key which should be parsed as a list when collecting [`Value`]s from the environment. /// Once `list_separator` is set, the type for string is [`Vec`]. /// To switch the default type back to type Strings you need to provide the keys which should be [`Vec`] using this function. pub fn with_list_parse_key(mut self, key: &str) -> Self { if self.list_parse_keys.is_none() { self.list_parse_keys = Some(vec![key.into()]); } else { self.list_parse_keys = self.list_parse_keys.map(|mut keys| { keys.push(key.into()); keys }); } self } /// Ignore empty env values (treat as unset). pub fn ignore_empty(mut self, ignore: bool) -> Self { self.ignore_empty = ignore; self } /// Note: enabling `try_parsing` can reduce performance it will try and parse /// each environment variable 3 times (bool, i64, f64) pub fn try_parsing(mut self, try_parsing: bool) -> Self { self.try_parsing = try_parsing; self } // Preserve the prefix while parsing pub fn keep_prefix(mut self, keep: bool) -> Self { self.keep_prefix = keep; self } /// Alternate source for the environment. This can be used when you want to test your own code /// using this source, without the need to change the actual system environment variables. /// /// ## Example /// /// ```rust /// # use config::{Environment, Config}; /// # use serde::Deserialize; /// # use std::collections::HashMap; /// # use std::convert::TryInto; /// # /// #[test] /// fn test_config() -> Result<(), config::ConfigError> { /// #[derive(Clone, Debug, Deserialize)] /// struct MyConfig { /// pub my_string: String, /// } /// /// let source = Environment::default() /// .source(Some({ /// let mut env = HashMap::new(); /// env.insert("MY_STRING".into(), "my-value".into()); /// env /// })); /// /// let config: MyConfig = Config::builder() /// .add_source(source) /// .build()? /// .try_into()?; /// assert_eq!(config.my_string, "my-value"); /// /// Ok(()) /// } /// ``` pub fn source(mut self, source: Option>) -> Self { self.source = source; self } } impl Source for Environment { fn clone_into_box(&self) -> Box { Box::new((*self).clone()) } fn collect(&self) -> Result> { let mut m = Map::new(); let uri: String = "the environment".into(); let separator = self.separator.as_deref().unwrap_or(""); #[cfg(feature = "convert-case")] let convert_case = &self.convert_case; let prefix_separator = match (self.prefix_separator.as_deref(), self.separator.as_deref()) { (Some(pre), _) => pre, (None, Some(sep)) => sep, (None, None) => "_", }; // Define a prefix pattern to test and exclude from keys let prefix_pattern = self .prefix .as_ref() .map(|prefix| format!("{prefix}{prefix_separator}").to_lowercase()); let collector = |(key, value): (OsString, OsString)| { let key = match key.into_string() { Ok(key) => key, // Key is not valid unicode, skip it Err(_) => return Ok(()), }; // Treat empty environment variables as unset if self.ignore_empty && value.is_empty() { return Ok(()); } let mut key = key.to_lowercase(); // Check for prefix if let Some(ref prefix_pattern) = prefix_pattern { if key.starts_with(prefix_pattern) { if !self.keep_prefix { // Remove this prefix from the key key = key[prefix_pattern.len()..].to_string(); } } else { // Skip this key return Ok(()); } } // At this point, we don't know if the key is required or not. // Therefore if the value is not a valid unicode string, we error out. let value = value.into_string().map_err(|os_string| { ConfigError::Message(format!( "env variable {key:?} contains non-Unicode data: {os_string:?}" )) })?; // If separator is given replace with `.` if !separator.is_empty() { key = key.replace(separator, "."); } #[cfg(feature = "convert-case")] if let Some(convert_case) = convert_case { key = key.to_case(*convert_case); } let value = if self.try_parsing { // convert to lowercase because bool parsing expects all lowercase if let Ok(parsed) = value.to_lowercase().parse::() { ValueKind::Boolean(parsed) } else if let Ok(parsed) = value.parse::() { ValueKind::I64(parsed) } else if let Ok(parsed) = value.parse::() { ValueKind::Float(parsed) } else if let Some(separator) = &self.list_separator { if let Some(keys) = &self.list_parse_keys { if keys.contains(&key) { let v: Vec = value .split(separator) .map(|s| Value::new(Some(&uri), ValueKind::String(s.to_owned()))) .collect(); ValueKind::Array(v) } else { ValueKind::String(value) } } else { let v: Vec = value .split(separator) .map(|s| Value::new(Some(&uri), ValueKind::String(s.to_owned()))) .collect(); ValueKind::Array(v) } } else { ValueKind::String(value) } } else { ValueKind::String(value) }; m.insert(key, Value::new(Some(&uri), value)); Ok(()) }; match &self.source { Some(source) => source .clone() .into_iter() .map(|(key, value)| (key.into(), value.into())) .try_for_each(collector), None => env::vars_os().try_for_each(collector), }?; Ok(m) } } config-0.15.9/src/error.rs000064400000000000000000000203731046102023000134600ustar 00000000000000use std::error::Error; use std::fmt; use std::result; use serde::de; use serde::ser; #[derive(Debug)] pub enum Unexpected { Bool(bool), I64(i64), I128(i128), U64(u64), U128(u128), Float(f64), Str(String), Unit, Seq, Map, } impl fmt::Display for Unexpected { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> { match *self { Unexpected::Bool(b) => write!(f, "boolean `{b}`"), Unexpected::I64(i) => write!(f, "64-bit integer `{i}`"), Unexpected::I128(i) => write!(f, "128-bit integer `{i}`"), Unexpected::U64(i) => write!(f, "64-bit unsigned integer `{i}`"), Unexpected::U128(i) => write!(f, "128-bit unsigned integer `{i}`"), Unexpected::Float(v) => write!(f, "floating point `{v}`"), Unexpected::Str(ref s) => write!(f, "string {s:?}"), Unexpected::Unit => write!(f, "unit value"), Unexpected::Seq => write!(f, "sequence"), Unexpected::Map => write!(f, "map"), } } } /// Represents all possible errors that can occur when working with /// configuration. #[non_exhaustive] pub enum ConfigError { /// Configuration is frozen and no further mutations can be made. Frozen, /// Configuration property was not found NotFound(String), /// Configuration path could not be parsed. PathParse { cause: Box }, /// Configuration could not be parsed from file. FileParse { /// The URI used to access the file (if not loaded from a string). /// Example: `/path/to/config.json` uri: Option, /// The captured error from attempting to parse the file in its desired format. /// This is the actual error object from the library used for the parsing. cause: Box, }, /// Value could not be converted into the requested type. Type { /// The URI that references the source that the value came from. /// Example: `/path/to/config.json` or `Environment` or `etcd://localhost` // TODO: Why is this called Origin but FileParse has a uri field? origin: Option, /// What we found when parsing the value unexpected: Unexpected, /// What was expected when parsing the value expected: &'static str, /// The key in the configuration hash of this value (if available where the /// error is generated). key: Option, }, /// Custom message At { /// Error being extended with a path error: Box, /// The URI that references the source that the value came from. /// Example: `/path/to/config.json` or `Environment` or `etcd://localhost` // TODO: Why is this called Origin but FileParse has a uri field? origin: Option, /// The key in the configuration hash of this value (if available where the /// error is generated). key: Option, }, /// Custom message Message(String), /// Unadorned error from a foreign origin. Foreign(Box), } impl ConfigError { // FIXME: pub(crate) #[doc(hidden)] pub fn invalid_type( origin: Option, unexpected: Unexpected, expected: &'static str, ) -> Self { Self::Type { origin, unexpected, expected, key: None, } } // Have a proper error fire if the root of a file is ever not a Table // TODO: for now only json5 checked, need to finish others #[doc(hidden)] pub fn invalid_root(origin: Option<&String>, unexpected: Unexpected) -> Box { Box::new(Self::Type { origin: origin.cloned(), unexpected, expected: "a map", key: None, }) } // FIXME: pub(crate) #[doc(hidden)] #[must_use] pub fn extend_with_key(self, key: &str) -> Self { match self { Self::Type { origin, unexpected, expected, .. } => Self::Type { origin, unexpected, expected, key: Some(key.into()), }, Self::At { origin, error, .. } => Self::At { error, origin, key: Some(key.into()), }, other => Self::At { error: Box::new(other), origin: None, key: Some(key.into()), }, } } #[must_use] fn prepend(self, segment: &str, add_dot: bool) -> Self { let concat = |key: Option| { let key = key.unwrap_or_default(); let dot = if add_dot && key.as_bytes().first().unwrap_or(&b'[') != &b'[' { "." } else { "" }; format!("{segment}{dot}{key}") }; match self { Self::Type { origin, unexpected, expected, key, } => Self::Type { origin, unexpected, expected, key: Some(concat(key)), }, Self::At { error, origin, key } => Self::At { error, origin, key: Some(concat(key)), }, Self::NotFound(key) => Self::NotFound(concat(Some(key))), other => Self::At { error: Box::new(other), origin: None, key: Some(concat(None)), }, } } #[must_use] pub(crate) fn prepend_key(self, key: &str) -> Self { self.prepend(key, true) } #[must_use] pub(crate) fn prepend_index(self, idx: usize) -> Self { self.prepend(&format!("[{idx}]"), false) } } /// Alias for a `Result` with the error type set to `ConfigError`. pub(crate) type Result = result::Result; // Forward Debug to Display for readable panic! messages impl fmt::Debug for ConfigError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", *self) } } impl fmt::Display for ConfigError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { ConfigError::Frozen => write!(f, "configuration is frozen"), ConfigError::PathParse { ref cause } => write!(f, "{cause}"), ConfigError::Message(ref s) => write!(f, "{s}"), ConfigError::Foreign(ref cause) => write!(f, "{cause}"), ConfigError::NotFound(ref key) => { write!(f, "configuration property {key:?} not found") } ConfigError::Type { ref origin, ref unexpected, expected, ref key, } => { write!(f, "invalid type: {unexpected}, expected {expected}")?; if let Some(ref key) = *key { write!(f, " for key `{key}`")?; } if let Some(ref origin) = *origin { write!(f, " in {origin}")?; } Ok(()) } ConfigError::At { ref error, ref origin, ref key, } => { write!(f, "{error}")?; if let Some(ref key) = *key { write!(f, " for key `{key}`")?; } if let Some(ref origin) = *origin { write!(f, " in {origin}")?; } Ok(()) } ConfigError::FileParse { ref cause, ref uri } => { write!(f, "{cause}")?; if let Some(ref uri) = *uri { write!(f, " in {uri}")?; } Ok(()) } } } } impl Error for ConfigError {} impl de::Error for ConfigError { fn custom(msg: T) -> Self { Self::Message(msg.to_string()) } } impl ser::Error for ConfigError { fn custom(msg: T) -> Self { Self::Message(msg.to_string()) } } config-0.15.9/src/file/format/ini.rs000064400000000000000000000021041046102023000153050ustar 00000000000000use std::error::Error; use ini::Ini; use crate::map::Map; use crate::value::{Value, ValueKind}; pub(crate) fn parse( uri: Option<&String>, text: &str, ) -> Result, Box> { let mut map: Map = Map::new(); let i = Ini::load_from_str(text)?; for (sec, prop) in i.iter() { match sec { Some(sec) => { let mut sec_map: Map = Map::new(); for (k, v) in prop.iter() { sec_map.insert( k.to_owned(), Value::new(uri, ValueKind::String(v.to_owned())), ); } map.insert(sec.to_owned(), Value::new(uri, ValueKind::Table(sec_map))); } None => { for (k, v) in prop.iter() { map.insert( k.to_owned(), Value::new(uri, ValueKind::String(v.to_owned())), ); } } } } Ok(map) } config-0.15.9/src/file/format/json.rs000064400000000000000000000031201046102023000154760ustar 00000000000000use std::error::Error; use crate::format; use crate::map::Map; use crate::value::{Value, ValueKind}; pub(crate) fn parse( uri: Option<&String>, text: &str, ) -> Result, Box> { // Parse a JSON object value from the text let value = from_json_value(uri, &serde_json::from_str(text)?); format::extract_root_table(uri, value) } fn from_json_value(uri: Option<&String>, value: &serde_json::Value) -> Value { match *value { serde_json::Value::String(ref value) => Value::new(uri, ValueKind::String(value.clone())), serde_json::Value::Number(ref value) => { if let Some(value) = value.as_i64() { Value::new(uri, ValueKind::I64(value)) } else if let Some(value) = value.as_f64() { Value::new(uri, ValueKind::Float(value)) } else { unreachable!(); } } serde_json::Value::Bool(value) => Value::new(uri, ValueKind::Boolean(value)), serde_json::Value::Object(ref table) => { let mut m = Map::new(); for (key, value) in table { m.insert(key.clone(), from_json_value(uri, value)); } Value::new(uri, ValueKind::Table(m)) } serde_json::Value::Array(ref array) => { let mut l = Vec::new(); for value in array { l.push(from_json_value(uri, value)); } Value::new(uri, ValueKind::Array(l)) } serde_json::Value::Null => Value::new(uri, ValueKind::Nil), } } config-0.15.9/src/file/format/json5.rs000064400000000000000000000025031046102023000155670ustar 00000000000000use std::error::Error; use crate::format; use crate::map::Map; use crate::value::{Value, ValueKind}; #[derive(serde::Deserialize, Debug)] #[serde(untagged)] pub(crate) enum Val { Null, Boolean(bool), Integer(i64), Float(f64), String(String), Array(Vec), Object(Map), } pub(crate) fn parse( uri: Option<&String>, text: &str, ) -> Result, Box> { let value = from_json5_value(uri, json5_rs::from_str::(text)?); format::extract_root_table(uri, value) } fn from_json5_value(uri: Option<&String>, value: Val) -> Value { let vk = match value { Val::Null => ValueKind::Nil, Val::String(v) => ValueKind::String(v), Val::Integer(v) => ValueKind::I64(v), Val::Float(v) => ValueKind::Float(v), Val::Boolean(v) => ValueKind::Boolean(v), Val::Object(table) => { let m = table .into_iter() .map(|(k, v)| (k, from_json5_value(uri, v))) .collect(); ValueKind::Table(m) } Val::Array(array) => { let l = array .into_iter() .map(|v| from_json5_value(uri, v)) .collect(); ValueKind::Array(l) } }; Value::new(uri, vk) } config-0.15.9/src/file/format/mod.rs000064400000000000000000000073671046102023000153250ustar 00000000000000use std::collections::HashMap; use std::error::Error; use std::sync::OnceLock; use crate::map::Map; use crate::{file::FileStoredFormat, value::Value, Format}; #[cfg(feature = "toml")] mod toml; #[cfg(feature = "json")] mod json; #[cfg(feature = "yaml")] mod yaml; #[cfg(feature = "ini")] mod ini; #[cfg(feature = "ron")] mod ron; #[cfg(feature = "json5")] mod json5; /// File formats provided by the library. /// /// Although it is possible to define custom formats using [`Format`] trait it is recommended to use `FileFormat` if possible. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] #[non_exhaustive] pub enum FileFormat { /// TOML (parsed with toml) #[cfg(feature = "toml")] Toml, /// JSON (parsed with `serde_json`) #[cfg(feature = "json")] Json, /// YAML (parsed with `yaml_rust2`) #[cfg(feature = "yaml")] Yaml, /// INI (parsed with `rust_ini`) #[cfg(feature = "ini")] Ini, /// RON (parsed with ron) #[cfg(feature = "ron")] Ron, /// JSON5 (parsed with json5) #[cfg(feature = "json5")] Json5, } pub(crate) fn all_extensions() -> &'static HashMap> { #![allow(unused_mut)] // If no features are used, there is an "unused mut" warning in `all_extensions` static ALL_EXTENSIONS: OnceLock>> = OnceLock::new(); ALL_EXTENSIONS.get_or_init(|| { let mut formats: HashMap> = HashMap::new(); #[cfg(feature = "toml")] formats.insert(FileFormat::Toml, vec!["toml"]); #[cfg(feature = "json")] formats.insert(FileFormat::Json, vec!["json"]); #[cfg(feature = "yaml")] formats.insert(FileFormat::Yaml, vec!["yaml", "yml"]); #[cfg(feature = "ini")] formats.insert(FileFormat::Ini, vec!["ini"]); #[cfg(feature = "ron")] formats.insert(FileFormat::Ron, vec!["ron"]); #[cfg(feature = "json5")] formats.insert(FileFormat::Json5, vec!["json5"]); formats }) } impl FileFormat { pub(crate) fn extensions(&self) -> &'static [&'static str] { // It should not be possible for this to fail // A FileFormat would need to be declared without being added to the // all_extensions map. all_extensions().get(self).unwrap() } pub(crate) fn parse( &self, uri: Option<&String>, text: &str, ) -> Result, Box> { match self { #[cfg(feature = "toml")] FileFormat::Toml => toml::parse(uri, text), #[cfg(feature = "json")] FileFormat::Json => json::parse(uri, text), #[cfg(feature = "yaml")] FileFormat::Yaml => yaml::parse(uri, text), #[cfg(feature = "ini")] FileFormat::Ini => ini::parse(uri, text), #[cfg(feature = "ron")] FileFormat::Ron => ron::parse(uri, text), #[cfg(feature = "json5")] FileFormat::Json5 => json5::parse(uri, text), #[cfg(all( not(feature = "toml"), not(feature = "json"), not(feature = "yaml"), not(feature = "ini"), not(feature = "ron"), not(feature = "json5"), ))] _ => unreachable!("No features are enabled, this library won't work without features"), } } } impl Format for FileFormat { fn parse( &self, uri: Option<&String>, text: &str, ) -> Result, Box> { self.parse(uri, text) } } impl FileStoredFormat for FileFormat { fn file_extensions(&self) -> &'static [&'static str] { self.extensions() } } config-0.15.9/src/file/format/ron.rs000064400000000000000000000034651046102023000153370ustar 00000000000000use std::error::Error; use crate::format; use crate::map::Map; use crate::value::{Value, ValueKind}; pub(crate) fn parse( uri: Option<&String>, text: &str, ) -> Result, Box> { let value = from_ron_value(uri, ron::from_str(text)?)?; format::extract_root_table(uri, value) } fn from_ron_value( uri: Option<&String>, value: ron::Value, ) -> Result> { let kind = match value { ron::Value::Option(value) => match value { Some(value) => from_ron_value(uri, *value)?.kind, None => ValueKind::Nil, }, ron::Value::Unit => ValueKind::Nil, ron::Value::Bool(value) => ValueKind::Boolean(value), ron::Value::Number(value) => match value { ron::Number::Float(value) => ValueKind::Float(value.get()), ron::Number::Integer(value) => ValueKind::I64(value), }, ron::Value::Char(value) => ValueKind::String(value.to_string()), ron::Value::String(value) => ValueKind::String(value), ron::Value::Seq(values) => { let array = values .into_iter() .map(|value| from_ron_value(uri, value)) .collect::, _>>()?; ValueKind::Array(array) } ron::Value::Map(values) => { let map = values .iter() .map(|(key, value)| -> Result<_, Box> { let key = key.clone().into_rust::()?; let value = from_ron_value(uri, value.clone())?; Ok((key, value)) }) .collect::, _>>()?; ValueKind::Table(map) } }; Ok(Value::new(uri, kind)) } config-0.15.9/src/file/format/toml.rs000064400000000000000000000024161046102023000155070ustar 00000000000000use std::error::Error; use crate::format; use crate::map::Map; use crate::value::Value; pub(crate) fn parse( uri: Option<&String>, text: &str, ) -> Result, Box> { // Parse a TOML value from the provided text let value = from_toml_value(uri, &toml::from_str(text)?); format::extract_root_table(uri, value) } fn from_toml_value(uri: Option<&String>, value: &toml::Value) -> Value { match *value { toml::Value::String(ref value) => Value::new(uri, value.to_string()), toml::Value::Float(value) => Value::new(uri, value), toml::Value::Integer(value) => Value::new(uri, value), toml::Value::Boolean(value) => Value::new(uri, value), toml::Value::Table(ref table) => { let mut m = Map::new(); for (key, value) in table { m.insert(key.clone(), from_toml_value(uri, value)); } Value::new(uri, m) } toml::Value::Array(ref array) => { let mut l = Vec::new(); for value in array { l.push(from_toml_value(uri, value)); } Value::new(uri, l) } toml::Value::Datetime(ref datetime) => Value::new(uri, datetime.to_string()), } } config-0.15.9/src/file/format/yaml.rs000064400000000000000000000065521046102023000155030ustar 00000000000000use std::error::Error; use std::fmt; use std::mem; use yaml_rust2 as yaml; use crate::format; use crate::map::Map; use crate::value::{Value, ValueKind}; pub(crate) fn parse( uri: Option<&String>, text: &str, ) -> Result, Box> { // Parse a YAML object from file let mut docs = yaml::YamlLoader::load_from_str(text)?; let root = match docs.len() { 0 => yaml::Yaml::Hash(yaml::yaml::Hash::new()), 1 => mem::replace(&mut docs[0], yaml::Yaml::Null), n => { return Err(Box::new(MultipleDocumentsError(n))); } }; let value = from_yaml_value(uri, &root)?; format::extract_root_table(uri, value) } fn from_yaml_value( uri: Option<&String>, value: &yaml::Yaml, ) -> Result> { match *value { yaml::Yaml::String(ref value) => Ok(Value::new(uri, ValueKind::String(value.clone()))), yaml::Yaml::Real(ref value) => { // TODO: Figure out in what cases this can panic? value .parse::() .map_err(|_| { Box::new(FloatParsingError(value.to_string())) as Box<(dyn Error + Send + Sync)> }) .map(ValueKind::Float) .map(|f| Value::new(uri, f)) } yaml::Yaml::Integer(value) => Ok(Value::new(uri, ValueKind::I64(value))), yaml::Yaml::Boolean(value) => Ok(Value::new(uri, ValueKind::Boolean(value))), yaml::Yaml::Hash(ref table) => { let mut m = Map::new(); for (key, value) in table { match key { yaml::Yaml::String(k) => m.insert(k.to_owned(), from_yaml_value(uri, value)?), yaml::Yaml::Integer(k) => m.insert(k.to_string(), from_yaml_value(uri, value)?), _ => unreachable!(), }; } Ok(Value::new(uri, ValueKind::Table(m))) } yaml::Yaml::Array(ref array) => { let mut l = Vec::new(); for value in array { l.push(from_yaml_value(uri, value)?); } Ok(Value::new(uri, ValueKind::Array(l))) } // 1. Yaml NULL // 2. BadValue – It shouldn't be possible to hit BadValue as this only happens when // using the index trait badly or on a type error but we send back nil. // 3. Alias – No idea what to do with this and there is a note in the lib that its // not fully supported yet anyway _ => Ok(Value::new(uri, ValueKind::Nil)), } } #[derive(Debug, Copy, Clone)] struct MultipleDocumentsError(usize); impl fmt::Display for MultipleDocumentsError { fn fmt(&self, format: &mut fmt::Formatter<'_>) -> fmt::Result { write!(format, "Got {} YAML documents, expected 1", self.0) } } impl Error for MultipleDocumentsError { fn description(&self) -> &str { "More than one YAML document provided" } } #[derive(Debug, Clone)] struct FloatParsingError(String); impl fmt::Display for FloatParsingError { fn fmt(&self, format: &mut fmt::Formatter<'_>) -> fmt::Result { write!(format, "Parsing {} as floating point number failed", self.0) } } impl Error for FloatParsingError { fn description(&self) -> &str { "Floating point number parsing failed" } } config-0.15.9/src/file/mod.rs000064400000000000000000000073001046102023000140200ustar 00000000000000mod format; pub(crate) mod source; use std::fmt::Debug; use std::path::{Path, PathBuf}; use self::source::FileSource; use crate::error::{ConfigError, Result}; use crate::map::Map; use crate::source::Source; use crate::value::Value; use crate::Format; pub use self::format::FileFormat; pub use self::source::file::FileSourceFile; pub use self::source::string::FileSourceString; /// An extension of [`Format`] trait. /// /// Associates format with file extensions, therefore linking storage-agnostic notion of format to a file system. pub trait FileStoredFormat: Format { /// Returns a vector of file extensions, for instance `[yml, yaml]`. fn file_extensions(&self) -> &'static [&'static str]; } /// A configuration source backed up by a file. /// /// It supports optional automatic file format discovery. #[derive(Clone, Debug)] #[must_use] pub struct File { source: T, /// Format of file (which dictates what driver to use). format: Option, /// A required File will error if it cannot be found required: bool, } impl File where F: FileStoredFormat + 'static, { pub fn from_str(s: &str, format: F) -> Self { Self { format: Some(format), required: true, source: s.into(), } } } impl File where F: FileStoredFormat + 'static, { pub fn new(name: &str, format: F) -> Self { Self { format: Some(format), required: true, source: FileSourceFile::new(name.into()), } } } impl File { /// Given the basename of a file, will attempt to locate a file by setting its /// extension to a registered format. pub fn with_name(base_name: &str) -> Self { Self { format: None, required: true, source: FileSourceFile::new(base_name.into()), } } } impl File where F: FileStoredFormat + 'static, T: FileSource, { pub fn format(mut self, format: F) -> Self { self.format = Some(format); self } /// Set required to false to make a file optional when building the config. pub fn required(mut self, required: bool) -> Self { self.required = required; self } } impl<'a> From<&'a Path> for File { fn from(path: &'a Path) -> Self { Self { format: None, required: true, source: FileSourceFile::new(path.to_path_buf()), } } } impl From for File { fn from(path: PathBuf) -> Self { Self { format: None, required: true, source: FileSourceFile::new(path), } } } impl Source for File where F: FileStoredFormat + Debug + Clone + Send + Sync + 'static, T: Sync + Send + FileSource + 'static, { fn clone_into_box(&self) -> Box { Box::new((*self).clone()) } fn collect(&self) -> Result> { // Coerce the file contents to a string let (uri, contents, format) = match self .source .resolve(self.format.clone()) .map_err(ConfigError::Foreign) { Ok(result) => (result.uri, result.content, result.format), Err(error) => { if !self.required { return Ok(Map::new()); } return Err(error); } }; // Parse the string using the given format format .parse(uri.as_ref(), &contents) .map_err(|cause| ConfigError::FileParse { uri, cause }) } } config-0.15.9/src/file/source/file.rs000064400000000000000000000101121046102023000154530ustar 00000000000000use std::env; use std::error::Error; use std::fs; use std::io; use std::path::PathBuf; use crate::file::{ format::all_extensions, source::FileSourceResult, FileSource, FileStoredFormat, Format, }; /// Describes a file sourced from a file #[derive(Clone, Debug)] pub struct FileSourceFile { /// Path of configuration file name: PathBuf, } impl FileSourceFile { pub fn new(name: PathBuf) -> Self { Self { name } } fn find_file( &self, format_hint: Option, ) -> Result<(PathBuf, Box), Box> where F: FileStoredFormat + Format + 'static, { let filename = if self.name.is_absolute() { self.name.clone() } else { env::current_dir()?.as_path().join(&self.name) }; // First check for an _exact_ match if filename.is_file() { return if let Some(format) = format_hint { Ok((filename, Box::new(format))) } else { for (format, extensions) in all_extensions().iter() { if extensions.contains( &filename .extension() .unwrap_or_default() .to_string_lossy() .as_ref(), ) { return Ok((filename, Box::new(*format))); } } Err(Box::new(io::Error::new( io::ErrorKind::NotFound, format!( "configuration file \"{}\" is not of a registered file format", filename.to_string_lossy() ), ))) }; } // Adding a dummy extension will make sure we will not override secondary extensions, i.e. "file.local" // This will make the following set_extension function calls to append the extension. let mut filename = add_dummy_extension(filename); match format_hint { Some(format) => { for ext in format.file_extensions() { filename.set_extension(ext); if filename.is_file() { return Ok((filename, Box::new(format))); } } } None => { for format in all_extensions().keys() { for ext in format.extensions() { filename.set_extension(ext); if filename.is_file() { return Ok((filename, Box::new(*format))); } } } } } Err(Box::new(io::Error::new( io::ErrorKind::NotFound, format!( "configuration file \"{}\" not found", self.name.to_string_lossy() ), ))) } } impl FileSource for FileSourceFile where F: Format + FileStoredFormat + 'static, { fn resolve( &self, format_hint: Option, ) -> Result> { // Find file let (filename, format) = self.find_file(format_hint)?; // Attempt to use a relative path for the URI let uri = env::current_dir() .ok() .and_then(|base| pathdiff::diff_paths(&filename, base)) .unwrap_or_else(|| filename.clone()); // Read contents from file let text = fs::read_to_string(filename)?; Ok(FileSourceResult { uri: Some(uri.to_string_lossy().into_owned()), content: text, format, }) } } fn add_dummy_extension(mut filename: PathBuf) -> PathBuf { match filename.extension() { Some(extension) => { let mut ext = extension.to_os_string(); ext.push("."); ext.push("dummy"); filename.set_extension(ext); } None => { filename.set_extension("dummy"); } } filename } config-0.15.9/src/file/source/mod.rs000064400000000000000000000014261046102023000153230ustar 00000000000000pub(crate) mod file; pub(crate) mod string; use std::error::Error; use std::fmt::Debug; use crate::{file::FileStoredFormat, Format}; /// Describes where the [`File`][super::File] is sourced pub trait FileSource: Debug + Clone where T: Format + FileStoredFormat, { fn resolve( &self, format_hint: Option, ) -> Result>; } pub struct FileSourceResult { pub(crate) uri: Option, pub(crate) content: String, pub(crate) format: Box, } impl FileSourceResult { pub fn uri(&self) -> &Option { &self.uri } pub fn content(&self) -> &str { self.content.as_str() } pub fn format(&self) -> &dyn Format { self.format.as_ref() } } config-0.15.9/src/file/source/string.rs000064400000000000000000000014021046102023000160440ustar 00000000000000use std::error::Error; use crate::{ file::source::FileSourceResult, file::{FileSource, FileStoredFormat}, Format, }; /// Describes a file sourced from a string #[derive(Clone, Debug)] pub struct FileSourceString(String); impl<'a> From<&'a str> for FileSourceString { fn from(s: &'a str) -> Self { Self(s.into()) } } impl FileSource for FileSourceString where F: Format + FileStoredFormat + 'static, { fn resolve( &self, format_hint: Option, ) -> Result> { Ok(FileSourceResult { uri: None, content: self.0.clone(), format: Box::new(format_hint.expect("from_str requires a set file format")), }) } } config-0.15.9/src/format.rs000064400000000000000000000037461046102023000136240ustar 00000000000000use std::error::Error; use crate::error::{ConfigError, Unexpected}; use crate::map::Map; use crate::value::{Value, ValueKind}; /// Describes a format of configuration source data /// /// Implementations of this trait can be used to convert [`File`](crate::File) sources to configuration data. /// /// There can be various formats, some of them provided by this library, such as JSON, Yaml and other. /// This trait enables users of the library to easily define their own, even proprietary formats without /// the need to alter library sources. /// /// What is more, it is recommended to use this trait with custom [`Source`](crate::Source)s and their async counterparts. pub trait Format { /// Parses provided content into configuration values understood by the library. /// /// It also allows specifying optional URI of the source associated with format instance that can facilitate debugging. fn parse( &self, uri: Option<&String>, text: &str, ) -> Result, Box>; } // Have a proper error fire if the root of a file is ever not a Table pub(crate) fn extract_root_table( uri: Option<&String>, value: Value, ) -> Result, Box> { match value.kind { ValueKind::Table(map) => Ok(map), ValueKind::Nil => Err(Unexpected::Unit), ValueKind::Array(_value) => Err(Unexpected::Seq), ValueKind::Boolean(value) => Err(Unexpected::Bool(value)), ValueKind::I64(value) => Err(Unexpected::I64(value)), ValueKind::I128(value) => Err(Unexpected::I128(value)), ValueKind::U64(value) => Err(Unexpected::U64(value)), ValueKind::U128(value) => Err(Unexpected::U128(value)), ValueKind::Float(value) => Err(Unexpected::Float(value)), ValueKind::String(value) => Err(Unexpected::Str(value)), } .map_err(|err| ConfigError::invalid_root(uri, err)) .map_err(|err| Box::new(err) as Box) } config-0.15.9/src/lib.rs000064400000000000000000000035531046102023000130760ustar 00000000000000//! [`Config`] organizes hierarchical or layered configurations for Rust applications. //! //! [`Config`] lets you set a set of [default parameters][ConfigBuilder::set_default] and then extend them via merging in //! configuration from a variety of sources: //! //! - [Environment variables][Environment] //! - [String literals][FileSourceString] in [well-known formats][FileFormat] //! - Another [`Config`] instance //! - [Files][FileSourceFile] in [well known formats][FileFormat] and custom ones defined with [`Format`] trait //! - Manual, programmatic [overrides][ConfigBuilder::set_override] //! //! Additionally, [`Config`] supports: //! //! - Live watching and re-reading of configuration files //! - Deep access into the merged configuration via a path syntax //! - Deserialization via `serde` of the configuration or any subset defined via a path //! //! # Example //! //! ```rust //! # #[cfg(feature = "toml")] { #![doc = include_str!("../examples/simple/main.rs")] //! # } //! ``` //! //! See more [examples](https://github.com/mehcode/config-rs/tree/master/examples) for //! general usage information. #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![warn(clippy::print_stderr)] #![warn(clippy::print_stdout)] pub mod builder; mod config; mod de; mod env; mod error; mod file; mod format; mod map; mod path; mod ser; mod source; mod value; // Re-export #[cfg(feature = "convert-case")] pub use convert_case::Case; pub use crate::builder::ConfigBuilder; pub use crate::config::Config; pub use crate::env::Environment; pub use crate::error::ConfigError; pub use crate::file::source::FileSource; pub use crate::file::{File, FileFormat, FileSourceFile, FileSourceString, FileStoredFormat}; pub use crate::format::Format; pub use crate::map::Map; #[cfg(feature = "async")] pub use crate::source::AsyncSource; pub use crate::source::Source; pub use crate::value::{Value, ValueKind}; config-0.15.9/src/map.rs000064400000000000000000000004251046102023000131000ustar 00000000000000/// The backing store for [`Config`][crate::Config] pub type Map = InternalMap; #[cfg(not(feature = "preserve_order"))] type InternalMap = std::collections::HashMap; #[cfg(feature = "preserve_order")] type InternalMap = indexmap::IndexMap; config-0.15.9/src/path/mod.rs000064400000000000000000000121061046102023000140350ustar 00000000000000use std::str::FromStr; use crate::error::{ConfigError, Result}; use crate::map::Map; use crate::value::{Value, ValueKind}; mod parser; #[derive(Debug, Eq, PartialEq, Clone, Hash)] pub(crate) struct Expression { root: String, postfix: Vec, } impl Expression { pub(crate) fn root(root: String) -> Self { Self { root, postfix: Vec::new(), } } } impl FromStr for Expression { type Err = ConfigError; fn from_str(s: &str) -> Result { parser::from_str(s).map_err(|e| ConfigError::PathParse { cause: Box::new(ParseError::new(e)), }) } } #[derive(Debug, Eq, PartialEq, Clone, Hash)] enum Postfix { Key(String), Index(isize), } #[derive(Debug)] struct ParseError(String); impl ParseError { fn new(inner: winnow::error::ParseError<&str, winnow::error::ContextError>) -> Self { Self(inner.to_string()) } } impl std::fmt::Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } impl std::error::Error for ParseError {} /// Convert a relative index into an absolute index fn abs_index(index: isize, len: usize) -> Result { if index >= 0 { Ok(index as usize) } else if let Some(index) = len.checked_sub(index.unsigned_abs()) { Ok(index) } else { Err((len as isize + index).unsigned_abs()) } } impl Expression { pub(crate) fn get(self, root: &Value) -> Option<&Value> { let ValueKind::Table(map) = &root.kind else { return None; }; let mut child = map.get(&self.root)?; for postfix in &self.postfix { match postfix { Postfix::Key(key) => { let ValueKind::Table(map) = &child.kind else { return None; }; child = map.get(key)?; } Postfix::Index(rel_index) => { let ValueKind::Array(array) = &child.kind else { return None; }; let index = abs_index(*rel_index, array.len()).ok()?; child = array.get(index)?; } } } Some(child) } pub(crate) fn get_mut_forcibly<'a>(&self, root: &'a mut Value) -> &'a mut Value { if !matches!(root.kind, ValueKind::Table(_)) { *root = Map::::new().into(); } let ValueKind::Table(map) = &mut root.kind else { unreachable!() }; let mut child = map .entry(self.root.clone()) .or_insert_with(|| Value::new(None, ValueKind::Nil)); for postfix in &self.postfix { match postfix { Postfix::Key(key) => { if !matches!(child.kind, ValueKind::Table(_)) { *child = Map::::new().into(); } let ValueKind::Table(ref mut map) = child.kind else { unreachable!() }; child = map .entry(key.clone()) .or_insert_with(|| Value::new(None, ValueKind::Nil)); } Postfix::Index(rel_index) => { if !matches!(child.kind, ValueKind::Array(_)) { *child = Vec::::new().into(); } let ValueKind::Array(ref mut array) = child.kind else { unreachable!() }; let uindex = match abs_index(*rel_index, array.len()) { Ok(uindex) => { if uindex >= array.len() { array.resize(uindex + 1, Value::new(None, ValueKind::Nil)); } uindex } Err(insertion) => { array.splice( 0..0, (0..insertion).map(|_| Value::new(None, ValueKind::Nil)), ); 0 } }; child = &mut array[uindex]; } } } child } pub(crate) fn set(&self, root: &mut Value, value: Value) { let parent = self.get_mut_forcibly(root); match value.kind { ValueKind::Table(ref incoming_map) => { // If the parent is not a table, overwrite it, treating it as a // table if !matches!(parent.kind, ValueKind::Table(_)) { *parent = Map::::new().into(); } // Continue the deep merge for (key, val) in incoming_map { Self::root(key.clone()).set(parent, val.clone()); } } _ => { *parent = value; } } } } config-0.15.9/src/path/parser.rs000064400000000000000000000124061046102023000145550ustar 00000000000000use std::str::FromStr; use winnow::ascii::digit1; use winnow::ascii::space0; use winnow::combinator::cut_err; use winnow::combinator::dispatch; use winnow::combinator::fail; use winnow::combinator::opt; use winnow::combinator::repeat; use winnow::combinator::seq; use winnow::error::ContextError; use winnow::error::ParseError; use winnow::error::StrContext; use winnow::error::StrContextValue; use winnow::prelude::*; use winnow::token::any; use winnow::token::take_while; use crate::path::Expression; use crate::path::Postfix; pub(crate) fn from_str(input: &str) -> Result> { path.parse(input) } fn path(i: &mut &str) -> ModalResult { let root = ident.parse_next(i)?; let postfix = repeat(0.., postfix).parse_next(i)?; let expr = Expression { root, postfix }; Ok(expr) } fn postfix(i: &mut &str) -> ModalResult { dispatch! {any; '[' => cut_err( seq!( integer.map(Postfix::Index), _: ']'.context(StrContext::Expected(StrContextValue::CharLiteral(']'))), ) .map(|(i,)| i) .context(StrContext::Label("subscript")) ), '.' => cut_err(ident.map(Postfix::Key)), _ => cut_err( fail .context(StrContext::Label("postfix")) .context(StrContext::Expected(StrContextValue::CharLiteral('['))) .context(StrContext::Expected(StrContextValue::CharLiteral('.'))) ), } .parse_next(i) } fn ident(i: &mut &str) -> ModalResult { take_while(1.., ('a'..='z', 'A'..='Z', '0'..='9', '_', '-')) .map(ToOwned::to_owned) .context(StrContext::Label("identifier")) .context(StrContext::Expected(StrContextValue::Description( "ASCII alphanumeric", ))) .context(StrContext::Expected(StrContextValue::CharLiteral('_'))) .context(StrContext::Expected(StrContextValue::CharLiteral('-'))) .parse_next(i) } fn integer(i: &mut &str) -> ModalResult { seq!( _: space0, (opt('-'), digit1).take().try_map(FromStr::from_str), _: space0 ) .context(StrContext::Expected(StrContextValue::Description( "integer", ))) .map(|(i,)| i) .parse_next(i) } #[cfg(test)] mod test { use snapbox::prelude::*; use snapbox::{assert_data_eq, str}; use super::*; #[test] fn test_id() { let parsed: Expression = from_str("abcd").unwrap(); assert_data_eq!( parsed.to_debug(), str![[r#" Expression { root: "abcd", postfix: [], } "#]] ); } #[test] fn test_id_dash() { let parsed: Expression = from_str("abcd-efgh").unwrap(); assert_data_eq!( parsed.to_debug(), str![[r#" Expression { root: "abcd-efgh", postfix: [], } "#]] ); } #[test] fn test_child() { let parsed: Expression = from_str("abcd.efgh").unwrap(); assert_data_eq!( parsed.to_debug(), str![[r#" Expression { root: "abcd", postfix: [ Key( "efgh", ), ], } "#]] ); let parsed: Expression = from_str("abcd.efgh.ijkl").unwrap(); assert_data_eq!( parsed.to_debug(), str![[r#" Expression { root: "abcd", postfix: [ Key( "efgh", ), Key( "ijkl", ), ], } "#]] ); } #[test] fn test_subscript() { let parsed: Expression = from_str("abcd[12]").unwrap(); assert_data_eq!( parsed.to_debug(), str![[r#" Expression { root: "abcd", postfix: [ Index( 12, ), ], } "#]] ); } #[test] fn test_subscript_neg() { let parsed: Expression = from_str("abcd[-1]").unwrap(); assert_data_eq!( parsed.to_debug(), str![[r#" Expression { root: "abcd", postfix: [ Index( -1, ), ], } "#]] ); } #[test] fn test_invalid_identifier() { let err = from_str("!").unwrap_err(); assert_data_eq!( err.to_string(), str![[r#" ! ^ invalid identifier expected ASCII alphanumeric, `_`, `-` "#]] ); } #[test] fn test_invalid_child() { let err = from_str("a..").unwrap_err(); assert_data_eq!( err.to_string(), str![[r#" a.. ^ invalid identifier expected ASCII alphanumeric, `_`, `-` "#]] ); } #[test] fn test_invalid_subscript() { let err = from_str("a[b]").unwrap_err(); assert_data_eq!( err.to_string(), str![[r#" a[b] ^ invalid subscript expected integer "#]] ); } #[test] fn test_incomplete_subscript() { let err = from_str("a[0").unwrap_err(); assert_data_eq!( err.to_string(), str![[r#" a[0 ^ invalid subscript expected `]` "#]] ); } #[test] fn test_invalid_postfix() { let err = from_str("a!b").unwrap_err(); assert_data_eq!( err.to_string(), str![[r#" a!b ^ invalid postfix expected `[`, `.` "#]] ); } } config-0.15.9/src/ser.rs000064400000000000000000000440751046102023000131250ustar 00000000000000use std::fmt::Display; use std::fmt::Write as _; use serde::ser; use crate::error::{ConfigError, Result}; use crate::value::{Value, ValueKind}; use crate::Config; #[derive(Default, Debug)] pub struct ConfigSerializer { keys: Vec, pub output: Config, } #[derive(Debug)] enum SerKey { Named(String), Seq(usize), } /// An uninhabited type: no values like this can ever exist! pub(crate) enum Unreachable {} /// Serializer for numbered sequences /// /// This wrapper is present when we are outputting a sequence (numbered indices). /// Making this a separate type centralises the handling of sequences /// and ensures we don't have any call sites for `ser::SerializeSeq::serialize_element` /// that don't do the necessary work of `SeqSerializer::new`. /// /// Existence of this wrapper implies that `.0.keys.last()` is /// `Some(SerKey::Seq(next_index))`. pub struct SeqSerializer<'a>(&'a mut ConfigSerializer); impl ConfigSerializer { fn serialize_primitive(&mut self, value: T) -> Result<()> where T: Into + Display, { // At some future point we could perhaps retain a cursor into the output `Config`, // rather than reifying the whole thing into a single string with `make_full_key` // and passing that whole path to the `set` method. // // That would be marginally more performant, but more fiddly. let key = self.make_full_key()?; self.output.set(&key, value.into())?; Ok(()) } fn make_full_key(&self) -> Result { let mut keys = self.keys.iter(); let mut whole = match keys.next() { Some(SerKey::Named(s)) => s.clone(), _ => return Err(ConfigError::Message("top level is not a struct".to_owned())), }; for k in keys { match k { SerKey::Named(s) => write!(whole, ".{s}"), SerKey::Seq(i) => write!(whole, "[{i}]"), } .expect("write! to a string failed"); } Ok(whole) } fn push_key(&mut self, key: &str) { self.keys.push(SerKey::Named(key.to_owned())); } fn pop_key(&mut self) { self.keys.pop(); } } impl<'a> ser::Serializer for &'a mut ConfigSerializer { type Ok = (); type Error = ConfigError; type SerializeSeq = SeqSerializer<'a>; type SerializeTuple = SeqSerializer<'a>; type SerializeTupleStruct = SeqSerializer<'a>; type SerializeTupleVariant = SeqSerializer<'a>; type SerializeMap = Self; type SerializeStruct = Self; type SerializeStructVariant = Self; fn serialize_bool(self, v: bool) -> Result { self.serialize_primitive(v) } fn serialize_i8(self, v: i8) -> Result { self.serialize_i64(v.into()) } fn serialize_i16(self, v: i16) -> Result { self.serialize_i64(v.into()) } fn serialize_i32(self, v: i32) -> Result { self.serialize_i64(v.into()) } fn serialize_i64(self, v: i64) -> Result { self.serialize_primitive(v) } fn serialize_u8(self, v: u8) -> Result { self.serialize_u64(v.into()) } fn serialize_u16(self, v: u16) -> Result { self.serialize_u64(v.into()) } fn serialize_u32(self, v: u32) -> Result { self.serialize_u64(v.into()) } fn serialize_u64(self, v: u64) -> Result { if v > (i64::MAX as u64) { Err(ConfigError::Message(format!( "value {} is greater than the max {}", v, i64::MAX ))) } else { self.serialize_i64(v as i64) } } fn serialize_f32(self, v: f32) -> Result { self.serialize_f64(v.into()) } fn serialize_f64(self, v: f64) -> Result { self.serialize_primitive(v) } fn serialize_char(self, v: char) -> Result { self.serialize_primitive(v.to_string()) } fn serialize_str(self, v: &str) -> Result { self.serialize_primitive(v.to_owned()) } fn serialize_bytes(self, v: &[u8]) -> Result { use serde::ser::SerializeSeq; let mut seq = self.serialize_seq(Some(v.len()))?; for byte in v { seq.serialize_element(byte)?; } seq.end(); Ok(()) } fn serialize_none(self) -> Result { self.serialize_unit() } fn serialize_some(self, value: &T) -> Result where T: ?Sized + ser::Serialize, { value.serialize(self) } fn serialize_unit(self) -> Result { self.serialize_primitive(Value::from(ValueKind::Nil)) } fn serialize_unit_struct(self, _name: &'static str) -> Result { self.serialize_unit() } fn serialize_unit_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, ) -> Result { self.serialize_str(variant) } fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result where T: ?Sized + ser::Serialize, { value.serialize(self) } fn serialize_newtype_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, value: &T, ) -> Result where T: ?Sized + ser::Serialize, { self.push_key(variant); value.serialize(&mut *self)?; self.pop_key(); Ok(()) } fn serialize_seq(self, _len: Option) -> Result { SeqSerializer::new(self) } fn serialize_tuple(self, len: usize) -> Result { self.serialize_seq(Some(len)) } fn serialize_tuple_struct( self, _name: &'static str, len: usize, ) -> Result { self.serialize_seq(Some(len)) } fn serialize_tuple_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.push_key(variant); self.serialize_seq(Some(len)) } fn serialize_map(self, _len: Option) -> Result { Ok(self) } fn serialize_struct(self, _name: &'static str, len: usize) -> Result { self.serialize_map(Some(len)) } fn serialize_struct_variant( self, _name: &'static str, _variant_index: u32, variant: &'static str, _len: usize, ) -> Result { self.push_key(variant); Ok(self) } } impl<'a> SeqSerializer<'a> { fn new(inner: &'a mut ConfigSerializer) -> Result { inner.keys.push(SerKey::Seq(0)); Ok(SeqSerializer(inner)) } fn end(self) -> &'a mut ConfigSerializer { // This ought to be Some(SerKey::Seq(..)) but we don't want to panic if we are buggy let _: Option = self.0.keys.pop(); self.0 } } impl ser::SerializeSeq for SeqSerializer<'_> { type Ok = (); type Error = ConfigError; fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { value.serialize(&mut *(self.0))?; match self.0.keys.last_mut() { Some(SerKey::Seq(i)) => *i += 1, _ => { return Err(ConfigError::Message( "config-rs internal error (ser._element but last not Seq!".to_owned(), )) } }; Ok(()) } fn end(self) -> Result { self.end(); Ok(()) } } impl ser::SerializeTuple for SeqSerializer<'_> { type Ok = (); type Error = ConfigError; fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result { ser::SerializeSeq::end(self) } } impl ser::SerializeTupleStruct for SeqSerializer<'_> { type Ok = (); type Error = ConfigError; fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result { ser::SerializeSeq::end(self) } } impl ser::SerializeTupleVariant for SeqSerializer<'_> { type Ok = (); type Error = ConfigError; fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { ser::SerializeSeq::serialize_element(self, value) } fn end(self) -> Result { let inner = self.end(); inner.pop_key(); Ok(()) } } impl ser::SerializeMap for &mut ConfigSerializer { type Ok = (); type Error = ConfigError; fn serialize_key(&mut self, key: &T) -> Result<()> where T: ?Sized + ser::Serialize, { let key_serializer = StringKeySerializer; let key = key.serialize(key_serializer)?; self.push_key(&key); Ok(()) } fn serialize_value(&mut self, value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { value.serialize(&mut **self)?; self.pop_key(); Ok(()) } fn end(self) -> Result { Ok(()) } } impl ser::SerializeStruct for &mut ConfigSerializer { type Ok = (); type Error = ConfigError; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { self.push_key(key); value.serialize(&mut **self)?; self.pop_key(); Ok(()) } fn end(self) -> Result { Ok(()) } } impl ser::SerializeStructVariant for &mut ConfigSerializer { type Ok = (); type Error = ConfigError; fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { self.push_key(key); value.serialize(&mut **self)?; self.pop_key(); Ok(()) } fn end(self) -> Result { self.pop_key(); Ok(()) } } pub(crate) struct StringKeySerializer; /// Define `$emthod`, `serialize_foo`, taking `$type` and serialising it via [`Display`] macro_rules! string_serialize_via_display { { $method:ident, $type:ty } => { fn $method(self, v: $type) -> Result { #[allow(clippy::str_to_string)] Ok(v.to_string()) } } } impl ser::Serializer for StringKeySerializer { type Ok = String; type Error = ConfigError; type SerializeSeq = Unreachable; type SerializeTuple = Unreachable; type SerializeTupleStruct = Unreachable; type SerializeTupleVariant = Unreachable; type SerializeMap = Unreachable; type SerializeStruct = Unreachable; type SerializeStructVariant = Unreachable; string_serialize_via_display!(serialize_bool, bool); string_serialize_via_display!(serialize_i8, i8); string_serialize_via_display!(serialize_i16, i16); string_serialize_via_display!(serialize_i32, i32); string_serialize_via_display!(serialize_i64, i64); string_serialize_via_display!(serialize_u8, u8); string_serialize_via_display!(serialize_u16, u16); string_serialize_via_display!(serialize_u32, u32); string_serialize_via_display!(serialize_u64, u64); string_serialize_via_display!(serialize_f32, f32); string_serialize_via_display!(serialize_f64, f64); string_serialize_via_display!(serialize_char, char); string_serialize_via_display!(serialize_str, &str); fn serialize_bytes(self, v: &[u8]) -> Result { Ok(String::from_utf8_lossy(v).to_string()) } fn serialize_none(self) -> Result { self.serialize_unit() } fn serialize_some(self, value: &T) -> Result where T: ?Sized + ser::Serialize, { value.serialize(self) } fn serialize_unit(self) -> Result { Ok(String::new()) } fn serialize_unit_struct(self, _name: &str) -> Result { self.serialize_unit() } fn serialize_unit_variant( self, _name: &str, _variant_index: u32, variant: &str, ) -> Result { Ok(variant.to_owned()) } fn serialize_newtype_struct(self, _name: &str, value: &T) -> Result where T: ?Sized + ser::Serialize, { value.serialize(self) } fn serialize_newtype_variant( self, _name: &str, _variant_index: u32, _variant: &str, value: &T, ) -> Result where T: ?Sized + ser::Serialize, { value.serialize(self) } fn serialize_seq(self, _len: Option) -> Result { Err(ConfigError::Message( "seq can't serialize to string key".to_owned(), )) } fn serialize_tuple(self, _len: usize) -> Result { Err(ConfigError::Message( "tuple can't serialize to string key".to_owned(), )) } fn serialize_tuple_struct(self, name: &str, _len: usize) -> Result { Err(ConfigError::Message(format!( "tuple struct {name} can't serialize to string key" ))) } fn serialize_tuple_variant( self, name: &str, _variant_index: u32, variant: &str, _len: usize, ) -> Result { Err(ConfigError::Message(format!( "tuple variant {name}::{variant} can't serialize to string key" ))) } fn serialize_map(self, _len: Option) -> Result { Err(ConfigError::Message( "map can't serialize to string key".to_owned(), )) } fn serialize_struct(self, name: &str, _len: usize) -> Result { Err(ConfigError::Message(format!( "struct {name} can't serialize to string key" ))) } fn serialize_struct_variant( self, name: &str, _variant_index: u32, variant: &str, _len: usize, ) -> Result { Err(ConfigError::Message(format!( "struct variant {name}::{variant} can't serialize to string key" ))) } } impl ser::SerializeSeq for Unreachable { type Ok = String; type Error = ConfigError; fn serialize_element(&mut self, _value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { match *self {} } fn end(self) -> Result { match self {} } } impl ser::SerializeTuple for Unreachable { type Ok = String; type Error = ConfigError; fn serialize_element(&mut self, _value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { match *self {} } fn end(self) -> Result { match self {} } } impl ser::SerializeTupleStruct for Unreachable { type Ok = String; type Error = ConfigError; fn serialize_field(&mut self, _value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { match *self {} } fn end(self) -> Result { match self {} } } impl ser::SerializeTupleVariant for Unreachable { type Ok = String; type Error = ConfigError; fn serialize_field(&mut self, _value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { match *self {} } fn end(self) -> Result { match self {} } } impl ser::SerializeMap for Unreachable { type Ok = String; type Error = ConfigError; fn serialize_key(&mut self, _key: &T) -> Result<()> where T: ?Sized + ser::Serialize, { match *self {} } fn serialize_value(&mut self, _value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { match *self {} } fn end(self) -> Result { match self {} } } impl ser::SerializeStruct for Unreachable { type Ok = String; type Error = ConfigError; fn serialize_field(&mut self, _key: &'static str, _value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { match *self {} } fn end(self) -> Result { match self {} } } impl ser::SerializeStructVariant for Unreachable { type Ok = String; type Error = ConfigError; fn serialize_field(&mut self, _key: &'static str, _value: &T) -> Result<()> where T: ?Sized + ser::Serialize, { match *self {} } fn end(self) -> Result { match self {} } } #[cfg(test)] mod test { use serde::{Deserialize, Serialize}; #[cfg(not(feature = "json5"))] use serde_derive::{Deserialize, Serialize}; use super::*; #[test] fn test_struct() { #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] struct Test { int: u32, seq: Vec, } let test = Test { int: 1, seq: vec!["a".to_owned(), "b".to_owned()], }; let config = Config::try_from(&test).unwrap(); let actual: Test = config.try_deserialize().unwrap(); assert_eq!(test, actual); } #[test] #[cfg(feature = "json")] fn test_nest() { let val = serde_json::json! { { "top": { "num": 1, "array": [2], "nested": [[3,4]], "deep": [{ "yes": true, }], "mixed": [ { "boolish": false, }, 42, ["hi"], { "inner": 66 }, 23, ], } } }; let config = Config::try_from(&val).unwrap(); let output: serde_json::Value = config.try_deserialize().unwrap(); assert_eq!(val, output); } } config-0.15.9/src/source.rs000064400000000000000000000103571046102023000136300ustar 00000000000000use std::fmt::Debug; use std::str::FromStr; #[cfg(feature = "async")] use async_trait::async_trait; use crate::error::Result; use crate::map::Map; use crate::path; use crate::value::{Value, ValueKind}; /// Describes a generic _source_ of configuration properties. pub trait Source: Debug { fn clone_into_box(&self) -> Box; /// Collect all configuration properties available from this source into /// a [`Map`]. fn collect(&self) -> Result>; /// Collects all configuration properties to a provided cache. fn collect_to(&self, cache: &mut Value) -> Result<()> { self.collect()? .into_iter() .for_each(|(key, val)| set_value(cache, key, val)); Ok(()) } } fn set_value(cache: &mut Value, key: String, value: Value) { match path::Expression::from_str(key.as_str()) { // Set using the path Ok(expr) => expr.set(cache, value), // Set directly anyway _ => path::Expression::root(key).set(cache, value), } } /// Describes a generic _source_ of configuration properties capable of using an async runtime. /// /// At the moment this library does not implement it, although it allows using its implementations /// within builders. Due to the scattered landscape of asynchronous runtimes, it is impossible to /// cater to all needs with one implementation. Also, this trait might be most useful with remote /// configuration sources, reachable via the network, probably using HTTP protocol. Numerous HTTP /// libraries exist, making it even harder to find one implementation that rules them all. /// /// For those reasons, it is left to other crates to implement runtime-specific or proprietary /// details. /// /// It is advised to use `async_trait` crate while implementing this trait. /// /// See examples for sample implementation. #[cfg(feature = "async")] #[async_trait] pub trait AsyncSource: Debug + Sync { // Sync is supertrait due to https://docs.rs/async-trait/0.1.50/async_trait/index.html#dyn-traits /// Collects all configuration properties available from this source and return /// a Map as an async operations. async fn collect(&self) -> Result>; /// Collects all configuration properties to a provided cache. async fn collect_to(&self, cache: &mut Value) -> Result<()> { self.collect() .await? .into_iter() .for_each(|(key, val)| set_value(cache, key, val)); Ok(()) } } #[cfg(feature = "async")] impl Clone for Box { fn clone(&self) -> Self { self.to_owned() } } impl Clone for Box { fn clone(&self) -> Self { self.clone_into_box() } } impl Source for Vec> { fn clone_into_box(&self) -> Box { Box::new((*self).clone()) } fn collect(&self) -> Result> { let mut cache: Value = Map::::new().into(); for source in self { source.collect_to(&mut cache)?; } if let ValueKind::Table(table) = cache.kind { Ok(table) } else { unreachable!(); } } } impl Source for [Box] { fn clone_into_box(&self) -> Box { Box::new(self.to_owned()) } fn collect(&self) -> Result> { let mut cache: Value = Map::::new().into(); for source in self { source.collect_to(&mut cache)?; } if let ValueKind::Table(table) = cache.kind { Ok(table) } else { unreachable!(); } } } impl Source for Vec where T: Source + Sync + Send + Clone + 'static, { fn clone_into_box(&self) -> Box { Box::new((*self).clone()) } fn collect(&self) -> Result> { let mut cache: Value = Map::::new().into(); for source in self { source.collect_to(&mut cache)?; } if let ValueKind::Table(table) = cache.kind { Ok(table) } else { unreachable!(); } } } config-0.15.9/src/value.rs000064400000000000000000000673071046102023000134530ustar 00000000000000use std::convert::TryInto; use std::fmt; use std::fmt::Display; use serde::de::{Deserialize, Deserializer, Visitor}; use crate::error::{ConfigError, Result, Unexpected}; use crate::map::Map; /// Underlying kind of the configuration value. /// /// Standard operations on a [`Value`] by users of this crate do not require /// knowledge of [`ValueKind`]. Introspection of underlying kind is only required /// when the configuration values are unstructured or do not have known types. #[derive(Debug, Clone, PartialEq)] pub enum ValueKind { Nil, Boolean(bool), I64(i64), I128(i128), U64(u64), U128(u128), Float(f64), String(String), Table(Table), Array(Array), } pub(crate) type Array = Vec; pub(crate) type Table = Map; impl Default for ValueKind { fn default() -> Self { Self::Nil } } impl From> for ValueKind where T: Into, { fn from(value: Option) -> Self { match value { Some(value) => value.into(), None => Self::Nil, } } } impl From for ValueKind { fn from(value: String) -> Self { Self::String(value) } } impl<'a> From<&'a str> for ValueKind { fn from(value: &'a str) -> Self { Self::String(value.into()) } } impl From for ValueKind { fn from(value: i8) -> Self { Self::I64(value.into()) } } impl From for ValueKind { fn from(value: i16) -> Self { Self::I64(value.into()) } } impl From for ValueKind { fn from(value: i32) -> Self { Self::I64(value.into()) } } impl From for ValueKind { fn from(value: i64) -> Self { Self::I64(value) } } impl From for ValueKind { fn from(value: i128) -> Self { Self::I128(value) } } impl From for ValueKind { fn from(value: u8) -> Self { Self::U64(value.into()) } } impl From for ValueKind { fn from(value: u16) -> Self { Self::U64(value.into()) } } impl From for ValueKind { fn from(value: u32) -> Self { Self::U64(value.into()) } } impl From for ValueKind { fn from(value: u64) -> Self { Self::U64(value) } } impl From for ValueKind { fn from(value: u128) -> Self { Self::U128(value) } } impl From for ValueKind { fn from(value: f64) -> Self { Self::Float(value) } } impl From for ValueKind { fn from(value: bool) -> Self { Self::Boolean(value) } } impl From> for ValueKind where T: Into, { fn from(values: Map) -> Self { let t = values.into_iter().map(|(k, v)| (k, v.into())).collect(); Self::Table(t) } } impl From> for ValueKind where T: Into, { fn from(values: Vec) -> Self { Self::Array(values.into_iter().map(T::into).collect()) } } impl Display for ValueKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use std::fmt::Write; match *self { Self::String(ref value) => write!(f, "{value}"), Self::Boolean(value) => write!(f, "{value}"), Self::I64(value) => write!(f, "{value}"), Self::I128(value) => write!(f, "{value}"), Self::U64(value) => write!(f, "{value}"), Self::U128(value) => write!(f, "{value}"), Self::Float(value) => write!(f, "{value}"), Self::Nil => write!(f, "nil"), Self::Table(ref table) => { let mut s = String::new(); for (k, v) in table.iter() { write!(s, "{k} => {v}, ")?; } write!(f, "{{ {s} }}") } Self::Array(ref array) => { let mut s = String::new(); for e in array.iter() { write!(s, "{e}, ")?; } write!(f, "{s:?}") } } } } /// A configuration value. #[derive(Default, Debug, Clone, PartialEq)] pub struct Value { /// A description of the original location of the value. /// /// A Value originating from a File might contain: /// ```text /// Settings.toml /// ``` /// /// A Value originating from the environment would contain: /// ```text /// the environment /// ``` /// /// A Value originating from a remote source might contain: /// ```text /// etcd+http://127.0.0.1:2379 /// ``` origin: Option, /// Underlying kind of the configuration value. pub kind: ValueKind, } impl Value { /// Create a new value instance that will remember its source uri. pub fn new(origin: Option<&String>, kind: V) -> Self where V: Into, { Self { origin: origin.cloned(), kind: kind.into(), } } /// Get the description of the original location of the value. pub fn origin(&self) -> Option<&str> { self.origin.as_ref().map(AsRef::as_ref) } /// Attempt to deserialize this value into the requested type. pub fn try_deserialize<'de, T: Deserialize<'de>>(self) -> Result { T::deserialize(self) } /// Returns `self` as a bool, if possible. // FIXME: Should this not be `try_into_*` ? pub fn into_bool(self) -> Result { match self.kind { ValueKind::Boolean(value) => Ok(value), ValueKind::I64(value) => Ok(value != 0), ValueKind::I128(value) => Ok(value != 0), ValueKind::U64(value) => Ok(value != 0), ValueKind::U128(value) => Ok(value != 0), ValueKind::Float(value) => Ok(value != 0.0), ValueKind::String(ref value) => { match value.to_lowercase().as_ref() { "1" | "true" | "on" | "yes" => Ok(true), "0" | "false" | "off" | "no" => Ok(false), // Unexpected string value s => Err(ConfigError::invalid_type( self.origin.clone(), Unexpected::Str(s.into()), "a boolean", )), } } // Unexpected type ValueKind::Nil => Err(ConfigError::invalid_type( self.origin, Unexpected::Unit, "a boolean", )), ValueKind::Table(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Map, "a boolean", )), ValueKind::Array(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Seq, "a boolean", )), } } /// Returns `self` into an i64, if possible. // FIXME: Should this not be `try_into_*` ? pub fn into_int(self) -> Result { match self.kind { ValueKind::I64(value) => Ok(value), ValueKind::I128(value) => value.try_into().map_err(|_| { ConfigError::invalid_type( self.origin, Unexpected::I128(value), "an signed 64 bit or less integer", ) }), ValueKind::U64(value) => value.try_into().map_err(|_| { ConfigError::invalid_type( self.origin, Unexpected::U64(value), "an signed 64 bit or less integer", ) }), ValueKind::U128(value) => value.try_into().map_err(|_| { ConfigError::invalid_type( self.origin, Unexpected::U128(value), "an signed 64 bit or less integer", ) }), ValueKind::String(ref s) => { match s.to_lowercase().as_ref() { "true" | "on" | "yes" => Ok(1), "false" | "off" | "no" => Ok(0), _ => { s.parse().map_err(|_| { // Unexpected string ConfigError::invalid_type( self.origin.clone(), Unexpected::Str(s.clone()), "an integer", ) }) } } } ValueKind::Boolean(value) => Ok(i64::from(value)), ValueKind::Float(value) => Ok(value.round() as i64), // Unexpected type ValueKind::Nil => Err(ConfigError::invalid_type( self.origin, Unexpected::Unit, "an integer", )), ValueKind::Table(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Map, "an integer", )), ValueKind::Array(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Seq, "an integer", )), } } /// Returns `self` into an i128, if possible. pub fn into_int128(self) -> Result { match self.kind { ValueKind::I64(value) => Ok(value.into()), ValueKind::I128(value) => Ok(value), ValueKind::U64(value) => Ok(value.into()), ValueKind::U128(value) => value.try_into().map_err(|_| { ConfigError::invalid_type( self.origin, Unexpected::U128(value), "an signed 128 bit integer", ) }), ValueKind::String(ref s) => { match s.to_lowercase().as_ref() { "true" | "on" | "yes" => Ok(1), "false" | "off" | "no" => Ok(0), _ => { s.parse().map_err(|_| { // Unexpected string ConfigError::invalid_type( self.origin.clone(), Unexpected::Str(s.clone()), "an integer", ) }) } } } ValueKind::Boolean(value) => Ok(i128::from(value)), ValueKind::Float(value) => Ok(value.round() as i128), // Unexpected type ValueKind::Nil => Err(ConfigError::invalid_type( self.origin, Unexpected::Unit, "an integer", )), ValueKind::Table(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Map, "an integer", )), ValueKind::Array(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Seq, "an integer", )), } } /// Returns `self` into an u64, if possible. // FIXME: Should this not be `try_into_*` ? pub fn into_uint(self) -> Result { match self.kind { ValueKind::U64(value) => Ok(value), ValueKind::U128(value) => value.try_into().map_err(|_| { ConfigError::invalid_type( self.origin, Unexpected::U128(value), "an unsigned 64 bit or less integer", ) }), ValueKind::I64(value) => value.try_into().map_err(|_| { ConfigError::invalid_type( self.origin, Unexpected::I64(value), "an unsigned 64 bit or less integer", ) }), ValueKind::I128(value) => value.try_into().map_err(|_| { ConfigError::invalid_type( self.origin, Unexpected::I128(value), "an unsigned 64 bit or less integer", ) }), ValueKind::String(ref s) => { match s.to_lowercase().as_ref() { "true" | "on" | "yes" => Ok(1), "false" | "off" | "no" => Ok(0), _ => { s.parse().map_err(|_| { // Unexpected string ConfigError::invalid_type( self.origin.clone(), Unexpected::Str(s.clone()), "an integer", ) }) } } } ValueKind::Boolean(value) => Ok(u64::from(value)), ValueKind::Float(value) => Ok(value.round() as u64), // Unexpected type ValueKind::Nil => Err(ConfigError::invalid_type( self.origin, Unexpected::Unit, "an integer", )), ValueKind::Table(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Map, "an integer", )), ValueKind::Array(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Seq, "an integer", )), } } /// Returns `self` into an u128, if possible. pub fn into_uint128(self) -> Result { match self.kind { ValueKind::U64(value) => Ok(value.into()), ValueKind::U128(value) => Ok(value), ValueKind::I64(value) => value.try_into().map_err(|_| { ConfigError::invalid_type( self.origin, Unexpected::I64(value), "an unsigned 128 bit or less integer", ) }), ValueKind::I128(value) => value.try_into().map_err(|_| { ConfigError::invalid_type( self.origin, Unexpected::I128(value), "an unsigned 128 bit or less integer", ) }), ValueKind::String(ref s) => { match s.to_lowercase().as_ref() { "true" | "on" | "yes" => Ok(1), "false" | "off" | "no" => Ok(0), _ => { s.parse().map_err(|_| { // Unexpected string ConfigError::invalid_type( self.origin.clone(), Unexpected::Str(s.clone()), "an integer", ) }) } } } ValueKind::Boolean(value) => Ok(u128::from(value)), ValueKind::Float(value) => Ok(value.round() as u128), // Unexpected type ValueKind::Nil => Err(ConfigError::invalid_type( self.origin, Unexpected::Unit, "an integer", )), ValueKind::Table(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Map, "an integer", )), ValueKind::Array(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Seq, "an integer", )), } } /// Returns `self` into a f64, if possible. // FIXME: Should this not be `try_into_*` ? pub fn into_float(self) -> Result { match self.kind { ValueKind::Float(value) => Ok(value), ValueKind::String(ref s) => { match s.to_lowercase().as_ref() { "true" | "on" | "yes" => Ok(1.0), "false" | "off" | "no" => Ok(0.0), _ => { s.parse().map_err(|_| { // Unexpected string ConfigError::invalid_type( self.origin.clone(), Unexpected::Str(s.clone()), "a floating point", ) }) } } } ValueKind::I64(value) => Ok(value as f64), ValueKind::I128(value) => Ok(value as f64), ValueKind::U64(value) => Ok(value as f64), ValueKind::U128(value) => Ok(value as f64), ValueKind::Boolean(value) => Ok(if value { 1.0 } else { 0.0 }), // Unexpected type ValueKind::Nil => Err(ConfigError::invalid_type( self.origin, Unexpected::Unit, "a floating point", )), ValueKind::Table(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Map, "a floating point", )), ValueKind::Array(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Seq, "a floating point", )), } } /// Returns `self` into a string, if possible. // FIXME: Should this not be `try_into_*` ? pub fn into_string(self) -> Result { match self.kind { ValueKind::String(value) => Ok(value), ValueKind::Boolean(value) => Ok(value.to_string()), ValueKind::I64(value) => Ok(value.to_string()), ValueKind::I128(value) => Ok(value.to_string()), ValueKind::U64(value) => Ok(value.to_string()), ValueKind::U128(value) => Ok(value.to_string()), ValueKind::Float(value) => Ok(value.to_string()), // Cannot convert ValueKind::Nil => Err(ConfigError::invalid_type( self.origin, Unexpected::Unit, "a string", )), ValueKind::Table(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Map, "a string", )), ValueKind::Array(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Seq, "a string", )), } } /// Returns `self` into an array, if possible // FIXME: Should this not be `try_into_*` ? pub fn into_array(self) -> Result> { match self.kind { ValueKind::Array(value) => Ok(value), // Cannot convert ValueKind::Float(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::Float(value), "an array", )), ValueKind::String(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::Str(value), "an array", )), ValueKind::I64(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::I64(value), "an array", )), ValueKind::I128(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::I128(value), "an array", )), ValueKind::U64(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::U64(value), "an array", )), ValueKind::U128(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::U128(value), "an array", )), ValueKind::Boolean(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::Bool(value), "an array", )), ValueKind::Nil => Err(ConfigError::invalid_type( self.origin, Unexpected::Unit, "an array", )), ValueKind::Table(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Map, "an array", )), } } /// If the `Value` is a Table, returns the associated Map. // FIXME: Should this not be `try_into_*` ? pub fn into_table(self) -> Result> { match self.kind { ValueKind::Table(value) => Ok(value), // Cannot convert ValueKind::Float(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::Float(value), "a map", )), ValueKind::String(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::Str(value), "a map", )), ValueKind::I64(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::I64(value), "a map", )), ValueKind::I128(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::I128(value), "a map", )), ValueKind::U64(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::U64(value), "a map", )), ValueKind::U128(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::U128(value), "a map", )), ValueKind::Boolean(value) => Err(ConfigError::invalid_type( self.origin, Unexpected::Bool(value), "a map", )), ValueKind::Nil => Err(ConfigError::invalid_type( self.origin, Unexpected::Unit, "a map", )), ValueKind::Array(_) => Err(ConfigError::invalid_type( self.origin, Unexpected::Seq, "a map", )), } } } impl<'de> Deserialize<'de> for Value { #[inline] fn deserialize(deserializer: D) -> ::std::result::Result where D: Deserializer<'de>, { struct ValueVisitor; impl<'de> Visitor<'de> for ValueVisitor { type Value = Value; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("any valid configuration value") } #[inline] fn visit_bool(self, value: bool) -> ::std::result::Result { Ok(value.into()) } #[inline] fn visit_i8(self, value: i8) -> ::std::result::Result { Ok((i64::from(value)).into()) } #[inline] fn visit_i16(self, value: i16) -> ::std::result::Result { Ok((i64::from(value)).into()) } #[inline] fn visit_i32(self, value: i32) -> ::std::result::Result { Ok((i64::from(value)).into()) } #[inline] fn visit_i64(self, value: i64) -> ::std::result::Result { Ok(value.into()) } #[inline] fn visit_i128(self, value: i128) -> ::std::result::Result { Ok(value.into()) } #[inline] fn visit_u8(self, value: u8) -> ::std::result::Result { Ok((i64::from(value)).into()) } #[inline] fn visit_u16(self, value: u16) -> ::std::result::Result { Ok((i64::from(value)).into()) } #[inline] fn visit_u32(self, value: u32) -> ::std::result::Result { Ok((i64::from(value)).into()) } #[inline] fn visit_u64(self, value: u64) -> ::std::result::Result where E: ::serde::de::Error, { let num: i64 = value.try_into().map_err(|_| { E::invalid_type(::serde::de::Unexpected::Unsigned(value), &self) })?; Ok(num.into()) } #[inline] fn visit_u128(self, value: u128) -> ::std::result::Result where E: ::serde::de::Error, { let num: i128 = value.try_into().map_err(|_| { E::invalid_type( ::serde::de::Unexpected::Other( format!("integer `{value}` as u128").as_str(), ), &self, ) })?; Ok(num.into()) } #[inline] fn visit_f64(self, value: f64) -> ::std::result::Result { Ok(value.into()) } #[inline] fn visit_str(self, value: &str) -> ::std::result::Result where E: ::serde::de::Error, { self.visit_string(String::from(value)) } #[inline] fn visit_string(self, value: String) -> ::std::result::Result { Ok(value.into()) } #[inline] fn visit_none(self) -> ::std::result::Result { Ok(Value::new(None, ValueKind::Nil)) } #[inline] fn visit_some(self, deserializer: D) -> ::std::result::Result where D: Deserializer<'de>, { Deserialize::deserialize(deserializer) } #[inline] fn visit_unit(self) -> ::std::result::Result { Ok(Value::new(None, ValueKind::Nil)) } #[inline] fn visit_seq(self, mut visitor: V) -> ::std::result::Result where V: ::serde::de::SeqAccess<'de>, { let mut vec = Array::new(); while let Some(elem) = visitor.next_element()? { vec.push(elem); } Ok(vec.into()) } fn visit_map(self, mut visitor: V) -> ::std::result::Result where V: ::serde::de::MapAccess<'de>, { let mut values = Table::new(); while let Some((key, value)) = visitor.next_entry()? { values.insert(key, value); } Ok(values.into()) } } deserializer.deserialize_any(ValueVisitor) } } impl From for Value where T: Into, { fn from(value: T) -> Self { Self { origin: None, kind: value.into(), } } } impl Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.kind) } } #[cfg(test)] mod tests { use super::ValueKind; use crate::Config; use crate::File; use crate::FileFormat; #[test] #[cfg(feature = "toml")] fn test_i64() { let c = Config::builder() .add_source(File::from_str( " value = 120 ", FileFormat::Toml, )) .build() .unwrap(); assert!(std::matches!(c.cache.kind, ValueKind::Table(_))); let v = match c.cache.kind { ValueKind::Table(t) => t, _ => unreachable!(), }; let value = v.get("value").unwrap(); assert!( std::matches!(value.kind, ValueKind::I64(120)), "Is not a i64(120): {:?}", value.kind ); } }