🏡 index : ~doyle/fwloki.git

author Jordan Doyle <jordan@doyle.la> 2020-05-24 16:02:33.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2020-05-24 16:02:33.0 +01:00:00
commit
1c5be4539324ef72ba3748033fef5f1a6967b298 [patch]
tree
cfad5e03855c93ff5ecd95b1ac45110ac82c5438
download
1c5be4539324ef72ba3748033fef5f1a6967b298.tar.gz

initial commit



Diff

 .gitignore     |    2 ++
 Cargo.lock     | 1923 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Cargo.toml     |   35 +++++++++++++++++++++++++++++++++++
 README.md      |   37 +++++++++++++++++++++++++++++++++++++
 build.rs       |    5 +++++
 src/config.rs  |   58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/gogo.proto |  144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/loki.proto |  131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/loki.rs    |   37 +++++++++++++++++++++++++++++++++++++
 src/main.rs    |  293 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/parser.rs  |  153 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 11 files changed, 2818 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2c7be4d 100644
--- /dev/null
+++ a/.gitignore
@@ -1,0 +1,2 @@
/target
/db
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..e727ae0 100644
--- /dev/null
+++ a/Cargo.lock
@@ -1,0 +1,1923 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
dependencies = [
 "memchr",
]

[[package]]
name = "anyhow"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"

[[package]]
name = "anymap"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344"

[[package]]
name = "arc-swap"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b585a98a234c46fc563103e9278c9391fde1f4e6850334da895d27edb9580f62"

[[package]]
name = "arrayvec"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"

[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
 "hermit-abi",
 "libc",
 "winapi 0.3.8",
]

[[package]]
name = "autocfg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"

[[package]]
name = "base64"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"

[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"

[[package]]
name = "bumpalo"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6"

[[package]]
name = "bytes"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1"

[[package]]
name = "cc"
version = "1.0.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"

[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"

[[package]]
name = "chashmap"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff41a3c2c1e39921b9003de14bf0439c7b63a9039637c291e1a64925d8ddfa45"
dependencies = [
 "owning_ref",
 "parking_lot",
]

[[package]]
name = "chrono"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2"
dependencies = [
 "num-integer",
 "num-traits",
 "time",
]

[[package]]
name = "clap"
version = "3.0.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "860643c53f980f0d38a5e25dfab6c3c93b2cb3aa1fe192643d17a293c6c41936"
dependencies = [
 "atty",
 "bitflags",
 "clap_derive",
 "indexmap",
 "lazy_static",
 "os_str_bytes",
 "strsim",
 "termcolor",
 "textwrap",
 "unicode-width",
 "vec_map",
]

[[package]]
name = "clap_derive"
version = "3.0.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb51c9e75b94452505acd21d929323f5a5c6c4735a852adbd39ef5fb1b014f30"
dependencies = [
 "heck",
 "proc-macro-error",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "core-foundation"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
dependencies = [
 "core-foundation-sys",
 "libc",
]

[[package]]
name = "core-foundation-sys"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"

[[package]]
name = "crossbeam-channel"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
dependencies = [
 "crossbeam-utils",
 "maybe-uninit",
]

[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
 "autocfg",
 "cfg-if",
 "lazy_static",
]

[[package]]
name = "ct-logs"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113"
dependencies = [
 "sct",
]

[[package]]
name = "dtoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"

[[package]]
name = "either"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"

[[package]]
name = "encoding_rs"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171"
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 = "filetime"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695"
dependencies = [
 "cfg-if",
 "libc",
 "redox_syscall",
 "winapi 0.3.8",
]

[[package]]
name = "fixedbitset"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"

[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"

[[package]]
name = "foreign-types"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
dependencies = [
 "foreign-types-shared",
]

[[package]]
name = "foreign-types-shared"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"

[[package]]
name = "fsevent"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1616e68919f49d311720c3cf316e0a3522d8f2bd08f8da35f6b8a0fa12f9234b"
dependencies = [
 "bitflags",
 "fsevent-sys",
]

[[package]]
name = "fsevent-sys"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41f1722e9bf862f62429d192f37d0c82c589aa18783aa06f0c4e5c3c90649fb"
dependencies = [
 "libc",
]

[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"

[[package]]
name = "fuchsia-zircon"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
dependencies = [
 "bitflags",
 "fuchsia-zircon-sys",
]

[[package]]
name = "fuchsia-zircon-sys"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"

[[package]]
name = "futures-channel"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5"
dependencies = [
 "futures-core",
]

[[package]]
name = "futures-core"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399"

[[package]]
name = "futures-macro"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
dependencies = [
 "proc-macro-hack",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "futures-sink"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc"

[[package]]
name = "futures-task"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626"
dependencies = [
 "once_cell",
]

[[package]]
name = "futures-util"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6"
dependencies = [
 "futures-core",
 "futures-macro",
 "futures-task",
 "pin-project",
 "pin-utils",
 "proc-macro-hack",
 "proc-macro-nested",
 "slab",
]

[[package]]
name = "fwloki"
version = "0.1.0"
dependencies = [
 "anyhow",
 "bytes",
 "chrono",
 "clap",
 "crossbeam-channel",
 "env_logger",
 "log",
 "maxminddb",
 "nom",
 "notify",
 "prost",
 "prost-build",
 "prost-types",
 "reqwest",
 "serde",
 "serde_derive",
 "snap",
 "tokio",
 "toml",
]

[[package]]
name = "getrandom"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
dependencies = [
 "cfg-if",
 "libc",
 "wasi",
]

[[package]]
name = "h2"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff"
dependencies = [
 "bytes",
 "fnv",
 "futures-core",
 "futures-sink",
 "futures-util",
 "http",
 "indexmap",
 "log",
 "slab",
 "tokio",
 "tokio-util",
]

[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [
 "unicode-segmentation",
]

[[package]]
name = "hermit-abi"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
dependencies = [
 "libc",
]

[[package]]
name = "http"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
dependencies = [
 "bytes",
 "fnv",
 "itoa",
]

[[package]]
name = "http-body"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
dependencies = [
 "bytes",
 "http",
]

[[package]]
name = "httparse"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9"

[[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.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14"
dependencies = [
 "bytes",
 "futures-channel",
 "futures-core",
 "futures-util",
 "h2",
 "http",
 "http-body",
 "httparse",
 "itoa",
 "log",
 "net2",
 "pin-project",
 "time",
 "tokio",
 "tower-service",
 "want",
]

[[package]]
name = "hyper-rustls"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac965ea399ec3a25ac7d13b8affd4b8f39325cca00858ddf5eb29b79e6b14b08"
dependencies = [
 "bytes",
 "ct-logs",
 "futures-util",
 "hyper",
 "log",
 "rustls",
 "rustls-native-certs",
 "tokio",
 "tokio-rustls",
 "webpki",
]

[[package]]
name = "hyper-tls"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa"
dependencies = [
 "bytes",
 "hyper",
 "native-tls",
 "tokio",
 "tokio-tls",
]

[[package]]
name = "idna"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
dependencies = [
 "matches",
 "unicode-bidi",
 "unicode-normalization",
]

[[package]]
name = "indexmap"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292"
dependencies = [
 "autocfg",
]

[[package]]
name = "inotify"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc39ee997811267bf8aa0b10e1674c5bea6caacc1957eede5ea45251fe33c6d5"
dependencies = [
 "bitflags",
 "inotify-sys",
 "libc",
]

[[package]]
name = "inotify-sys"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
dependencies = [
 "libc",
]

[[package]]
name = "iovec"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
dependencies = [
 "libc",
]

[[package]]
name = "itertools"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
dependencies = [
 "either",
]

[[package]]
name = "itoa"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"

[[package]]
name = "js-sys"
version = "0.3.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7"
dependencies = [
 "wasm-bindgen",
]

[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
dependencies = [
 "winapi 0.2.8",
 "winapi-build",
]

[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"

[[package]]
name = "lazycell"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"

[[package]]
name = "lexical-core"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
dependencies = [
 "arrayvec",
 "bitflags",
 "cfg-if",
 "ryu",
 "static_assertions",
]

[[package]]
name = "libc"
version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"

[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
dependencies = [
 "cfg-if",
]

[[package]]
name = "matches"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"

[[package]]
name = "maxminddb"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9412a854bf1355d1ff92ef6ffe557dcc4a866e20cdffc7d3fc082174dba7436e"
dependencies = [
 "log",
 "memmap",
 "serde",
 "serde_derive",
]

[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"

[[package]]
name = "memchr"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"

[[package]]
name = "memmap"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
dependencies = [
 "libc",
 "winapi 0.3.8",
]

[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"

[[package]]
name = "mime_guess"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212"
dependencies = [
 "mime",
 "unicase",
]

[[package]]
name = "mio"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
dependencies = [
 "cfg-if",
 "fuchsia-zircon",
 "fuchsia-zircon-sys",
 "iovec",
 "kernel32-sys",
 "libc",
 "log",
 "miow 0.2.1",
 "net2",
 "slab",
 "winapi 0.2.8",
]

[[package]]
name = "mio-extras"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [
 "lazycell",
 "log",
 "mio",
 "slab",
]

[[package]]
name = "mio-named-pipes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
dependencies = [
 "log",
 "mio",
 "miow 0.3.4",
 "winapi 0.3.8",
]

[[package]]
name = "mio-uds"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
dependencies = [
 "iovec",
 "libc",
 "mio",
]

[[package]]
name = "miow"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
dependencies = [
 "kernel32-sys",
 "net2",
 "winapi 0.2.8",
 "ws2_32-sys",
]

[[package]]
name = "miow"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22dfdd1d51b2639a5abd17ed07005c3af05fb7a2a3b1a1d0d7af1000a520c1c7"
dependencies = [
 "socket2",
 "winapi 0.3.8",
]

[[package]]
name = "multimap"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8883adfde9756c1d30b0f519c9b8c502a94b41ac62f696453c37c7fc0a958ce"

[[package]]
name = "native-tls"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d"
dependencies = [
 "lazy_static",
 "libc",
 "log",
 "openssl",
 "openssl-probe",
 "openssl-sys",
 "schannel",
 "security-framework",
 "security-framework-sys",
 "tempfile",
]

[[package]]
name = "net2"
version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
dependencies = [
 "cfg-if",
 "libc",
 "winapi 0.3.8",
]

[[package]]
name = "nom"
version = "6.0.0-alpha1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25020779544bf717b917323b42f23c9dbef1bd2b97b576df6484a0c8709705c6"
dependencies = [
 "lexical-core",
 "memchr",
 "version_check",
]

[[package]]
name = "notify"
version = "5.0.0-pre.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b00c0b65188bffb5598c302e19b062feb94adef02c31f15622a163c95d673c3"
dependencies = [
 "anymap",
 "bitflags",
 "chashmap",
 "crossbeam-channel",
 "filetime",
 "fsevent",
 "fsevent-sys",
 "inotify",
 "libc",
 "mio",
 "mio-extras",
 "walkdir",
 "winapi 0.3.8",
]

[[package]]
name = "num-integer"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
dependencies = [
 "autocfg",
 "num-traits",
]

[[package]]
name = "num-traits"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
dependencies = [
 "autocfg",
]

[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
 "hermit-abi",
 "libc",
]

[[package]]
name = "once_cell"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d"

[[package]]
name = "openssl"
version = "0.10.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd"
dependencies = [
 "bitflags",
 "cfg-if",
 "foreign-types",
 "lazy_static",
 "libc",
 "openssl-sys",
]

[[package]]
name = "openssl-probe"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"

[[package]]
name = "openssl-sys"
version = "0.9.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e"
dependencies = [
 "autocfg",
 "cc",
 "libc",
 "pkg-config",
 "vcpkg",
]

[[package]]
name = "os_str_bytes"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06de47b848347d8c4c94219ad8ecd35eb90231704b067e67e6ae2e36ee023510"

[[package]]
name = "owning_ref"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
dependencies = [
 "stable_deref_trait",
]

[[package]]
name = "parking_lot"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
dependencies = [
 "owning_ref",
 "parking_lot_core",
]

[[package]]
name = "parking_lot_core"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa"
dependencies = [
 "libc",
 "rand 0.4.6",
 "smallvec 0.6.13",
 "winapi 0.3.8",
]

[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"

[[package]]
name = "petgraph"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
dependencies = [
 "fixedbitset",
 "indexmap",
]

[[package]]
name = "pin-project"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791"
dependencies = [
 "pin-project-internal",
]

[[package]]
name = "pin-project-internal"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "pin-project-lite"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f"

[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"

[[package]]
name = "pkg-config"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"

[[package]]
name = "ppv-lite86"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"

[[package]]
name = "proc-macro-error"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
dependencies = [
 "proc-macro-error-attr",
 "proc-macro2",
 "quote",
 "syn",
 "version_check",
]

[[package]]
name = "proc-macro-error-attr"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "syn-mid",
 "version_check",
]

[[package]]
name = "proc-macro-hack"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"

[[package]]
name = "proc-macro-nested"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694"

[[package]]
name = "proc-macro2"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70a50b9351bfa8d65a7d93ce712dc63d2fd15ddbf2c36990fc7cac344859c04f"
dependencies = [
 "unicode-xid",
]

[[package]]
name = "prost"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce49aefe0a6144a45de32927c77bd2859a5f7677b55f220ae5b744e87389c212"
dependencies = [
 "bytes",
 "prost-derive",
]

[[package]]
name = "prost-build"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b10678c913ecbd69350e8535c3aef91a8676c0773fc1d7b95cdd196d7f2f26"
dependencies = [
 "bytes",
 "heck",
 "itertools",
 "log",
 "multimap",
 "petgraph",
 "prost",
 "prost-types",
 "tempfile",
 "which",
]

[[package]]
name = "prost-derive"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "537aa19b95acde10a12fec4301466386f757403de4cd4e5b4fa78fb5ecb18f72"
dependencies = [
 "anyhow",
 "itertools",
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "prost-types"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1834f67c0697c001304b75be76f67add9c89742eda3a085ad8ee0bb38c3417aa"
dependencies = [
 "bytes",
 "prost",
]

[[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.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
dependencies = [
 "proc-macro2",
]

[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
 "fuchsia-cprng",
 "libc",
 "rand_core 0.3.1",
 "rdrand",
 "winapi 0.3.8",
]

[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
dependencies = [
 "getrandom",
 "libc",
 "rand_chacha",
 "rand_core 0.5.1",
 "rand_hc",
]

[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
dependencies = [
 "ppv-lite86",
 "rand_core 0.5.1",
]

[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
 "rand_core 0.4.2",
]

[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"

[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
 "getrandom",
]

[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
dependencies = [
 "rand_core 0.5.1",
]

[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
 "rand_core 0.3.1",
]

[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"

[[package]]
name = "regex"
version = "1.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
dependencies = [
 "aho-corasick",
 "memchr",
 "regex-syntax",
 "thread_local",
]

[[package]]
name = "regex-syntax"
version = "0.6.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"

[[package]]
name = "remove_dir_all"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
dependencies = [
 "winapi 0.3.8",
]

[[package]]
name = "reqwest"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b81e49ddec5109a9dcfc5f2a317ff53377c915e9ae9d4f2fb50914b85614e2"
dependencies = [
 "base64",
 "bytes",
 "encoding_rs",
 "futures-core",
 "futures-util",
 "http",
 "http-body",
 "hyper",
 "hyper-rustls",
 "hyper-tls",
 "js-sys",
 "lazy_static",
 "log",
 "mime",
 "mime_guess",
 "native-tls",
 "percent-encoding",
 "pin-project-lite",
 "rustls",
 "serde",
 "serde_urlencoded",
 "time",
 "tokio",
 "tokio-rustls",
 "tokio-tls",
 "url",
 "wasm-bindgen",
 "wasm-bindgen-futures",
 "web-sys",
 "webpki-roots",
 "winreg",
]

[[package]]
name = "ring"
version = "0.16.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703516ae74571f24b465b4a1431e81e2ad51336cb0ded733a55a1aa3eccac196"
dependencies = [
 "cc",
 "libc",
 "once_cell",
 "spin",
 "untrusted",
 "web-sys",
 "winapi 0.3.8",
]

[[package]]
name = "rustls"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1"
dependencies = [
 "base64",
 "log",
 "ring",
 "sct",
 "webpki",
]

[[package]]
name = "rustls-native-certs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75ffeb84a6bd9d014713119542ce415db3a3e4748f0bfce1e1416cd224a23a5"
dependencies = [
 "openssl-probe",
 "rustls",
 "schannel",
 "security-framework",
]

[[package]]
name = "ryu"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"

[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
 "winapi-util",
]

[[package]]
name = "schannel"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
dependencies = [
 "lazy_static",
 "winapi 0.3.8",
]

[[package]]
name = "sct"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
dependencies = [
 "ring",
 "untrusted",
]

[[package]]
name = "security-framework"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535"
dependencies = [
 "bitflags",
 "core-foundation",
 "core-foundation-sys",
 "libc",
 "security-framework-sys",
]

[[package]]
name = "security-framework-sys"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405"
dependencies = [
 "core-foundation-sys",
 "libc",
]

[[package]]
name = "serde"
version = "1.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c"

[[package]]
name = "serde_derive"
version = "1.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "serde_json"
version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2"
dependencies = [
 "itoa",
 "ryu",
 "serde",
]

[[package]]
name = "serde_urlencoded"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
dependencies = [
 "dtoa",
 "itoa",
 "serde",
 "url",
]

[[package]]
name = "signal-hook-registry"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41"
dependencies = [
 "arc-swap",
 "libc",
]

[[package]]
name = "slab"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"

[[package]]
name = "smallvec"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"
dependencies = [
 "maybe-uninit",
]

[[package]]
name = "smallvec"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"

[[package]]
name = "snap"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fb9b0bb877b35a1cc1474a3b43d9c226a2625311760cdda2cbccbc0c7a8376"

[[package]]
name = "socket2"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
dependencies = [
 "cfg-if",
 "libc",
 "redox_syscall",
 "winapi 0.3.8",
]

[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"

[[package]]
name = "stable_deref_trait"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"

[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"

[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"

[[package]]
name = "syn"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269"
dependencies = [
 "proc-macro2",
 "quote",
 "unicode-xid",
]

[[package]]
name = "syn-mid"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
 "cfg-if",
 "libc",
 "rand 0.7.3",
 "redox_syscall",
 "remove_dir_all",
 "winapi 0.3.8",
]

[[package]]
name = "termcolor"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
dependencies = [
 "winapi-util",
]

[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
 "unicode-width",
]

[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
 "lazy_static",
]

[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
 "libc",
 "winapi 0.3.8",
]

[[package]]
name = "tokio"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58"
dependencies = [
 "bytes",
 "fnv",
 "futures-core",
 "iovec",
 "lazy_static",
 "libc",
 "memchr",
 "mio",
 "mio-named-pipes",
 "mio-uds",
 "num_cpus",
 "pin-project-lite",
 "signal-hook-registry",
 "slab",
 "tokio-macros",
 "winapi 0.3.8",
]

[[package]]
name = "tokio-macros"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
]

[[package]]
name = "tokio-rustls"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4"
dependencies = [
 "futures-core",
 "rustls",
 "tokio",
 "webpki",
]

[[package]]
name = "tokio-tls"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343"
dependencies = [
 "native-tls",
 "tokio",
]

[[package]]
name = "tokio-util"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
dependencies = [
 "bytes",
 "futures-core",
 "futures-sink",
 "log",
 "pin-project-lite",
 "tokio",
]

[[package]]
name = "toml"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
dependencies = [
 "serde",
]

[[package]]
name = "tower-service"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860"

[[package]]
name = "try-lock"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382"

[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
 "version_check",
]

[[package]]
name = "unicode-bidi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
dependencies = [
 "matches",
]

[[package]]
name = "unicode-normalization"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
dependencies = [
 "smallvec 1.4.0",
]

[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"

[[package]]
name = "unicode-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"

[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"

[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"

[[package]]
name = "url"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
dependencies = [
 "idna",
 "matches",
 "percent-encoding",
]

[[package]]
name = "vcpkg"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168"

[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"

[[package]]
name = "version_check"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"

[[package]]
name = "walkdir"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d"
dependencies = [
 "same-file",
 "winapi 0.3.8",
 "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.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"

[[package]]
name = "wasm-bindgen"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551"
dependencies = [
 "cfg-if",
 "serde",
 "serde_json",
 "wasm-bindgen-macro",
]

[[package]]
name = "wasm-bindgen-backend"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94"
dependencies = [
 "bumpalo",
 "lazy_static",
 "log",
 "proc-macro2",
 "quote",
 "syn",
 "wasm-bindgen-shared",
]

[[package]]
name = "wasm-bindgen-futures"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a369c5e1dfb7569e14d62af4da642a3cbc2f9a3652fe586e26ac22222aa4b04"
dependencies = [
 "cfg-if",
 "js-sys",
 "wasm-bindgen",
 "web-sys",
]

[[package]]
name = "wasm-bindgen-macro"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776"
dependencies = [
 "quote",
 "wasm-bindgen-macro-support",
]

[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
dependencies = [
 "proc-macro2",
 "quote",
 "syn",
 "wasm-bindgen-backend",
 "wasm-bindgen-shared",
]

[[package]]
name = "wasm-bindgen-shared"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad"

[[package]]
name = "web-sys"
version = "0.3.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642"
dependencies = [
 "js-sys",
 "wasm-bindgen",
]

[[package]]
name = "webpki"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef"
dependencies = [
 "ring",
 "untrusted",
]

[[package]]
name = "webpki-roots"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4"
dependencies = [
 "webpki",
]

[[package]]
name = "which"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
dependencies = [
 "libc",
]

[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"

[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
dependencies = [
 "winapi-i686-pc-windows-gnu",
 "winapi-x86_64-pc-windows-gnu",
]

[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"

[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
 "winapi 0.3.8",
]

[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

[[package]]
name = "winreg"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
dependencies = [
 "winapi 0.3.8",
]

[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
 "winapi 0.2.8",
 "winapi-build",
]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..17a7e78 100644
--- /dev/null
+++ a/Cargo.toml
@@ -1,0 +1,35 @@
[package]
name = "fwloki"
version = "0.1.0"
authors = ["Jordan Doyle <jordan@doyle.la>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
notify = "5.0.0-pre.2"
crossbeam-channel = "0.4"
maxminddb = { version = "0.13", features = ["mmap"] }

clap = "3.0.0-beta.1"
env_logger = "0.7"
log = { version = "0.4", features = ["max_level_debug", "release_max_level_info"] }
serde = "1"
serde_derive = "1"
toml = "0.5"

nom = "6.0.0-alpha1"
chrono = "0.4"

tokio = { version = "0.2", features = ["full"] }
reqwest = { version = "0.10", features = ["rustls-tls"] }

prost = "0.6"
prost-types = "0.6"
bytes = "0.5"
snap = "1"

anyhow = "1"

[build-dependencies]
prost-build = "0.6"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9f864af 100644
--- /dev/null
+++ a/README.md
@@ -1,0 +1,37 @@
# fwloki

fwloki is a simple daemon to watch a log file (typically /var/log/messages)
for iptables logs, parse them and write them back out to [loki][] in a few
short microseconds. Optionally, you can also add some GeoIP metadata to the
logs at the expense of a bit more extra processing time.

Once in loki, you're free to do what you like with the logs, stick them in a
table in Grafana? Sure. Show the data in a world map to see who's targeting
your network? Of course. Inspect and drop traffic from repeat offenders? If
that's what you're into.

The world's your clam, really.

[loki]: https://github.com/grafana/loki

#### Config

```toml

log-file = "/var/log/messages"

# [geoip]
# asn-db = "./db/GeoLite2-ASN.mmdb"
# city-db = "./db/GeoLite2-City.mmdb"
# country-db = "./db/GeoLite2-Country.mmdb"

[firewall]
rules = [
    "OUTSIDE-LOCAL-default-D",

    "OUTSIDE-LOCAL-V6-default-D",

    "OUTSIDE-IN-default-D",

    "OUTSIDE-IN-V6-default-D"

]

[loki]
push-url = "http://10.0.0.28:3100/loki/api/v1/push"
```

diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..8e20562 100644
--- /dev/null
+++ a/build.rs
@@ -1,0 +1,5 @@
extern crate prost_build;

fn main() {
    prost_build::compile_protos(&["src/loki.proto"], &["src"]).unwrap();
}
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..8a14ea5 100644
--- /dev/null
+++ a/src/config.rs
@@ -1,0 +1,58 @@
use std::path::PathBuf;

use anyhow::Result;
use clap::Clap;
use serde_derive::Deserialize;

#[derive(Deserialize, Debug, Default)]
pub struct GeoIp {
    #[serde(default, rename = "asn-db")]
    pub asn_db: Option<PathBuf>,
    #[serde(default, rename = "city-db")]
    pub city_db: Option<PathBuf>,
    #[serde(default, rename = "country-db")]
    pub country_db: Option<PathBuf>,
}

#[derive(Deserialize, Debug, Default)]
pub struct Firewall {
    #[serde(default)]
    pub rules: Vec<String>,
}

#[derive(Deserialize, Debug, Default)]
pub struct Loki {
    #[serde(rename = "push-url")]
    pub push_url: String,
}

#[derive(Deserialize, Debug, Default)]
pub struct Config {
    #[serde(default = "default_log_file", rename = "log-file")]
    pub log_file: PathBuf,
    #[serde(default)]
    pub geoip: GeoIp,
    #[serde(default)]
    pub firewall: Firewall,
    #[serde(default)]
    pub loki: Loki,
}
impl Config {
    pub fn load(args: &Args) -> Result<Self> {
        let cfg = std::fs::read_to_string(&args.config)?;
        Ok(toml::from_str(&cfg)?)
    }
}

fn default_log_file() -> PathBuf {
    "/var/log/messages".into()
}

/// iptables-to-loki

#[derive(Clap)]
#[clap(version = "1.0", author = "Jordan D. <jordan@doyle.la>")]
pub struct Args {
    /// Names of the iptables rules to watch

    #[clap(short, long)]
    config: PathBuf,
}
diff --git a/src/gogo.proto b/src/gogo.proto
new file mode 100644
index 0000000..b80c856 100644
--- /dev/null
+++ a/src/gogo.proto
@@ -1,0 +1,144 @@
// Protocol Buffers for Go with Gadgets
//
// Copyright (c) 2013, The GoGo Authors. All rights reserved.
// http://github.com/gogo/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

syntax = "proto2";
package gogoproto;

import "google/protobuf/descriptor.proto";

option java_package = "com.google.protobuf";
option java_outer_classname = "GoGoProtos";
option go_package = "github.com/gogo/protobuf/gogoproto";

extend google.protobuf.EnumOptions {
	optional bool goproto_enum_prefix = 62001;
	optional bool goproto_enum_stringer = 62021;
	optional bool enum_stringer = 62022;
	optional string enum_customname = 62023;
	optional bool enumdecl = 62024;
}

extend google.protobuf.EnumValueOptions {
	optional string enumvalue_customname = 66001;
}

extend google.protobuf.FileOptions {
	optional bool goproto_getters_all = 63001;
	optional bool goproto_enum_prefix_all = 63002;
	optional bool goproto_stringer_all = 63003;
	optional bool verbose_equal_all = 63004;
	optional bool face_all = 63005;
	optional bool gostring_all = 63006;
	optional bool populate_all = 63007;
	optional bool stringer_all = 63008;
	optional bool onlyone_all = 63009;

	optional bool equal_all = 63013;
	optional bool description_all = 63014;
	optional bool testgen_all = 63015;
	optional bool benchgen_all = 63016;
	optional bool marshaler_all = 63017;
	optional bool unmarshaler_all = 63018;
	optional bool stable_marshaler_all = 63019;

	optional bool sizer_all = 63020;

	optional bool goproto_enum_stringer_all = 63021;
	optional bool enum_stringer_all = 63022;

	optional bool unsafe_marshaler_all = 63023;
	optional bool unsafe_unmarshaler_all = 63024;

	optional bool goproto_extensions_map_all = 63025;
	optional bool goproto_unrecognized_all = 63026;
	optional bool gogoproto_import = 63027;
	optional bool protosizer_all = 63028;
	optional bool compare_all = 63029;
    optional bool typedecl_all = 63030;
    optional bool enumdecl_all = 63031;

	optional bool goproto_registration = 63032;
	optional bool messagename_all = 63033;

	optional bool goproto_sizecache_all = 63034;
	optional bool goproto_unkeyed_all = 63035;
}

extend google.protobuf.MessageOptions {
	optional bool goproto_getters = 64001;
	optional bool goproto_stringer = 64003;
	optional bool verbose_equal = 64004;
	optional bool face = 64005;
	optional bool gostring = 64006;
	optional bool populate = 64007;
	optional bool stringer = 67008;
	optional bool onlyone = 64009;

	optional bool equal = 64013;
	optional bool description = 64014;
	optional bool testgen = 64015;
	optional bool benchgen = 64016;
	optional bool marshaler = 64017;
	optional bool unmarshaler = 64018;
	optional bool stable_marshaler = 64019;

	optional bool sizer = 64020;

	optional bool unsafe_marshaler = 64023;
	optional bool unsafe_unmarshaler = 64024;

	optional bool goproto_extensions_map = 64025;
	optional bool goproto_unrecognized = 64026;

	optional bool protosizer = 64028;
	optional bool compare = 64029;

	optional bool typedecl = 64030;

	optional bool messagename = 64033;

	optional bool goproto_sizecache = 64034;
	optional bool goproto_unkeyed = 64035;
}

extend google.protobuf.FieldOptions {
	optional bool nullable = 65001;
	optional bool embed = 65002;
	optional string customtype = 65003;
	optional string customname = 65004;
	optional string jsontag = 65005;
	optional string moretags = 65006;
	optional string casttype = 65007;
	optional string castkey = 65008;
	optional string castvalue = 65009;

	optional bool stdtime = 65010;
	optional bool stdduration = 65011;
	optional bool wktpointer = 65012;

}
diff --git a/src/loki.proto b/src/loki.proto
new file mode 100644
index 0000000..90078bf 100644
--- /dev/null
+++ a/src/loki.proto
@@ -1,0 +1,131 @@
syntax = "proto3";

package logproto;

option go_package = "github.com/grafana/loki/pkg/logproto";

import "google/protobuf/timestamp.proto";
import "gogo.proto";

service Pusher {
  rpc Push(PushRequest) returns (PushResponse) {};
}

service Querier {
  rpc Query(QueryRequest) returns (stream QueryResponse) {};
  rpc Label(LabelRequest) returns (LabelResponse) {};
  rpc Tail(TailRequest) returns (stream TailResponse) {};
  rpc Series(SeriesRequest) returns (SeriesResponse) {};
  rpc TailersCount(TailersCountRequest) returns (TailersCountResponse) {};
}

service Ingester {
  rpc TransferChunks(stream TimeSeriesChunk) returns (TransferChunksResponse) {};
}

message PushRequest {
  repeated StreamAdapter streams = 1 [(gogoproto.jsontag) = "streams", (gogoproto.customtype) = "Stream"];
}

message PushResponse {
}

message QueryRequest {
  string selector = 1;
  uint32 limit = 2;
  google.protobuf.Timestamp start = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
  google.protobuf.Timestamp end = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
  Direction direction = 5;
  reserved 6;
}

enum Direction {
  FORWARD = 0;
  BACKWARD = 1;
}

message QueryResponse {
  repeated StreamAdapter streams = 1 [(gogoproto.customtype) = "Stream", (gogoproto.nullable) = true];
}

message LabelRequest {
  string name = 1;
  bool values = 2; // True to fetch label values, false for fetch labels names.
  google.protobuf.Timestamp start = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true];
  google.protobuf.Timestamp end = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true];
}

message LabelResponse {
  repeated string values = 1;
}

message StreamAdapter {
  string labels = 1 [(gogoproto.jsontag) = "labels"];
  repeated EntryAdapter entries = 2 [(gogoproto.nullable) = false, (gogoproto.jsontag) = "entries"];
}

message EntryAdapter {
  google.protobuf.Timestamp timestamp = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.jsontag) = "ts"];
  string line = 2 [(gogoproto.jsontag) = "line"];
}

message TailRequest {
  string query = 1;
  reserved 2;
  uint32 delayFor = 3;
  uint32 limit = 4;
  google.protobuf.Timestamp start = 5 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}

message TailResponse {
  StreamAdapter stream = 1 [(gogoproto.customtype) = "Stream"];
  repeated DroppedStream droppedStreams = 2;
}

message SeriesRequest {
  google.protobuf.Timestamp start = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
  google.protobuf.Timestamp end = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
  repeated string groups = 3;
}

message SeriesResponse {
  repeated SeriesIdentifier series = 1 [(gogoproto.nullable) = false];
}

message SeriesIdentifier {
  map<string,string> labels = 1;
}

message DroppedStream {
  google.protobuf.Timestamp from = 1 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
  google.protobuf.Timestamp to = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
  string labels = 3;
}

message TimeSeriesChunk {
  string from_ingester_id = 1;
  string user_id = 2;
  repeated LabelPair labels = 3;
  repeated Chunk chunks = 4;
}

message LabelPair {
  string name = 1;
  string value = 2;
}

message Chunk {
  bytes data = 1;
}

message TransferChunksResponse {

}

message TailersCountRequest {

}

message TailersCountResponse {
  uint32 count = 1;
}
diff --git a/src/loki.rs b/src/loki.rs
new file mode 100644
index 0000000..f0871cf 100644
--- /dev/null
+++ a/src/loki.rs
@@ -1,0 +1,37 @@
#[allow(clippy::default_trait_access)]
mod proto {
    include!(concat!(env!("OUT_DIR"), "/logproto.rs"));
}

use anyhow::Result;
use bytes::{Bytes, BytesMut};
use prost::Message;

pub fn create_push_request(entries: Vec<(i64, String)>) -> Result<Bytes> {
    let mut entries_transformed = Vec::new();
    for (timestamp, line) in entries {
        entries_transformed.push(proto::EntryAdapter {
            timestamp: Some(prost_types::Timestamp {
                seconds: timestamp,
                nanos: 0,
            }),
            line,
        });
    }

    let req = proto::PushRequest {
        streams: vec![proto::StreamAdapter {
            labels: "{namespace=\"iptables\"}".to_string(),
            entries: entries_transformed,
        }],
    };

    let mut s = BytesMut::new();
    req.encode(&mut s)?;
    let s = s.freeze();

    // TODO: can we do this without the copy?
    Ok(Bytes::from(
        snap::raw::Encoder::new().compress_vec(s.as_ref())?,
    ))
}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..a68f908 100644
--- /dev/null
+++ a/src/main.rs
@@ -1,0 +1,293 @@
#![deny(clippy::all, clippy::pedantic)]

mod config;
mod loki;
mod parser;

use crate::parser::Log;
use chrono::DateTime;
use chrono::Utc;
use clap::Clap;
use std::fmt::Display;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::net::IpAddr;
use std::path::Path;
use std::time::Duration;

use log::{debug, error};

use crossbeam_channel::Receiver;
use maxminddb::geoip2;
use notify::{
    event::ModifyKind, immediate_watcher, Event, EventKind, RecommendedWatcher, RecursiveMode,
    Watcher,
};

use anyhow::Result;

enum ModifyType {
    Data,
    Rotate,
}

struct LogReader {
    _watcher: RecommendedWatcher,
    reader: BufReader<File>,
    recv: Receiver<ModifyType>,
}

fn open_log_file(path: &Path) -> Result<LogReader> {
    while !path.exists() {
        std::thread::sleep(Duration::new(1, 0));
    }

    // setup an inotify watcher and forward events on to the channel
    let (send, recv) = crossbeam_channel::bounded(0);
    let mut watcher = immediate_watcher(move |e| match e {
        Ok(Event {
            kind: EventKind::Modify(ModifyKind::Data(_)),
            ..
        }) => {
            // we don't really care if the receiever isn't listening on the channel
            let _ = send.try_send(ModifyType::Data);
        }
        Ok(Event {
            kind: EventKind::Modify(ModifyKind::Name(_)),
            ..
        }) => {
            let _ = send.try_send(ModifyType::Rotate);
        }
        Err(e) => error!("Error watching file: {:?}", e),
        _ => {}
    })?;

    watcher.watch(path, RecursiveMode::NonRecursive)?;

    Ok(LogReader {
        _watcher: watcher,
        reader: BufReader::new(File::open(path)?),
        recv,
    })
}

#[derive(Debug)]
struct FirewallEntry<'a> {
    time: &'a DateTime<Utc>,
    hostname: &'a str,
    rule: &'a str,

    interface: &'a str,
    mac: &'a str,
    src: IpAddr,
    src_port: u16,
    dst: IpAddr,
    dst_port: u16,
    proto: &'a str,

    asn: Option<u32>,
    asn_org: Option<&'a String>,
    city: Option<&'a String>,
    country_code: Option<&'a String>,
    country: Option<&'a String>,
    lat: Option<f64>,
    lng: Option<f64>,
}
impl<'a> FirewallEntry<'a> {
    fn from(
        log: &'a Log<'a>,
        city: &'a Option<geoip2::City>,
        country: &'a Option<geoip2::Country>,
        asn: &'a Option<geoip2::Asn>,
    ) -> Result<FirewallEntry<'a>> {
        let country = country
            .as_ref()
            .and_then(|country| country.country.as_ref());
        let location = city.as_ref().and_then(|city| city.location.as_ref());

        Ok(FirewallEntry {
            time: &log.time,
            hostname: log.hostname,
            rule: log.rule,

            interface: log.values.get("IN").unwrap_or(&""),
            mac: log.values.get("MAC").unwrap_or(&""),
            src: log.values.get("SRC").unwrap_or(&"").parse()?,
            src_port: log.values.get("SPT").unwrap_or(&"").parse()?,
            dst: log.values.get("DST").unwrap_or(&"").parse()?,
            dst_port: log.values.get("DPT").unwrap_or(&"").parse()?,
            proto: log.values.get("PROTO").unwrap_or(&""),

            asn: asn.as_ref().and_then(|asn| asn.autonomous_system_number),
            asn_org: asn
                .as_ref()
                .and_then(|asn| asn.autonomous_system_organization.as_ref()),
            city: city
                .as_ref()
                .and_then(|city| city.city.as_ref())
                .and_then(|city| city.names.as_ref())
                .and_then(|names| names.get("en")),
            country_code: country.and_then(|country| country.iso_code.as_ref()),
            country: country
                .and_then(|country| country.names.as_ref())
                .and_then(|names| names.get("en")),
            lat: location.and_then(|loc| loc.latitude),
            lng: location.and_then(|loc| loc.longitude),
        })
    }
}
impl Display for FirewallEntry<'_> {
    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
        write!(
            fmt,
            concat!(
                "hostname=\"{}\" rule=\"{}\" interface=\"{}\" mac=\"{}\" src=\"{}\" src_port=\"{}\" ",
                "dst=\"{}\" dst_port=\"{}\" proto=\"{}\" asn=\"{}\" asn_org=\"{}\" city=\"{}\" ",
                "country_code=\"{}\" country=\"{}\" lat=\"{}\" lng=\"{}\"",
            ),
            self.hostname,
            self.rule,
            self.interface,
            self.mac,
            self.src,
            self.src_port,
            self.dst,
            self.dst_port,
            self.proto,
            self.asn.as_ref().map(ToString::to_string).unwrap_or_default(),
            self.asn_org.as_ref().map(ToString::to_string).unwrap_or_default(),
            self.city.as_ref().map(ToString::to_string).unwrap_or_default(),
            self.country_code.as_ref().map(ToString::to_string).unwrap_or_default(),
            self.country.as_ref().map(ToString::to_string).unwrap_or_default(),
            self.lat.as_ref().map(ToString::to_string).unwrap_or_default(),
            self.lng.as_ref().map(ToString::to_string).unwrap_or_default(),
        )
    }
}

#[tokio::main]
async fn main() {
    env_logger::init();

    let args: config::Args = config::Args::parse();
    let config = match config::Config::load(&args) {
        Ok(v) => v,
        Err(e) => {
            error!("Failed to load config file: {}", e);
            std::process::exit(1);
        }
    };

    if !config.log_file.exists() {
        error!("Log file '{}' does not exist.", config.log_file.display());
        std::process::exit(1);
    }

    let open_log_file_or_exit = || match open_log_file(&config.log_file) {
        Ok(v) => v,
        Err(e) => {
            error!(
                "Failed to watch log file '{}': {:?}",
                config.log_file.display(),
                e
            );
            std::process::exit(1);
        }
    };

    let open_geoip_reader = |v: Option<&std::path::PathBuf>| {
        v.map(|v| match maxminddb::Reader::open_mmap(v) {
            Ok(db) => db,
            Err(e) => {
                error!("Failed to load GeoIP DB '{}': {}", v.display(), e);
                std::process::exit(1);
            }
        })
    };

    let asn_db = open_geoip_reader(config.geoip.asn_db.as_ref());
    let city_db = open_geoip_reader(config.geoip.city_db.as_ref());
    let country_db = open_geoip_reader(config.geoip.country_db.as_ref());

    let mut reader = open_log_file_or_exit();
    let mut buf = String::new();
    let client = reqwest::Client::new();

    loop {
        buf.clear();
        let read_bytes = match reader.reader.read_line(&mut buf) {
            Ok(v) => v,
            Err(e) => {
                error!("Failed to read line from file: {:?}", e);
                std::thread::sleep(Duration::new(1, 0));
                continue;
            }
        };

        if read_bytes == 0 {
            // block until we receive a notification from inotify
            match reader.recv.recv() {
                Ok(ModifyType::Data) => {}
                Ok(ModifyType::Rotate) => reader = open_log_file_or_exit(),
                // crossbeam channel disconnected, wait a second before polling file again
                Err(_) => std::thread::sleep(Duration::new(1, 0)),
            }
            continue;
        }

        let (_, log_line) = match parser::parse_log_line(&buf) {
            Ok((_, v)) if !config.firewall.rules.iter().any(|rule| v.rule == rule) => {
                debug!("Non-matching firewall rule: {}", v.rule);
                continue;
            }
            Err(e) => {
                debug!("Non-matching log line: {:?}", e);
                continue;
            }
            Ok(v) => v,
        };

        if let Some(ip) = log_line.values.get("SRC") {
            let ip = if let Ok(ip) = ip.parse() {
                ip
            } else {
                error!("Malformed src ip in iptables logs {}", ip);
                continue;
            };
            let asn: Option<geoip2::Asn> = asn_db.as_ref().and_then(|db| db.lookup(ip).ok());
            let city: Option<geoip2::City> = city_db.as_ref().and_then(|db| db.lookup(ip).ok());
            let country: Option<geoip2::Country> =
                country_db.as_ref().and_then(|db| db.lookup(ip).ok());

            let timestamp = log_line.time.timestamp();
            let entry = match FirewallEntry::from(&log_line, &city, &country, &asn) {
                Ok(v) => v.to_string(),
                Err(e) => {
                    error!("Failed to build firewall entry for log: {:?}", e);
                    continue;
                }
            };

            let req = match crate::loki::create_push_request(vec![(timestamp, entry)]) {
                Ok(v) => v,
                Err(e) => {
                    error!("Error creating a Loki push request: {:?}", e);
                    continue;
                }
            };

            match client.post(&config.loki.push_url).body(req).send().await {
                Ok(resp) => {
                    if !resp.status().is_success() {
                        error!(
                            "Error pushing log to Loki ({}): {:?}",
                            resp.status(),
                            resp.text().await
                        );
                    }
                }
                Err(e) => error!("Error pushing log to Loki: {:?}", e),
            }
        }
    }
}
diff --git a/src/parser.rs b/src/parser.rs
new file mode 100644
index 0000000..b8cb88c 100644
--- /dev/null
+++ a/src/parser.rs
@@ -1,0 +1,153 @@
use nom::bytes::complete::take_until;

use std::collections::HashMap;

use chrono::offset::TimeZone;
use chrono::{DateTime, Datelike, Utc};
use nom::{
    bytes::complete::{tag, take},
    IResult,
};

fn parse_month<'a>((i, s): (&'a str, &str)) -> IResult<&'a str, u32> {
    match s {
        "Jan" => Ok((i, 1)),
        "Feb" => Ok((i, 2)),
        "Mar" => Ok((i, 3)),
        "Apr" => Ok((i, 4)),
        "May" => Ok((i, 5)),
        "Jun" => Ok((i, 6)),
        "Jul" => Ok((i, 7)),
        "Aug" => Ok((i, 8)),
        "Sep" => Ok((i, 9)),
        "Oct" => Ok((i, 10)),
        "Nov" => Ok((i, 11)),
        "Dec" => Ok((i, 12)),
        _ => Err(nom::Err::Failure(nom::error_position!(
            "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
            nom::error::ErrorKind::OneOf
        ))),
    }
}

fn take_n_digits(i: &str, n: usize) -> IResult<&str, u32> {
    let (i, digits) = take(n)(i)?;

    match digits.parse() {
        Ok(res) => Ok((i, res)),
        Err(_) => Err(nom::Err::Failure(nom::error_position!(
            "Invalid string, expected ASCII representation of a number",
            nom::error::ErrorKind::Digit
        ))),
    }
}

fn parse_date_time(i: &str) -> IResult<&str, DateTime<Utc>> {
    let (i, month) = parse_month(take(3_usize)(i)?)?;
    let (i, _) = tag(" ")(i)?;
    let (i, day) = take_n_digits(i, 2)?;
    let (i, _) = tag(" ")(i)?;
    let (i, hour) = take_n_digits(i, 2)?;
    let (i, _) = tag(":")(i)?;
    let (i, min) = take_n_digits(i, 2)?;
    let (i, _) = tag(":")(i)?;
    let (i, sec) = take_n_digits(i, 2)?;
    Ok((
        i,
        chrono::Utc
            .ymd(chrono::Utc::today().year(), month, day)
            .and_hms(hour, min, sec),
    ))
}

fn parse_hostname(i: &str) -> IResult<&str, &str> {
    take_until(" ")(i)
}

fn parse_bracketed_param(i: &str) -> IResult<&str, &str> {
    let (i, _) = tag("[")(i)?;
    let (i, time) = take_until("]")(i)?;
    let (i, _) = tag("]")(i)?;

    Ok((i, time))
}

fn parse_kvs(i: &str) -> IResult<&str, HashMap<&str, &str>> {
    let (i, kvs) = nom::multi::separated_list0(
        nom::character::complete::char(' '),
        nom::sequence::separated_pair(take_until("="), tag("="), take_until(" ")),
    )(i)?;

    let mut map = HashMap::new();
    for (key, value) in kvs {
        map.insert(key, value);
    }

    Ok((i, map))
}

pub fn parse_log_line(i: &str) -> IResult<&str, Log> {
    let (i, time) = parse_date_time(i)?;
    let (i, _) = tag(" ")(i)?;
    let (i, hostname) = parse_hostname(i)?;
    let (i, _) = tag(" kernel: ")(i)?;
    let (i, _) = parse_bracketed_param(i)?; // kernel time
    let (i, _) = tag(" ")(i)?;
    let (i, rule) = parse_bracketed_param(i)?;
    let (i, values) = parse_kvs(i)?;

    Ok((
        i,
        Log {
            time,
            hostname,
            rule,
            values,
        },
    ))
}

#[derive(Debug)]
pub struct Log<'a> {
    pub hostname: &'a str,
    pub time: DateTime<Utc>,
    pub rule: &'a str,
    pub values: HashMap<&'a str, &'a str>,
}

#[cfg(test)]
mod tests {
    use chrono::Datelike;
    use chrono::Timelike;

    use crate::parser::parse_date_time;
    use crate::parser::parse_hostname;
    use crate::parser::parse_log_line;

    #[test]
    fn test_parse_date_time() {
        let (_, parsed) = parse_date_time("Apr 19 12:31:53").unwrap();
        assert_eq!(
            parsed.to_rfc3339(),
            format!("{}-04-19T12:31:53+00:00", chrono::Utc::today().year())
        );
    }

    #[test]
    fn test_parse_hostname() {
        let (_, parsed) = parse_hostname("vyos ignore the rest of this").unwrap();
        assert_eq!(parsed, "vyos");
    }

    #[test]
    fn test_parse_log_line() {
        let (rest, parsed) = parse_log_line("May 23 12:31:53 vyos kernel: [213370.255870] [OUTSIDE-LOCAL-default-D]IN=pppoe0 OUT= MAC= SRC=125.166.96.62 DST=80.80.80.80 LEN=143 TOS=0x00 PREC=0x00 TTL=110 ID=9398 PROTO=UDP SPT=1025 DPT=7140 LEN=123").unwrap();
        assert_eq!(parsed.time.hour(), 12);
        assert_eq!(parsed.hostname, "vyos");
        assert_eq!(parsed.rule, "OUTSIDE-LOCAL-default-D");
        assert_eq!(parsed.values.get("IN"), Some(&"pppoe0"));
        assert_eq!(parsed.values.get("MAC"), Some(&""));
        assert_eq!(parsed.values.get("DST"), Some(&"80.80.80.80"));
        assert_eq!(rest, "");
    }
}