From 21e6aba2705ca8c3901946a38384d1c2bd28892f Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 22 Jul 2022 13:05:54 +0300 Subject: [PATCH 01/10] refac: improve core design - removed all functions, features and examples - recreate core types - recreate get_env and get_env_or_set_default --- .vim/coc-settings.json | 3 + Cargo.lock | 1336 ----------------------------------- examples/hyper.rs | 43 -- examples/readme.md | 11 - examples/rocket.rs | 22 - itconfig/Cargo.toml | 49 +- itconfig/src/core.rs | 56 ++ itconfig/src/core/prim.rs | 39 + itconfig/src/core/vec.rs | 59 ++ itconfig/src/envstr.rs | 176 ----- itconfig/src/error.rs | 38 +- itconfig/src/get_env.rs | 240 ------- itconfig/src/get_vec_env.rs | 304 -------- itconfig/src/lib.rs | 13 +- itconfig/src/utils.rs | 131 +++- 15 files changed, 301 insertions(+), 2219 deletions(-) create mode 100644 .vim/coc-settings.json delete mode 100644 examples/hyper.rs delete mode 100644 examples/readme.md delete mode 100644 examples/rocket.rs create mode 100644 itconfig/src/core.rs create mode 100644 itconfig/src/core/prim.rs create mode 100644 itconfig/src/core/vec.rs delete mode 100644 itconfig/src/envstr.rs delete mode 100644 itconfig/src/get_env.rs delete mode 100644 itconfig/src/get_vec_env.rs diff --git a/.vim/coc-settings.json b/.vim/coc-settings.json new file mode 100644 index 0000000..6f5c4ed --- /dev/null +++ b/.vim/coc-settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.cargo.features": "all" +} diff --git a/Cargo.lock b/Cargo.lock index 9714b4a..e5e135a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,100 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aead" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" -dependencies = [ - "generic-array", -] - -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", - "opaque-debug", -] - -[[package]] -name = "aes-gcm" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "async-stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" -dependencies = [ - "autocfg", -] - [[package]] name = "atty" version = "0.2.14" @@ -113,33 +19,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "binascii" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "383d29d513d8764dcdc42ea295d979eb99c3c9f00607b3692cf68a431f7dca72" - [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "block-buffer" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" -dependencies = [ - "generic-array", -] - [[package]] name = "bstr" version = "0.2.17" @@ -158,39 +43,18 @@ version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" -[[package]] -name = "bytes" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" - [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", -] - [[package]] name = "clap" version = "2.34.0" @@ -202,33 +66,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "cookie" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d4706de1b0fa5b132270cddffa8585166037822e260a944fe161acd137ca05" -dependencies = [ - "aes-gcm", - "base64", - "hkdf", - "hmac", - "percent-encoding", - "rand", - "sha2", - "subtle", - "time", - "version_check", -] - -[[package]] -name = "cpufeatures" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" -dependencies = [ - "libc", -] - [[package]] name = "criterion" version = "0.3.6" @@ -310,16 +147,6 @@ dependencies = [ "once_cell", ] -[[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 = "csv" version = "1.1.6" @@ -342,262 +169,18 @@ dependencies = [ "memchr", ] -[[package]] -name = "ctr" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" -dependencies = [ - "cipher", -] - -[[package]] -name = "devise" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c7580b072f1c8476148f16e0a0d5dedddab787da98d86c5082c5e9ed8ab595" -dependencies = [ - "devise_codegen", - "devise_core", -] - -[[package]] -name = "devise_codegen" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123c73e7a6e51b05c75fe1a1b2f4e241399ea5740ed810b0e3e6cacd9db5e7b2" -dependencies = [ - "devise_core", - "quote", -] - -[[package]] -name = "devise_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841ef46f4787d9097405cac4e70fb8644fc037b526e8c14054247c0263c400d0" -dependencies = [ - "bitflags", - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" -dependencies = [ - "block-buffer", - "crypto-common", - "subtle", -] - [[package]] name = "either" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "fastrand" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] - -[[package]] -name = "figment" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df" -dependencies = [ - "atomic", - "pear", - "serde", - "toml", - "uncased", - "version_check", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "futures" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" - -[[package]] -name = "futures-io" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" - -[[package]] -name = "futures-sink" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" - -[[package]] -name = "futures-task" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" - -[[package]] -name = "futures-util" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generator" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc184cace1cea8335047a471cc1da80f18acf8a76f3bab2028d499e328948ec7" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows", -] - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "ghash" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "h2" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -607,130 +190,12 @@ dependencies = [ "libc", ] -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "http" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.2", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "hyper" -version = "0.14.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa 1.0.2", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "indexmap" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" -dependencies = [ - "autocfg", - "hashbrown", - "serde", -] - -[[package]] -name = "inlinable_string" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "itconfig" version = "1.1.1" dependencies = [ - "bytes", - "futures-util", - "hyper", "itconfig-macro", "lazy_static", - "pretty_env_logger", - "rocket", - "serde_json", - "tokio", ] [[package]] @@ -795,16 +260,6 @@ version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" -[[package]] -name = "lock_api" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.17" @@ -814,30 +269,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "scoped-tls", - "serde", - "serde_json", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata", -] - [[package]] name = "memchr" version = "2.5.0" @@ -853,44 +284,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "mio" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys", -] - -[[package]] -name = "multer" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a30ba6d97eb198c5e8a35d67d5779d6680cca35652a60ee90fc23dc431d4fde8" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http", - "httparse", - "log", - "memchr", - "mime", - "spin", - "tokio", - "tokio-util", - "version_check", -] - [[package]] name = "num-traits" version = "0.2.15" @@ -910,15 +303,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "once_cell" version = "1.13.0" @@ -931,76 +315,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys", -] - -[[package]] -name = "pear" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702" -dependencies = [ - "inlinable_string", - "pear_codegen", - "yansi", -] - -[[package]] -name = "pear_codegen" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0" -dependencies = [ - "proc-macro2", - "proc-macro2-diagnostics", - "quote", - "syn", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "plotters" version = "0.3.2" @@ -1029,34 +343,6 @@ dependencies = [ "plotters-backend", ] -[[package]] -name = "polyval" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "pretty_env_logger" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" -dependencies = [ - "env_logger", - "log", -] - [[package]] name = "proc-macro2" version = "1.0.40" @@ -1066,25 +352,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "proc-macro2-diagnostics" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "version_check", - "yansi", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - [[package]] name = "quote" version = "1.0.20" @@ -1094,36 +361,6 @@ 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.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom", -] - [[package]] name = "rayon" version = "1.5.3" @@ -1148,43 +385,12 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "redox_syscall" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" -dependencies = [ - "bitflags", -] - -[[package]] -name = "ref-cast" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "776c8940430cf563f66a93f9111d1cd39306dc6c68149ecc6b934742a44a828a" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f26c4704460286103bff62ea1fb78d137febc86aaf76952e6c5a2249af01f54" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "regex" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ - "aho-corasick", - "memchr", "regex-syntax", ] @@ -1193,9 +399,6 @@ name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] [[package]] name = "regex-syntax" @@ -1203,102 +406,6 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "rocket" -version = "0.5.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ead083fce4a405feb349cf09abdf64471c6077f14e0ce59364aa90d4b99317" -dependencies = [ - "async-stream", - "async-trait", - "atomic", - "atty", - "binascii", - "bytes", - "either", - "figment", - "futures", - "indexmap", - "log", - "memchr", - "multer", - "num_cpus", - "parking_lot", - "pin-project-lite", - "rand", - "ref-cast", - "rocket_codegen", - "rocket_http", - "serde", - "state", - "tempfile", - "time", - "tokio", - "tokio-stream", - "tokio-util", - "ubyte", - "version_check", - "yansi", -] - -[[package]] -name = "rocket_codegen" -version = "0.5.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47" -dependencies = [ - "devise", - "glob", - "indexmap", - "proc-macro2", - "quote", - "rocket_http", - "syn", - "unicode-xid", -] - -[[package]] -name = "rocket_http" -version = "0.5.0-rc.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ded65d127954de3c12471630bf4b81a2792f065984461e65b91d0fdaafc17a2" -dependencies = [ - "cookie", - "either", - "futures", - "http", - "hyper", - "indexmap", - "log", - "memchr", - "pear", - "percent-encoding", - "pin-project-lite", - "ref-cast", - "serde", - "smallvec", - "stable-pattern", - "state", - "time", - "tokio", - "uncased", -] - -[[package]] -name = "rustversion" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8" - [[package]] name = "ryu" version = "1.0.10" @@ -1314,12 +421,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped-tls" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" - [[package]] name = "scopeguard" version = "1.1.0" @@ -1331,9 +432,6 @@ name = "serde" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" -dependencies = [ - "serde_derive", -] [[package]] name = "serde_cbor" @@ -1367,90 +465,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha2" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" - -[[package]] -name = "socket2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" - -[[package]] -name = "stable-pattern" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4564168c00635f88eaed410d5efa8131afa8d8699a612c80c455a0ba05c21045" -dependencies = [ - "memchr", -] - -[[package]] -name = "state" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe866e1e51e8260c9eed836a042a5e7f6726bb2b411dffeaa712e19c388f23b" -dependencies = [ - "loom", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - [[package]] name = "syn" version = "1.0.98" @@ -1462,29 +476,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -1494,33 +485,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" -dependencies = [ - "itoa 1.0.2", - "libc", - "num_threads", - "time-macros", -] - -[[package]] -name = "time-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" - [[package]] name = "tinytemplate" version = "1.2.1" @@ -1531,170 +495,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "tokio" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57aec3cfa4c296db7255446efb4928a6be304b431a806216105542a67b6ca82e" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-stream" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" -dependencies = [ - "serde", -] - -[[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.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" -dependencies = [ - "ansi_term", - "matchers", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "ubyte" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a58e29f263341a29bb79e14ad7fda5f63b1c7e48929bad4c685d7876b1d04e94" -dependencies = [ - "serde", -] - -[[package]] -name = "uncased" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b01702b0fd0b3fadcf98e098780badda8742d4f4a7676615cad90e8ac73622" -dependencies = [ - "serde", - "version_check", -] - [[package]] name = "unicode-ident" version = "1.0.2" @@ -1707,34 +507,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" -[[package]] -name = "unicode-xid" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" - -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[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.3.2" @@ -1746,22 +518,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[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.81" @@ -1856,95 +612,3 @@ 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" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec" -dependencies = [ - "windows_aarch64_msvc 0.32.0", - "windows_i686_gnu 0.32.0", - "windows_i686_msvc 0.32.0", - "windows_x86_64_gnu 0.32.0", - "windows_x86_64_msvc 0.32.0", -] - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_i686_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/examples/hyper.rs b/examples/hyper.rs deleted file mode 100644 index 8be5d79..0000000 --- a/examples/hyper.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::convert::Infallible; - -use hyper::service::{make_service_fn, service_fn}; -use hyper::{Body, Request, Response, Server}; - -itconfig::config! { - hyper { - static HOST < ( - ADDR => "127.0.0.1", - ":", - PORT => 8000, - ), - } -} - -async fn hello(_: Request) -> Result, Infallible> { - Ok(Response::new(Body::from("Hello World!"))) -} - -#[tokio::main] -pub async fn main() -> Result<(), Box> { - config::init(); - pretty_env_logger::init(); - - // For every connection, we must make a `Service` to handle all - // incoming HTTP requests on said connection. - let make_svc = make_service_fn(|_conn| { - // This is the `Service` that will handle the connection. - // `service_fn` is a helper to convert a function that - // returns a Response into a `Service`. - async { Ok::<_, Infallible>(service_fn(hello)) } - }); - - let addr = config::hyper::HOST().parse()?; - - let server = Server::bind(&addr).serve(make_svc); - - println!("Listening on http://{}", addr); - - server.await?; - - Ok(()) -} diff --git a/examples/readme.md b/examples/readme.md deleted file mode 100644 index 1ed1656..0000000 --- a/examples/readme.md +++ /dev/null @@ -1,11 +0,0 @@ -# Hyper "hello world" - -```bash -cargo run --example hyper -``` - -# Rocket "hello world" - -```bash -cargo run --example roket -``` diff --git a/examples/rocket.rs b/examples/rocket.rs deleted file mode 100644 index da1898d..0000000 --- a/examples/rocket.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[macro_use] -extern crate rocket; - -itconfig::config! { - rocket { - HOST: String => "localhost", - PORT: u16 => 9000, - BASE_URL => "/", - } -} - -#[get("/")] -fn hello() -> &'static str { - "Hello, world!" -} - -#[launch] -fn rocket() -> _ { - config::init(); - - rocket::build().mount(config::rocket::BASE_URL(), routes![hello]) -} diff --git a/itconfig/Cargo.toml b/itconfig/Cargo.toml index e5ce921..b094bee 100644 --- a/itconfig/Cargo.toml +++ b/itconfig/Cargo.toml @@ -15,52 +15,19 @@ readme = "../README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["primitives"] +default = [] macro = ["itconfig-macro"] -primitives = ["numbers", "bool"] -numbers = ["int", "uint", "float"] -int = ["i8", "i16", "i32", "i64", "i128", "isize"] -uint = ["u8", "u16", "u32", "u64", "u128", "usize"] -float = ["f32", "f64"] - -i8 = [] -i16 = [] -i32 = [] -i64 = [] -i128 = [] -isize = [] - -u8 = [] -u16 = [] -u32 = [] -u64 = [] -u128 = [] -usize = [] - -f32 = [] -f64 = [] - +number = [] bool = [] - -# deprecated since 1.1 -json_array = ["serde_json"] +vec = [] [dependencies] -serde_json = { version = "1", optional = true } itconfig-macro = { version = "1.1", path = "../itconfig-macro", optional = true } [dev-dependencies] lazy_static = "1.4.0" -# required for examples -rocket = "0.5.0-rc.2" -hyper = { version = "0.14.4", features = ["full"] } -serde_json = "1.0.62" -tokio = { version = "1.2.0", features = ["macros", "rt-multi-thread"] } -bytes = "1.0.1" -futures-util = { version = "0.3.13", default-features = false } -pretty_env_logger = "0.4.0" [badges] maintenance = { status = "passively-maintained" } @@ -69,13 +36,3 @@ maintenance = { status = "passively-maintained" } [package.metadata.docs.rs] all-features = true -[[example]] -name = "hyper" -path = "../examples/hyper.rs" -required-features = ["macro"] - -[[example]] -name = "rocket" -path = "../examples/rocket.rs" -required-features = ["macro"] - diff --git a/itconfig/src/core.rs b/itconfig/src/core.rs new file mode 100644 index 0000000..c31af94 --- /dev/null +++ b/itconfig/src/core.rs @@ -0,0 +1,56 @@ +#[cfg(any(feature = "number", feature = "bool"))] +pub mod prim; +#[cfg(any(feature = "number", feature = "bool"))] +pub use prim::*; + +#[cfg(feature = "vec")] +pub mod vec; +#[cfg(feature = "vec")] +pub use vec::*; + +use std::convert::{Infallible, TryFrom}; + +/// Wrapper under String type. +/// +/// When we read the environment variable, we automatically convert the value +/// to EnvString and then convert it to your expected type. +/// +#[derive(Debug, Default, PartialEq, Eq, Clone)] +pub struct EString(String); + +impl From for EString +where + T: std::fmt::Display, +{ + #[inline] + fn from(val: T) -> Self { + Self(val.to_string()) + } +} + +impl std::ops::Deref for EString { + type Target = String; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom for String { + type Error = Infallible; + + #[inline] + fn try_from(s: EString) -> Result { + Ok(s.0) + } +} + +impl TryFrom for &'static str { + type Error = Infallible; + + #[inline] + fn try_from(s: EString) -> Result { + Ok(Box::leak(s.0.into_boxed_str())) + } +} diff --git a/itconfig/src/core/prim.rs b/itconfig/src/core/prim.rs new file mode 100644 index 0000000..d0ba318 --- /dev/null +++ b/itconfig/src/core/prim.rs @@ -0,0 +1,39 @@ +use crate::core::EString; +use std::convert::{Infallible, TryFrom}; + +#[doc(hidden)] +macro_rules! from_env_string_numbers_impl { + ($($ty:ty),+$(,)?) => { + $( + #[cfg(feature = "number")] + impl TryFrom for $ty { + type Error = <$ty as std::str::FromStr>::Err; + + #[inline] + fn try_from(s: EString) -> Result { + s.0.parse::() + } + } + )+ + }; +} + +#[rustfmt::skip] +from_env_string_numbers_impl![ + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize, + f32, f64 +]; + +#[cfg(feature = "bool")] +impl TryFrom for bool { + type Error = Infallible; + + #[inline] + fn try_from(s: EString) -> Result { + Ok(matches!( + s.to_lowercase().as_str(), + "true" | "t" | "yes" | "y" | "on" | "1" + )) + } +} diff --git a/itconfig/src/core/vec.rs b/itconfig/src/core/vec.rs new file mode 100644 index 0000000..307440c --- /dev/null +++ b/itconfig/src/core/vec.rs @@ -0,0 +1,59 @@ +use crate::core::EnvString; +use std::convert::TryFrom; +use std::fmt::Write; + +pub const COMMA: char = ','; +pub const SEMI: char = ';'; + +pub type CommaVec = SepVec; +pub type SemiVec = SepVec; + +#[derive(Debug, PartialEq, Clone)] +pub struct SepVec(pub Vec); + +impl std::ops::Deref for SepVec { + type Target = Vec; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for SepVec { + #[inline] + fn from(vec: Vec) -> Self { + Self(vec) + } +} + +impl std::fmt::Display for SepVec +where + T: std::fmt::Display, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.iter().enumerate().try_for_each(|(i, part)| { + if i != 0 { + f.write_char(SEP)?; + } + + f.write_str(&part.to_string()) + }) + } +} + +impl TryFrom for SepVec +where + T: TryFrom + std::fmt::Display, +{ + type Error = T::Error; + + fn try_from(value: EnvString) -> Result { + let inner = value + .split(SEP) + .map(EnvString::from) + .map(T::try_from) + .collect::, _>>()?; + Ok(Self(inner)) + } +} diff --git a/itconfig/src/envstr.rs b/itconfig/src/envstr.rs deleted file mode 100644 index 5908daa..0000000 --- a/itconfig/src/envstr.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::ops::Deref; - -/// Wrapper under String type. -/// -/// When we read the environment variable, we automatically convert the value -/// to EnvString and then convert it to your expected type. -/// -#[derive(Debug, Default, PartialEq, Eq, Clone)] -pub struct EnvString(String); - -impl From for EnvString -where - T: ToEnvString, -{ - fn from(val: T) -> Self { - val.to_env_string() - } -} - -impl Deref for EnvString { - type Target = String; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -/// A trait for converting value to EnvString. -/// -/// This trait automatically implemented for any type which implements the -/// [`Display`] trait. As such, `ToEnvString` shouldn't be implemented directly: -/// [`Display`] should be implemented instead, and you get the `ToEnvString` -/// implementation for free. -/// -/// [`Display`]: std::fmt::Display -pub trait ToEnvString { - /// Converts the giving value to a `EnvString`. - /// - /// # Examples - /// - /// basic usage - /// - /// ```rust - /// # use itconfig::{EnvString, ToEnvString}; - /// let i = 5; - /// let five = EnvString::from("5"); - /// assert_eq!(five, i.to_env_string()); - /// ``` - fn to_env_string(&self) -> EnvString; -} - -/// Simple and safe type conversions that may fail in a controlled way under -/// some circumstances. -/// -/// This trait automatically implemented for all standard primitives. If you -/// want to use your custom type in the library you need to implement -/// `ToEnvString` and `FromEnvString` manually. -pub trait FromEnvString: Sized { - /// The type returned in the event of a conversion error. - type Err; - - /// Performs the conversion. - fn from_env_string(s: &EnvString) -> Result; -} - -impl ToEnvString for T -where - T: std::fmt::Display, -{ - #[inline] - fn to_env_string(&self) -> EnvString { - EnvString(self.to_string()) - } -} - -#[doc(hidden)] -macro_rules! from_env_string_numbers_impl { - ($($ty:ty => $feature:expr),+) => { - $( - #[cfg(feature = $feature)] - impl FromEnvString for $ty { - type Err = <$ty as std::str::FromStr>::Err; - - #[inline] - fn from_env_string(s: &EnvString) -> Result { - s.0.parse::() - } - } - )+ - }; -} - -from_env_string_numbers_impl![ - i8 => "i8", - i16 => "i16", - i32 => "i32", - i64 => "i64", - i128 => "i128", - isize => "isize", - u8 => "u8", - u16 => "u16", - u32 => "u32", - u64 => "u64", - u128 => "u128", - usize => "usize", - f32 => "f32", - f64 => "f64" -]; - -#[cfg(feature = "bool")] -impl FromEnvString for bool { - type Err = (); - - fn from_env_string(s: &EnvString) -> Result { - match s.to_lowercase().as_str() { - "true" | "t" | "yes" | "y" | "on" | "1" => Ok(true), - _ => Ok(false), - } - } -} - -impl FromEnvString for String { - type Err = (); - - fn from_env_string(s: &EnvString) -> Result { - Ok(s.0.clone()) - } -} - -impl FromEnvString for &'static str { - type Err = (); - - fn from_env_string(s: &EnvString) -> Result { - Ok(Box::leak(s.0.clone().into_boxed_str())) - } -} - -//===========================================================================// -// DEPRECATED // -//===========================================================================// - -/// Error type for json array implementation -#[cfg(feature = "json_array")] -#[derive(Debug)] -#[deprecated(since = "1.1.0")] -pub enum ArrayEnvError { - /// Invalid type. - InvalidType, - - /// Failed to parse environment variable - FailedToParse, -} - -#[cfg(feature = "json_array")] -#[allow(deprecated)] -impl FromEnvString for Vec -where - T: FromEnvString, -{ - type Err = ArrayEnvError; - - fn from_env_string(s: &EnvString) -> Result { - serde_json::from_str::>(s.trim()) - .map(|vec| vec.iter().map(|v| v.to_string()).collect::>()) - .or_else(|_| serde_json::from_str::>(s.trim())) - .map_err(|_| ArrayEnvError::InvalidType) - .and_then(|vec| { - vec.iter() - .map(|v| { - FromEnvString::from_env_string(&v.to_env_string()) - .map_err(|_| ArrayEnvError::FailedToParse) - }) - .collect::, _>>() - }) - } -} diff --git a/itconfig/src/error.rs b/itconfig/src/error.rs index 7173c70..002554e 100644 --- a/itconfig/src/error.rs +++ b/itconfig/src/error.rs @@ -1,27 +1,47 @@ +use std::env::VarError; use std::error; +use std::ffi::OsString; use std::fmt; /// The error type for operations interacting with environment variables #[derive(Debug, PartialEq)] -pub enum EnvError { +pub enum Error { /// The specified environment variable was not present in the current process's environment. - MissingVariable(String), + NotPresent, /// Failed to parse the specified environment variable. - FailedToParse(String), + Parse(String), + + /// The specified environment variable was found, but it did not contain + /// valid unicode data. The found data is returned as a payload of this + /// variant. + Invalid(OsString), } -impl fmt::Display for EnvError { +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use Error::*; match &self { - EnvError::MissingVariable(env_name) => { - write!(f, r#"Environment variable "{}" is missing"#, env_name) - } - EnvError::FailedToParse(env_name) => { + NotPresent => f.write_str("The specified env variable was not present"), + Invalid(inner) => write!( + f, + "The specified env variable was found, but it did not valid: '{:?}'", + inner, + ), + Parse(env_name) => { write!(f, r#"Failed to parse environment variable "{}""#, env_name) } } } } -impl error::Error for EnvError {} +impl error::Error for Error {} + +impl From for Error { + fn from(err: VarError) -> Self { + match err { + VarError::NotPresent => Error::NotPresent, + VarError::NotUnicode(inner) => Error::Invalid(inner), + } + } +} diff --git a/itconfig/src/get_env.rs b/itconfig/src/get_env.rs deleted file mode 100644 index 5cb3307..0000000 --- a/itconfig/src/get_env.rs +++ /dev/null @@ -1,240 +0,0 @@ -use crate::envstr::*; -use crate::error::*; -use crate::utils::*; -use std::env; - -/// Same as get_env but returns Option enum instead Result -/// -/// Example -/// ------- -/// -/// ```rust -/// # extern crate itconfig; -/// # use itconfig::maybe_get_env; -/// use std::env; -/// -/// fn main () { -/// env::set_var("HOST", "https://example.com"); -/// -/// let host: Option<&'static str> = maybe_get_env("HOST"); -/// let not_existence_host: Option<&'static str> = maybe_get_env("NOT_EXISTENCE_HOST"); -/// -/// assert_eq!(host, Some("https://example.com")); -/// assert_eq!(not_existence_host, None); -/// } -/// ``` -/// -pub fn maybe_get_env(env_name: &str) -> Option -where - T: FromEnvString, -{ - get_env(env_name).ok() -} - -/// This function is similar as `get_env`, but it unwraps result with panic on error. -/// -/// Panics -/// ------ -/// Application will panic if environment variable is missing or cannot parse variable to -/// expected type -/// -pub fn get_env_or_panic(env_name: &str) -> T -where - T: FromEnvString, -{ - get_env(env_name).unwrap_or_else(make_panic) -} - -/// Try to read environment variable and parse to expected type. You may to put to argument -/// any type with `FromEnvString` trait. -/// -/// Example -/// ------- -/// -/// ```rust -/// # extern crate itconfig; -/// # use itconfig::get_env; -/// use std::env; -/// -/// fn main () { -/// env::set_var("DEBUG", "true"); -/// -/// let result: bool = get_env("DEBUG").unwrap(); -/// -/// assert_eq!(result, true); -/// } -/// ``` -/// -pub fn get_env(env_name: &str) -> Result -where - T: FromEnvString, -{ - get_env_or(env_name, |_| { - Err(EnvError::MissingVariable(env_name.to_string())) - }) -} - -/// This function is similar as `get_env_or_panic`, but you can pass default value for -/// environment variable with `ToEnvString` trait. -/// -/// Panics -/// ------ -/// Application will panic if cannot parse variable to expected type -/// -/// Example -/// ------- -/// -/// ```rust -/// # extern crate itconfig; -/// # use itconfig::get_env_or_default; -/// use std::env; -/// -/// fn main () { -/// let result: bool = get_env_or_default("TESTING", "true"); -/// assert_eq!(result, true); -/// } -/// ``` -/// -pub fn get_env_or_default(env_name: &str, default: D) -> T -where - T: FromEnvString, - D: ToEnvString, -{ - get_env_or(env_name, |_| Ok(default.to_env_string())).unwrap_or_else(make_panic) -} - -/// This function is similar as `get_env_or_default`, but the default value will be set to environment -/// variable, if env variable is missed. -/// -/// Panics -/// ------ -/// Application will panic if cannot parse variable to expected type -/// -/// Example -/// ------- -/// -/// ```rust -/// # extern crate itconfig; -/// # use itconfig::get_env_or_set_default; -/// use std::env; -/// -/// fn main () { -/// let result: bool = get_env_or_set_default("TESTING", "true"); -/// assert_eq!(result, true); -/// -/// let var = env::var("TESTING").unwrap(); -/// assert_eq!(var, "true"); -/// } -/// ``` -/// -pub fn get_env_or_set_default(env_name: &str, default: D) -> T -where - T: FromEnvString, - D: ToEnvString, -{ - get_env_or(env_name, |_| { - let val = default.to_env_string(); - env::set_var(env_name, val.as_str()); - Ok(val) - }) - .unwrap_or_else(make_panic) -} - -/// This function returns env variable as `EnvString` structure. You can pass callback for custom -/// default expression. Callback should return `EnvString` value or `EnvError` -pub fn get_env_or(env_name: &str, cb: F) -> Result -where - T: FromEnvString, - F: FnOnce(env::VarError) -> Result, -{ - env::var(env_name) - .map(|s| s.to_env_string()) - .or_else(cb) - .and_then(|env_str| parse_env_variable(env_name, env_str)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[should_panic(expected = "Environment variable \"TEST_CASE_1\" is missing")] - fn get_missing_env() { - get_env_or_panic::("TEST_CASE_1"); - } - - #[test] - #[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_2\"")] - fn get_env_with_invalid_value() { - let env_name = "TEST_CASE_2"; - env::set_var(&env_name, "30r"); - get_env_or_panic::(env_name); - } - - #[test] - fn get_result_of_missing_env() { - let env_name = String::from("TEST_CASE_3"); - let env_val = get_env::(&env_name); - assert_eq!(env_val, Err(EnvError::MissingVariable(env_name))) - } - - #[test] - fn get_result_of_env_with_invalid_value() { - let env_name = String::from("TEST_CASE_4"); - env::set_var(&env_name, "30r"); - let env_val = get_env::(&env_name); - assert_eq!(env_val, Err(EnvError::FailedToParse(env_name))) - } - - #[test] - fn get_result_of_env_successfully() { - env::set_var("TEST_CASE_5", "30"); - let env_var = get_env("TEST_CASE_5"); - assert_eq!(env_var, Ok(30)); - } - - #[test] - fn get_missing_env_with_default_value() { - let flag: bool = get_env_or_default("TEST_CASE_6", "true"); - assert!(flag); - } - - #[test] - #[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_7\"")] - fn get_invalid_env_with_default_value() { - env::set_var("TEST_CASE_7", "30r"); - get_env_or_default::("TEST_CASE_7", 30); - } - - #[test] - #[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_8\"")] - fn get_env_with_invalid_default_value() { - get_env_or_default::("TEST_CASE_8", "30r"); - } - - #[test] - fn get_env_with_default_successfully() { - env::set_var("TEST_CASE_9", "10"); - let env_val: u32 = get_env_or_default("TEST_CASE_9", 30); - assert_eq!(env_val, 10) - } - - #[test] - fn get_missing_env_with_set_default_value() { - let flag: bool = get_env_or_set_default("TEST_CASE_10", "true"); - assert!(flag); - - let env_var = env::var("TEST_CASE_10"); - assert_eq!(env_var, Ok(String::from("true"))) - } - - #[test] - fn get_optional_env() { - env::set_var("TEST_CASE_11", "something"); - let something: Option<&'static str> = maybe_get_env("TEST_CASE_11"); - assert_eq!(something, Some("something")); - - let nothing: Option<&'static str> = maybe_get_env("TEST_CASE_11_NONE"); - assert_eq!(nothing, None); - } -} diff --git a/itconfig/src/get_vec_env.rs b/itconfig/src/get_vec_env.rs deleted file mode 100644 index 9096fc5..0000000 --- a/itconfig/src/get_vec_env.rs +++ /dev/null @@ -1,304 +0,0 @@ -use crate::envstr::*; -use crate::error::*; -use crate::utils::*; -use std::env; - -/// Same as get_vec_env but returns Option enum instead Result -/// -/// Example -/// ------- -/// -/// ```rust -/// # extern crate itconfig; -/// # use itconfig::*; -/// use std::env; -/// -/// #[derive(Debug, PartialEq, Eq)] -/// enum PaymentPlatform { -/// PayPal, -/// Stripe, -/// SomethingElse, -/// } -/// -/// impl FromEnvString for PaymentPlatform { -/// type Err = &'static str; -/// -/// fn from_env_string(envstr: &EnvString) -> Result { -/// match envstr.to_lowercase().as_str() { -/// "paypal" => Ok(Self::PayPal), -/// "stripe" => Ok(Self::Stripe), -/// "smth" => Ok(Self::SomethingElse), -/// _ => Err("Unsupported payment platform"), -/// } -/// } -/// } -/// -/// -/// fn main () { -/// env::set_var("PAYMENT_PLATFORMS", "paypal,stripe"); -/// -/// let payment_platforms: Option> = maybe_get_vec_env("PAYMENT_PLATFORMS", ","); -/// assert_eq!( -/// payment_platforms, -/// Some(vec![PaymentPlatform::PayPal, PaymentPlatform::Stripe]) -/// ); -/// } -/// ``` -/// -pub fn maybe_get_vec_env(env_name: &str, sep: &'static str) -> Option> -where - T: FromEnvString, -{ - get_vec_env(env_name, sep).ok() -} - -/// This function is similar as `get_vec_env`, but it unwraps result with panic on error. -/// -/// Panics -/// ------ -/// Application will panic if environment variable is missing or cannot parse variable to -/// expected type -/// -pub fn get_vec_env_or_panic(env_name: &str, sep: &'static str) -> Vec -where - T: FromEnvString, -{ - get_vec_env(env_name, sep).unwrap_or_else(make_panic) -} - -/// Try to read environment variable, split by separator and parse each item to expected -/// type. -/// -/// Example -/// ------- -/// -/// ```rust -/// # extern crate itconfig; -/// # use itconfig::get_vec_env; -/// use std::env; -/// -/// fn main () { -/// env::set_var("DEBUG", "true"); -/// -/// let result: Vec = get_vec_env("DEBUG", ",").unwrap(); -/// -/// assert_eq!(result, vec![true]); -/// } -/// ``` -/// -pub fn get_vec_env(env_name: &str, sep: &str) -> Result, EnvError> -where - T: FromEnvString, -{ - get_vec_env_or(env_name, sep, |_| { - Err(EnvError::MissingVariable(env_name.to_string())) - }) -} - -/// This function is similar as `get_vec_env_or_panic`, but you can pass default value for -/// environment variable with `ToEnvString` trait. -/// -/// Panics -/// ------ -/// Application will panic if cannot parse variable to expected type -/// -/// Example -/// ------- -/// -/// ```rust -/// # extern crate itconfig; -/// # use itconfig::get_vec_env_or_default; -/// use std::env; -/// -/// fn main () { -/// let result: Vec = get_vec_env_or_default("TESTING", ",", vec!["true"]); -/// assert_eq!(result, vec![true]); -/// } -/// ``` -/// -pub fn get_vec_env_or_default(env_name: &str, sep: &str, default: Vec) -> Vec -where - T: FromEnvString, - D: ToEnvString, -{ - get_vec_env_or(env_name, sep, |_| Ok(vec_to_env_strings(default))).unwrap_or_else(make_panic) -} - -/// This function is similar as `get_vec_env_or_default`, but the default value will be set to environment -/// variable, if env variable is missed. -/// -/// Panics -/// ------ -/// Application will panic if cannot parse variable to expected type -/// -/// Example -/// ------- -/// -/// ```rust -/// # extern crate itconfig; -/// # use itconfig::get_vec_env_or_set_default; -/// use std::env; -/// -/// fn main () { -/// let result: Vec = get_vec_env_or_set_default("TESTING", ",", vec!["true"]); -/// assert_eq!(result, vec![true]); -/// -/// let var = env::var("TESTING").unwrap(); -/// assert_eq!(var, "true"); -/// } -/// ``` -/// -pub fn get_vec_env_or_set_default(env_name: &str, sep: &str, default: Vec) -> Vec -where - T: FromEnvString, - D: ToEnvString, -{ - get_vec_env_or(env_name, sep, |_| { - let default_env_strings = vec_to_env_strings(default); - let env_val = join(&default_env_strings, sep); - env::set_var(env_name, env_val.as_str()); - Ok(default_env_strings) - }) - .unwrap_or_else(make_panic) -} - -/// This function returns env variable as `EnvString` structure. You can pass callback for custom -/// default expression. Callback should return `EnvString` value or `EnvError` -pub fn get_vec_env_or(env_name: &str, sep: &str, cb: F) -> Result, EnvError> -where - T: FromEnvString, - F: FnOnce(env::VarError) -> Result, EnvError>, -{ - env::var(env_name) - .map(|s| { - s.split(sep) - .into_iter() - .map(|item| item.to_env_string()) - .collect() - }) - .or_else(cb) - .and_then(|items| { - items - .into_iter() - .map(|env_str| parse_env_variable(env_name, env_str)) - .collect() - }) -} - -#[cfg(test)] -mod tests { - use super::*; - - const SEP: &str = ","; - - #[test] - #[should_panic(expected = "Environment variable \"TEST_CASE_VEC_1\" is missing")] - fn get_missing_vec_env() { - let _: Vec<&'static str> = get_vec_env_or_panic("TEST_CASE_VEC_1", SEP); - } - - #[test] - #[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_VEC_2\"")] - fn get_vec_env_with_invalid_value() { - let env_name = "TEST_CASE_VEC_2"; - env::set_var(&env_name, "30r"); - let _: Vec = get_vec_env_or_panic(env_name, SEP); - } - - #[test] - fn get_result_of_missing_vec_env() { - let env_name = String::from("TEST_CASE_VEC_3"); - let env_val = get_vec_env::(&env_name, SEP); - assert_eq!(env_val, Err(EnvError::MissingVariable(env_name))) - } - - #[test] - fn get_result_of_vec_env_with_invalid_value() { - let env_name = String::from("TEST_CASE_VEC_4"); - env::set_var(&env_name, "30r"); - let env_val = get_vec_env::(&env_name, SEP); - assert_eq!(env_val, Err(EnvError::FailedToParse(env_name))) - } - - #[test] - fn get_result_of_vec_env_successfully() { - env::set_var("TEST_CASE_VEC_5", "30"); - let env_var = get_vec_env("TEST_CASE_VEC_5", SEP); - assert_eq!(env_var, Ok(vec![30])); - } - - #[test] - fn get_missing_vec_env_with_default_value() { - let flag: Vec = get_vec_env_or_default("TEST_CASE_VEC_6", SEP, vec!["true"]); - assert_eq!(flag, vec![true]); - } - - #[test] - #[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_VEC_7\"")] - fn get_invalid_vec_env_with_default_value() { - env::set_var("TEST_CASE_VEC_7", "30r"); - get_vec_env_or_default::("TEST_CASE_VEC_7", SEP, vec![30]); - } - - #[test] - #[should_panic(expected = "Failed to parse environment variable \"TEST_CASE_VEC_8\"")] - fn get_vec_env_with_invalid_default_value() { - get_vec_env_or_default::("TEST_CASE_VEC_8", SEP, vec!["30r"]); - } - - #[test] - fn get_vec_env_with_default_successfully() { - env::set_var("TEST_CASE_VEC_9", "10"); - let env_val: Vec = get_vec_env_or_default("TEST_CASE_VEC_9", SEP, vec![30]); - assert_eq!(env_val, vec![10]) - } - - #[test] - fn get_missing_vec_env_with_set_default_value() { - let flag: Vec = get_vec_env_or_set_default("TEST_CASE_VEC_10", SEP, vec!["true"]); - assert_eq!(flag, vec![true]); - - let env_var = env::var("TEST_CASE_VEC_10"); - assert_eq!(env_var, Ok(String::from("true"))) - } - - #[test] - fn get_optional_vec_env() { - env::set_var("TEST_CASE_VEC_11", "something"); - let something: Option> = maybe_get_vec_env("TEST_CASE_VEC_11", SEP); - assert_eq!(something, Some(vec!["something"])); - - let nothing: Option> = maybe_get_vec_env("TEST_CASE_VEC_11_NONE", SEP); - assert_eq!(nothing, None); - } - - #[test] - fn get_custom_type_from_vec_env() { - #[derive(Debug, PartialEq, Eq)] - enum PaymentPlatform { - PayPal, - Stripe, - SomethingElse, - } - - impl FromEnvString for PaymentPlatform { - type Err = &'static str; - - fn from_env_string(envstr: &EnvString) -> Result { - match envstr.to_lowercase().as_str() { - "paypal" => Ok(Self::PayPal), - "stripe" => Ok(Self::Stripe), - "smth" => Ok(Self::SomethingElse), - _ => Err("Unsupported payment platform"), - } - } - } - - env::set_var("TEST_CASE_VEC_12", "paypal,stripe"); - let something: Option> = maybe_get_vec_env("TEST_CASE_VEC_12", SEP); - assert_eq!( - something, - Some(vec![PaymentPlatform::PayPal, PaymentPlatform::Stripe]) - ); - } -} diff --git a/itconfig/src/lib.rs b/itconfig/src/lib.rs index a5ef282..4182a44 100644 --- a/itconfig/src/lib.rs +++ b/itconfig/src/lib.rs @@ -137,7 +137,7 @@ #![forbid(non_ascii_idents)] #![deny( missing_debug_implementations, - missing_docs, + // missing_docs, unstable_features, unused_imports, unused_qualifications @@ -148,16 +148,13 @@ ///////////////////////////////////////////////////////////////////////////// -mod envstr; +pub mod core; mod error; -mod get_env; -mod get_vec_env; -pub(crate) mod utils; +mod utils; -pub use self::envstr::*; +pub use self::core::*; pub use self::error::*; -pub use self::get_env::*; -pub use self::get_vec_env::*; +pub use self::utils::*; #[cfg(feature = "macro")] extern crate itconfig_macro; diff --git a/itconfig/src/utils.rs b/itconfig/src/utils.rs index 6ae25ce..6f33da4 100644 --- a/itconfig/src/utils.rs +++ b/itconfig/src/utils.rs @@ -1,33 +1,116 @@ -use crate::{EnvError, EnvString, FromEnvString, ToEnvString}; +use crate::core::EString; +use crate::error::Error; +use std::convert::{TryFrom, TryInto}; +use std::env; -pub(crate) fn parse_env_variable(env_name: &str, env_str: EnvString) -> Result +pub fn get_env_or_set_default(env_name: &str, default: R) -> Result where - T: FromEnvString, + R: TryFrom + std::fmt::Display, { - FromEnvString::from_env_string(&env_str) - .map_err(|_| EnvError::FailedToParse(env_name.to_string())) + get_env::(env_name).or_else(|err| match err { + Error::NotPresent => { + let val = default.to_string(); + env::set_var(env_name, &val); + EString::from(val) + .try_into() + .map_err(|_| Error::Parse(default.to_string())) + } + _ => Err(err), + }) } -pub(crate) fn make_panic(e: EnvError) -> T { - panic!("{}", e) -} - -pub(crate) fn join(env_strings: &[EnvString], sep: &str) -> String { - env_strings - .iter() - .enumerate() - .fold(String::new(), |mut res, (i, item)| { - if i > 0 { - res.push_str(sep); - } - res.push_str(item); - res +pub fn get_env(env_name: &str) -> Result +where + R: TryFrom, +{ + env::var(env_name) + .map_err(From::from) + .map(EString::from) + .and_then(|val| { + val.clone() + .try_into() + .map_err(|_| Error::Parse(val.to_string())) }) } -pub(crate) fn vec_to_env_strings(values: Vec) -> Vec -where - T: ToEnvString, -{ - values.into_iter().map(EnvString::from).collect() +#[cfg(test)] +mod tests { + use super::*; + #[cfg(feature = "vec")] + use crate::core::vec::{CommaVec, SepVec}; + + #[test] + fn should_return_variable() { + env::set_var("get_env_1", "hello"); + match get_env::<&str>("get_env_1") { + Ok(res) => assert_eq!(res, "hello"), + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_no_present_error() { + match get_env::<&str>("get_env_2") { + Err(Error::NotPresent) => {} + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_parse_error() { + env::set_var("get_env_3", "-10"); + match get_env::("get_env_3") { + Err(Error::Parse(orig)) => { + assert_eq!(orig, String::from("-10")) + } + _ => unreachable!(), + }; + } + + #[test] + fn should_set_default_if_var_is_no_present() { + let orig = 10; + match get_env_or_set_default("get_env_4", orig) { + Ok(res) => { + assert_eq!(res, orig); + assert_eq!(env::var("get_env_4").unwrap(), "10"); + } + _ => unreachable!(), + }; + } + + #[cfg(feature = "vec")] + #[test] + fn should_return_var_as_vector() { + env::set_var("get_env_5", "1,2,3,4,5"); + match get_env::>("get_env_5") { + Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), + _ => unreachable!(), + }; + } + + #[cfg(feature = "vec")] + #[test] + fn should_throw_parse_vec_error() { + env::set_var("get_env_6", "1,2,3,4,5"); + match get_env::>("get_env_6") { + Err(Error::Parse(orig)) => { + assert_eq!(orig, String::from("1,2,3,4,5")) + } + _ => unreachable!(), + }; + } + + #[cfg(feature = "vec")] + #[test] + fn should_set_default_vector_if_var_is_no_present() { + let orig = CommaVec::from(vec![1, 2, 3, 4]); + match get_env_or_set_default("get_env_7", orig.clone()) { + Ok(res) => { + assert_eq!(res, orig); + assert_eq!(env::var("get_env_7").unwrap(), "1,2,3,4"); + } + _ => unreachable!(), + }; + } } From 9448c2633709b7f280281f4dbd7905255d41115c Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 22 Jul 2022 14:10:44 +0300 Subject: [PATCH 02/10] chore: change manifests --- itconfig-macro/Cargo.toml | 7 +++---- itconfig/Cargo.toml | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/itconfig-macro/Cargo.toml b/itconfig-macro/Cargo.toml index ecd3b13..45c81ba 100644 --- a/itconfig-macro/Cargo.toml +++ b/itconfig-macro/Cargo.toml @@ -7,8 +7,8 @@ categories = ["config", "web-programming"] keywords = ["config", "env", "configuration", "environment", "macro"] edition = "2018" license = "MIT" -repository = "https://github.com/icetemple/itconfig-rs" -homepage = "https://github.com/icetemple/itconfig-rs" +repository = "https://github.com/pleshevskiy/itconfig-rs" +homepage = "https://github.com/pleshevskiy/itconfig-rs" documentation = "https://docs.rs/itconfig" readme = "../README.md" @@ -26,5 +26,4 @@ itconfig = { path = "../itconfig" } lazy_static = "1.4.0" [badges] -travis-ci = { repository = "icetemple/itconfig-rs" } -maintenance = { status = "passively-maintained" } +maintenance = { status = "actively-developed" } diff --git a/itconfig/Cargo.toml b/itconfig/Cargo.toml index b094bee..5a408eb 100644 --- a/itconfig/Cargo.toml +++ b/itconfig/Cargo.toml @@ -7,8 +7,8 @@ categories = ["config", "web-programming"] keywords = ["config", "env", "configuration", "environment", "macro"] edition = "2018" license = "MIT" -repository = "https://github.com/icetemple/itconfig-rs" -homepage = "https://github.com/icetemple/itconfig-rs" +repository = "https://github.com/pleshevskiy/itconfig-rs" +homepage = "https://github.com/pleshevskiy/itconfig-rs" documentation = "https://docs.rs/itconfig" readme = "../README.md" @@ -30,7 +30,7 @@ itconfig-macro = { version = "1.1", path = "../itconfig-macro", optional = true lazy_static = "1.4.0" [badges] -maintenance = { status = "passively-maintained" } +maintenance = { status = "actively-developed" } # https://docs.rs/about [package.metadata.docs.rs] From bc7ab940b0c9e73b8f1064da541ea5016a141e02 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 22 Jul 2022 14:53:45 +0300 Subject: [PATCH 03/10] refac: change structure - core/vec: rename envstring to estring - tests: improve test cases + created helper TestCase + move tests that required a feature to a separate module --- Cargo.lock | 10 +- Cargo.toml | 41 +++- .../benches => benches}/main_benches.rs | 0 itconfig-macro/Cargo.toml | 2 +- itconfig-tests/Cargo.toml | 22 --- itconfig/Cargo.toml | 38 ---- itconfig/src/utils.rs | 116 ----------- {itconfig/src => src}/core.rs | 0 {itconfig/src => src}/core/prim.rs | 0 {itconfig/src => src}/core/vec.rs | 10 +- {itconfig/src => src}/error.rs | 0 {itconfig/src => src}/lib.rs | 0 src/utils.rs | 182 ++++++++++++++++++ .../tests => tests}/config_macro.rs | 0 14 files changed, 228 insertions(+), 193 deletions(-) rename {itconfig-tests/benches => benches}/main_benches.rs (100%) delete mode 100644 itconfig-tests/Cargo.toml delete mode 100644 itconfig/Cargo.toml delete mode 100644 itconfig/src/utils.rs rename {itconfig/src => src}/core.rs (100%) rename {itconfig/src => src}/core/prim.rs (100%) rename {itconfig/src => src}/core/vec.rs (82%) rename {itconfig/src => src}/error.rs (100%) rename {itconfig/src => src}/lib.rs (100%) create mode 100644 src/utils.rs rename {itconfig-tests/tests => tests}/config_macro.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index e5e135a..664d82e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -194,6 +194,7 @@ dependencies = [ name = "itconfig" version = "1.1.1" dependencies = [ + "criterion", "itconfig-macro", "lazy_static", ] @@ -209,15 +210,6 @@ dependencies = [ "syn", ] -[[package]] -name = "itconfig_tests" -version = "0.1.0" -dependencies = [ - "criterion", - "itconfig", - "lazy_static", -] - [[package]] name = "itertools" version = "0.10.3" diff --git a/Cargo.toml b/Cargo.toml index c9ef965..42e7df4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,44 @@ [workspace] members = [ - "itconfig", "itconfig-macro", - "itconfig-tests", ] +[package] +name = "itconfig" +version = "1.1.1" +authors = ["Dmitriy Pleshevskiy "] +description = "Easy build a configs from environment variables and use it in globally." +categories = ["config", "web-programming"] +keywords = ["config", "env", "configuration", "environment", "macro"] +edition = "2018" +license = "MIT" +repository = "https://github.com/pleshevskiy/itconfig-rs" +homepage = "https://github.com/pleshevskiy/itconfig-rs" +documentation = "https://docs.rs/itconfig" +readme = "../README.md" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = [] + +macro = ["itconfig-macro"] + +number = [] +bool = [] +vec = [] + +[dependencies] +itconfig-macro = { version = "1.1", path = "itconfig-macro", optional = true } + +[dev-dependencies] +lazy_static = "1.4.0" +criterion = "0.3.1" + +[badges] +maintenance = { status = "actively-developed" } + +# https://docs.rs/about +[package.metadata.docs.rs] +all-features = true + diff --git a/itconfig-tests/benches/main_benches.rs b/benches/main_benches.rs similarity index 100% rename from itconfig-tests/benches/main_benches.rs rename to benches/main_benches.rs diff --git a/itconfig-macro/Cargo.toml b/itconfig-macro/Cargo.toml index 45c81ba..4d88505 100644 --- a/itconfig-macro/Cargo.toml +++ b/itconfig-macro/Cargo.toml @@ -22,7 +22,7 @@ quote = "1.0.9" proc-macro2 = "1.0.24" [dev-dependencies] -itconfig = { path = "../itconfig" } +itconfig = { path = ".." } lazy_static = "1.4.0" [badges] diff --git a/itconfig-tests/Cargo.toml b/itconfig-tests/Cargo.toml deleted file mode 100644 index 7a72838..0000000 --- a/itconfig-tests/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "itconfig_tests" -version = "0.1.0" -authors = ["Dmitriy Pleshevskiy "] -edition = "2018" -license = "MIT" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -itconfig = { path = '../itconfig', features = ["macro"] } -criterion = "0.3.1" -lazy_static = "1.4.0" - -[features] -default = ["meta_namespace"] -meta_namespace = [] - -[[bench]] -name = "main_benches" -harness = false diff --git a/itconfig/Cargo.toml b/itconfig/Cargo.toml deleted file mode 100644 index 5a408eb..0000000 --- a/itconfig/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "itconfig" -version = "1.1.1" -authors = ["Dmitriy Pleshevskiy "] -description = "Easy build a configs from environment variables and use it in globally." -categories = ["config", "web-programming"] -keywords = ["config", "env", "configuration", "environment", "macro"] -edition = "2018" -license = "MIT" -repository = "https://github.com/pleshevskiy/itconfig-rs" -homepage = "https://github.com/pleshevskiy/itconfig-rs" -documentation = "https://docs.rs/itconfig" -readme = "../README.md" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[features] -default = [] - -macro = ["itconfig-macro"] - -number = [] -bool = [] -vec = [] - -[dependencies] -itconfig-macro = { version = "1.1", path = "../itconfig-macro", optional = true } - -[dev-dependencies] -lazy_static = "1.4.0" - -[badges] -maintenance = { status = "actively-developed" } - -# https://docs.rs/about -[package.metadata.docs.rs] -all-features = true - diff --git a/itconfig/src/utils.rs b/itconfig/src/utils.rs deleted file mode 100644 index 6f33da4..0000000 --- a/itconfig/src/utils.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::core::EString; -use crate::error::Error; -use std::convert::{TryFrom, TryInto}; -use std::env; - -pub fn get_env_or_set_default(env_name: &str, default: R) -> Result -where - R: TryFrom + std::fmt::Display, -{ - get_env::(env_name).or_else(|err| match err { - Error::NotPresent => { - let val = default.to_string(); - env::set_var(env_name, &val); - EString::from(val) - .try_into() - .map_err(|_| Error::Parse(default.to_string())) - } - _ => Err(err), - }) -} - -pub fn get_env(env_name: &str) -> Result -where - R: TryFrom, -{ - env::var(env_name) - .map_err(From::from) - .map(EString::from) - .and_then(|val| { - val.clone() - .try_into() - .map_err(|_| Error::Parse(val.to_string())) - }) -} - -#[cfg(test)] -mod tests { - use super::*; - #[cfg(feature = "vec")] - use crate::core::vec::{CommaVec, SepVec}; - - #[test] - fn should_return_variable() { - env::set_var("get_env_1", "hello"); - match get_env::<&str>("get_env_1") { - Ok(res) => assert_eq!(res, "hello"), - _ => unreachable!(), - }; - } - - #[test] - fn should_throw_no_present_error() { - match get_env::<&str>("get_env_2") { - Err(Error::NotPresent) => {} - _ => unreachable!(), - }; - } - - #[test] - fn should_throw_parse_error() { - env::set_var("get_env_3", "-10"); - match get_env::("get_env_3") { - Err(Error::Parse(orig)) => { - assert_eq!(orig, String::from("-10")) - } - _ => unreachable!(), - }; - } - - #[test] - fn should_set_default_if_var_is_no_present() { - let orig = 10; - match get_env_or_set_default("get_env_4", orig) { - Ok(res) => { - assert_eq!(res, orig); - assert_eq!(env::var("get_env_4").unwrap(), "10"); - } - _ => unreachable!(), - }; - } - - #[cfg(feature = "vec")] - #[test] - fn should_return_var_as_vector() { - env::set_var("get_env_5", "1,2,3,4,5"); - match get_env::>("get_env_5") { - Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), - _ => unreachable!(), - }; - } - - #[cfg(feature = "vec")] - #[test] - fn should_throw_parse_vec_error() { - env::set_var("get_env_6", "1,2,3,4,5"); - match get_env::>("get_env_6") { - Err(Error::Parse(orig)) => { - assert_eq!(orig, String::from("1,2,3,4,5")) - } - _ => unreachable!(), - }; - } - - #[cfg(feature = "vec")] - #[test] - fn should_set_default_vector_if_var_is_no_present() { - let orig = CommaVec::from(vec![1, 2, 3, 4]); - match get_env_or_set_default("get_env_7", orig.clone()) { - Ok(res) => { - assert_eq!(res, orig); - assert_eq!(env::var("get_env_7").unwrap(), "1,2,3,4"); - } - _ => unreachable!(), - }; - } -} diff --git a/itconfig/src/core.rs b/src/core.rs similarity index 100% rename from itconfig/src/core.rs rename to src/core.rs diff --git a/itconfig/src/core/prim.rs b/src/core/prim.rs similarity index 100% rename from itconfig/src/core/prim.rs rename to src/core/prim.rs diff --git a/itconfig/src/core/vec.rs b/src/core/vec.rs similarity index 82% rename from itconfig/src/core/vec.rs rename to src/core/vec.rs index 307440c..0857285 100644 --- a/itconfig/src/core/vec.rs +++ b/src/core/vec.rs @@ -1,4 +1,4 @@ -use crate::core::EnvString; +use crate::core::EString; use std::convert::TryFrom; use std::fmt::Write; @@ -42,16 +42,16 @@ where } } -impl TryFrom for SepVec +impl TryFrom for SepVec where - T: TryFrom + std::fmt::Display, + T: TryFrom + std::fmt::Display, { type Error = T::Error; - fn try_from(value: EnvString) -> Result { + fn try_from(value: EString) -> Result { let inner = value .split(SEP) - .map(EnvString::from) + .map(EString::from) .map(T::try_from) .collect::, _>>()?; Ok(Self(inner)) diff --git a/itconfig/src/error.rs b/src/error.rs similarity index 100% rename from itconfig/src/error.rs rename to src/error.rs diff --git a/itconfig/src/lib.rs b/src/lib.rs similarity index 100% rename from itconfig/src/lib.rs rename to src/lib.rs diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..fa1ed61 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,182 @@ +use crate::core::EString; +use crate::error::Error; +use std::convert::{TryFrom, TryInto}; +use std::env; + +pub fn get_env_or_set_default(env_name: &str, default: R) -> Result +where + R: TryFrom + std::fmt::Display, +{ + get_env::(env_name).or_else(|err| match err { + Error::NotPresent => { + let val = default.to_string(); + env::set_var(env_name, &val); + EString::from(val) + .try_into() + .map_err(|_| Error::Parse(default.to_string())) + } + _ => Err(err), + }) +} + +pub fn get_env(env_name: &str) -> Result +where + R: TryFrom, +{ + env::var(env_name) + .map_err(From::from) + .map(EString::from) + .and_then(|val| { + val.clone() + .try_into() + .map_err(|_| Error::Parse(val.to_string())) + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + struct TestCase; + + impl std::fmt::Display for TestCase { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "get_env_{}", N) + } + } + + #[test] + fn should_return_variable() { + let en = TestCase::<1>.to_string(); + env::set_var(&en, "hello"); + match get_env::<&str>(&en) { + Ok(res) => assert_eq!(res, "hello"), + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_no_present_error() { + let en = TestCase::<2>.to_string(); + match get_env::<&str>(&en) { + Err(Error::NotPresent) => {} + _ => unreachable!(), + }; + } + + #[test] + fn should_set_default_if_var_is_no_present() { + let en = TestCase::<3>.to_string(); + let orig = "hello"; + match get_env_or_set_default(&en, orig) { + Ok(res) => { + assert_eq!(res, orig); + assert_eq!(env::var(&en).unwrap(), orig); + } + _ => unreachable!(), + }; + } + + #[cfg(feature = "number")] + mod numbers { + use super::*; + + #[test] + fn should_throw_parse_error() { + let en = TestCase::<4>.to_string(); + env::set_var(&en, "-10"); + match get_env::(&en) { + Err(Error::Parse(orig)) => { + assert_eq!(orig, String::from("-10")) + } + _ => unreachable!(), + }; + } + + #[test] + fn should_set_default_num_if_var_is_no_present() { + let en = TestCase::<5>.to_string(); + let orig = 10; + match get_env_or_set_default(&en, orig) { + Ok(res) => { + assert_eq!(res, orig); + assert_eq!(env::var(&en).unwrap(), "10"); + } + _ => unreachable!(), + }; + } + } + + #[cfg(feature = "bool")] + mod boolean { + use super::*; + + #[test] + fn should_parse_bool_variable() { + let en = TestCase::<5>.to_string(); + + [ + ("y", true), + ("yes", true), + ("true", true), + ("t", true), + ("on", true), + ("false", false), + ("f", false), + ] + .iter() + .for_each(|(val, expected)| { + let mut en = en.clone(); + en.push_str(val.as_ref()); + + env::set_var(&en, val); + match get_env::(&en) { + Ok(res) => assert_eq!(res, *expected), + _ => unreachable!(), + }; + }) + } + } + + #[cfg(feature = "vec")] + mod vector { + use super::*; + use crate::core::vec::{CommaVec, SepVec}; + + #[test] + fn should_return_var_as_vector() { + let en = TestCase::<6>.to_string(); + + env::set_var(&en, "1,2,3,4,5"); + match get_env::>(&en) { + Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_parse_vec_error() { + let en = TestCase::<7>.to_string(); + env::set_var(&en, "1,2,3,4,5"); + match get_env::>(&en) { + Err(Error::Parse(orig)) => { + assert_eq!(orig, String::from("1,2,3,4,5")) + } + _ => unreachable!(), + }; + } + + #[test] + fn should_set_default_vector_if_var_is_no_present() { + let en = TestCase::<8>.to_string(); + let orig = CommaVec::from(vec![1, 2, 3, 4]); + match get_env_or_set_default(&en, orig.clone()) { + Ok(res) => { + assert_eq!(res, orig); + assert_eq!(env::var(&en).unwrap(), "1,2,3,4"); + } + _ => unreachable!(), + }; + } + } +} diff --git a/itconfig-tests/tests/config_macro.rs b/tests/config_macro.rs similarity index 100% rename from itconfig-tests/tests/config_macro.rs rename to tests/config_macro.rs From 71d6fe42e89c12890ecee1e26e10ae9d024e23bb Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 22 Jul 2022 15:05:10 +0300 Subject: [PATCH 04/10] rebrand lib --- Cargo.lock | 40 +++++++------- Cargo.toml | 14 ++--- LICENSE | 2 +- README.md | 56 ++++++-------------- benches/main_benches.rs | 4 +- {itconfig-macro => enve_mod}/Cargo.toml | 10 ++-- {itconfig-macro => enve_mod}/src/ast.rs | 0 {itconfig-macro => enve_mod}/src/expand.rs | 0 {itconfig-macro => enve_mod}/src/lib.rs | 0 {itconfig-macro => enve_mod}/src/parse.rs | 0 {itconfig-macro => enve_mod}/src/utils.rs | 0 src/core.rs | 9 ++++ src/lib.rs | 14 ++--- src/utils.rs | 61 +++++++++++++--------- 14 files changed, 103 insertions(+), 107 deletions(-) rename {itconfig-macro => enve_mod}/Cargo.toml (75%) rename {itconfig-macro => enve_mod}/src/ast.rs (100%) rename {itconfig-macro => enve_mod}/src/expand.rs (100%) rename {itconfig-macro => enve_mod}/src/lib.rs (100%) rename {itconfig-macro => enve_mod}/src/parse.rs (100%) rename {itconfig-macro => enve_mod}/src/utils.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 664d82e..abb2a7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -175,6 +175,26 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +[[package]] +name = "enve" +version = "1.1.1" +dependencies = [ + "criterion", + "enve_mod", + "lazy_static", +] + +[[package]] +name = "enve_mod" +version = "1.1.1" +dependencies = [ + "enve", + "lazy_static", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "half" version = "1.8.2" @@ -190,26 +210,6 @@ dependencies = [ "libc", ] -[[package]] -name = "itconfig" -version = "1.1.1" -dependencies = [ - "criterion", - "itconfig-macro", - "lazy_static", -] - -[[package]] -name = "itconfig-macro" -version = "1.1.1" -dependencies = [ - "itconfig", - "lazy_static", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "itertools" version = "0.10.3" diff --git a/Cargo.toml b/Cargo.toml index 42e7df4..e7ac56f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [workspace] members = [ - "itconfig-macro", + "enve_mod", ] [package] -name = "itconfig" +name = "enve" version = "1.1.1" authors = ["Dmitriy Pleshevskiy "] description = "Easy build a configs from environment variables and use it in globally." @@ -12,9 +12,9 @@ categories = ["config", "web-programming"] keywords = ["config", "env", "configuration", "environment", "macro"] edition = "2018" license = "MIT" -repository = "https://github.com/pleshevskiy/itconfig-rs" -homepage = "https://github.com/pleshevskiy/itconfig-rs" -documentation = "https://docs.rs/itconfig" +repository = "https://github.com/pleshevskiy/enve" +homepage = "https://github.com/pleshevskiy/enve" +documentation = "https://docs.rs/enve" readme = "../README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -22,14 +22,14 @@ readme = "../README.md" [features] default = [] -macro = ["itconfig-macro"] +macro = ["enve_mod"] number = [] bool = [] vec = [] [dependencies] -itconfig-macro = { version = "1.1", path = "itconfig-macro", optional = true } +enve_mod = { version = "1.1", path = "./enve_mod", optional = true } [dev-dependencies] lazy_static = "1.4.0" diff --git a/LICENSE b/LICENSE index ae03a7f..96a1a25 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 IceTemple +Copyright (c) 2019 pleshevskiy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ca2f8aa..1bb5dec 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ -# itconfig +# enve -[![CI](https://github.com/icetemple/itconfig-rs/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/icetemple/itconfig-rs/actions/workflows/ci.yml) +[![CI](https://github.com/pleshevskiy/enve/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/pleshevskiy/enve/actions/workflows/ci.yml) [![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) -[![Documentation](https://docs.rs/itconfig/badge.svg)](https://docs.rs/itconfig) -[![Crates.io](https://img.shields.io/crates/v/itconfig)](https://crates.io/crates/itconfig) -![Crates.io](https://img.shields.io/crates/l/itconfig) +[![Documentation](https://docs.rs/pleshevskiy/badge.svg)](https://docs.rs/enve) +[![Crates.io](https://img.shields.io/crates/v/enve)](https://crates.io/crates/enve) +![Crates.io](https://img.shields.io/crates/l/enve) Easy build a configs from environment variables and use it in globally. @@ -23,7 +23,7 @@ of it I decided to create my own library. The MSRV is 1.39.0 -Add `itconfig = { version = "1.0", features = ["macro"] }` as a dependency in +Add `enve = { version = "1.0", features = ["mod"] }` as a dependency in `Cargo.toml`. `Cargo.toml` example: @@ -35,17 +35,16 @@ version = "0.1.0" authors = ["Me "] [dependencies] -itconfig = { version = "1.0", features = ["macro"] } +enve = { version = "1.0", features = ["mod"] } ``` ## Basic usage ```rust -use itconfig::config; use std::env; //use dotenv::dotenv; -config! { +enve::mod! { DEBUG: bool => false, #[env_name = "APP_HOST"] @@ -97,7 +96,6 @@ Macro is an optional feature, disabled by default. You can use this library without macro ```rust -use itconfig::*; use std::env; // use dotenv::dotenv; @@ -106,9 +104,9 @@ fn main() { // or env::set_var("DATABASE_URL", "postgres://127.0.0.1:5432/test"); - let database_url = get_env::("DATABASE_URL").unwrap(); - let new_profile: bool = get_env_or_default("FEATURE_NEW_PROFILE", false); - let articles_per_page: u32 = get_env_or_set_default("ARTICLES_PER_PAGE", 10); + let database_url = enve::get::("DATABASE_URL").unwrap(); + let new_profile: bool = enve::get("FEATURE_NEW_PROFILE").unwrap_or_default(); + let articles_per_page: u32 = enve::get_or_set_default("ARTICLES_PER_PAGE", 10); } ``` @@ -120,39 +118,19 @@ cargo test --all-features ## Available features -- **default** - ["primitives"] - **macro** - Activates `config!` macros for easy configure web application. -- **primitives** - Group for features: `numbers` and `bool`. -- **numbers** - Group for features: `int`, `uint` and `float`. -- **int** - Group for features: `i8`, `i16`, `i32`, `i64`, `i128` and `isize`. -- **uint** - Group for features: `u8`, `u16`, `u32`, `u64`, `u128` and `usize`. -- **float** - Group for features: `f32` and `f64` -- **i8** - impl EnvString for `i8` type -- **i16** - impl EnvString for `i16` type -- **i32** - impl EnvString for `i32` type -- **i64** - impl EnvString for `i64` type -- **i128** - impl EnvString for `i128` type -- **isize** - impl EnvString for `isize` type -- **u8** - impl EnvString for `u8` type -- **u16** - impl EnvString for `u16` type -- **u32** - impl EnvString for `u32` type -- **u64** - impl EnvString for `u64` type -- **u128** - impl EnvString for `u128` type -- **usize** - impl EnvString for `usize` type -- **f32** - impl EnvString for `f32` type -- **f64** - impl EnvString for `f64` type -- **bool** - impl EnvString for `bool` type -- **json_array** - Add EnvString impl for vector type (uses optional - `serde_json` package). ⚠ **_DEPRECATED_** +- **number** - Group for features: `int`, `uint` and `float`. +- **bool** - impl EnvString for `bool` type `serde_json` package). ⚠ + **_DEPRECATED_** ## License -[MIT] © [Ice Temple](https://github.com/icetemple) +[MIT] © [pleshevskiy](https://github.com/pleshevskiy) ## Contributors [pleshevskiy](https://github.com/pleshevskiy) (Dmitriy Pleshevskiy) – creator, maintainer. -[documentation]: https://docs.rs/itconfig -[MIT]: https://github.com/icetemple/itconfig-rs/blob/master/LICENSE +[documentation]: https://docs.rs/enve +[MIT]: https://github.com/icetemple/enve-rs/blob/master/LICENSE diff --git a/benches/main_benches.rs b/benches/main_benches.rs index bce427e..3142d78 100644 --- a/benches/main_benches.rs +++ b/benches/main_benches.rs @@ -4,14 +4,14 @@ use std::env; #[macro_use] extern crate lazy_static; #[macro_use] -extern crate itconfig; +extern crate enve; fn setup_env_var(key: &'static str, initial: String) { env::set_var(key, initial); } fn source_get_env() -> u32 { - itconfig::get_env::("TEST").unwrap() + enve::get::("TEST").unwrap() } fn lazy_get_env() -> u32 { diff --git a/itconfig-macro/Cargo.toml b/enve_mod/Cargo.toml similarity index 75% rename from itconfig-macro/Cargo.toml rename to enve_mod/Cargo.toml index 4d88505..bb3b161 100644 --- a/itconfig-macro/Cargo.toml +++ b/enve_mod/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "itconfig-macro" +name = "enve_mod" version = "1.1.1" authors = ["Dmitriy Pleshevskiy "] description = "Easy build a configs from environment variables and use it in globally." @@ -7,9 +7,9 @@ categories = ["config", "web-programming"] keywords = ["config", "env", "configuration", "environment", "macro"] edition = "2018" license = "MIT" -repository = "https://github.com/pleshevskiy/itconfig-rs" -homepage = "https://github.com/pleshevskiy/itconfig-rs" -documentation = "https://docs.rs/itconfig" +repository = "https://github.com/pleshevskiy/enve" +homepage = "https://github.com/pleshevskiy/enve" +documentation = "https://docs.rs/enve" readme = "../README.md" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -22,7 +22,7 @@ quote = "1.0.9" proc-macro2 = "1.0.24" [dev-dependencies] -itconfig = { path = ".." } +enve = { path = ".." } lazy_static = "1.4.0" [badges] diff --git a/itconfig-macro/src/ast.rs b/enve_mod/src/ast.rs similarity index 100% rename from itconfig-macro/src/ast.rs rename to enve_mod/src/ast.rs diff --git a/itconfig-macro/src/expand.rs b/enve_mod/src/expand.rs similarity index 100% rename from itconfig-macro/src/expand.rs rename to enve_mod/src/expand.rs diff --git a/itconfig-macro/src/lib.rs b/enve_mod/src/lib.rs similarity index 100% rename from itconfig-macro/src/lib.rs rename to enve_mod/src/lib.rs diff --git a/itconfig-macro/src/parse.rs b/enve_mod/src/parse.rs similarity index 100% rename from itconfig-macro/src/parse.rs rename to enve_mod/src/parse.rs diff --git a/itconfig-macro/src/utils.rs b/enve_mod/src/utils.rs similarity index 100% rename from itconfig-macro/src/utils.rs rename to enve_mod/src/utils.rs diff --git a/src/core.rs b/src/core.rs index c31af94..95a9f85 100644 --- a/src/core.rs +++ b/src/core.rs @@ -8,6 +8,7 @@ pub mod vec; #[cfg(feature = "vec")] pub use vec::*; +use crate::error::Error; use std::convert::{Infallible, TryFrom}; /// Wrapper under String type. @@ -18,6 +19,14 @@ use std::convert::{Infallible, TryFrom}; #[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct EString(String); +impl EString { + #[inline] + pub fn parse>(self) -> Result { + let orig = self.0.clone(); + >::try_from(self).map_err(|_| Error::Parse(orig)) + } +} + impl From for EString where T: std::fmt::Display, diff --git a/src/lib.rs b/src/lib.rs index 4182a44..d442af4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -//! # itconfig +//! # enve //! //! Simple configuration with macro for rust application. //! @@ -16,7 +16,7 @@ //! //! These macros require a Rust compiler version 1.31 or newer. //! -//! Add `itconfig = { version = "1.0", features = ["macro"] }` as a dependency in `Cargo.toml`. +//! Add `enve = { version = "1.0", features = ["macro"] }` as a dependency in `Cargo.toml`. //! //! //! `Cargo.toml` example: @@ -28,14 +28,14 @@ //! authors = ["Me "] //! //! [dependencies] -//! itconfig = { version = "1.0", features = ["macro"] } +//! enve = { version = "1.0", features = ["macro"] } //! ``` //! //! //! ## Basic usage //! //! ```rust -//! use itconfig::config; +//! use enve::config; //! use std::env; //! //use dotenv::dotenv; //! @@ -90,7 +90,7 @@ //! Macro is an optional feature, disabled by default. You can use this library without macro. //! //! ```rust -//! use itconfig::*; +//! use enve::*; //! use std::env; //! // use dotenv::dotenv; //! @@ -157,6 +157,6 @@ pub use self::error::*; pub use self::utils::*; #[cfg(feature = "macro")] -extern crate itconfig_macro; +extern crate enve_mod; #[cfg(feature = "macro")] -pub use itconfig_macro::*; +pub use enve_mod::*; diff --git a/src/utils.rs b/src/utils.rs index fa1ed61..1f06865 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,36 +1,35 @@ use crate::core::EString; use crate::error::Error; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryFrom; use std::env; -pub fn get_env_or_set_default(env_name: &str, default: R) -> Result +pub fn get_or_set_default(env_name: &str, default: R) -> Result where R: TryFrom + std::fmt::Display, { - get_env::(env_name).or_else(|err| match err { - Error::NotPresent => { - let val = default.to_string(); - env::set_var(env_name, &val); - EString::from(val) - .try_into() - .map_err(|_| Error::Parse(default.to_string())) - } + get::(env_name).or_else(|err| match err { + Error::NotPresent => set(env_name, &default).parse(), _ => Err(err), }) } -pub fn get_env(env_name: &str) -> Result +pub fn get(env_name: &str) -> Result where R: TryFrom, { env::var(env_name) .map_err(From::from) .map(EString::from) - .and_then(|val| { - val.clone() - .try_into() - .map_err(|_| Error::Parse(val.to_string())) - }) + .and_then(EString::parse) +} + +pub fn set(env_name: &str, value: V) -> EString +where + V: std::fmt::Display, +{ + let val = value.to_string(); + env::set_var(env_name, &val); + val.into() } #[cfg(test)] @@ -41,7 +40,17 @@ mod tests { impl std::fmt::Display for TestCase { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "get_env_{}", N) + write!(f, "test_case_{}", N) + } + } + + #[test] + fn should_add_env_variable_to_process() { + let en = TestCase::<0>.to_string(); + set(&en, "hello"); + match env::var(&en) { + Ok(var) => assert_eq!(&var, "hello"), + _ => unreachable!(), } } @@ -49,7 +58,7 @@ mod tests { fn should_return_variable() { let en = TestCase::<1>.to_string(); env::set_var(&en, "hello"); - match get_env::<&str>(&en) { + match get::<&str>(&en) { Ok(res) => assert_eq!(res, "hello"), _ => unreachable!(), }; @@ -58,7 +67,7 @@ mod tests { #[test] fn should_throw_no_present_error() { let en = TestCase::<2>.to_string(); - match get_env::<&str>(&en) { + match get::<&str>(&en) { Err(Error::NotPresent) => {} _ => unreachable!(), }; @@ -68,7 +77,7 @@ mod tests { fn should_set_default_if_var_is_no_present() { let en = TestCase::<3>.to_string(); let orig = "hello"; - match get_env_or_set_default(&en, orig) { + match get_or_set_default(&en, orig) { Ok(res) => { assert_eq!(res, orig); assert_eq!(env::var(&en).unwrap(), orig); @@ -85,7 +94,7 @@ mod tests { fn should_throw_parse_error() { let en = TestCase::<4>.to_string(); env::set_var(&en, "-10"); - match get_env::(&en) { + match get::(&en) { Err(Error::Parse(orig)) => { assert_eq!(orig, String::from("-10")) } @@ -97,7 +106,7 @@ mod tests { fn should_set_default_num_if_var_is_no_present() { let en = TestCase::<5>.to_string(); let orig = 10; - match get_env_or_set_default(&en, orig) { + match get_or_set_default(&en, orig) { Ok(res) => { assert_eq!(res, orig); assert_eq!(env::var(&en).unwrap(), "10"); @@ -130,7 +139,7 @@ mod tests { en.push_str(val.as_ref()); env::set_var(&en, val); - match get_env::(&en) { + match get::(&en) { Ok(res) => assert_eq!(res, *expected), _ => unreachable!(), }; @@ -148,7 +157,7 @@ mod tests { let en = TestCase::<6>.to_string(); env::set_var(&en, "1,2,3,4,5"); - match get_env::>(&en) { + match get::>(&en) { Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), _ => unreachable!(), }; @@ -158,7 +167,7 @@ mod tests { fn should_throw_parse_vec_error() { let en = TestCase::<7>.to_string(); env::set_var(&en, "1,2,3,4,5"); - match get_env::>(&en) { + match get::>(&en) { Err(Error::Parse(orig)) => { assert_eq!(orig, String::from("1,2,3,4,5")) } @@ -170,7 +179,7 @@ mod tests { fn should_set_default_vector_if_var_is_no_present() { let en = TestCase::<8>.to_string(); let orig = CommaVec::from(vec![1, 2, 3, 4]); - match get_env_or_set_default(&en, orig.clone()) { + match get_or_set_default(&en, orig.clone()) { Ok(res) => { assert_eq!(res, orig); assert_eq!(env::var(&en).unwrap(), "1,2,3,4"); From 332b14bcd9a3a0f968dad2b11da82013e57cae62 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 22 Jul 2022 18:39:29 +0300 Subject: [PATCH 05/10] core/vec: trim parts before parsing --- examples/calc.rs | 15 +++++++++++++ src/core/vec.rs | 1 + src/utils.rs | 57 ++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 examples/calc.rs diff --git a/examples/calc.rs b/examples/calc.rs new file mode 100644 index 0000000..858d835 --- /dev/null +++ b/examples/calc.rs @@ -0,0 +1,15 @@ +use enve::core::SepVec; + +type PlusVec = SepVec; +type MulVec = SepVec; + +fn main() -> Result<(), enve::Error> { + let res: f32 = enve::get::>>("E")? + .iter() + .map(|m| m.iter().product::()) + .sum(); + + println!("result: {}", res); + + Ok(()) +} diff --git a/src/core/vec.rs b/src/core/vec.rs index 0857285..cc2a76a 100644 --- a/src/core/vec.rs +++ b/src/core/vec.rs @@ -51,6 +51,7 @@ where fn try_from(value: EString) -> Result { let inner = value .split(SEP) + .map(|p| p.trim()) .map(EString::from) .map(T::try_from) .collect::, _>>()?; diff --git a/src/utils.rs b/src/utils.rs index 1f06865..b12ea48 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -91,9 +91,19 @@ mod tests { use super::*; #[test] - fn should_throw_parse_error() { + fn should_return_parsed_num() { let en = TestCase::<4>.to_string(); env::set_var(&en, "-10"); + match get::(&en) { + Ok(res) => assert_eq!(res, -10), + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_parse_error() { + let en = TestCase::<5>.to_string(); + env::set_var(&en, "-10"); match get::(&en) { Err(Error::Parse(orig)) => { assert_eq!(orig, String::from("-10")) @@ -104,7 +114,7 @@ mod tests { #[test] fn should_set_default_num_if_var_is_no_present() { - let en = TestCase::<5>.to_string(); + let en = TestCase::<6>.to_string(); let orig = 10; match get_or_set_default(&en, orig) { Ok(res) => { @@ -122,7 +132,7 @@ mod tests { #[test] fn should_parse_bool_variable() { - let en = TestCase::<5>.to_string(); + let en = TestCase::<7>.to_string(); [ ("y", true), @@ -150,11 +160,11 @@ mod tests { #[cfg(feature = "vec")] mod vector { use super::*; - use crate::core::vec::{CommaVec, SepVec}; + use crate::core::vec::{CommaVec, SemiVec, SepVec}; #[test] fn should_return_var_as_vector() { - let en = TestCase::<6>.to_string(); + let en = TestCase::<8>.to_string(); env::set_var(&en, "1,2,3,4,5"); match get::>(&en) { @@ -163,9 +173,42 @@ mod tests { }; } + #[test] + fn should_trim_identations_before_parsing() { + let en = TestCase::<9>.to_string(); + + let input = " +1 , 2, 3, +4,5"; + + env::set_var(&en, input); + match get::>(&en) { + Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), + _ => unreachable!(), + }; + } + + #[test] + fn should_return_vector_of_vectors() { + let en = TestCase::<10>.to_string(); + + env::set_var(&en, "1,2; 3,4,5; 6,7"); + match get::>>(&en) { + Ok(res) => assert_eq!( + res, + SemiVec::from(vec![ + CommaVec::from(vec![1, 2]), + CommaVec::from(vec![3, 4, 5]), + CommaVec::from(vec![6, 7]) + ]) + ), + _ => unreachable!(), + }; + } + #[test] fn should_throw_parse_vec_error() { - let en = TestCase::<7>.to_string(); + let en = TestCase::<11>.to_string(); env::set_var(&en, "1,2,3,4,5"); match get::>(&en) { Err(Error::Parse(orig)) => { @@ -177,7 +220,7 @@ mod tests { #[test] fn should_set_default_vector_if_var_is_no_present() { - let en = TestCase::<8>.to_string(); + let en = TestCase::<12>.to_string(); let orig = CommaVec::from(vec![1, 2, 3, 4]); match get_or_set_default(&en, orig.clone()) { Ok(res) => { From 04b777e214e148efb32276cb1ec948081527f663 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 22 Jul 2022 18:41:41 +0300 Subject: [PATCH 06/10] example/calc: add required features to manifest --- Cargo.toml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e7ac56f..fdd4e78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,13 +21,12 @@ readme = "../README.md" [features] default = [] - -macro = ["enve_mod"] - number = [] bool = [] vec = [] +macro = ["enve_mod"] + [dependencies] enve_mod = { version = "1.1", path = "./enve_mod", optional = true } @@ -42,3 +41,7 @@ maintenance = { status = "actively-developed" } [package.metadata.docs.rs] all-features = true +[[example]] +name = "calc" +required-features = ["number", "vec"] + From 7b1746ef8342a94a8d65bb50a0a7eb579ba12d65 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 22 Jul 2022 19:15:38 +0300 Subject: [PATCH 07/10] example/calc: add help message --- examples/README.md | 15 +++++++++++++++ examples/calc.rs | 27 ++++++++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 examples/README.md diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..d917ddd --- /dev/null +++ b/examples/README.md @@ -0,0 +1,15 @@ +# Examples + +## Calculator + +Fun calculator based on environment variables + +``` +E=2*2-1-1+5*3-10 cargo run --example calc --all-features +``` + +Limits: + +- Supports `*`, `+`, `-` +- You cannot start from a negative number. `E=-10`. Solution: start from `0`. + `E=0-10`. diff --git a/examples/calc.rs b/examples/calc.rs index 858d835..3460667 100644 --- a/examples/calc.rs +++ b/examples/calc.rs @@ -1,13 +1,34 @@ use enve::core::SepVec; +type MinusVec = SepVec; type PlusVec = SepVec; type MulVec = SepVec; +const HELP_MESSAGE: &str = " +USAGE: +E=10+10*2+4 cargo run --example calc --all-features +"; + fn main() -> Result<(), enve::Error> { - let res: f32 = enve::get::>>("E")? + let res: f32 = enve::get::>>>("E") + .map_err(|err| { + match err { + enve::Error::NotPresent => eprintln!("The expression was not found"), + rest => eprintln!("ERROR: {}", rest), + } + + eprintln!("{}", HELP_MESSAGE); + std::process::exit(0); + }) + .unwrap() .iter() - .map(|m| m.iter().product::()) - .sum(); + .map(|p| { + p.iter() + .map(|m| m.iter().product::()) + .reduce(|acc, v| acc - v) + .unwrap_or_default() + }) + .sum::(); println!("result: {}", res); From a8c124aabca8d0526976ffc46d1a4f6ce72cc319 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Fri, 22 Jul 2022 23:11:09 +0300 Subject: [PATCH 08/10] tests: add more cases for bool --- src/utils.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils.rs b/src/utils.rs index b12ea48..5673892 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -135,6 +135,7 @@ mod tests { let en = TestCase::<7>.to_string(); [ + ("1", true), ("y", true), ("yes", true), ("true", true), @@ -142,6 +143,7 @@ mod tests { ("on", true), ("false", false), ("f", false), + ("0", false), ] .iter() .for_each(|(val, expected)| { From 2171d6b6a71d1205a278e2c78207a969403a697b Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Sat, 23 Jul 2022 14:05:53 +0300 Subject: [PATCH 09/10] refac: move core to estring crate --- Cargo.lock | 7 +- Cargo.toml | 10 +- LICENSE | 2 +- estring/Cargo.toml | 28 ++++ estring/LICENSE | 21 +++ estring/README.md | 57 +++++++ estring/src/core.rs | 99 ++++++++++++ estring/src/core/prim.rs | 14 ++ estring/src/core/prim/bool.rs | 57 +++++++ estring/src/core/prim/number.rs | 62 +++++++ estring/src/core/vec.rs | 153 ++++++++++++++++++ estring/src/error.rs | 19 +++ estring/src/lib.rs | 72 +++++++++ examples/calc.rs | 2 +- src/core.rs | 277 ++++++++++++++++++++++++++------ src/core/prim.rs | 39 ----- src/core/vec.rs | 60 ------- src/error.rs | 8 +- src/estr.rs | 6 + src/estr/vec.rs | 7 + src/lib.rs | 11 +- src/utils.rs | 236 --------------------------- 22 files changed, 846 insertions(+), 401 deletions(-) create mode 100644 estring/Cargo.toml create mode 100644 estring/LICENSE create mode 100644 estring/README.md create mode 100644 estring/src/core.rs create mode 100644 estring/src/core/prim.rs create mode 100644 estring/src/core/prim/bool.rs create mode 100644 estring/src/core/prim/number.rs create mode 100644 estring/src/core/vec.rs create mode 100644 estring/src/error.rs create mode 100644 estring/src/lib.rs delete mode 100644 src/core/prim.rs delete mode 100644 src/core/vec.rs create mode 100644 src/estr.rs create mode 100644 src/estr/vec.rs delete mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index abb2a7a..ac6a9ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,7 +1,5 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - [[package]] name = "atty" version = "0.2.14" @@ -181,6 +179,7 @@ version = "1.1.1" dependencies = [ "criterion", "enve_mod", + "estring", "lazy_static", ] @@ -195,6 +194,10 @@ dependencies = [ "syn", ] +[[package]] +name = "estring" +version = "0.1.0" + [[package]] name = "half" version = "1.8.2" diff --git a/Cargo.toml b/Cargo.toml index fdd4e78..83d08e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ - "enve_mod", + "estring", + "enve_mod", ] [package] @@ -21,13 +22,14 @@ readme = "../README.md" [features] default = [] -number = [] -bool = [] -vec = [] +number = ["estring/number"] +bool = ["estring/bool"] +vec = ["estring/vec"] macro = ["enve_mod"] [dependencies] +estring = { version = "0", path = "./estring" } enve_mod = { version = "1.1", path = "./enve_mod", optional = true } [dev-dependencies] diff --git a/LICENSE b/LICENSE index 96a1a25..6b5ce3d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 pleshevskiy +Copyright (c) 2019-2022 pleshevskiy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/estring/Cargo.toml b/estring/Cargo.toml new file mode 100644 index 0000000..20da45e --- /dev/null +++ b/estring/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "estring" +description = "A simple way to parse a string using type annotations" +version = "0.1.0" +edition = "2018" +authors = ["Dmitriy Pleshevskiy "] +readme = "README.md" +repository = "https://github.com/pleshevskiy/itconfig-rs/tree/redesign/estring" +license = "MIT" +keywords = ["parsing", "type", "annotations", "customizable"] +categories = ["data-structures", "parsing"] + +# rust-version = "1.51.0" # The first version of Cargo that supports this field is 1.56.0 + +[metadata] +msrv = "1.51.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +number = [] +bool = [] +vec = [] + +[dependencies] + +[badges] +maintenance = { status = "actively-developed" } diff --git a/estring/LICENSE b/estring/LICENSE new file mode 100644 index 0000000..8afbf27 --- /dev/null +++ b/estring/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 pleshevskiy + +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. diff --git a/estring/README.md b/estring/README.md new file mode 100644 index 0000000..b49dbaa --- /dev/null +++ b/estring/README.md @@ -0,0 +1,57 @@ +# EString + +A simple way to parse a string using type annotations. + +This package was originally designed for [enve] + +[enve]: https://github.com/pleshevskiy/itconfig-rs/tree/redesign + +## Getting started + +```rust +use estring::{SepVec, EString}; + +type PlusVec = SepVec; +type MulVec = SepVec; + +fn main() -> Result<(), estring::ParseError> { + let res = EString::from("10+5*2+3") + .parse::>>()? + .iter() + .map(|m| m.iter().product::()) + .sum::(); + + assert_eq!(res, 23.0); + Ok(()) +} +``` + +You can use custom types as annotations! Just implement `TryFrom`! + +## Installation + +**The MSRV is 1.51.0** + +Add `estring = { version = "0.1", features = ["vec", "number"] }` as a +dependency in `Cargo.toml`. + +`Cargo.toml` example: + +```toml +[package] +name = "my-crate" +version = "0.1.0" +authors = ["Me "] + +[dependencies] +estring = { version = "0.1", features = ["vec", "number"] } +``` + +## License + +**MIT**. See [LICENSE](./LICENSE) to see the full text. + +## Contributors + +[pleshevskiy](https://github.com/pleshevskiy) (Dmitriy Pleshevskiy) – creator, +maintainer. diff --git a/estring/src/core.rs b/estring/src/core.rs new file mode 100644 index 0000000..68a2151 --- /dev/null +++ b/estring/src/core.rs @@ -0,0 +1,99 @@ +//! Contains the ``EString`` type, as well as the basic implementation of conversions to +//! string types +//! +#[cfg(any(feature = "number", feature = "bool"))] +pub mod prim; +#[cfg(any(feature = "number", feature = "bool"))] +pub use prim::*; + +#[cfg(feature = "vec")] +pub mod vec; +#[cfg(feature = "vec")] +pub use vec::*; + +use crate::ParseError; +use std::convert::{Infallible, TryFrom}; + +/// Wrapper under String type. +#[derive(Debug, Default, PartialEq, Eq, Clone)] +pub struct EString(pub String); + +impl EString { + /// Parses inner string by type annotations and returns result. + /// + /// # Errors + /// + /// Will return `Err` if estring cannot parse inner fragment + /// + #[inline] + pub fn parse>(self) -> Result { + let orig = self.0.clone(); + >::try_from(self).map_err(|_| ParseError(orig)) + } +} + +impl From for EString +where + T: std::fmt::Display, +{ + #[inline] + fn from(val: T) -> Self { + Self(val.to_string()) + } +} + +impl std::ops::Deref for EString { + type Target = String; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom for String { + type Error = Infallible; + + #[inline] + fn try_from(s: EString) -> Result { + Ok(s.0) + } +} + +impl TryFrom for &'static str { + type Error = Infallible; + + #[inline] + fn try_from(s: EString) -> Result { + Ok(Box::leak(s.0.into_boxed_str())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_deref_to_string() { + let estr = EString::from("hello"); + assert_eq!(*estr, String::from("hello")); + } + + #[test] + fn should_parse_into_itself() { + let estr = EString::from("hello"); + match estr.parse::() { + Ok(res) => assert_eq!(res, EString::from("hello")), + _ => unreachable!(), + } + } + + #[test] + fn should_parse_into_string() { + let estr = EString::from("hello"); + match estr.parse::() { + Ok(res) => assert_eq!(res, String::from("hello")), + _ => unreachable!(), + } + } +} diff --git a/estring/src/core/prim.rs b/estring/src/core/prim.rs new file mode 100644 index 0000000..ff19878 --- /dev/null +++ b/estring/src/core/prim.rs @@ -0,0 +1,14 @@ +//! Contains the implementations to primitive types (number, boolean) +//! +//! **NOTE**: Require the enabling of the same-name features +//! + +#[cfg(feature = "bool")] +mod bool; +#[cfg(feature = "bool")] +pub use self::bool::*; + +#[cfg(feature = "number")] +mod number; +#[cfg(feature = "number")] +pub use self::number::*; diff --git a/estring/src/core/prim/bool.rs b/estring/src/core/prim/bool.rs new file mode 100644 index 0000000..f0aacc9 --- /dev/null +++ b/estring/src/core/prim/bool.rs @@ -0,0 +1,57 @@ +use crate::core::EString; +use std::convert::TryFrom; + +impl TryFrom for bool { + type Error = (); + + #[inline] + fn try_from(s: EString) -> Result { + match s.to_lowercase().as_str() { + "true" | "t" | "yes" | "y" | "on" | "1" => Ok(true), + "false" | "f" | "no" | "n" | "off" | "0" | "" => Ok(false), + _ => Err(()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ParseError; + + #[test] + fn should_parse_bool_variable() { + let test_cases = [ + ("1", true), + ("0", false), + ("y", true), + ("f", false), + ("yes", true), + ("true", true), + ("false", false), + ("t", true), + ("f", false), + ("on", true), + ("off", false), + ]; + + for (val, expected) in test_cases { + let estr = EString::from(val); + match estr.parse::() { + Ok(res) => assert_eq!(res, expected), + _ => unreachable!(), + }; + } + } + + #[test] + fn should_throw_parse_error() { + let estr = EString::from("something"); + match estr.parse::() { + Err(ParseError(orig)) => { + assert_eq!(orig, String::from("something")); + } + _ => unreachable!(), + }; + } +} diff --git a/estring/src/core/prim/number.rs b/estring/src/core/prim/number.rs new file mode 100644 index 0000000..3b93389 --- /dev/null +++ b/estring/src/core/prim/number.rs @@ -0,0 +1,62 @@ +use crate::core::EString; +use std::convert::TryFrom; + +#[doc(hidden)] +macro_rules! from_env_string_numbers_impl { + ($($ty:ty),+$(,)?) => { + $( + #[cfg(feature = "number")] + impl TryFrom for $ty { + type Error = <$ty as std::str::FromStr>::Err; + + #[inline] + fn try_from(s: EString) -> Result { + s.0.parse::() + } + } + )+ + }; +} + +#[rustfmt::skip] +from_env_string_numbers_impl![ + i8, i16, i32, i64, i128, isize, + u8, u16, u32, u64, u128, usize, + f32, f64 +]; + +#[cfg(test)] +mod tests { + use super::*; + use crate::ParseError; + + #[test] + fn should_parse_number() { + let estr = EString::from("-10"); + match estr.parse::() { + Ok(res) => assert_eq!(res, -10), + _ => unreachable!(), + }; + } + + #[test] + fn should_parse_float_number() { + let estr = EString::from("-0.15"); + match estr.parse::() { + #[allow(clippy::float_cmp)] + Ok(res) => assert_eq!(res, -0.15), + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_parse_error() { + let estr = EString::from("-10"); + match estr.parse::() { + Err(ParseError(orig)) => { + assert_eq!(orig, String::from("-10")); + } + _ => unreachable!(), + }; + } +} diff --git a/estring/src/core/vec.rs b/estring/src/core/vec.rs new file mode 100644 index 0000000..c59d45c --- /dev/null +++ b/estring/src/core/vec.rs @@ -0,0 +1,153 @@ +//! Contains the implementations to vec type +//! +//! **NOTE**: Require the enabling of the `vec` features +//! + +use crate::core::EString; +use std::convert::TryFrom; +use std::fmt::Write; + +/// Wrapper for ``Vec`` to split string by a separator (`SEP`). +/// +/// **NOTE**: Required the enabling of the `vec` feature. +/// +/// # Examples +/// +/// ```rust +/// use estring::{SepVec, EString}; +/// +/// type CommaVec = SepVec; +/// +/// fn main() -> Result<(), estring::ParseError> { +/// let res = EString::from("1,2,3").parse::>()?; +/// assert_eq!(*res, vec![1, 2, 3]); +/// +/// Ok(()) +/// } +/// +/// ``` +/// +#[derive(Debug, PartialEq, Clone)] +pub struct SepVec(pub Vec); + +impl std::ops::Deref for SepVec { + type Target = Vec; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for SepVec { + #[inline] + fn from(vec: Vec) -> Self { + Self(vec) + } +} + +impl std::fmt::Display for SepVec +where + T: std::fmt::Display, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.iter().enumerate().try_for_each(|(i, part)| { + if i != 0 { + f.write_char(SEP)?; + } + + f.write_str(&part.to_string()) + }) + } +} + +impl TryFrom for SepVec +where + T: TryFrom + std::fmt::Display, +{ + type Error = T::Error; + + fn try_from(value: EString) -> Result { + let inner = value + .split(SEP) + .map(str::trim) + .map(EString::from) + .map(T::try_from) + .collect::, _>>()?; + Ok(Self(inner)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const COMMA: char = ','; + const SEMI: char = ';'; + + type CommaVec = SepVec; + type SemiVec = SepVec; + + #[test] + fn should_parse_into_vec() { + let estr = EString::from("a,b,c,d,e"); + match estr.parse::>() { + Ok(res) => assert_eq!(*res, vec!["a", "b", "c", "d", "e"]), + _ => unreachable!(), + }; + } + + #[test] + fn should_trim_identations_before_parsing() { + let input = " +a , b, c, +d,e"; + let estr = EString::from(input); + match estr.parse::>() { + Ok(res) => assert_eq!(*res, vec!["a", "b", "c", "d", "e"]), + _ => unreachable!(), + }; + } + + #[test] + fn should_parse_into_vector_of_vectors() { + let estr = EString::from("a,b; c,d,e; f,g"); + match estr.parse::>>() { + Ok(res) => assert_eq!( + res, + SemiVec::from(vec![ + CommaVec::from(vec!["a", "b"]), + CommaVec::from(vec!["c", "d", "e"]), + CommaVec::from(vec!["f", "g"]) + ]) + ), + _ => unreachable!(), + }; + } + + #[cfg(feature = "number")] + mod numbers { + use super::*; + use crate::ParseError; + + #[test] + fn should_parse_into_num_vec() { + let estr = EString::from("1,2,3,4,5"); + match estr.parse::>() { + Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_parse_vec_error() { + let estr = EString::from("1,2,3,4,5"); + match estr.parse::>() { + Err(ParseError(orig)) => { + assert_eq!(orig, String::from("1,2,3,4,5")); + } + _ => unreachable!(), + }; + } + } +} diff --git a/estring/src/error.rs b/estring/src/error.rs new file mode 100644 index 0000000..a484b68 --- /dev/null +++ b/estring/src/error.rs @@ -0,0 +1,19 @@ +/// Failed to parse the specified string. +#[derive(Debug)] +pub struct ParseError(pub String); + +impl std::fmt::Display for ParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, r#"Failed to parse: "{}""#, self.0) + } +} + +impl std::error::Error for ParseError {} + +impl std::ops::Deref for ParseError { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/estring/src/lib.rs b/estring/src/lib.rs new file mode 100644 index 0000000..e38b638 --- /dev/null +++ b/estring/src/lib.rs @@ -0,0 +1,72 @@ +//! +//! # ``EString`` +//! +//! A simple way to parse a string using type annotations. +//! +//! This package was originally designed for [enve] +//! +//! [enve]: https://github.com/pleshevskiy/itconfig-rs +//! +//! ## Getting started +//! +//! ```rust +//! use estring::{SepVec, EString}; +//! +//! type PlusVec = SepVec; +//! type MulVec = SepVec; +//! +//! fn main() -> Result<(), estring::ParseError> { +//! let res = EString::from("10+5*2+3") +//! .parse::>>()? +//! .iter() +//! .map(|m| m.iter().product::()) +//! .sum::(); +//! +//! assert_eq!(res, 23.0); +//! Ok(()) +//! } +//! ``` +//! +//! ## Installation +//! +//! **The MSRV is 1.51.0** +//! +//! Add `estring = { version = "0.1", features = ["vec", "number"] }` as a +//! dependency in `Cargo.toml`. +//! +//! `Cargo.toml` example: +//! +//! ```toml +//! [package] +//! name = "my-crate" +//! version = "0.1.0" +//! authors = ["Me "] +//! +//! [dependencies] +//! estring = { version = "0.1", features = ["vec", "number"] } +//! ``` +//! +//! ## License +//! +//! **MIT**. +//! +//! See [LICENSE](./LICENSE) to see the full text. +//! +#![deny(clippy::pedantic)] +#![allow(clippy::module_name_repetitions)] +#![deny(missing_docs)] + +pub mod core; +mod error; + +pub use crate::core::*; +pub use crate::error::ParseError; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + let result = 2 + 2; + assert_eq!(result, 4); + } +} diff --git a/examples/calc.rs b/examples/calc.rs index 3460667..129b488 100644 --- a/examples/calc.rs +++ b/examples/calc.rs @@ -1,4 +1,4 @@ -use enve::core::SepVec; +use enve::estr::SepVec; type MinusVec = SepVec; type PlusVec = SepVec; diff --git a/src/core.rs b/src/core.rs index 95a9f85..61712a4 100644 --- a/src/core.rs +++ b/src/core.rs @@ -1,65 +1,238 @@ -#[cfg(any(feature = "number", feature = "bool"))] -pub mod prim; -#[cfg(any(feature = "number", feature = "bool"))] -pub use prim::*; - -#[cfg(feature = "vec")] -pub mod vec; -#[cfg(feature = "vec")] -pub use vec::*; - use crate::error::Error; -use std::convert::{Infallible, TryFrom}; +use estring::EString; +use std::convert::TryFrom; -/// Wrapper under String type. -/// -/// When we read the environment variable, we automatically convert the value -/// to EnvString and then convert it to your expected type. -/// -#[derive(Debug, Default, PartialEq, Eq, Clone)] -pub struct EString(String); - -impl EString { - #[inline] - pub fn parse>(self) -> Result { - let orig = self.0.clone(); - >::try_from(self).map_err(|_| Error::Parse(orig)) - } -} - -impl From for EString +pub fn get_or_set_default(env_name: &str, default: R) -> Result where - T: std::fmt::Display, + R: TryFrom + std::fmt::Display, { - #[inline] - fn from(val: T) -> Self { - Self(val.to_string()) - } + get::(env_name).or_else(|err| match err { + Error::NotPresent => sset(env_name, &default).parse().map_err(Error::from), + _ => Err(err), + }) } -impl std::ops::Deref for EString { - type Target = String; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } +pub fn get(env_name: &str) -> Result +where + R: TryFrom, +{ + sget(env_name).and_then(|v| v.parse().map_err(Error::from)) } -impl TryFrom for String { - type Error = Infallible; - - #[inline] - fn try_from(s: EString) -> Result { - Ok(s.0) - } +pub fn sget(env_name: &str) -> Result { + std::env::var(env_name) + .map_err(Error::from) + .map(EString::from) } -impl TryFrom for &'static str { - type Error = Infallible; +pub fn sset(env_name: &str, value: V) -> EString +where + V: std::fmt::Display, +{ + let val = value.to_string(); + std::env::set_var(env_name, &val); + val.into() +} - #[inline] - fn try_from(s: EString) -> Result { - Ok(Box::leak(s.0.into_boxed_str())) +#[cfg(test)] +mod tests { + use super::*; + + struct TestCase; + + impl std::fmt::Display for TestCase { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "test_case_{}", N) + } + } + + #[test] + fn should_add_env_variable_to_process() { + let en = TestCase::<0>.to_string(); + sset(&en, "hello"); + match std::env::var(&en) { + Ok(var) => assert_eq!(&var, "hello"), + _ => unreachable!(), + } + } + + #[test] + fn should_return_variable() { + let en = TestCase::<1>.to_string(); + std::env::set_var(&en, "hello"); + match get::<&str>(&en) { + Ok(res) => assert_eq!(res, "hello"), + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_no_present_error() { + let en = TestCase::<2>.to_string(); + match get::<&str>(&en) { + Err(Error::NotPresent) => {} + _ => unreachable!(), + }; + } + + #[test] + fn should_set_default_if_var_is_no_present() { + let en = TestCase::<3>.to_string(); + let orig = "hello"; + match get_or_set_default(&en, orig) { + Ok(res) => { + assert_eq!(res, orig); + assert_eq!(std::env::var(&en).unwrap(), orig); + } + _ => unreachable!(), + }; + } + + #[cfg(feature = "number")] + mod numbers { + use super::*; + + #[test] + fn should_return_parsed_num() { + let en = TestCase::<4>.to_string(); + std::env::set_var(&en, "-10"); + match get::(&en) { + Ok(res) => assert_eq!(res, -10), + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_parse_error() { + let en = TestCase::<5>.to_string(); + std::env::set_var(&en, "-10"); + match get::(&en) { + Err(Error::Parse(orig)) => { + assert_eq!(orig, String::from("-10")) + } + _ => unreachable!(), + }; + } + + #[test] + fn should_set_default_num_if_var_is_no_present() { + let en = TestCase::<6>.to_string(); + let orig = 10; + match get_or_set_default(&en, orig) { + Ok(res) => { + assert_eq!(res, orig); + assert_eq!(std::env::var(&en).unwrap(), "10"); + } + _ => unreachable!(), + }; + } + } + + #[cfg(feature = "bool")] + mod boolean { + use super::*; + + #[test] + fn should_parse_bool_variable() { + let en = TestCase::<7>.to_string(); + + [ + ("1", true), + ("y", true), + ("yes", true), + ("true", true), + ("t", true), + ("on", true), + ("false", false), + ("f", false), + ("0", false), + ] + .iter() + .for_each(|(val, expected)| { + let mut en = en.clone(); + en.push_str(val.as_ref()); + + std::env::set_var(&en, val); + match get::(&en) { + Ok(res) => assert_eq!(res, *expected), + _ => unreachable!(), + }; + }) + } + } + + #[cfg(feature = "vec")] + mod vector { + use super::*; + use crate::estr::{CommaVec, SemiVec, SepVec}; + + #[test] + fn should_return_var_as_vector() { + let en = TestCase::<8>.to_string(); + + std::env::set_var(&en, "1,2,3,4,5"); + match get::>(&en) { + Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), + _ => unreachable!(), + }; + } + + #[test] + fn should_trim_identations_before_parsing() { + let en = TestCase::<9>.to_string(); + + let input = " +1 , 2, 3, +4,5"; + + std::env::set_var(&en, input); + match get::>(&en) { + Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), + _ => unreachable!(), + }; + } + + #[test] + fn should_return_vector_of_vectors() { + let en = TestCase::<10>.to_string(); + + std::env::set_var(&en, "1,2; 3,4,5; 6,7"); + match get::>>(&en) { + Ok(res) => assert_eq!( + res, + SemiVec::from(vec![ + CommaVec::from(vec![1, 2]), + CommaVec::from(vec![3, 4, 5]), + CommaVec::from(vec![6, 7]) + ]) + ), + _ => unreachable!(), + }; + } + + #[test] + fn should_throw_parse_vec_error() { + let en = TestCase::<11>.to_string(); + std::env::set_var(&en, "1,2,3,4,5"); + match get::>(&en) { + Err(Error::Parse(orig)) => { + assert_eq!(orig, String::from("1,2,3,4,5")) + } + _ => unreachable!(), + }; + } + + #[test] + fn should_set_default_vector_if_var_is_no_present() { + let en = TestCase::<12>.to_string(); + let orig = CommaVec::from(vec![1, 2, 3, 4]); + match get_or_set_default(&en, orig.clone()) { + Ok(res) => { + assert_eq!(res, orig); + assert_eq!(std::env::var(&en).unwrap(), "1,2,3,4"); + } + _ => unreachable!(), + }; + } } } diff --git a/src/core/prim.rs b/src/core/prim.rs deleted file mode 100644 index d0ba318..0000000 --- a/src/core/prim.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::core::EString; -use std::convert::{Infallible, TryFrom}; - -#[doc(hidden)] -macro_rules! from_env_string_numbers_impl { - ($($ty:ty),+$(,)?) => { - $( - #[cfg(feature = "number")] - impl TryFrom for $ty { - type Error = <$ty as std::str::FromStr>::Err; - - #[inline] - fn try_from(s: EString) -> Result { - s.0.parse::() - } - } - )+ - }; -} - -#[rustfmt::skip] -from_env_string_numbers_impl![ - i8, i16, i32, i64, i128, isize, - u8, u16, u32, u64, u128, usize, - f32, f64 -]; - -#[cfg(feature = "bool")] -impl TryFrom for bool { - type Error = Infallible; - - #[inline] - fn try_from(s: EString) -> Result { - Ok(matches!( - s.to_lowercase().as_str(), - "true" | "t" | "yes" | "y" | "on" | "1" - )) - } -} diff --git a/src/core/vec.rs b/src/core/vec.rs deleted file mode 100644 index cc2a76a..0000000 --- a/src/core/vec.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::core::EString; -use std::convert::TryFrom; -use std::fmt::Write; - -pub const COMMA: char = ','; -pub const SEMI: char = ';'; - -pub type CommaVec = SepVec; -pub type SemiVec = SepVec; - -#[derive(Debug, PartialEq, Clone)] -pub struct SepVec(pub Vec); - -impl std::ops::Deref for SepVec { - type Target = Vec; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From> for SepVec { - #[inline] - fn from(vec: Vec) -> Self { - Self(vec) - } -} - -impl std::fmt::Display for SepVec -where - T: std::fmt::Display, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.iter().enumerate().try_for_each(|(i, part)| { - if i != 0 { - f.write_char(SEP)?; - } - - f.write_str(&part.to_string()) - }) - } -} - -impl TryFrom for SepVec -where - T: TryFrom + std::fmt::Display, -{ - type Error = T::Error; - - fn try_from(value: EString) -> Result { - let inner = value - .split(SEP) - .map(|p| p.trim()) - .map(EString::from) - .map(T::try_from) - .collect::, _>>()?; - Ok(Self(inner)) - } -} diff --git a/src/error.rs b/src/error.rs index 002554e..a6ce455 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,7 +4,7 @@ use std::ffi::OsString; use std::fmt; /// The error type for operations interacting with environment variables -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum Error { /// The specified environment variable was not present in the current process's environment. NotPresent, @@ -45,3 +45,9 @@ impl From for Error { } } } + +impl From for Error { + fn from(err: estring::ParseError) -> Self { + Error::Parse(err.clone()) + } +} diff --git a/src/estr.rs b/src/estr.rs new file mode 100644 index 0000000..e0fe52c --- /dev/null +++ b/src/estr.rs @@ -0,0 +1,6 @@ +#[cfg(feature = "vec")] +pub mod vec; +#[cfg(feature = "vec")] +pub use vec::{CommaVec, SemiVec}; + +pub use estring::core::*; diff --git a/src/estr/vec.rs b/src/estr/vec.rs new file mode 100644 index 0000000..4307856 --- /dev/null +++ b/src/estr/vec.rs @@ -0,0 +1,7 @@ +use estring::SepVec; + +const COMMA: char = ','; +const SEMI: char = ';'; + +pub type CommaVec = SepVec; +pub type SemiVec = SepVec; diff --git a/src/lib.rs b/src/lib.rs index d442af4..ebf8312 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,24 +137,25 @@ #![forbid(non_ascii_idents)] #![deny( missing_debug_implementations, - // missing_docs, + missing_docs, unstable_features, unused_imports, unused_qualifications )] +#![warn(missing_docs)] // Clippy lints #![deny(clippy::all)] #![allow(clippy::needless_doctest_main)] ///////////////////////////////////////////////////////////////////////////// -pub mod core; +mod core; mod error; -mod utils; +pub mod estr; pub use self::core::*; -pub use self::error::*; -pub use self::utils::*; +pub use self::core::{get, get_or_set_default, sget, sset}; +pub use self::error::Error; #[cfg(feature = "macro")] extern crate enve_mod; diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 5673892..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,236 +0,0 @@ -use crate::core::EString; -use crate::error::Error; -use std::convert::TryFrom; -use std::env; - -pub fn get_or_set_default(env_name: &str, default: R) -> Result -where - R: TryFrom + std::fmt::Display, -{ - get::(env_name).or_else(|err| match err { - Error::NotPresent => set(env_name, &default).parse(), - _ => Err(err), - }) -} - -pub fn get(env_name: &str) -> Result -where - R: TryFrom, -{ - env::var(env_name) - .map_err(From::from) - .map(EString::from) - .and_then(EString::parse) -} - -pub fn set(env_name: &str, value: V) -> EString -where - V: std::fmt::Display, -{ - let val = value.to_string(); - env::set_var(env_name, &val); - val.into() -} - -#[cfg(test)] -mod tests { - use super::*; - - struct TestCase; - - impl std::fmt::Display for TestCase { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "test_case_{}", N) - } - } - - #[test] - fn should_add_env_variable_to_process() { - let en = TestCase::<0>.to_string(); - set(&en, "hello"); - match env::var(&en) { - Ok(var) => assert_eq!(&var, "hello"), - _ => unreachable!(), - } - } - - #[test] - fn should_return_variable() { - let en = TestCase::<1>.to_string(); - env::set_var(&en, "hello"); - match get::<&str>(&en) { - Ok(res) => assert_eq!(res, "hello"), - _ => unreachable!(), - }; - } - - #[test] - fn should_throw_no_present_error() { - let en = TestCase::<2>.to_string(); - match get::<&str>(&en) { - Err(Error::NotPresent) => {} - _ => unreachable!(), - }; - } - - #[test] - fn should_set_default_if_var_is_no_present() { - let en = TestCase::<3>.to_string(); - let orig = "hello"; - match get_or_set_default(&en, orig) { - Ok(res) => { - assert_eq!(res, orig); - assert_eq!(env::var(&en).unwrap(), orig); - } - _ => unreachable!(), - }; - } - - #[cfg(feature = "number")] - mod numbers { - use super::*; - - #[test] - fn should_return_parsed_num() { - let en = TestCase::<4>.to_string(); - env::set_var(&en, "-10"); - match get::(&en) { - Ok(res) => assert_eq!(res, -10), - _ => unreachable!(), - }; - } - - #[test] - fn should_throw_parse_error() { - let en = TestCase::<5>.to_string(); - env::set_var(&en, "-10"); - match get::(&en) { - Err(Error::Parse(orig)) => { - assert_eq!(orig, String::from("-10")) - } - _ => unreachable!(), - }; - } - - #[test] - fn should_set_default_num_if_var_is_no_present() { - let en = TestCase::<6>.to_string(); - let orig = 10; - match get_or_set_default(&en, orig) { - Ok(res) => { - assert_eq!(res, orig); - assert_eq!(env::var(&en).unwrap(), "10"); - } - _ => unreachable!(), - }; - } - } - - #[cfg(feature = "bool")] - mod boolean { - use super::*; - - #[test] - fn should_parse_bool_variable() { - let en = TestCase::<7>.to_string(); - - [ - ("1", true), - ("y", true), - ("yes", true), - ("true", true), - ("t", true), - ("on", true), - ("false", false), - ("f", false), - ("0", false), - ] - .iter() - .for_each(|(val, expected)| { - let mut en = en.clone(); - en.push_str(val.as_ref()); - - env::set_var(&en, val); - match get::(&en) { - Ok(res) => assert_eq!(res, *expected), - _ => unreachable!(), - }; - }) - } - } - - #[cfg(feature = "vec")] - mod vector { - use super::*; - use crate::core::vec::{CommaVec, SemiVec, SepVec}; - - #[test] - fn should_return_var_as_vector() { - let en = TestCase::<8>.to_string(); - - env::set_var(&en, "1,2,3,4,5"); - match get::>(&en) { - Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), - _ => unreachable!(), - }; - } - - #[test] - fn should_trim_identations_before_parsing() { - let en = TestCase::<9>.to_string(); - - let input = " -1 , 2, 3, -4,5"; - - env::set_var(&en, input); - match get::>(&en) { - Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), - _ => unreachable!(), - }; - } - - #[test] - fn should_return_vector_of_vectors() { - let en = TestCase::<10>.to_string(); - - env::set_var(&en, "1,2; 3,4,5; 6,7"); - match get::>>(&en) { - Ok(res) => assert_eq!( - res, - SemiVec::from(vec![ - CommaVec::from(vec![1, 2]), - CommaVec::from(vec![3, 4, 5]), - CommaVec::from(vec![6, 7]) - ]) - ), - _ => unreachable!(), - }; - } - - #[test] - fn should_throw_parse_vec_error() { - let en = TestCase::<11>.to_string(); - env::set_var(&en, "1,2,3,4,5"); - match get::>(&en) { - Err(Error::Parse(orig)) => { - assert_eq!(orig, String::from("1,2,3,4,5")) - } - _ => unreachable!(), - }; - } - - #[test] - fn should_set_default_vector_if_var_is_no_present() { - let en = TestCase::<12>.to_string(); - let orig = CommaVec::from(vec![1, 2, 3, 4]); - match get_or_set_default(&en, orig.clone()) { - Ok(res) => { - assert_eq!(res, orig); - assert_eq!(env::var(&en).unwrap(), "1,2,3,4"); - } - _ => unreachable!(), - }; - } - } -} From bced02a1fd555c37ded5eef560c8aa8588c271f9 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Sat, 23 Jul 2022 23:08:08 +0300 Subject: [PATCH 10/10] refac: move estring to a separate repo --- Cargo.toml | 2 +- estring/Cargo.toml | 28 ------ estring/LICENSE | 21 ----- estring/README.md | 57 ------------ estring/src/core.rs | 99 --------------------- estring/src/core/prim.rs | 14 --- estring/src/core/prim/bool.rs | 57 ------------ estring/src/core/prim/number.rs | 62 ------------- estring/src/core/vec.rs | 153 -------------------------------- estring/src/error.rs | 19 ---- estring/src/lib.rs | 72 --------------- 11 files changed, 1 insertion(+), 583 deletions(-) delete mode 100644 estring/Cargo.toml delete mode 100644 estring/LICENSE delete mode 100644 estring/README.md delete mode 100644 estring/src/core.rs delete mode 100644 estring/src/core/prim.rs delete mode 100644 estring/src/core/prim/bool.rs delete mode 100644 estring/src/core/prim/number.rs delete mode 100644 estring/src/core/vec.rs delete mode 100644 estring/src/error.rs delete mode 100644 estring/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 83d08e4..c4ca931 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ vec = ["estring/vec"] macro = ["enve_mod"] [dependencies] -estring = { version = "0", path = "./estring" } +estring = "0.1" enve_mod = { version = "1.1", path = "./enve_mod", optional = true } [dev-dependencies] diff --git a/estring/Cargo.toml b/estring/Cargo.toml deleted file mode 100644 index 20da45e..0000000 --- a/estring/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "estring" -description = "A simple way to parse a string using type annotations" -version = "0.1.0" -edition = "2018" -authors = ["Dmitriy Pleshevskiy "] -readme = "README.md" -repository = "https://github.com/pleshevskiy/itconfig-rs/tree/redesign/estring" -license = "MIT" -keywords = ["parsing", "type", "annotations", "customizable"] -categories = ["data-structures", "parsing"] - -# rust-version = "1.51.0" # The first version of Cargo that supports this field is 1.56.0 - -[metadata] -msrv = "1.51.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[features] -number = [] -bool = [] -vec = [] - -[dependencies] - -[badges] -maintenance = { status = "actively-developed" } diff --git a/estring/LICENSE b/estring/LICENSE deleted file mode 100644 index 8afbf27..0000000 --- a/estring/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 pleshevskiy - -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. diff --git a/estring/README.md b/estring/README.md deleted file mode 100644 index b49dbaa..0000000 --- a/estring/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# EString - -A simple way to parse a string using type annotations. - -This package was originally designed for [enve] - -[enve]: https://github.com/pleshevskiy/itconfig-rs/tree/redesign - -## Getting started - -```rust -use estring::{SepVec, EString}; - -type PlusVec = SepVec; -type MulVec = SepVec; - -fn main() -> Result<(), estring::ParseError> { - let res = EString::from("10+5*2+3") - .parse::>>()? - .iter() - .map(|m| m.iter().product::()) - .sum::(); - - assert_eq!(res, 23.0); - Ok(()) -} -``` - -You can use custom types as annotations! Just implement `TryFrom`! - -## Installation - -**The MSRV is 1.51.0** - -Add `estring = { version = "0.1", features = ["vec", "number"] }` as a -dependency in `Cargo.toml`. - -`Cargo.toml` example: - -```toml -[package] -name = "my-crate" -version = "0.1.0" -authors = ["Me "] - -[dependencies] -estring = { version = "0.1", features = ["vec", "number"] } -``` - -## License - -**MIT**. See [LICENSE](./LICENSE) to see the full text. - -## Contributors - -[pleshevskiy](https://github.com/pleshevskiy) (Dmitriy Pleshevskiy) – creator, -maintainer. diff --git a/estring/src/core.rs b/estring/src/core.rs deleted file mode 100644 index 68a2151..0000000 --- a/estring/src/core.rs +++ /dev/null @@ -1,99 +0,0 @@ -//! Contains the ``EString`` type, as well as the basic implementation of conversions to -//! string types -//! -#[cfg(any(feature = "number", feature = "bool"))] -pub mod prim; -#[cfg(any(feature = "number", feature = "bool"))] -pub use prim::*; - -#[cfg(feature = "vec")] -pub mod vec; -#[cfg(feature = "vec")] -pub use vec::*; - -use crate::ParseError; -use std::convert::{Infallible, TryFrom}; - -/// Wrapper under String type. -#[derive(Debug, Default, PartialEq, Eq, Clone)] -pub struct EString(pub String); - -impl EString { - /// Parses inner string by type annotations and returns result. - /// - /// # Errors - /// - /// Will return `Err` if estring cannot parse inner fragment - /// - #[inline] - pub fn parse>(self) -> Result { - let orig = self.0.clone(); - >::try_from(self).map_err(|_| ParseError(orig)) - } -} - -impl From for EString -where - T: std::fmt::Display, -{ - #[inline] - fn from(val: T) -> Self { - Self(val.to_string()) - } -} - -impl std::ops::Deref for EString { - type Target = String; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl TryFrom for String { - type Error = Infallible; - - #[inline] - fn try_from(s: EString) -> Result { - Ok(s.0) - } -} - -impl TryFrom for &'static str { - type Error = Infallible; - - #[inline] - fn try_from(s: EString) -> Result { - Ok(Box::leak(s.0.into_boxed_str())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_deref_to_string() { - let estr = EString::from("hello"); - assert_eq!(*estr, String::from("hello")); - } - - #[test] - fn should_parse_into_itself() { - let estr = EString::from("hello"); - match estr.parse::() { - Ok(res) => assert_eq!(res, EString::from("hello")), - _ => unreachable!(), - } - } - - #[test] - fn should_parse_into_string() { - let estr = EString::from("hello"); - match estr.parse::() { - Ok(res) => assert_eq!(res, String::from("hello")), - _ => unreachable!(), - } - } -} diff --git a/estring/src/core/prim.rs b/estring/src/core/prim.rs deleted file mode 100644 index ff19878..0000000 --- a/estring/src/core/prim.rs +++ /dev/null @@ -1,14 +0,0 @@ -//! Contains the implementations to primitive types (number, boolean) -//! -//! **NOTE**: Require the enabling of the same-name features -//! - -#[cfg(feature = "bool")] -mod bool; -#[cfg(feature = "bool")] -pub use self::bool::*; - -#[cfg(feature = "number")] -mod number; -#[cfg(feature = "number")] -pub use self::number::*; diff --git a/estring/src/core/prim/bool.rs b/estring/src/core/prim/bool.rs deleted file mode 100644 index f0aacc9..0000000 --- a/estring/src/core/prim/bool.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::core::EString; -use std::convert::TryFrom; - -impl TryFrom for bool { - type Error = (); - - #[inline] - fn try_from(s: EString) -> Result { - match s.to_lowercase().as_str() { - "true" | "t" | "yes" | "y" | "on" | "1" => Ok(true), - "false" | "f" | "no" | "n" | "off" | "0" | "" => Ok(false), - _ => Err(()), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::ParseError; - - #[test] - fn should_parse_bool_variable() { - let test_cases = [ - ("1", true), - ("0", false), - ("y", true), - ("f", false), - ("yes", true), - ("true", true), - ("false", false), - ("t", true), - ("f", false), - ("on", true), - ("off", false), - ]; - - for (val, expected) in test_cases { - let estr = EString::from(val); - match estr.parse::() { - Ok(res) => assert_eq!(res, expected), - _ => unreachable!(), - }; - } - } - - #[test] - fn should_throw_parse_error() { - let estr = EString::from("something"); - match estr.parse::() { - Err(ParseError(orig)) => { - assert_eq!(orig, String::from("something")); - } - _ => unreachable!(), - }; - } -} diff --git a/estring/src/core/prim/number.rs b/estring/src/core/prim/number.rs deleted file mode 100644 index 3b93389..0000000 --- a/estring/src/core/prim/number.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::core::EString; -use std::convert::TryFrom; - -#[doc(hidden)] -macro_rules! from_env_string_numbers_impl { - ($($ty:ty),+$(,)?) => { - $( - #[cfg(feature = "number")] - impl TryFrom for $ty { - type Error = <$ty as std::str::FromStr>::Err; - - #[inline] - fn try_from(s: EString) -> Result { - s.0.parse::() - } - } - )+ - }; -} - -#[rustfmt::skip] -from_env_string_numbers_impl![ - i8, i16, i32, i64, i128, isize, - u8, u16, u32, u64, u128, usize, - f32, f64 -]; - -#[cfg(test)] -mod tests { - use super::*; - use crate::ParseError; - - #[test] - fn should_parse_number() { - let estr = EString::from("-10"); - match estr.parse::() { - Ok(res) => assert_eq!(res, -10), - _ => unreachable!(), - }; - } - - #[test] - fn should_parse_float_number() { - let estr = EString::from("-0.15"); - match estr.parse::() { - #[allow(clippy::float_cmp)] - Ok(res) => assert_eq!(res, -0.15), - _ => unreachable!(), - }; - } - - #[test] - fn should_throw_parse_error() { - let estr = EString::from("-10"); - match estr.parse::() { - Err(ParseError(orig)) => { - assert_eq!(orig, String::from("-10")); - } - _ => unreachable!(), - }; - } -} diff --git a/estring/src/core/vec.rs b/estring/src/core/vec.rs deleted file mode 100644 index c59d45c..0000000 --- a/estring/src/core/vec.rs +++ /dev/null @@ -1,153 +0,0 @@ -//! Contains the implementations to vec type -//! -//! **NOTE**: Require the enabling of the `vec` features -//! - -use crate::core::EString; -use std::convert::TryFrom; -use std::fmt::Write; - -/// Wrapper for ``Vec`` to split string by a separator (`SEP`). -/// -/// **NOTE**: Required the enabling of the `vec` feature. -/// -/// # Examples -/// -/// ```rust -/// use estring::{SepVec, EString}; -/// -/// type CommaVec = SepVec; -/// -/// fn main() -> Result<(), estring::ParseError> { -/// let res = EString::from("1,2,3").parse::>()?; -/// assert_eq!(*res, vec![1, 2, 3]); -/// -/// Ok(()) -/// } -/// -/// ``` -/// -#[derive(Debug, PartialEq, Clone)] -pub struct SepVec(pub Vec); - -impl std::ops::Deref for SepVec { - type Target = Vec; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From> for SepVec { - #[inline] - fn from(vec: Vec) -> Self { - Self(vec) - } -} - -impl std::fmt::Display for SepVec -where - T: std::fmt::Display, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.iter().enumerate().try_for_each(|(i, part)| { - if i != 0 { - f.write_char(SEP)?; - } - - f.write_str(&part.to_string()) - }) - } -} - -impl TryFrom for SepVec -where - T: TryFrom + std::fmt::Display, -{ - type Error = T::Error; - - fn try_from(value: EString) -> Result { - let inner = value - .split(SEP) - .map(str::trim) - .map(EString::from) - .map(T::try_from) - .collect::, _>>()?; - Ok(Self(inner)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const COMMA: char = ','; - const SEMI: char = ';'; - - type CommaVec = SepVec; - type SemiVec = SepVec; - - #[test] - fn should_parse_into_vec() { - let estr = EString::from("a,b,c,d,e"); - match estr.parse::>() { - Ok(res) => assert_eq!(*res, vec!["a", "b", "c", "d", "e"]), - _ => unreachable!(), - }; - } - - #[test] - fn should_trim_identations_before_parsing() { - let input = " -a , b, c, -d,e"; - let estr = EString::from(input); - match estr.parse::>() { - Ok(res) => assert_eq!(*res, vec!["a", "b", "c", "d", "e"]), - _ => unreachable!(), - }; - } - - #[test] - fn should_parse_into_vector_of_vectors() { - let estr = EString::from("a,b; c,d,e; f,g"); - match estr.parse::>>() { - Ok(res) => assert_eq!( - res, - SemiVec::from(vec![ - CommaVec::from(vec!["a", "b"]), - CommaVec::from(vec!["c", "d", "e"]), - CommaVec::from(vec!["f", "g"]) - ]) - ), - _ => unreachable!(), - }; - } - - #[cfg(feature = "number")] - mod numbers { - use super::*; - use crate::ParseError; - - #[test] - fn should_parse_into_num_vec() { - let estr = EString::from("1,2,3,4,5"); - match estr.parse::>() { - Ok(res) => assert_eq!(*res, vec![1, 2, 3, 4, 5]), - _ => unreachable!(), - }; - } - - #[test] - fn should_throw_parse_vec_error() { - let estr = EString::from("1,2,3,4,5"); - match estr.parse::>() { - Err(ParseError(orig)) => { - assert_eq!(orig, String::from("1,2,3,4,5")); - } - _ => unreachable!(), - }; - } - } -} diff --git a/estring/src/error.rs b/estring/src/error.rs deleted file mode 100644 index a484b68..0000000 --- a/estring/src/error.rs +++ /dev/null @@ -1,19 +0,0 @@ -/// Failed to parse the specified string. -#[derive(Debug)] -pub struct ParseError(pub String); - -impl std::fmt::Display for ParseError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, r#"Failed to parse: "{}""#, self.0) - } -} - -impl std::error::Error for ParseError {} - -impl std::ops::Deref for ParseError { - type Target = String; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/estring/src/lib.rs b/estring/src/lib.rs deleted file mode 100644 index e38b638..0000000 --- a/estring/src/lib.rs +++ /dev/null @@ -1,72 +0,0 @@ -//! -//! # ``EString`` -//! -//! A simple way to parse a string using type annotations. -//! -//! This package was originally designed for [enve] -//! -//! [enve]: https://github.com/pleshevskiy/itconfig-rs -//! -//! ## Getting started -//! -//! ```rust -//! use estring::{SepVec, EString}; -//! -//! type PlusVec = SepVec; -//! type MulVec = SepVec; -//! -//! fn main() -> Result<(), estring::ParseError> { -//! let res = EString::from("10+5*2+3") -//! .parse::>>()? -//! .iter() -//! .map(|m| m.iter().product::()) -//! .sum::(); -//! -//! assert_eq!(res, 23.0); -//! Ok(()) -//! } -//! ``` -//! -//! ## Installation -//! -//! **The MSRV is 1.51.0** -//! -//! Add `estring = { version = "0.1", features = ["vec", "number"] }` as a -//! dependency in `Cargo.toml`. -//! -//! `Cargo.toml` example: -//! -//! ```toml -//! [package] -//! name = "my-crate" -//! version = "0.1.0" -//! authors = ["Me "] -//! -//! [dependencies] -//! estring = { version = "0.1", features = ["vec", "number"] } -//! ``` -//! -//! ## License -//! -//! **MIT**. -//! -//! See [LICENSE](./LICENSE) to see the full text. -//! -#![deny(clippy::pedantic)] -#![allow(clippy::module_name_repetitions)] -#![deny(missing_docs)] - -pub mod core; -mod error; - -pub use crate::core::*; -pub use crate::error::ParseError; - -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -}