Browse Source

Adding config parser (#18)

* Adding config parser

* Minor changes to improver the macros

* No unwrap calls
* Remove () from ifs statements

* Adding first version of the config parser

* Adding serde deserializer

* Prepare server to accept Unix socket connections

Prepare server to accept connections via TCP and Unix sockets at the
same time depending on the configuration.

* Add config format to parse

* Add unix socket support
César D. Rodas 2 years ago
parent
commit
4b3b8377b1

+ 326 - 178
Cargo.lock

@@ -18,6 +18,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
 name = "aspect"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -95,9 +104,9 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "base64"
@@ -140,9 +149,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.5.1"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
+checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
@@ -150,15 +159,49 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.5"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
+checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
 dependencies = [
  "cfg-if",
  "lazy_static",
 ]
 
 [[package]]
+name = "darling"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
+dependencies = [
+ "darling_core",
+ "darling_macro",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "derive_utils"
 version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -176,29 +219,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
 
 [[package]]
-name = "env_logger"
-version = "0.8.4"
+name = "flate2"
+version = "1.0.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
+checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
 dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "flexi_logger"
+version = "0.22.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee9a6796ff68a1014f6665dac55341820f26e63ec706e58bfaee468cf0ac174f"
+dependencies = [
+ "ansi_term",
  "atty",
- "humantime",
+ "glob",
+ "lazy_static",
  "log",
  "regex",
- "termcolor",
+ "rustversion",
+ "thiserror",
+ "time",
 ]
 
 [[package]]
-name = "flate2"
-version = "1.0.22"
+name = "fnv"
+version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
-dependencies = [
- "cfg-if",
- "crc32fast",
- "libc",
- "miniz_oxide",
-]
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "ftoa"
@@ -208,9 +259,9 @@ checksum = "ca45aac12b6c561b6289bc68957cb1db3dccf870e1951d590202de5e24f1dd35"
 
 [[package]]
 name = "futures"
-version = "0.3.19"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
+checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -223,9 +274,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.19"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -233,15 +284,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.19"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.19"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
+checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -251,15 +302,15 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.19"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
+checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.19"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
+checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -268,21 +319,21 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.19"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
+checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
 
 [[package]]
 name = "futures-task"
-version = "0.3.19"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
+checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
 
 [[package]]
 name = "futures-util"
-version = "0.3.19"
+version = "0.3.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
+checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -298,13 +349,13 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.3"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
+checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
 dependencies = [
  "cfg-if",
  "libc",
- "wasi",
+ "wasi 0.10.2+wasi-snapshot-preview1",
 ]
 
 [[package]]
@@ -321,9 +372,9 @@ checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
 
 [[package]]
 name = "hdrhistogram"
-version = "7.4.0"
+version = "7.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6490be71f07a5f62b564bc58e36953f675833df11c7e4a0647bee7a07ca1ec5e"
+checksum = "31672b7011be2c4f7456c4ddbcb40e7e9a4a9fad8efe49a6ebaf5f307d0109c0"
 dependencies = [
  "base64",
  "byteorder",
@@ -358,16 +409,16 @@ dependencies = [
 ]
 
 [[package]]
-name = "humantime"
-version = "2.1.0"
+name = "ident_case"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
 
 [[package]]
 name = "indexmap"
-version = "1.7.0"
+version = "1.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
+checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a"
 dependencies = [
  "autocfg",
  "hashbrown",
@@ -391,9 +442,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
 
 [[package]]
 name = "itoa"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
+checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
 
 [[package]]
 name = "lazy_static"
@@ -403,33 +454,34 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.112"
+version = "0.2.126"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
+checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
 
 [[package]]
 name = "lock_api"
-version = "0.4.6"
+version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
+checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
 dependencies = [
+ "autocfg",
  "scopeguard",
 ]
 
 [[package]]
 name = "log"
-version = "0.4.14"
+version = "0.4.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
 dependencies = [
  "cfg-if",
 ]
 
 [[package]]
 name = "memchr"
-version = "2.4.1"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
 name = "metered"
@@ -441,7 +493,7 @@ dependencies = [
  "atomic",
  "hdrhistogram",
  "metered-macro",
- "parking_lot",
+ "parking_lot 0.11.2",
  "serde",
 ]
 
@@ -467,16 +519,18 @@ dependencies = [
  "byteorder",
  "bytes",
  "crc32fast",
- "env_logger",
+ "flexi_logger",
  "futures",
  "glob",
  "log",
  "metered",
- "parking_lot",
+ "parking_lot 0.11.2",
  "rand",
+ "redis-config-parser",
  "redis-zero-protocol-parser",
  "seahash",
  "serde",
+ "serde-enum-str",
  "serde_json",
  "serde_prometheus",
  "strum",
@@ -495,61 +549,40 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.4.4"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
 dependencies = [
  "adler",
- "autocfg",
 ]
 
 [[package]]
 name = "mio"
-version = "0.7.14"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
+checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
 dependencies = [
  "libc",
  "log",
- "miow",
- "ntapi",
- "winapi",
-]
-
-[[package]]
-name = "miow"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
-dependencies = [
- "winapi",
+ "wasi 0.11.0+wasi-snapshot-preview1",
+ "windows-sys",
 ]
 
 [[package]]
 name = "nom"
-version = "7.1.0"
+version = "7.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
+checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
 dependencies = [
  "memchr",
  "minimal-lexical",
- "version_check",
-]
-
-[[package]]
-name = "ntapi"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
-dependencies = [
- "winapi",
 ]
 
 [[package]]
 name = "num-traits"
-version = "0.2.14"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
 dependencies = [
  "autocfg",
 ]
@@ -565,10 +598,19 @@ dependencies = [
 ]
 
 [[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.9.0"
+version = "1.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
+checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
 
 [[package]]
 name = "parking_lot"
@@ -578,7 +620,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
 dependencies = [
  "instant",
  "lock_api",
- "parking_lot_core",
+ "parking_lot_core 0.8.5",
+]
+
+[[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 0.9.3",
 ]
 
 [[package]]
@@ -596,10 +648,23 @@ dependencies = [
 ]
 
 [[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 = "pin-project-lite"
-version = "0.2.7"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
 
 [[package]]
 name = "pin-utils"
@@ -609,38 +674,37 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
 [[package]]
 name = "ppv-lite86"
-version = "0.2.15"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
+checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.36"
+version = "1.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
 dependencies = [
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.13"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82fa34aad6be8c6fe936865499578c8a927795b167973e837c149af879fac2fd"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "rand"
-version = "0.8.4"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 dependencies = [
  "libc",
  "rand_chacha",
  "rand_core",
- "rand_hc",
 ]
 
 [[package]]
@@ -663,12 +727,12 @@ dependencies = [
 ]
 
 [[package]]
-name = "rand_hc"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+name = "redis-config-parser"
+version = "0.1.0"
 dependencies = [
- "rand_core",
+ "serde",
+ "serde-enum-str",
+ "thiserror",
 ]
 
 [[package]]
@@ -679,18 +743,18 @@ checksum = "299d79f6c9095164339b8ed3c47951772538a375e6811e76ffe9a4544f2cdbf5"
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.10"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.4"
+version = "1.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -699,9 +763,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.25"
+version = "0.6.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
 
 [[package]]
 name = "rustversion"
@@ -711,9 +775,9 @@ checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
 
 [[package]]
 name = "ryu"
-version = "1.0.9"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
 
 [[package]]
 name = "scopeguard"
@@ -729,18 +793,48 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
 
 [[package]]
 name = "serde"
-version = "1.0.136"
+version = "1.0.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
+name = "serde-attributes"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3aba2af3c3b9cd6f3a919056dac6005b71fceecc1cdfa65c4df3912f64e07e60"
+dependencies = [
+ "darling_core",
+ "serde-rename-rule",
+ "syn",
+]
+
+[[package]]
+name = "serde-enum-str"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2a41bf2fc78a58589b9a6948bfc918c9b2dc918732f2ac14eed982ffb876b39"
+dependencies = [
+ "darling",
+ "proc-macro2",
+ "quote",
+ "serde-attributes",
+ "syn",
+]
+
+[[package]]
+name = "serde-rename-rule"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd2930103714ccef4f1fe5b6a5f2b6fdcfe462a6c802464714bd41e5b5097c33"
+
+[[package]]
 name = "serde_derive"
-version = "1.0.136"
+version = "1.0.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -749,11 +843,11 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.73"
+version = "1.0.81"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
+checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
 dependencies = [
- "itoa 1.0.1",
+ "itoa 1.0.2",
  "ryu",
  "serde",
 ]
@@ -784,15 +878,15 @@ dependencies = [
 
 [[package]]
 name = "slab"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
+checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
 
 [[package]]
 name = "smallvec"
-version = "1.7.0"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
+checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
 
 [[package]]
 name = "snafu"
@@ -816,6 +910,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "socket2"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
 name = "strum"
 version = "0.24.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -836,13 +940,13 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.84"
+version = "1.0.96"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
+checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
 dependencies = [
  "proc-macro2",
  "quote",
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
@@ -857,28 +961,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "termcolor"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
 name = "thiserror"
-version = "1.0.30"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.30"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -886,10 +981,28 @@ dependencies = [
 ]
 
 [[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 = "tokio"
-version = "1.15.0"
+version = "1.19.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
+checksum = "95eec79ea28c00a365f539f1961e9278fbcaf81c0ff6aaf0e93c181352446948"
 dependencies = [
  "bytes",
  "libc",
@@ -897,9 +1010,10 @@ dependencies = [
  "mio",
  "num_cpus",
  "once_cell",
- "parking_lot",
+ "parking_lot 0.12.1",
  "pin-project-lite",
  "signal-hook-registry",
+ "socket2",
  "tokio-macros",
  "tracing",
  "winapi",
@@ -907,9 +1021,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-macros"
-version = "1.7.0"
+version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
+checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -918,9 +1032,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-stream"
-version = "0.1.8"
+version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3"
+checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9"
 dependencies = [
  "futures-core",
  "pin-project-lite",
@@ -929,9 +1043,9 @@ dependencies = [
 
 [[package]]
 name = "tokio-util"
-version = "0.6.9"
+version = "0.6.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
+checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507"
 dependencies = [
  "bytes",
  "futures-core",
@@ -945,9 +1059,9 @@ dependencies = [
 
 [[package]]
 name = "tracing"
-version = "0.1.29"
+version = "0.1.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
+checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
 dependencies = [
  "cfg-if",
  "pin-project-lite",
@@ -956,36 +1070,36 @@ dependencies = [
 
 [[package]]
 name = "tracing-core"
-version = "0.1.21"
+version = "0.1.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
+checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f"
 dependencies = [
  "lazy_static",
 ]
 
 [[package]]
-name = "unicode-segmentation"
-version = "1.8.0"
+name = "unicode-ident"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
+checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
 
 [[package]]
-name = "unicode-xid"
-version = "0.2.2"
+name = "unicode-segmentation"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
 
 [[package]]
-name = "version_check"
-version = "0.9.3"
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
 
 [[package]]
 name = "wasi"
-version = "0.10.2+wasi-snapshot-preview1"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "winapi"
@@ -1004,16 +1118,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
-name = "winapi-util"
-version = "0.1.5"
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows-sys"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
 dependencies = [
- "winapi",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_msvc",
 ]
 
 [[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "windows_aarch64_msvc"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+
+[[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.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+
+[[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.36.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"

+ 8 - 3
Cargo.toml

@@ -7,8 +7,10 @@ edition = "2018"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+bytes = "1"
 byteorder = "1.2.2"
 redis-zero-protocol-parser = "^0.2"
+redis-config-parser = {path = "redis-config-parser"}
 tokio={version="1", features = ["full", "tracing"] }
 parking_lot="0.11.2"
 tokio-util={version="^0.6", features = ["full"] }
@@ -16,15 +18,18 @@ crc32fast="1.3.2"
 futures = { version = "0.3.0", features = ["thread-pool"]}
 tokio-stream="0.1"
 seahash = "4"
-log="0.4"
 glob="0.3.0"
+flexi_logger="0.22.5"
 metered="0.8.0"
 serde="1.0.136"
+serde-enum-str = "0.2"
 serde_json = "1.0.70"
 serde_prometheus="0.1.6"
-env_logger = "0.8.4"
-bytes = "1"
 rand = "0.8.0"
+log="0.4"
 thiserror = "1.0.30"
 strum = "0.24"
 strum_macros = "0.24"
+
+[workspace]
+members = ["redis-config-parser"]

+ 86 - 0
redis-config-parser/Cargo.lock

@@ -0,0 +1,86 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redis-config-parser"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.138"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.138"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"

+ 14 - 0
redis-config-parser/Cargo.toml

@@ -0,0 +1,14 @@
+[package]
+name = "redis-config-parser"
+version = "0.1.0"
+authors = ["Cesar Rodas <cesar@rodasm.com.py>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+serde = { version="1.0.97", features = ["derive"]}
+thiserror = "1.0.30"
+
+[dev-dependencies]
+serde-enum-str = "0.2"

+ 59 - 0
redis-config-parser/src/de/args.rs

@@ -0,0 +1,59 @@
+use super::{value::ValueDeserializer, Error};
+use crate::parser::Args;
+use serde::de;
+use std::borrow::Cow;
+
+pub struct ArgsDeserializer<'a> {
+    pub input: Args<'a>,
+}
+
+impl<'de> de::Deserializer<'de> for ArgsDeserializer<'de> {
+    type Error = Error;
+
+    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        match self.input {
+            Args::None => visitor.visit_none(),
+            Args::Single(v) => {
+                let des = ValueDeserializer { input: v };
+                des.deserialize_any(visitor)
+            }
+            Args::Multiple(v) => visitor.visit_seq(SeqArgsDeserializer { input: v, id: 0 }),
+        }
+    }
+
+    serde::forward_to_deserialize_any! {
+        bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+        bytes byte_buf map unit newtype_struct
+        ignored_any unit_struct tuple_struct tuple option identifier
+        enum struct
+    }
+}
+
+pub struct SeqArgsDeserializer<'a> {
+    input: Vec<Cow<'a, str>>,
+    id: usize,
+}
+
+impl<'de> de::SeqAccess<'de> for SeqArgsDeserializer<'de> {
+    type Error = Error;
+
+    fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Error>
+    where
+        T: de::DeserializeSeed<'de>,
+    {
+        let value = match self.input.get(self.id) {
+            Some(v) => v,
+            None => return Ok(None),
+        };
+        let out = seed
+            .deserialize(ValueDeserializer {
+                input: value.clone(),
+            })
+            .map(Some);
+        self.id += 1;
+        out
+    }
+}

+ 268 - 0
redis-config-parser/src/de/mod.rs

@@ -0,0 +1,268 @@
+use crate::parser::{parse, ConfigValue, Error as ParsingError};
+use args::ArgsDeserializer;
+use serde::de::{self, IntoDeserializer};
+use std::str;
+use thiserror::Error as ThisError;
+
+mod args;
+mod value;
+
+/// Errors that can occur when deserializing a type.
+#[derive(Debug, PartialEq, Eq, Clone, ThisError)]
+pub enum Error {
+    /// EOF was reached when looking for a value
+    #[error("Unexpected end of file")]
+    UnexpectedEof(ErrorInfo),
+
+    #[error("End of stream")]
+    EndOfStream,
+
+    /// Custom errors
+    #[error("Custom error")]
+    Custom(ErrorInfo),
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub struct ErrorInfo {
+    line: Option<usize>,
+    col: usize,
+    at: Option<usize>,
+    message: String,
+}
+
+pub fn from_str<'de, T>(s: &'de str) -> Result<T, Error>
+where
+    T: de::Deserialize<'de>,
+{
+    from_slice(s.as_bytes())
+}
+
+pub fn from_slice<'de, T>(bytes: &'de [u8]) -> Result<T, Error>
+where
+    T: de::Deserialize<'de>,
+{
+    let mut d = Deserializer::new(bytes);
+    let ret = T::deserialize(&mut d)?;
+    d.end()?;
+    Ok(ret)
+}
+
+/// Deserialization implementation for Config protocol
+pub struct Deserializer<'a> {
+    input: &'a [u8],
+}
+
+impl<'a> Deserializer<'a> {
+    pub fn new(input: &'a [u8]) -> Self {
+        Self { input }
+    }
+
+    pub fn end(&mut self) -> Result<(), Error> {
+        Ok(())
+    }
+
+    /// Return the next value
+    #[inline]
+    pub fn parse_next(&mut self) -> Result<ConfigValue<'a>, Error> {
+        match parse(self.input) {
+            Ok((new_stream, value)) => {
+                self.input = new_stream;
+                Ok(value)
+            }
+            Err(ParsingError::Partial) => Err(Error::EndOfStream),
+        }
+    }
+}
+
+impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
+    type Error = Error;
+
+    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        visitor.visit_map(MapVisitor {
+            de: self,
+            last_value: None,
+        })
+    }
+
+    serde::forward_to_deserialize_any! {
+        bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+        bytes byte_buf map unit newtype_struct
+        ignored_any unit_struct tuple_struct tuple option identifier
+        enum struct
+    }
+}
+
+struct MapVisitor<'de, 'b> {
+    de: &'b mut Deserializer<'de>,
+    last_value: Option<ConfigValue<'de>>,
+}
+
+impl<'de, 'b> de::MapAccess<'de> for MapVisitor<'de, 'b> {
+    type Error = Error;
+
+    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+    where
+        K: de::DeserializeSeed<'de>,
+    {
+        match self.de.parse_next() {
+            Ok(v) => {
+                let name = v.name.clone();
+                self.last_value = Some(v);
+                seed.deserialize(name.into_deserializer()).map(Some)
+            }
+            _ => Ok(None),
+        }
+    }
+
+    #[inline]
+    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
+    where
+        V: de::DeserializeSeed<'de>,
+    {
+        seed.deserialize(ArgsDeserializer {
+            input: self.last_value.as_ref().unwrap().args.clone(),
+        })
+    }
+}
+
+impl Error {
+    pub fn custom(at: Option<usize>, s: String) -> Self {
+        Self::Custom(ErrorInfo {
+            line: None,
+            col: 0,
+            at,
+            message: s,
+        })
+    }
+}
+
+impl de::Error for Error {
+    fn custom<T: std::fmt::Display>(msg: T) -> Error {
+        Error::custom(None, msg.to_string())
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use serde::Deserialize;
+    use serde_enum_str::Deserialize_enum_str;
+
+    #[derive(Deserialize, Debug)]
+    pub struct Foo {
+        foo: Vec<i32>,
+        bar: u8,
+        xxx: Option<String>,
+    }
+
+    #[derive(Deserialize, Debug, Default)]
+    pub struct SaveInfo(pub u64, pub u64);
+
+    #[derive(Deserialize_enum_str, Debug, PartialEq)]
+    pub enum AppendFsync {
+        #[serde(rename = "always")]
+        Always,
+        #[serde(rename = "everysec")]
+        EverySecond,
+        #[serde(other, rename = "no")]
+        No,
+    }
+
+    #[derive(Deserialize_enum_str, Debug, PartialEq)]
+    pub enum LogLevel {
+        #[serde(rename = "debug")]
+        Debug,
+        #[serde(rename = "verbose")]
+        Verbose,
+        #[serde(rename = "notice")]
+        Notice,
+        #[serde(rename = "warning")]
+        Warning,
+    }
+
+    impl Default for LogLevel {
+        fn default() -> Self {
+            Self::Warning
+        }
+    }
+
+    impl Default for AppendFsync {
+        fn default() -> Self {
+            Self::No
+        }
+    }
+
+    #[derive(Deserialize, Debug, Default)]
+    pub struct Config {
+        #[serde(rename = "always-show-logo")]
+        always_show_logo: bool,
+        #[serde(rename = "notify-keyspace-events")]
+        notify_keyspace_events: String,
+        daemonize: bool,
+        port: u32,
+        save: SaveInfo,
+        #[serde(rename = "appendfsync")]
+        append_fsync: AppendFsync,
+        #[serde(flatten)]
+        log: Log,
+        databases: u8,
+    }
+
+    #[derive(Deserialize, Debug, Default)]
+    pub struct Log {
+        #[serde(rename = "loglevel")]
+        level: LogLevel,
+        #[serde(rename = "logfile")]
+        file: String,
+    }
+
+    #[test]
+    fn de() {
+        let x: Foo = from_str("foo 32 44 12\r\nbar 32\r\n").unwrap();
+        assert_eq!(32, x.bar);
+        assert_eq!(None, x.xxx);
+        assert_eq!(3, x.foo.len());
+    }
+
+    #[test]
+    fn real_config() {
+        let x: Config = from_str(
+            "always-show-logo yes
+        notify-keyspace-events KEA
+        daemonize no
+        pidfile /var/run/redis.pid
+        port 24611
+        timeout 0
+        bind 127.0.0.1
+        loglevel verbose
+        logfile '' 
+        databases 16
+        latency-monitor-threshold 1
+        save 60 10000
+        rdbcompression yes
+        dbfilename dump.rdb
+        dir ./tests/tmp/server.64463.1
+        slave-serve-stale-data yes
+        appendonly no
+        appendfsync everysec 
+        no-appendfsync-on-rewrite no
+        activerehashing yes
+        unixsocket /home/crodas/redis/tests/tmp/server.64463.1/socket
+        ",
+        )
+        .unwrap();
+        assert!(x.always_show_logo);
+        assert_eq!(60, x.save.0);
+        assert_eq!(10_000, x.save.1);
+        assert_eq!(24_611, x.port);
+        assert_eq!("KEA", x.notify_keyspace_events);
+        assert_eq!(AppendFsync::EverySecond, x.append_fsync);
+        assert!(!x.daemonize);
+        assert_eq!(LogLevel::Verbose, x.log.level);
+        assert_eq!("", x.log.file);
+        assert_eq!(16, x.databases);
+    }
+}

+ 32 - 0
redis-config-parser/src/de/value.rs

@@ -0,0 +1,32 @@
+use super::Error;
+use serde::de;
+use std::{borrow::Cow, str::FromStr};
+
+pub struct ValueDeserializer<'a> {
+    pub input: Cow<'a, str>,
+}
+
+impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
+    type Error = Error;
+
+    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+    where
+        V: de::Visitor<'de>,
+    {
+        match i64::from_str(&self.input) {
+            Ok(number) => visitor.visit_i64(number),
+            Err(_) => match self.input.to_ascii_lowercase().as_str() {
+                "true" | "yes" => visitor.visit_bool(true),
+                "false" | "no" => visitor.visit_bool(false),
+                _ => visitor.visit_str(&self.input),
+            },
+        }
+    }
+
+    serde::forward_to_deserialize_any! {
+        bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+        bytes byte_buf map unit newtype_struct
+        ignored_any unit_struct tuple_struct tuple option identifier
+        enum struct
+    }
+}

+ 2 - 0
redis-config-parser/src/lib.rs

@@ -0,0 +1,2 @@
+pub mod de;
+pub mod parser;

+ 218 - 0
redis-config-parser/src/parser.rs

@@ -0,0 +1,218 @@
+use std::borrow::Cow;
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum Error {
+    /// The data is incomplete. This it not an error per-se, but rather a
+    /// mechanism to let the caller know they should keep buffering data before
+    /// calling the parser again.
+    Partial,
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub enum Args<'a> {
+    None,
+    Single(Cow<'a, str>),
+    Multiple(Vec<Cow<'a, str>>),
+}
+
+#[derive(Debug, PartialEq, Clone)]
+pub struct ConfigValue<'a> {
+    pub name: Cow<'a, str>,
+    pub args: Args<'a>,
+}
+
+macro_rules! skip {
+    ($bytes:ident, $to_skip:expr) => {{
+        let len = $bytes.len();
+        let mut ret = &$bytes[len..];
+
+        for i in 0..len {
+            if $to_skip.iter().find(|x| **x == $bytes[i]).is_none() {
+                ret = &$bytes[i..];
+                break;
+            }
+        }
+        ret
+    }};
+}
+
+macro_rules! read_until {
+    ($bytes:ident, $stop:expr) => {{
+        let len = $bytes.len();
+        let mut result = None;
+        for i in 0..len {
+            if $stop.iter().find(|x| **x == $bytes[i]).is_some() {
+                result = Some((&$bytes[i..], String::from_utf8_lossy(&$bytes[0..i])));
+                break;
+            }
+        }
+
+        if let Some(result) = result {
+            result
+        } else {
+            return Err(Error::Partial);
+        }
+    }};
+}
+
+pub fn parse(bytes: &'_ [u8]) -> Result<(&'_ [u8], ConfigValue<'_>), Error> {
+    let bytes = skip!(bytes, vec![b' ', b'\t', b'\r', b'\n']);
+    let (bytes, name) = read_until!(bytes, vec![b' ', b'\t', b'\r']);
+    let bytes = skip!(bytes, vec![b' ', b'\t', b'\r']);
+
+    let mut args = vec![];
+    let len = bytes.len();
+    let mut i = 0;
+
+    #[allow(unused_variables)]
+    #[allow(unreachable_patterns)]
+    loop {
+        if i >= len {
+            return Err(Error::Partial);
+        }
+        match bytes[i] {
+            b' ' | b'\t' | b'\r' => {}
+            b'\n' => {
+                i += 1;
+                break;
+            }
+            b'"' | b'\'' => {
+                let e = i;
+                let stop_at = bytes[e];
+                i += 1;
+                loop {
+                    if i >= len {
+                        return Err(Error::Partial);
+                    }
+                    match bytes[i] {
+                        stop_at => {
+                            args.push(String::from_utf8_lossy(&bytes[e + 1..i]));
+                            break;
+                        }
+                        b'\\' => i += 1,
+                        _ => continue,
+                    };
+                }
+            }
+            _ => {
+                let e = i;
+                loop {
+                    if i >= len {
+                        return Err(Error::Partial);
+                    }
+                    if bytes[i] == b' '
+                        || bytes[i] == b'\t'
+                        || bytes[i] == b'\r'
+                        || bytes[i] == b'\n'
+                    {
+                        args.push(String::from_utf8_lossy(&bytes[e..i]));
+                        i -= 1;
+                        break;
+                    }
+                    i += 1;
+                }
+            }
+        };
+
+        i += 1;
+    }
+
+    let bytes = &bytes[i..];
+
+    let args = match args.len() {
+        0 => Args::None,
+        1 => Args::Single(args[0].clone()),
+        _ => Args::Multiple(args),
+    };
+
+    Ok((bytes, ConfigValue { name, args }))
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_parse_partial() {
+        let data = b"foo bar foo";
+        assert_eq!(Err(Error::Partial), parse(data));
+    }
+
+    #[test]
+    fn test_parse_single_argument() {
+        let data = b"foo bar\r\nsomething";
+        let (bytes, config) = parse(data).unwrap();
+        assert_eq!("foo", config.name);
+        assert_eq!(Args::Single("bar".into()), config.args);
+        assert_eq!(b"something", bytes);
+    }
+
+    #[test]
+    fn test_parse_multi_argument() {
+        let data = b"foo bar something\r\n";
+        let (bytes, config) = parse(data).unwrap();
+        assert_eq!("foo", config.name);
+        assert_eq!(
+            Args::Multiple(vec!["bar".into(), "something".into()]),
+            config.args
+        );
+        assert_eq!(b"", bytes);
+    }
+
+    #[test]
+    fn test_real_config() {
+        let mut data: &[u8] = b"
+        always-show-logo yes
+        notify-keyspace-events KEA
+        daemonize no
+        pidfile /var/run/redis.pid
+        port 24611
+        timeout 0
+        bind 127.0.0.1
+        loglevel verbose
+        logfile ''
+        databases 16
+        latency-monitor-threshold 1
+        save 60 10000
+        rdbcompression yes
+        dbfilename dump.rdb
+        dir ./tests/tmp/server.64463.1
+        slave-serve-stale-data yes
+        appendonly no
+        appendfsync everysec
+        no-appendfsync-on-rewrite no
+        activerehashing yes
+        unixsocket /home/crodas/redis/tests/tmp/server.64463.1/socket
+        ";
+        vec![
+            "always-show-logo",
+            "notify-keyspace-events",
+            "daemonize",
+            "pidfile",
+            "port",
+            "timeout",
+            "bind",
+            "loglevel",
+            "logfile",
+            "databases",
+            "latency-monitor-threshold",
+            "save",
+            "rdbcompression",
+            "dbfilename",
+            "dir",
+            "slave-serve-stale-data",
+            "appendonly",
+            "appendfsync",
+            "no-appendfsync-on-rewrite",
+            "activerehashing",
+            "unixsocket",
+        ]
+        .iter()
+        .map(|command| {
+            let (bytes, config) = parse(data).unwrap();
+            assert_eq!(*command, config.name);
+            data = bytes;
+        })
+        .for_each(drop);
+    }
+}

+ 94 - 0
src/config.rs

@@ -0,0 +1,94 @@
+//! # Redis Config parsing
+//!
+//! This module loads and parses the config, compatible with Redis format, to run the service
+use crate::error::Error;
+use redis_config_parser::de::from_slice;
+use serde::Deserialize;
+use serde_enum_str::Deserialize_enum_str;
+use strum_macros::Display;
+
+/// Config
+///
+/// Holds the parsed configuration to start the service
+#[derive(Deserialize, Debug, Clone)]
+pub struct Config {
+    /// Run the server as a deamon
+    pub daemonize: bool,
+    /// Port to listen
+    pub port: u32,
+    /// List of addresses to bind
+    pub bind: Vec<String>,
+    /// Logging settings
+    #[serde(flatten)]
+    pub log: Log,
+    /// Number of databases
+    pub databases: u8,
+    /// Unix socket
+    pub unixsocket: Option<String>,
+}
+
+impl Config {
+    /// Returns all addresses to bind
+    pub fn get_tcp_hostnames(&self) -> Vec<String> {
+        self.bind
+            .iter()
+            .map(|host| format!("{}:{}", host, self.port))
+            .collect::<Vec<String>>()
+    }
+}
+
+impl Default for Config {
+    fn default() -> Self {
+        Self {
+            daemonize: false,
+            port: 6379,
+            bind: vec!["127.0.0.1".to_owned()],
+            log: Log::default(),
+            databases: 16,
+            unixsocket: None,
+        }
+    }
+}
+
+/// Log levels
+#[derive(Deserialize_enum_str, Debug, PartialEq, Clone, Display)]
+pub enum LogLevel {
+    /// Trace
+    #[serde(rename = "trace")]
+    Trace,
+    /// Debug
+    #[serde(rename = "debug")]
+    Debug,
+    /// Verbose
+    #[serde(rename = "verbose")]
+    Verbose,
+    /// Notice
+    #[serde(rename = "notice")]
+    Notice,
+    /// Warning
+    #[serde(rename = "warning")]
+    Warning,
+}
+
+impl Default for LogLevel {
+    fn default() -> Self {
+        Self::Debug
+    }
+}
+
+/// Logging settings
+#[derive(Deserialize, Debug, Default, Clone)]
+pub struct Log {
+    /// Log level
+    #[serde(rename = "loglevel")]
+    pub level: LogLevel,
+    /// File where to store the log
+    #[serde(rename = "logfile")]
+    pub file: Option<String>,
+}
+
+/// Loads and parses the config from a file path
+pub async fn parse(path: String) -> Result<Config, Error> {
+    let content = tokio::fs::read(path).await?;
+    Ok(from_slice(&content)?)
+}

+ 4 - 5
src/connection/connections.rs

@@ -5,8 +5,7 @@
 use super::{pubsub_connection::PubsubClient, pubsub_server::Pubsub, Connection, ConnectionInfo};
 use crate::{db::pool::Databases, db::Db, dispatcher::Dispatcher, value::Value};
 use parking_lot::RwLock;
-use std::{collections::BTreeMap, net::SocketAddr, sync::Arc};
-
+use std::{collections::BTreeMap, sync::Arc};
 use tokio::sync::mpsc;
 
 /// Connections struct
@@ -53,10 +52,10 @@ impl Connections {
     }
 
     /// Creates a new connection
-    pub fn new_connection(
+    pub fn new_connection<T: ToString>(
         self: &Arc<Connections>,
         db: Arc<Db>,
-        addr: SocketAddr,
+        addr: T,
     ) -> (mpsc::Receiver<Value>, Arc<Connection>) {
         let mut id = self.counter.write();
         *id += 1;
@@ -65,7 +64,7 @@ impl Connections {
 
         let conn = Arc::new(Connection {
             id: *id,
-            addr,
+            addr: addr.to_string(),
             all_connections: self.clone(),
             info: RwLock::new(ConnectionInfo::new(db.new_db_instance(*id))),
             pubsub_client: PubsubClient::new(pubsub_sender),

+ 2 - 2
src/connection/mod.rs

@@ -2,7 +2,7 @@
 use crate::{db::Db, error::Error, value::Value};
 use bytes::Bytes;
 use parking_lot::RwLock;
-use std::{collections::HashSet, net::SocketAddr, sync::Arc};
+use std::{collections::HashSet, sync::Arc};
 
 use self::pubsub_server::Pubsub;
 
@@ -46,7 +46,7 @@ pub struct ConnectionInfo {
 pub struct Connection {
     id: u128,
     all_connections: Arc<connections::Connections>,
-    addr: SocketAddr,
+    addr: String,
     info: RwLock<ConnectionInfo>,
     pubsub_client: pubsub_connection::PubsubClient,
 }

+ 14 - 0
src/error.rs

@@ -7,6 +7,12 @@ use thiserror::Error;
 /// Redis errors
 #[derive(Debug, PartialEq, Error)]
 pub enum Error {
+    /// IO Error
+    #[error("IO error {0}")]
+    Io(String),
+    /// Config
+    #[error("Config error {0}")]
+    Config(#[from] redis_config_parser::de::Error),
     /// A command is not found
     #[error("Command {0} not found")]
     CommandNotFound(String),
@@ -63,6 +69,12 @@ pub enum Error {
     Cursor(#[from] crate::value::cursor::Error),
 }
 
+impl From<std::io::Error> for Error {
+    fn from(err: std::io::Error) -> Self {
+        Self::Io(err.to_string())
+    }
+}
+
 impl From<Error> for Value {
     fn from(value: Error) -> Value {
         let err_type = match value {
@@ -88,6 +100,8 @@ impl From<Error> for Value {
             Error::NotFound => "no such key".to_owned(),
             Error::NotSuchDatabase => "DB index is out of range".to_owned(),
             Error::NestedTx => "calls can not be nested".to_owned(),
+            Error::Io(io) => format!("io error: {}", io),
+            Error::Config(c) => format!("failed to parse config: {}", c),
             Error::PubsubOnly(x) => format!("Can't execute '{}': only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT / RESET are allowed in this context", x),
             Error::WrongArgument(x, y) => format!(
                 "Unknown subcommand or wrong number of arguments for '{}'. Try {} HELP.",

+ 1 - 0
src/lib.rs

@@ -5,6 +5,7 @@
 #![allow(warnings)]
 
 pub mod cmd;
+pub mod config;
 pub mod connection;
 pub mod db;
 pub mod dispatcher;

+ 24 - 8
src/main.rs

@@ -1,13 +1,29 @@
-use microredis::server;
-use std::{env, error::Error};
+use flexi_logger::{FileSpec, Logger};
+use microredis::{
+    config::{parse, Config},
+    error::Error,
+    server,
+};
+use std::env;
 
 #[tokio::main]
-async fn main() -> Result<(), Box<dyn Error>> {
-    let addr = env::args()
-        .nth(1)
-        .unwrap_or_else(|| "127.0.0.1:8080".to_string());
+async fn main() -> Result<(), Error> {
+    let config = if let Some(path) = env::args().nth(1) {
+        parse(path).await?
+    } else {
+        Config::default()
+    };
 
-    env_logger::init();
+    let logger = Logger::try_with_str(config.log.level.to_string()).unwrap();
 
-    server::serve(addr).await
+    if let Some(log_path) = config.log.file.as_ref() {
+        logger
+            .log_to_file(FileSpec::try_from(log_path).unwrap())
+            .start()
+            .unwrap();
+    } else {
+        logger.start().unwrap();
+    }
+
+    server::serve(config).await
 }

+ 143 - 60
src/server.rs

@@ -3,18 +3,22 @@
 //! Redis TCP server. This module also includes a simple HTTP server to dump the prometheus
 //! metrics.
 use crate::{
-    connection::{connections::Connections, ConnectionStatus},
-    db::pool::Databases,
+    config::Config,
+    connection::{connections::Connections, Connection, ConnectionStatus},
+    db::{pool::Databases, Db},
+    error::Error,
     value::Value,
 };
 use bytes::{Buf, Bytes, BytesMut};
-use futures::SinkExt;
+use futures::{channel::mpsc::Receiver, future, SinkExt};
 use log::{info, trace, warn};
 use redis_zero_protocol_parser::{parse_server, Error as RedisError};
-use std::{error::Error, io, sync::Arc};
+use std::{io, net::SocketAddr, sync::Arc};
+#[cfg(unix)]
+use tokio::net::UnixListener;
 use tokio::{
     io::{AsyncReadExt, AsyncWriteExt},
-    net::TcpListener,
+    net::{TcpListener, TcpStream},
     time::{sleep, Duration},
 };
 use tokio_stream::StreamExt;
@@ -60,7 +64,7 @@ impl Decoder for RedisParser {
 ///
 /// The incoming HTTP request is discarded and the response is always the metrics in a prometheus
 /// format
-pub async fn server_metrics(all_connections: Arc<Connections>) {
+async fn server_metrics(all_connections: Arc<Connections>) -> Result<(), Error> {
     info!("Listening on 127.0.0.1:7878 for metrics");
     let listener = tokio::net::TcpListener::bind("127.0.0.1:7878")
         .await
@@ -96,6 +100,114 @@ pub async fn server_metrics(all_connections: Arc<Connections>) {
         let _ = stream.write_all(response.as_bytes()).await;
         let _ = stream.flush().await;
     }
+
+    Ok(())
+}
+
+/// Spawn the TCP/IP micro-redis server.
+async fn serve_tcp(
+    addr: &str,
+    default_db: Arc<Db>,
+    all_connections: Arc<Connections>,
+) -> Result<(), Error> {
+    let listener = TcpListener::bind(addr).await?;
+    info!("Listening on {}", addr);
+    loop {
+        match listener.accept().await {
+            Ok((socket, addr)) => {
+                let mut transport = Framed::new(socket, RedisParser);
+                let all_connections = all_connections.clone();
+                let default_db = default_db.clone();
+
+                tokio::spawn(async move {
+                    handle_new_connection(transport, all_connections, default_db, addr).await;
+                });
+            }
+            Err(e) => println!("error accepting socket; error = {:?}", e),
+        }
+    }
+
+    Ok(())
+}
+
+#[cfg(unix)]
+async fn serve_unixsocket(
+    file: &str,
+    default_db: Arc<Db>,
+    all_connections: Arc<Connections>,
+) -> Result<(), Error> {
+    info!("Listening on unix://{}", file);
+    let listener = UnixListener::bind(file)?;
+    loop {
+        match listener.accept().await {
+            Ok((socket, addr)) => {
+                let mut transport = Framed::new(socket, RedisParser);
+                let all_connections = all_connections.clone();
+                let default_db = default_db.clone();
+
+                tokio::spawn(async move {
+                    handle_new_connection(
+                        transport,
+                        all_connections,
+                        default_db,
+                        addr.as_pathname()
+                            .and_then(|p| p.to_str())
+                            .unwrap_or_default(),
+                    )
+                    .await;
+                });
+            }
+            Err(e) => println!("error accepting socket; error = {:?}", e),
+        }
+    }
+    Ok(())
+}
+
+/// Handles a new connection
+///
+/// The new connection can be created from a new TCP or Unix stream.
+#[inline]
+async fn handle_new_connection<T: AsyncReadExt + AsyncWriteExt + Unpin, A: ToString>(
+    mut transport: Framed<T, RedisParser>,
+    all_connections: Arc<Connections>,
+    default_db: Arc<Db>,
+    addr: A,
+) {
+    let (mut pubsub, conn) = all_connections.new_connection(default_db, addr);
+    trace!("New connection {}", conn.id());
+
+    loop {
+        tokio::select! {
+            Some(msg) = pubsub.recv() => {
+                if transport.send(msg).await.is_err() {
+                    break;
+                }
+            }
+            result = transport.next() => match result {
+                Some(Ok(args)) => match all_connections.get_dispatcher().execute(&conn, &args).await {
+                    Ok(result) => {
+                        if conn.status() == ConnectionStatus::Pubsub {
+                            continue;
+                        }
+                        if transport.send(result).await.is_err() {
+                            break;
+                        }
+                    },
+                    Err(err) => {
+                        if transport.send(err.into()).await.is_err() {
+                            break;
+                        }
+                    }
+                },
+                Some(Err(e)) => {
+                    warn!("error on decoding from socket; error = {:?}", e);
+                    break;
+                },
+                None => break,
+            }
+        }
+    }
+    conn.destroy();
 }
 
 /// Spawn redis server
@@ -106,10 +218,7 @@ pub async fn server_metrics(all_connections: Arc<Connections>) {
 /// This process is also listening for any incoming message through the internal pub-sub.
 ///
 /// This function will block the main thread and will never exit.
-pub async fn serve(addr: String) -> Result<(), Box<dyn Error>> {
-    let listener = TcpListener::bind(&addr).await?;
-    info!("Listening on: {}", addr);
-
+pub async fn serve(config: Config) -> Result<(), Error> {
     let (default_db, all_dbs) = Databases::new(16, 1000);
     let all_connections = Arc::new(Connections::new(all_dbs.clone()));
     let all_connections_for_metrics = all_connections.clone();
@@ -126,57 +235,31 @@ pub async fn serve(addr: String) -> Result<(), Box<dyn Error>> {
         })
         .for_each(drop);
 
-    tokio::spawn(async move {
-        server_metrics(all_connections_for_metrics.clone()).await;
-    });
+    let mut services = vec![tokio::spawn(async move {
+        server_metrics(all_connections_for_metrics).await
+    })];
 
-    loop {
-        match listener.accept().await {
-            Ok((socket, addr)) => {
-                let all_connections = all_connections.clone();
-                let (mut pubsub, conn) = all_connections.new_connection(default_db.clone(), addr);
-
-                tokio::spawn(async move {
-                    let mut transport = Framed::new(socket, RedisParser);
-
-                    trace!("New connection {}", conn.id());
-
-                    loop {
-                        tokio::select! {
-                            Some(msg) = pubsub.recv() => {
-                                if transport.send(msg).await.is_err() {
-                                    break;
-                                }
-                            }
-                            result = transport.next() => match result {
-                                Some(Ok(args)) => match all_connections.get_dispatcher().execute(&conn, &args).await {
-                                    Ok(result) => {
-                                        if conn.status() == ConnectionStatus::Pubsub {
-                                            continue;
-                                        }
-                                        if transport.send(result).await.is_err() {
-                                            break;
-                                        }
-                                    },
-                                    Err(err) => {
-                                        if transport.send(err.into()).await.is_err() {
-                                            break;
-                                        }
-                                    }
-                                },
-                                Some(Err(e)) => {
-                                    warn!("error on decoding from socket; error = {:?}", e);
-                                    break;
-                                },
-                                None => break,
-                            }
-                        }
-                    }
+    config
+        .get_tcp_hostnames()
+        .iter()
+        .map(|host| {
+            let default_db = default_db.clone();
+            let all_connections = all_connections.clone();
+            let host = host.clone();
+            services.push(tokio::spawn(async move {
+                serve_tcp(&host, default_db, all_connections).await
+            }));
+        })
+        .for_each(drop);
 
-                    conn.destroy();
-                });
-            }
-            Err(e) => println!("error accepting socket; error = {:?}", e),
-        }
+    #[cfg(unix)]
+    if let Some(file) = config.unixsocket {
+        services.push(tokio::spawn(async move {
+            serve_unixsocket(&file, default_db, all_connections).await
+        }))
     }
+
+    future::join_all(services).await;
+
+    Ok(())
 }