From e6624dd4df077e741b3dcae7b157dbfff3597e5f Mon Sep 17 00:00:00 2001 From: Takanori Hirano Date: Tue, 26 Jul 2022 01:23:06 +0900 Subject: [PATCH 1/5] experimental version --- .github/workflows/build.yml | 39 +- .gitignore | 24 +- Cargo.lock | 2249 -------------------- Cargo.toml | 25 - backends/backends.go | 138 ++ functions.go | 137 ++ go.mod | 22 + go.sum | 68 + main.go | 54 + src/ntf-shell-hook.sh => ntf-shell-hook.sh | 0 src/backends/common.rs | 55 - src/backends/line.rs | 45 - src/backends/mod.rs | 6 - src/backends/pushbullet.rs | 51 - src/backends/pushover.rs | 95 - src/backends/slack.rs | 65 - src/backends/syslog.rs | 99 - src/lib.rs | 1 - src/main.rs | 433 ---- utils.go | 88 + utils_test.go | 96 + 21 files changed, 639 insertions(+), 3151 deletions(-) delete mode 100644 Cargo.lock delete mode 100644 Cargo.toml create mode 100644 backends/backends.go create mode 100644 functions.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go rename src/ntf-shell-hook.sh => ntf-shell-hook.sh (100%) delete mode 100644 src/backends/common.rs delete mode 100644 src/backends/line.rs delete mode 100644 src/backends/mod.rs delete mode 100644 src/backends/pushbullet.rs delete mode 100644 src/backends/pushover.rs delete mode 100644 src/backends/slack.rs delete mode 100644 src/backends/syslog.rs delete mode 100644 src/lib.rs delete mode 100644 src/main.rs create mode 100644 utils.go create mode 100644 utils_test.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fe94629..b38c6e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,38 +16,25 @@ jobs: os: macos-latest runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 - - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo index - uses: actions/cache@v1 + - uses: actions/setup-go@v2 with: - path: ~/.cargo/git - key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} + go-version: ^1.18 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - uses: actions-rs/cargo@v1 - with: - command: build - args: --release - - run: strip target/release/ntf + - uses: actions/checkout@v2 + + - run: go build -ldflags "-s -w" - uses: actions/upload-artifact@v1 with: name: ntf-${{ matrix.target }} - path: target/release/ntf + path: ntf + + - uses: actions/cache@v2 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- create-release: needs: diff --git a/.gitignore b/.gitignore index ea8c4bf..21217ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,23 @@ -/target +/ntf + +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index b8addc4..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,2249 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "advapi32-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e06588080cb19d0acb6739808aafa5f26bfb2ca015b2b6370028b44cf7cb8a9a" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "aead" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher", -] - -[[package]] -name = "aes-gcm" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher", - "opaque-debug 0.3.0", -] - -[[package]] -name = "ahash" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "anyhow" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" - -[[package]] -name = "async-channel" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" -dependencies = [ - "concurrent-queue", - "event-listener", - "futures-core", -] - -[[package]] -name = "async-executor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "once_cell", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c026b7e44f1316b567ee750fea85103f87fcb80792b860e979f221259796ca0a" -dependencies = [ - "async-channel", - "async-executor", - "async-io", - "async-mutex", - "blocking", - "futures-lite", - "num_cpus", - "once_cell", -] - -[[package]] -name = "async-io" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" -dependencies = [ - "concurrent-queue", - "futures-lite", - "libc", - "log", - "once_cell", - "parking", - "polling", - "slab", - "socket2", - "waker-fn", - "winapi 0.3.9", -] - -[[package]] -name = "async-lock" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-mutex" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" -dependencies = [ - "event-listener", -] - -[[package]] -name = "async-std" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952" -dependencies = [ - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "num_cpus", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-task" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" - -[[package]] -name = "async-trait" -version = "0.1.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic-waker" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" - -[[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.9", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "blocking" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc" -dependencies = [ - "async-channel", - "async-task", - "atomic-waker", - "fastrand", - "futures-lite", - "once_cell", -] - -[[package]] -name = "bumpalo" -version = "3.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" - -[[package]] -name = "cc" -version = "1.0.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "clap" -version = "3.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123" -dependencies = [ - "atty", - "bitflags", - "indexmap", - "os_str_bytes", - "strsim", - "termcolor", - "textwrap", -] - -[[package]] -name = "concurrent-queue" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" -dependencies = [ - "cache-padded", -] - -[[package]] -name = "config" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ad70579325f1a38ea4c13412b82241c5900700a69785d73e2736bd65a33f86" -dependencies = [ - "async-trait", - "json5", - "lazy_static", - "nom", - "pathdiff", - "ron", - "rust-ini", - "serde", - "serde_json", - "toml", - "yaml-rust", -] - -[[package]] -name = "const_fn" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" - -[[package]] -name = "cookie" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" -dependencies = [ - "aes-gcm", - "base64", - "hkdf", - "hmac", - "percent-encoding", - "rand 0.8.5", - "sha2", - "time 0.2.27", - "version_check", -] - -[[package]] -name = "cpufeatures" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" -dependencies = [ - "libc", -] - -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - -[[package]] -name = "crossbeam-utils" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" -dependencies = [ - "cfg-if", - "lazy_static", -] - -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array 0.14.5", - "subtle", -] - -[[package]] -name = "ctor" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "ctr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" -dependencies = [ - "cipher", -] - -[[package]] -name = "curl" -version = "0.4.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2", - "winapi 0.3.9", -] - -[[package]] -name = "curl-sys" -version = "0.4.53+curl-7.82.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8092905a5a9502c312f223b2775f57ec5c5b715f9a15ee9d2a8591d1364a0352" -dependencies = [ - "cc", - "libc", - "libnghttp2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "winapi 0.3.9", -] - -[[package]] -name = "dashmap" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" -dependencies = [ - "cfg-if", - "num_cpus", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.5", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi 0.3.9", -] - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "dlv-list" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b" -dependencies = [ - "rand 0.8.5", -] - -[[package]] -name = "encoding_rs" -version = "0.8.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "error-chain" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -dependencies = [ - "version_check", -] - -[[package]] -name = "event-listener" -version = "2.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" - -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "fastrand" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] - -[[package]] -name = "flume" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bebadab126f8120d410b677ed95eee4ba6eb7c6dd8e34a5ec88a08050e26132" -dependencies = [ - "futures-core", - "futures-sink", - "spinning_top", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" - -[[package]] -name = "futures-io" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" - -[[package]] -name = "futures-lite" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-macro" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" - -[[package]] -name = "futures-task" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" - -[[package]] -name = "futures-util" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" -dependencies = [ - "futures-core", - "futures-io", - "futures-macro", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.10.2+wasi-snapshot-preview1", -] - -[[package]] -name = "ghash" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" -dependencies = [ - "opaque-debug 0.3.0", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" - -[[package]] -name = "gloo-timers" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" -dependencies = [ - "digest 0.9.0", - "hmac", -] - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi 0.3.9", -] - -[[package]] -name = "http" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" -dependencies = [ - "bytes 1.1.0", - "fnv", - "itoa", -] - -[[package]] -name = "http-client" -version = "6.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea880b03c18a7e981d7fb3608b8904a98425d53c440758fcebf7d934aa56547c" -dependencies = [ - "async-std", - "async-trait", - "cfg-if", - "dashmap", - "http-types", - "isahc", - "log", -] - -[[package]] -name = "http-types" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" -dependencies = [ - "anyhow", - "async-channel", - "async-std", - "base64", - "cookie", - "futures-lite", - "infer", - "pin-project-lite", - "rand 0.7.3", - "serde", - "serde_json", - "serde_qs", - "serde_urlencoded", - "url", -] - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" -dependencies = [ - "autocfg", - "hashbrown 0.11.2", -] - -[[package]] -name = "infer" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "isahc" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2948a0ce43e2c2ef11d7edf6816508998d99e13badd1150be0914205df9388a" -dependencies = [ - "bytes 0.5.6", - "crossbeam-utils", - "curl", - "curl-sys", - "flume", - "futures-lite", - "http", - "log", - "once_cell", - "slab", - "sluice", - "tracing", - "tracing-futures", - "url", - "waker-fn", -] - -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - -[[package]] -name = "js-sys" -version = "0.3.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.121" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" - -[[package]] -name = "libnghttp2-sys" -version = "0.1.7+1.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "libz-sys" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" - -[[package]] -name = "lock_api" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", - "value-bag", -] - -[[package]] -name = "maplit" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" - -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - -[[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.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "nom" -version = "7.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "ntf" -version = "0.1.9" -dependencies = [ - "async-std", - "async-trait", - "clap", - "config", - "dirs", - "failure", - "hostname", - "regex", - "serde", - "surf", - "syslog", - "thiserror", - "username", -] - -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" -dependencies = [ - "libc", -] - -[[package]] -name = "object" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ordered-multimap" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" -dependencies = [ - "dlv-list", - "hashbrown 0.9.1", -] - -[[package]] -name = "os_str_bytes" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] - -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pest_meta" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" -dependencies = [ - "maplit", - "pest", - "sha-1", -] - -[[package]] -name = "pin-project" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" - -[[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.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" - -[[package]] -name = "polling" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" -dependencies = [ - "cfg-if", - "libc", - "log", - "wepoll-ffi", - "winapi 0.3.9", -] - -[[package]] -name = "polyval" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" -dependencies = [ - "cpuid-bool", - "opaque-debug 0.3.0", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro2" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", -] - -[[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_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom 0.2.5", -] - -[[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 = "redox_syscall" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55" -dependencies = [ - "getrandom 0.2.5", - "redox_syscall", - "thiserror", -] - -[[package]] -name = "regex" -version = "1.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "ron" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b861ecaade43ac97886a512b360d01d66be9f41f3c61088b42cedf92e03d678" -dependencies = [ - "base64", - "bitflags", - "serde", -] - -[[package]] -name = "rust-ini" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63471c4aa97a1cf8332a5f97709a79a4234698de6a1f5087faf66f2dae810e22" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - -[[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.9", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_qs" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha-1" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "slab" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" - -[[package]] -name = "sluice" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel", - "futures-core", - "futures-io", -] - -[[package]] -name = "socket2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" -dependencies = [ - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "spinning_top" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c" -dependencies = [ - "lock_api", -] - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "surf" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718b1ae6b50351982dedff021db0def601677f2120938b070eadb10ba4038dd7" -dependencies = [ - "async-std", - "async-trait", - "cfg-if", - "encoding_rs", - "futures-util", - "getrandom 0.2.5", - "http-client", - "http-types", - "log", - "mime_guess", - "once_cell", - "pin-project-lite", - "serde", - "serde_json", - "web-sys", -] - -[[package]] -name = "syn" -version = "1.0.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "syslog" -version = "6.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978044cc68150ad5e40083c9f6a725e6fd02d7ba1bcf691ec2ff0d66c0b41acc" -dependencies = [ - "error-chain", - "hostname", - "libc", - "log", - "time 0.3.7", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - -[[package]] -name = "thiserror" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi 0.3.9", -] - -[[package]] -name = "time" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d" -dependencies = [ - "itoa", - "libc", - "num_threads", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] - -[[package]] -name = "tinyvec" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "tracing" -version = "0.1.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - -[[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.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" - -[[package]] -name = "unicode-normalization" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array 0.14.5", - "subtle", -] - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", - "serde", -] - -[[package]] -name = "username" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e425df6527f7bc1adc7eb3b829ecaec746fbbc0b05e42133ff84afef3b1a09" -dependencies = [ - "advapi32-sys", - "winapi 0.2.8", -] - -[[package]] -name = "value-bag" -version = "1.0.0-alpha.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f" -dependencies = [ - "ctor", - "version_check", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "wasm-bindgen" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" - -[[package]] -name = "web-sys" -version = "0.3.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wepoll-ffi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" -dependencies = [ - "cc", -] - -[[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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-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.9", -] - -[[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 = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index d6f5ed9..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "ntf" -version = "0.1.9" -authors = ["Takanori Hirano "] -edition = "2018" - -[dependencies] -surf = "2.3.2" -config = "0.12.0" -serde = { version = "1.0.136", features = ["derive"] } -thiserror = "1.0.30" -async-trait = "0.1.52" -async-std = "1.10.0" -dirs = "4.0.0" -hostname = "0.3.1" -username = "0.2.0" -clap = "3.1.6" -failure = "0.1.8" -syslog = "6.0.1" - -[dev-dependencies] -regex = "1.5.5" - -[profile.release] -lto = true diff --git a/backends/backends.go b/backends/backends.go new file mode 100644 index 0000000..190694f --- /dev/null +++ b/backends/backends.go @@ -0,0 +1,138 @@ +package backends + +import ( + "fmt" + "log" + "reflect" + + "github.com/go-playground/validator/v10" + "github.com/mitchellh/mapstructure" + "github.com/spf13/cobra" +) + +type BackendInterface interface { + GetConfig() interface{} + Send(config interface{}, title string, message string, status *bool) error +} + +var backends = make(map[string]BackendInterface) +var validate = validator.New() + +func RegisterFlags(cmd ...*cobra.Command) { + for _, c := range cmd { + c.Flags().StringSlice("backends", []string{}, "") + } + for backendKey, b := range backends { + t := reflect.TypeOf(b.GetConfig()) + for i := 0; i < t.NumField(); i++ { + registerFlag(backendKey, t.Field(i).Tag.Get("mapstructure"), t.Field(i), cmd...) + } + } +} + +func registerFlag(backendKey string, key string, field reflect.StructField, cmd ...*cobra.Command) { + kind := field.Type.Kind() + if kind == reflect.Ptr { + kind = field.Type.Elem().Kind() + } + switch kind { + case reflect.String: + for _, c := range cmd { + c.Flags().String(fmt.Sprintf("%s.%s", backendKey, key), "", "") + } + case reflect.Int: + for _, c := range cmd { + c.Flags().Int(fmt.Sprintf("%s.%s", backendKey, key), 0, "") + } + } +} + +func OverrideConfigs(cfg *map[string]interface{}, cmd *cobra.Command) { + bflag := cmd.Flags().Lookup("backends") + var bs []interface{} + if bflag.Changed { + val, err := cmd.Flags().GetStringSlice("backends") + if err != nil { + log.Fatal(err) + } + for _, v := range val { + bs = append(bs, v) + } + (*cfg)["backends"] = bs + } + for backendKey, b := range backends { + if (*cfg)[backendKey] == nil { + (*cfg)[backendKey] = map[interface{}]interface{}{} + } + t := reflect.TypeOf(b.GetConfig()) + for i := 0; i < t.NumField(); i++ { + overrideConfig((*cfg)[backendKey], backendKey, t.Field(i).Tag.Get("mapstructure"), t.Field(i), cmd) + } + } +} + +func overrideConfig(cfg interface{}, backendKey string, key string, field reflect.StructField, cmd *cobra.Command) { + kind := field.Type.Kind() + if kind == reflect.Ptr { + kind = field.Type.Elem().Kind() + } + switch kind { + case reflect.String: + v := cmd.Flags().Lookup(fmt.Sprintf("%s.%s", backendKey, key)) + if !v.Changed { + return + } + val, err := cmd.Flags().GetString(fmt.Sprintf("%s.%s", backendKey, key)) + if err != nil { + log.Fatal(err) + } + cfg.(map[interface{}]interface{})[key] = val + case reflect.Int: + v := cmd.Flags().Lookup(fmt.Sprintf("%s.%s", backendKey, key)) + if !v.Changed { + return + } + val, err := cmd.Flags().GetInt(fmt.Sprintf("%s.%s", backendKey, key)) + if err != nil { + log.Fatal(err) + } + cfg.(map[interface{}]interface{})[key] = val + } +} + +func Send(cfg map[string]interface{}, title string, msg string, status *bool) error { + backendIfaces, ok := cfg["backends"] + if !ok { + return fmt.Errorf("no backends configured") + } + backendKeysIface, ok := backendIfaces.([]interface{}) + if !ok { + return fmt.Errorf("backends is not a list") + } + for _, backendKeyIface := range backendKeysIface { + backendKey, ok := backendKeyIface.(string) + if !ok { + log.Println(fmt.Errorf("backend is not a string")) + continue + } + b, ok := backends[backendKey] + if !ok { + log.Println(fmt.Errorf("backend %s not found", backendKey)) + continue + } + cfgStruct := b.GetConfig() + if err := mapstructure.Decode(cfg[backendKey], &cfgStruct); err != nil { + log.Println(fmt.Errorf("error decoding backend %s config: %s", backendKey, err)) + continue + } + if err := validate.Struct(cfgStruct); err != nil { + log.Println(fmt.Errorf("error validating backend %s config: %s", backendKey, err)) + continue + } + + if err := b.Send(cfgStruct, title, msg, status); err != nil { + return err + } + } + return nil +} diff --git a/functions.go b/functions.go new file mode 100644 index 0000000..9e1b249 --- /dev/null +++ b/functions.go @@ -0,0 +1,137 @@ +package main + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "strconv" + "strings" + "time" + + "github.com/adrg/xdg" + "github.com/hrntknr/ntf/backends" + "github.com/spf13/cobra" +) + +func doneFunc(cmd *cobra.Command, args []string) { + cfg, err := getConfig() + if err != nil { + log.Fatal(err) + } + backends.OverrideConfigs(&cfg, cmd) + var title string + if cmd.Flags().Lookup("title").Changed { + title = cmd.Flags().Lookup("title").Value.String() + } else { + title, err = getContext() + if err != nil { + log.Fatal(err) + } + } + if len(args) < 1 { + log.Fatal("no argument") + } + execCmd := exec.Command(args[0], args[1:]...) + execCmd.Stdout = os.Stdout + execCmd.Stderr = os.Stderr + execCmd.Stdin = os.Stdin + start := time.Now() + execCmd.Run() + elapsed := time.Since(start) + var msg string + status := execCmd.ProcessState.ExitCode() == 0 + if status { + msg = fmt.Sprintf("`%s` success in %s", strings.Join(args, " "), formatDuration(elapsed)) + } else { + msg = fmt.Sprintf("`%s` failed (code %d) in %s", strings.Join(args, " "), execCmd.ProcessState.ExitCode(), formatDuration(elapsed)) + } + if err := backends.Send(cfg, title, msg, &status); err != nil { + log.Fatal(err) + } +} + +func sendFunc(cmd *cobra.Command, args []string) { + cfg, err := getConfig() + if err != nil { + log.Fatal(err) + } + backends.OverrideConfigs(&cfg, cmd) + var title string + if cmd.Flags().Lookup("title").Changed { + title = cmd.Flags().Lookup("title").Value.String() + } else { + title, err = getContext() + if err != nil { + log.Fatal(err) + } + } + if len(args) < 1 { + log.Fatal("no argument") + } + if err := backends.Send(cfg, title, strings.Join(args, " "), nil); err != nil { + log.Fatal(err) + } +} + +func shellIntegrationFunc(cmd *cobra.Command, args []string) { + shellPath := xdg.DataHome + "/ntf/ntf-shell-hook.sh" + if _, err := os.Stat(shellPath); os.IsNotExist(err) { + if err := os.MkdirAll(xdg.DataHome+"/ntf", 0755); err != nil { + log.Fatal(err) + } + if err := ioutil.WriteFile(shellPath, ntf_shell_hook, 0755); err != nil { + log.Fatal(err) + } + } + fmt.Println( + strings.Join([]string{ + "export AUTO_NTF_DONE_LONGER_THAN=${AUTO_NTF_DONE_LONGER_THAN:=10}", + fmt.Sprintf("source %s", shellPath), + "# To use ntf's shell integration, run this and add it to your shell's rc file:", + "# eval \"$(ntf shell-integration)\"", + }, "\n"), + ) +} + +func shellDoneFunc(cmd *cobra.Command, args []string) { + cfg, err := getConfig() + if err != nil { + log.Fatal(err) + } + backends.OverrideConfigs(&cfg, cmd) + var title string + if cmd.Flags().Lookup("title").Changed { + title = cmd.Flags().Lookup("title").Value.String() + } else { + title, err = getContext() + if err != nil { + log.Fatal(err) + } + } + if len(args) < 3 { + log.Fatal("invalid arguments") + } + code, err := strconv.Atoi(args[0]) + if err != nil { + log.Fatal(err) + } + duration, err := strconv.Atoi(args[1]) + if err != nil { + log.Fatal(err) + } + command := strings.Join(args[2:], " ") + status := code == 0 + if status { + msg := fmt.Sprintf("`%s` success in %s", command, formatDuration(time.Second*time.Duration(duration))) + if err := backends.Send(cfg, title, msg, &status); err != nil { + log.Fatal(err) + } + } else { + msg := fmt.Sprintf("`%s` failed (code %d) in %s", command, code, formatDuration(time.Second*time.Duration(duration))) + if err := backends.Send(cfg, title, msg, &status); err != nil { + log.Fatal(err) + } + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..671be25 --- /dev/null +++ b/go.mod @@ -0,0 +1,22 @@ +module github.com/hrntknr/ntf + +go 1.18 + +require ( + github.com/adrg/xdg v0.4.0 + github.com/go-playground/validator/v10 v10.11.0 + github.com/mitchellh/mapstructure v1.5.0 + github.com/spf13/cobra v1.5.0 + github.com/spf13/pflag v1.0.5 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 // indirect + golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect + golang.org/x/text v0.3.7 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d628ce9 --- /dev/null +++ b/go.sum @@ -0,0 +1,68 @@ +github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= +github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= +github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= +github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..535f0fb --- /dev/null +++ b/main.go @@ -0,0 +1,54 @@ +package main + +import ( + _ "embed" + "log" + + "github.com/hrntknr/ntf/backends" + "github.com/spf13/cobra" +) + +func main() { + if err := _main(); err != nil { + log.Fatal(err) + } +} + +//go:embed ntf-shell-hook.sh +var ntf_shell_hook []byte + +func _main() error { + cmd := &cobra.Command{} + cmd.CompletionOptions.DisableDefaultCmd = true + + doneCmd := &cobra.Command{ + Use: "done", + Short: "Execute the command and notify the message", + Run: doneFunc, + } + doneCmd.Flags().StringP("title", "t", "", "override title") + + sendCmd := &cobra.Command{ + Use: "send", + Short: "send notification", + Run: sendFunc, + } + sendCmd.Flags().StringP("title", "t", "", "override title") + + shellDoneCmd := &cobra.Command{ + Use: "shell-done", + Hidden: true, + Run: shellDoneFunc, + } + shellDoneCmd.Flags().StringP("title", "t", "", "override title") + + shellIntegrationCmd := &cobra.Command{ + Use: "shell-integration", + Short: "shell-integration", + Run: shellIntegrationFunc, + } + + backends.RegisterFlags(doneCmd, sendCmd, shellIntegrationCmd, shellDoneCmd) + cmd.AddCommand(doneCmd, sendCmd, shellIntegrationCmd, shellDoneCmd) + return cmd.Execute() +} diff --git a/src/ntf-shell-hook.sh b/ntf-shell-hook.sh similarity index 100% rename from src/ntf-shell-hook.sh rename to ntf-shell-hook.sh diff --git a/src/backends/common.rs b/src/backends/common.rs deleted file mode 100644 index 4618213..0000000 --- a/src/backends/common.rs +++ /dev/null @@ -1,55 +0,0 @@ -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; -use std::str::FromStr; -use syslog::Facility; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum BackendError { - #[error("`{0}`")] - Message(String), -} - -pub struct SendOption { - pub slack_color: Option, - pub pushover_device: Option, - pub pushover_priority: Option, - pub pushover_retry: Option, - pub pushover_expire: Option, - pub syslog_facility: Option, - pub syslog_severity: Option, -} - -#[derive(Serialize, Deserialize, Debug)] -pub enum Priority { - #[serde(rename = "emergency")] - Emergency, - #[serde(rename = "high")] - High, - #[serde(rename = "normal")] - Normal, - #[serde(rename = "low")] - Low, - #[serde(rename = "lowest")] - Lowest, -} - -impl FromStr for Priority { - type Err = failure::Error; - - fn from_str(s: &str) -> Result { - match s { - "emergency" => Ok(Priority::Emergency), - "high" => Ok(Priority::High), - "normal" => Ok(Priority::Normal), - "low" => Ok(Priority::Low), - "lowest" => Ok(Priority::Lowest), - _ => Err(failure::format_err!("invalid priority: {}", s)), - } - } -} - -#[async_trait] -pub trait Backend { - async fn send(&self, msg: &str, title: &str, option: &SendOption) -> Result<(), BackendError>; -} diff --git a/src/backends/line.rs b/src/backends/line.rs deleted file mode 100644 index 1f0821e..0000000 --- a/src/backends/line.rs +++ /dev/null @@ -1,45 +0,0 @@ -use super::common::{Backend, BackendError, SendOption}; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; - -const API_URL: &str = "https://notify-api.line.me/api/notify"; - -#[derive(Deserialize, Serialize)] -struct Body { - message: String, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct LineConfig { - pub line: LineBackend, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct LineBackend { - token: String, -} - -#[async_trait] -impl Backend for LineBackend { - async fn send(&self, msg: &str, title: &str, _option: &SendOption) -> Result<(), BackendError> { - let body = &Body { - message: format!("{}\n{}", title, msg.to_string()), - }; - let req = match surf::post(API_URL) - .header("Authorization", format!("Bearer {}", self.token)) - .body_json(body) - { - Ok(req) => req, - Err(err) => return Err(BackendError::Message(err.to_string())), - }; - let res = match req.await { - Ok(res) => res, - Err(err) => return Err(BackendError::Message(err.to_string())), - }; - if res.status() != 200 { - return Err(BackendError::Message(res.status().to_string())); - } - - Ok(()) - } -} diff --git a/src/backends/mod.rs b/src/backends/mod.rs deleted file mode 100644 index 63ca391..0000000 --- a/src/backends/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod common; -pub mod line; -pub mod pushbullet; -pub mod pushover; -pub mod slack; -pub mod syslog; diff --git a/src/backends/pushbullet.rs b/src/backends/pushbullet.rs deleted file mode 100644 index da00d37..0000000 --- a/src/backends/pushbullet.rs +++ /dev/null @@ -1,51 +0,0 @@ -use super::common::{Backend, BackendError, SendOption}; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; - -const API_URL: &str = "https://api.pushbullet.com/v2/pushes"; - -#[derive(Deserialize, Serialize)] -struct Body { - #[serde(rename = "type")] - _type: String, - title: String, - body: String, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct PushbulletConfig { - pub pushbullet: PushbulletBackend, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct PushbulletBackend { - token: String, -} - -#[async_trait] -impl Backend for PushbulletBackend { - async fn send(&self, msg: &str, title: &str, _option: &SendOption) -> Result<(), BackendError> { - let body = &Body { - _type: "note".to_string(), - title: title.to_string(), - body: msg.to_string(), - }; - let req = match surf::post(API_URL) - .header("Access-Token", self.token.to_string()) - .header("Content-Type", "application/json") - .body_json(body) - { - Ok(req) => req, - Err(err) => return Err(BackendError::Message(err.to_string())), - }; - let res = match req.await { - Ok(res) => res, - Err(err) => return Err(BackendError::Message(err.to_string())), - }; - if res.status() != 200 { - return Err(BackendError::Message(res.status().to_string())); - } - - Ok(()) - } -} diff --git a/src/backends/pushover.rs b/src/backends/pushover.rs deleted file mode 100644 index 693c981..0000000 --- a/src/backends/pushover.rs +++ /dev/null @@ -1,95 +0,0 @@ -use super::common::{Backend, BackendError, Priority, SendOption}; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; - -const API_TOKEN: &str = "abughxjjtuofgt89bz21mibut67j5t"; -const API_URL: &str = "https://api.pushover.net/1/messages.json"; - -#[derive(Deserialize, Serialize)] -struct Body { - token: String, - user: String, - title: String, - message: String, - device: Option, - priority: Option, - retry: Option, - expire: Option, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct PushoverConfig { - pub pushover: PushoverBackend, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct PushoverBackend { - user_key: String, - device: Option, - priority: Option, - retry: Option, - expire: Option, -} - -#[async_trait] -impl Backend for PushoverBackend { - async fn send(&self, msg: &str, title: &str, option: &SendOption) -> Result<(), BackendError> { - let body = &Body { - token: API_TOKEN.to_string(), - user: self.user_key.to_string(), - title: title.to_string(), - message: msg.to_string(), - device: match option.pushover_device.clone() { - Some(device) => Some(device), - None => match self.device.clone() { - Some(device) => Some(device), - None => None, - }, - }, - priority: match &option.pushover_priority { - Some(priority) => Some(priority_to_pushover_code(priority)), - None => match &self.priority { - Some(priority) => Some(priority_to_pushover_code(priority)), - None => None, - }, - }, - retry: match option.pushover_retry { - Some(retry) => Some(retry), - None => match self.retry { - Some(retry) => Some(retry), - None => None, - }, - }, - expire: match option.pushover_expire { - Some(expire) => Some(expire), - None => match self.expire { - Some(expire) => Some(expire), - None => None, - }, - }, - }; - let req = match surf::post(API_URL).body_json(body) { - Ok(req) => req, - Err(err) => return Err(BackendError::Message(err.to_string())), - }; - let res = match req.await { - Ok(res) => res, - Err(err) => return Err(BackendError::Message(err.to_string())), - }; - if res.status() != 200 { - return Err(BackendError::Message(res.status().to_string())); - } - - Ok(()) - } -} - -pub fn priority_to_pushover_code(priority: &Priority) -> isize { - match priority { - Priority::Emergency => 2, - Priority::High => 1, - Priority::Normal => 0, - Priority::Low => -1, - Priority::Lowest => -2, - } -} diff --git a/src/backends/slack.rs b/src/backends/slack.rs deleted file mode 100644 index 5b14a0a..0000000 --- a/src/backends/slack.rs +++ /dev/null @@ -1,65 +0,0 @@ -use super::common::{Backend, BackendError, SendOption}; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; - -const COLOR: &str = "good"; -const ICON_URL: &str = "https://raw.githubusercontent.com/hrntknr/ntf/master/assets/icon.png"; - -#[derive(Deserialize, Serialize)] -struct Body { - icon_url: String, - attachments: Vec, -} - -#[derive(Deserialize, Serialize)] -struct Attachment { - title: String, - text: String, - color: String, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct SlackConfig { - pub slack: SlackBackend, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct SlackBackend { - webhook: String, - color: Option, -} - -#[async_trait] -impl Backend for SlackBackend { - async fn send(&self, msg: &str, title: &str, option: &SendOption) -> Result<(), BackendError> { - let mut attachments = Vec::new(); - attachments.push(Attachment { - title: title.to_string(), - text: msg.to_string(), - color: match option.slack_color.clone() { - Some(color) => color, - None => match self.color.clone() { - Some(color) => color, - None => COLOR.to_string(), - }, - }, - }); - let body = &Body { - icon_url: ICON_URL.to_string(), - attachments: attachments, - }; - let req = match surf::post(&self.webhook).body_json(body) { - Ok(req) => req, - Err(err) => return Err(BackendError::Message(err.to_string())), - }; - let res = match req.await { - Ok(res) => res, - Err(err) => return Err(BackendError::Message(err.to_string())), - }; - if res.status() != 200 { - return Err(BackendError::Message(res.status().to_string())); - } - - Ok(()) - } -} diff --git a/src/backends/syslog.rs b/src/backends/syslog.rs deleted file mode 100644 index e8b5aaa..0000000 --- a/src/backends/syslog.rs +++ /dev/null @@ -1,99 +0,0 @@ -use super::common::{Backend, BackendError, SendOption}; -use async_trait::async_trait; -use serde::{Deserialize, Serialize}; -use std::process; -use syslog::{Facility, Formatter3164}; - -#[derive(Serialize, Deserialize, Debug)] -pub struct SyslogConfig { - pub syslog: SyslogBackend, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct SyslogBackend { - facility: Option, - severity: Option, -} - -#[async_trait] -impl Backend for SyslogBackend { - async fn send(&self, msg: &str, _title: &str, option: &SendOption) -> Result<(), BackendError> { - let formatter = Formatter3164 { - facility: match option.syslog_facility { - Some(facility) => facility, - None => match self.facility.clone() { - Some(facility) => match facility.parse() { - Ok(facility) => facility, - Err(_) => { - return Err(BackendError::Message( - "failed to parse facility".to_string(), - )) - } - }, - None => Facility::LOG_USER, - }, - }, - hostname: None, - process: "ntf".into(), - pid: process::id() as u32, - }; - let mut writer = match syslog::unix(formatter) { - Ok(writer) => writer, - Err(_) => { - return Err(BackendError::Message( - "faild to get syslog writer".to_string(), - )) - } - }; - - let result = match option.syslog_severity.clone() { - Some(severity) => match severity.as_str() { - "emerg" => writer.emerg(msg), - "alert" => writer.alert(msg), - "crit" => writer.crit(msg), - "err" => writer.err(msg), - "warning" => writer.warning(msg), - "notice" => writer.notice(msg), - "info" => writer.info(msg), - "debug" => writer.debug(msg), - _ => { - return Err(BackendError::Message(format!( - "{} is not valid severity", - severity - ))) - } - }, - None => match self.severity.clone() { - Some(severity) => match severity.as_str() { - "emerg" => writer.emerg(msg), - "alert" => writer.alert(msg), - "crit" => writer.crit(msg), - "err" => writer.err(msg), - "warning" => writer.warning(msg), - "notice" => writer.notice(msg), - "info" => writer.info(msg), - "debug" => writer.debug(msg), - _ => { - return Err(BackendError::Message(format!( - "{} is not valid severity", - severity - ))) - } - }, - None => writer.info(msg), - }, - }; - - match result { - Ok(_) => (), - Err(err) => { - return Err(BackendError::Message(format!( - "faild to send syslog: {}", - err.to_string() - ))) - } - } - - Ok(()) - } -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 257edb5..0000000 --- a/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod backends; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index 7bf70b0..0000000 --- a/src/main.rs +++ /dev/null @@ -1,433 +0,0 @@ -extern crate ntf; -use ntf::backends::common::{Backend, BackendError, Priority, SendOption}; -use ntf::backends::line::LineConfig; -use ntf::backends::pushbullet::PushbulletConfig; -use ntf::backends::pushover::PushoverConfig; -use ntf::backends::slack::SlackConfig; -use ntf::backends::syslog::SyslogConfig; - -use async_std::task; -use clap::{Arg, ArgMatches, Command}; -use config::{Config, File}; -use failure::{format_err, Error}; -use std::env; -use std::fs; -use std::process; -use std::time::{Duration, Instant}; -use std::vec::Vec; -use syslog::Facility; - -fn main() { - let config = match get_config() { - Ok(config) => config, - Err(err) => { - println!("{}", err.to_string()); - process::exit(1); - } - }; - - let basic_args = &[ - Arg::new("title") - .help("override title") - .long("title") - .short('t') - .multiple_values(false), - Arg::new("pushover_device") - .help("override pushover device") - .long("pushover.device") - .multiple_values(false), - Arg::new("pushover_priority") - .help("override pushover priority") - .long("pushover.priority") - .multiple_values(false) - .possible_values(&["emergency", "high", "normal", "low", "lowest"]), - Arg::new("pushover_retry") - .help("override pushover retry") - .long("pushover.retry") - .multiple_values(false), - Arg::new("pushover_expire") - .help("override pushover expire") - .long("pushover.expire") - .multiple_values(false), - Arg::new("slack_color") - .help("override slack color") - .long("slack.color") - .multiple_values(false), - Arg::new("syslog_facility") - .help("override syslog facility") - .long("syslog.facility") - .multiple_values(false) - .possible_values(&[ - "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news", "uucp", "cron", - "authpriv", "ftp", "local0", "local1", "local2", "local3", "local4", "local5", - "local6", "local7", - ]), - Arg::new("syslog_severity") - .help("override syslog severity") - .long("syslog.severity") - .multiple_values(false) - .possible_values(&[ - "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug", - ]), - ]; - - let app = Command::new("ntf") - .subcommand( - Command::new("send") - .about("send notification") - .args(basic_args) - .arg(Arg::new("message").required(true).multiple_values(true)), - ) - .subcommand( - Command::new("done") - .about("Execute the command and notify the message") - .args(basic_args) - .arg(Arg::new("cmd").required(true).multiple_values(true)), - ) - .subcommand( - Command::new("shell-done") - .hide(true) - .args(basic_args) - .arg(Arg::new("code").required(true).multiple_values(false)) - .arg(Arg::new("duration").required(true).multiple_values(false)) - .arg(Arg::new("cmd").required(true).multiple_values(true)), - ) - .subcommand(Command::new("shell-integration").about("shell-integration")); - let matches = app.get_matches(); - - if let Some(ref sub_matches) = matches.subcommand_matches("send") { - match send(config, sub_matches) { - Ok(_) => {} - Err(err) => { - println!("{}", err.to_string()); - process::exit(1); - } - } - } else if let Some(ref sub_matches) = matches.subcommand_matches("done") { - match done(config, sub_matches) { - Ok(_) => {} - Err(err) => { - println!("{}", err.to_string()); - process::exit(1); - } - } - } else if let Some(ref sub_matches) = matches.subcommand_matches("shell-done") { - match shell_done(config, sub_matches) { - Ok(_) => {} - Err(err) => { - println!("{}", err.to_string()); - process::exit(1); - } - } - } else if let Some(ref sub_matches) = matches.subcommand_matches("shell-integration") { - match shell_integration(config, sub_matches) { - Ok(_) => {} - Err(err) => { - println!("{}", err.to_string()); - process::exit(1); - } - } - } -} - -fn get_send_option(sub_matches: &&ArgMatches) -> Result { - let opt = SendOption { - slack_color: sub_matches.value_of("slack_color").map(|s| s.to_string()), - pushover_device: sub_matches - .value_of("pushover_device") - .map(|s| s.to_string()), - pushover_priority: match sub_matches - .value_of("pushover_priority") - .map(|s| s.parse::()) - { - Some(value) => Some(value?), - None => None, - }, - pushover_retry: match sub_matches - .value_of("pushover_retry") - .map(|s| s.parse::()) - { - Some(value) => Some(value?), - None => None, - }, - pushover_expire: match sub_matches - .value_of("pushover_expire") - .map(|s| s.parse::()) - { - Some(value) => Some(value?), - None => None, - }, - syslog_facility: match sub_matches - .value_of("syslog_facility") - .map(|s| s.parse::()) - { - Some(value) => Some(match value { - Ok(facility) => facility, - Err(_) => return Err(format_err!("can't get message")), - }), - None => None, - }, - syslog_severity: sub_matches - .value_of("syslog_severity") - .map(|s| s.to_string()), - }; - Ok(opt) -} - -fn send(backends: Vec>, sub_matches: &&ArgMatches) -> Result<(), Error> { - let title = match sub_matches.value_of("title") { - Some(title) => title.to_string(), - None => get_title()?, - }; - let message = sub_matches - .values_of("message") - .ok_or(format_err!("can't get message"))?; - let message = message.fold(String::new(), |mut acc: String, cur: &str| { - if acc != "" { - acc.push_str(" "); - } - acc.push_str(cur); - acc - }); - let message = unescape(message); - let opt = get_send_option(sub_matches)?; - - let result: Result<(), BackendError> = backends - .into_iter() - .map(|backend| task::block_on(backend.send(message.as_str(), title.as_str(), &opt))) - .collect(); - result?; - - Ok(()) -} - -fn done(backends: Vec>, sub_matches: &&ArgMatches) -> Result<(), Error> { - let title = match sub_matches.value_of("title") { - Some(title) => title.to_string(), - None => get_title()?, - }; - let cmd = sub_matches - .values_of("cmd") - .ok_or(format_err!("can't get cmd"))?; - let cmd: Vec = cmd.map(|s| s.to_string()).collect(); - let start = Instant::now(); - let code = process::Command::new(&cmd[0]) - .args(&cmd[1..]) - .stdin(process::Stdio::inherit()) - .stdout(process::Stdio::inherit()) - .stderr(process::Stdio::inherit()) - .spawn()? - .wait()? - .code() - .ok_or(format_err!("can't get code"))?; - let duration = start.elapsed(); - - let message = if code == 0 { - format!( - "`{}` success in {}", - cmd.join(" ").escape_default().to_string(), - format_duration(duration), - ) - } else { - format!( - "`{}` failed (code {}) in {}", - cmd.join(" ").escape_default().to_string(), - code, - format_duration(duration), - ) - }; - let mut opt = get_send_option(sub_matches)?; - if opt.slack_color == None { - match code { - 0 => opt.slack_color = Some("good".to_string()), - _ => opt.slack_color = Some("warning".to_string()), - } - } - - let result: Result<(), BackendError> = backends - .into_iter() - .map(|backend| task::block_on(backend.send(message.as_str(), title.as_str(), &opt))) - .collect(); - result?; - - Ok(()) -} - -fn shell_done(backends: Vec>, sub_matches: &&ArgMatches) -> Result<(), Error> { - let title = match sub_matches.value_of("title") { - Some(title) => title.to_string(), - None => get_title()?, - }; - - let code: i32 = sub_matches - .value_of("code") - .ok_or(format_err!("can't get code"))? - .parse()?; - let duration = sub_matches - .value_of("duration") - .ok_or(format_err!("can't get duration"))? - .parse()?; - let duration = Duration::from_secs(duration); - let cmd: Vec = sub_matches - .values_of("cmd") - .ok_or(format_err!("can't get cmd"))? - .map(|s| s.to_string()) - .collect(); - - let message = if code == 0 { - format!( - "`{}` success in {}", - cmd.join(" ").escape_default().to_string(), - format_duration(duration), - ) - } else { - format!( - "`{}` failed (code {}) in {}", - cmd.join(" ").escape_default().to_string(), - code, - format_duration(duration), - ) - }; - let mut opt = get_send_option(sub_matches)?; - if opt.slack_color == None { - match code { - 0 => opt.slack_color = Some("good".to_string()), - _ => opt.slack_color = Some("warning".to_string()), - } - } - - let result: Result<(), BackendError> = backends - .into_iter() - .map(|backend| task::block_on(backend.send(message.as_str(), title.as_str(), &opt))) - .collect(); - result?; - - Ok(()) -} - -fn shell_integration( - _backends: Vec>, - _sub_matches: &&ArgMatches, -) -> Result<(), Error> { - let mut dir = dirs::data_local_dir().ok_or(format_err!("can't get data_local_dir"))?; - dir.push("ntf"); - if !dir.exists() { - fs::create_dir_all(dir)? - }; - - let mut file = dirs::data_local_dir().ok_or(format_err!("can't get data_local_dir"))?; - file.push("ntf/ntf-shell-hook.sh"); - if !file.exists() { - fs::write(file, include_str!("./ntf-shell-hook.sh"))? - }; - println!("export ${{AUTO_NTF_DONE_LONGER_THAN:=10}}"); - println!( - "source {}/ntf/ntf-shell-hook.sh", - dirs::data_local_dir() - .ok_or(format_err!("can't get data_local_dir"))? - .to_str() - .ok_or(format_err!("can't get data_local_dir"))? - ); - println!("# To use ntf's shell integration, run this and add it to your shell's rc file:"); - println!("# eval \"$(ntf shell-integration)\""); - - Ok(()) -} - -fn format_duration(duration: Duration) -> String { - let sec = duration.as_secs(); - if sec < 60 { - return format!("{}m {}s", sec / 60, sec % 60); - } - format!("{}h {}m {}s", sec / 60 / 60, (sec / 60) % 60, sec % 60) -} - -pub fn get_title() -> Result { - let path = env::current_dir()?; - let path = path.to_str().ok_or(format_err!("can't get current_dir"))?; - let home = dirs::home_dir().ok_or(format_err!("can't get home_dir"))?; - let home = home.to_str().ok_or(format_err!("can't get home_dir"))?; - let host = hostname::get()?.into_string().unwrap(); - let user = username::get_user_name()?; - let relative_path = if path.starts_with(home) { - path.replacen(home, "~", 1) - } else { - path.to_string() - }; - - Ok(format!( - "{}@{}:{}", - user.to_string(), - host.to_string(), - relative_path, - )) -} - -fn get_config() -> Result>, Error> { - let mut path = dirs::home_dir().ok_or(format_err!("can't get home_dir"))?; - path.push(".ntf.yml"); - - let settings = Config::builder().add_source(File::from(path)).build()?; - - let backends_str = settings.get_array("backends")?; - let mut backends: Vec> = Vec::new(); - - for backend_str in backends_str { - let settings = settings.clone(); - let backend_str = backend_str.into_string()?; - match backend_str.as_str() { - "line" => { - let conf: LineConfig = settings.try_deserialize()?; - backends.push(Box::new(conf.line)); - } - "pushbullet" => { - let conf: PushbulletConfig = settings.try_deserialize()?; - backends.push(Box::new(conf.pushbullet)); - } - "pushover" => { - let conf: PushoverConfig = settings.try_deserialize()?; - backends.push(Box::new(conf.pushover)); - } - "slack" => { - let conf: SlackConfig = settings.try_deserialize()?; - backends.push(Box::new(conf.slack)); - } - "syslog" => { - let conf: SyslogConfig = settings.try_deserialize()?; - backends.push(Box::new(conf.syslog)); - } - _ => { - return Err(format_err!("invalid backend: {}", backend_str.as_str())); - } - } - } - - Ok(backends) -} - -fn unescape(txt: String) -> String { - txt.replace("\\\\", "\\") - .replace("\\n", "\n") - .replace("\\r", "\r") - .replace("\\t", "\t") -} - -#[cfg(test)] -mod tests { - use super::*; - use regex::Regex; - - #[test] - fn get_title_test() { - let title = get_title().unwrap(); - let re = Regex::new(r"^.+@.+:.+$").unwrap(); - assert!(re.is_match(title.as_str())); - } - - #[test] - fn unescape_test() { - let test = "\\n\\r\\t\\\\\\'\\\"\\0"; - let expect = "\n\r\t\\\\'\\\"\\0"; - assert_eq!(unescape(test.to_string()), expect); - } -} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..843b1e3 --- /dev/null +++ b/utils.go @@ -0,0 +1,88 @@ +package main + +import ( + "fmt" + "log" + "os" + "os/user" + "strings" + "time" + + "github.com/adrg/xdg" + "gopkg.in/yaml.v2" +) + +func getConfig() (map[string]interface{}, error) { + homeDir, err := os.UserHomeDir() + if err != nil { + return nil, err + } + configPath := []string{ + homeDir + "/.ntf.yml", + xdg.ConfigHome + "/.ntf.yml", + } + for _, path := range configPath { + cfg, err := tryConfig(path) + if err != nil { + log.Fatal(err) + } + if cfg != nil { + return cfg, nil + } + } + return map[string]interface{}{}, nil +} + +func tryConfig(configPath string) (map[string]interface{}, error) { + if _, err := os.Stat(configPath); os.IsNotExist(err) { + return nil, nil + } + fp, err := os.Open(configPath) + if err != nil { + return nil, err + } + defer fp.Close() + + var config map[string]interface{} + if err := yaml.NewDecoder(fp).Decode(&config); err != nil { + return nil, err + } + + return config, nil +} + +func getContext() (string, error) { + user, err := user.Current() + if err != nil { + return "", err + } + hostname, err := os.Hostname() + if err != nil { + return "", err + } + path, err := os.Getwd() + if err != nil { + return "", err + } + homeDir, err := os.UserHomeDir() + if err != nil { + return "", err + } + if strings.HasPrefix(path, homeDir) { + path = "~" + path[len(homeDir):] + } + return fmt.Sprintf("%s@%s:%s", user.Username, hostname, path), nil +} + +func formatDuration(d time.Duration) string { + if d.Seconds() < 60 { + return fmt.Sprintf("%ds", int(d.Seconds())) + } + if d.Minutes() < 60 { + return fmt.Sprintf("%dm %ds", int(d.Minutes()), int(d.Seconds())-int(d.Minutes())*60) + } + if d.Hours() < 24 { + return fmt.Sprintf("%dh %dm %ds", int(d.Hours()), int(d.Minutes())-int(d.Hours())*60, int(d.Seconds())-int(d.Minutes())*60) + } + return fmt.Sprintf("%dd %dh %dm %ds", int(d.Hours())/24, int(d.Hours())-int(d.Hours())/24*24, int(d.Minutes())-int(d.Hours())*60, int(d.Seconds())-int(d.Minutes())*60) +} diff --git a/utils_test.go b/utils_test.go new file mode 100644 index 0000000..6711afd --- /dev/null +++ b/utils_test.go @@ -0,0 +1,96 @@ +package main + +import ( + "errors" + "math/rand" + "os" + "path" + "regexp" + "testing" + "time" +) + +func TestFormatDuration(t *testing.T) { + tests := []struct { + in time.Duration + out string + }{ + {0, "0s"}, + {1 * time.Second, "1s"}, + {1 * time.Minute, "1m 0s"}, + {1*time.Minute + 30*time.Second, "1m 30s"}, + {1*time.Hour + 1*time.Minute + 30*time.Second, "1h 1m 30s"}, + {25*time.Hour + 1*time.Minute + 30*time.Second, "1d 1h 1m 30s"}, + } + for _, test := range tests { + if out := formatDuration(test.in); out != test.out { + t.Errorf("formatDuration(%d) = %s, want %s", test.in, out, test.out) + return + } + } +} + +func TestGetContext(t *testing.T) { + home, err := os.UserHomeDir() + if err != nil { + t.Fatal(err) + } + if err := os.Chdir(home); err != nil { + t.Fatal(err) + } + expect := regexp.MustCompile(`^[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+:~$`) + str, err := getContext() + if err != nil { + t.Fatal(err) + } + if !expect.MatchString(str) { + t.Errorf("getContext() = %s, want regex %s", str, expect.String()) + return + } +} + +func TestTryConfig(t *testing.T) { + rnd := MakeRandomStr(8) + home, err := os.UserHomeDir() + if err != nil { + t.Fatal(err) + } + fp, err := os.Create(path.Join(home, rnd)) + if err != nil { + t.Fatal(err) + } + defer func() { + fp.Close() + os.Remove(path.Join(home, rnd)) + }() + fp.WriteString(` +backends: ["dummy"] +dummy: + item: dummy +`) + + cfg, err := tryConfig(path.Join(home, rnd)) + if err != nil { + t.Errorf("tryConfig(%s) = %s, want nil", path.Join(home, rnd), err) + return + } + if cfg == nil { + t.Errorf("tryConfig(%s) = nil, want non-nil", path.Join(home, rnd)) + return + } +} + +func MakeRandomStr(digit uint32) string { + const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + + b := make([]byte, digit) + if _, err := rand.Read(b); err != nil { + panic(errors.New("unexpected error...")) + } + + var result string + for _, v := range b { + result += string(letters[int(v)%len(letters)]) + } + return result +} From 422e9ba0bc5c808d878149e03a4a5aa2760c88e8 Mon Sep 17 00:00:00 2001 From: Takanori Hirano Date: Tue, 26 Jul 2022 01:28:59 +0900 Subject: [PATCH 2/5] add line backend --- backends/line.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 backends/line.go diff --git a/backends/line.go b/backends/line.go new file mode 100644 index 0000000..8fa9a32 --- /dev/null +++ b/backends/line.go @@ -0,0 +1,54 @@ +package backends + +import ( + "fmt" + "net/http" + "net/url" + "strings" +) + +func init() { + backends["line"] = NewLine() +} + +const LINE_API_URL = "https://notify-api.line.me/api/notify" + +type LineConfig struct { + Token string `mapstructure:"token" validate:"required"` +} + +type Line struct { +} + +func NewLine() BackendInterface { + return &Line{} +} + +func (*Line) GetConfig() interface{} { + return LineConfig{} +} + +func (*Line) Send(configIface interface{}, title string, message string, status *bool) error { + config, ok := configIface.(LineConfig) + if !ok { + return fmt.Errorf("invalid config") + } + form := url.Values{} + form.Add("message", fmt.Sprintf("%s\n%s", title, message)) + body := strings.NewReader(form.Encode()) + req, err := http.NewRequest("POST", LINE_API_URL, body) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", config.Token)) + res, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + if res.StatusCode < 200 || res.StatusCode >= 300 { + return fmt.Errorf("line: %s", res.Status) + } + + return nil +} From 0ac9f8529b1d8bae7174d434fbf69c0fd456e263 Mon Sep 17 00:00:00 2001 From: Takanori Hirano Date: Tue, 26 Jul 2022 01:29:10 +0900 Subject: [PATCH 3/5] add slack backend --- backends/slack.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 backends/slack.go diff --git a/backends/slack.go b/backends/slack.go new file mode 100644 index 0000000..a8bd458 --- /dev/null +++ b/backends/slack.go @@ -0,0 +1,70 @@ +package backends + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" +) + +func init() { + backends["slack"] = NewSlack() +} + +type SlackConfig struct { + Webhook string `mapstructure:"webhook" validate:"required"` + Color *string `mapstructure:"color" validate:"omitempty,hexcolor"` +} + +type Slack struct { +} + +func NewSlack() BackendInterface { + return &Slack{} +} + +func (*Slack) GetConfig() interface{} { + return SlackConfig{} +} + +func (*Slack) Send(configIface interface{}, title string, message string, status *bool) error { + config, ok := configIface.(SlackConfig) + if !ok { + return fmt.Errorf("invalid config") + } + var color string + if config.Color != nil { + color = *config.Color + } else if status != nil { + if *status { + color = "good" + } else { + color = "danger" + } + } else { + color = "#ffffff" + } + + body := map[string]interface{}{ + "attachments": []map[string]interface{}{ + { + "title": title, + "text": message, + "color": color, + }, + }, + } + jsonBody, err := json.Marshal(body) + if err != nil { + return err + } + res, err := http.Post(config.Webhook, "application/json", bytes.NewBuffer(jsonBody)) + if err != nil { + return err + } + if res.StatusCode < 200 || res.StatusCode >= 300 { + return fmt.Errorf("slack: %s", res.Status) + } + + return nil +} From 0ea8d3f19fded6dadaeb489f3b8835c016aca639 Mon Sep 17 00:00:00 2001 From: Takanori Hirano Date: Tue, 26 Jul 2022 01:29:18 +0900 Subject: [PATCH 4/5] add some backend --- backends/pushbullet.go | 60 ++++++++++++++++++++++ backends/pushover.go | 83 ++++++++++++++++++++++++++++++ backends/syslog.go | 112 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 backends/pushbullet.go create mode 100644 backends/pushover.go create mode 100644 backends/syslog.go diff --git a/backends/pushbullet.go b/backends/pushbullet.go new file mode 100644 index 0000000..6bc644f --- /dev/null +++ b/backends/pushbullet.go @@ -0,0 +1,60 @@ +package backends + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" +) + +func init() { + backends["pushbullet"] = NewPushbullet() +} + +const PUSHBULET_API_URL = "https://api.pushbullet.com/v2/pushes" + +type PushbulletConfig struct { + Token string `mapstructure:"token" validate:"required"` +} + +type Pushbullet struct { +} + +func NewPushbullet() BackendInterface { + return &Pushbullet{} +} + +func (*Pushbullet) GetConfig() interface{} { + return PushbulletConfig{} +} + +func (*Pushbullet) Send(configIface interface{}, title string, message string, status *bool) error { + config, ok := configIface.(PushbulletConfig) + if !ok { + return fmt.Errorf("invalid config") + } + body := map[string]interface{}{ + "type": "note", + "title": title, + "body": message, + } + jsonBody, err := json.Marshal(body) + if err != nil { + return err + } + req, err := http.NewRequest("POST", PUSHBULET_API_URL, bytes.NewBuffer(jsonBody)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", config.Token)) + res, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + if res.StatusCode < 200 || res.StatusCode >= 300 { + return fmt.Errorf("pushbullet: %s", res.Status) + } + + return nil +} diff --git a/backends/pushover.go b/backends/pushover.go new file mode 100644 index 0000000..35d1c43 --- /dev/null +++ b/backends/pushover.go @@ -0,0 +1,83 @@ +package backends + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" +) + +func init() { + backends["pushover"] = NewPushover() +} + +const PUSHOVER_API_TOKEN = "abughxjjtuofgt89bz21mibut67j5t" +const PUSHOVER_API_URL = "https://api.pushover.net/1/messages.json" + +type PushoverConfig struct { + UserKey string `mapstructure:"user_key" validate:"required"` + Device *string `mapstructure:"device" validate:"omitempty"` + Priority *string `mapstructure:"priority" validate:"omitempty,oneof=emergency high normal low lowest"` + Retry *int `mapstructure:"retry" validate:"omitempty,min=30"` + Expire *int `mapstructure:"expire" validate:"omitempty,min=0,max=10800"` +} + +type Pushover struct { +} + +func NewPushover() BackendInterface { + return &Pushover{} +} + +func (*Pushover) GetConfig() interface{} { + return PushoverConfig{} +} + +func (*Pushover) Send(configIface interface{}, title string, message string, status *bool) error { + config, ok := configIface.(PushoverConfig) + if !ok { + return fmt.Errorf("invalid config") + } + + body := map[string]interface{}{ + "token": PUSHOVER_API_TOKEN, + "user": config.UserKey, + "title": title, + "message": message, + } + if config.Device != nil { + body["device"] = *config.Device + } + if config.Priority != nil { + switch *config.Priority { + case "emergency": + body["priority"] = 2 + case "high": + body["priority"] = 1 + case "normal": + body["priority"] = 0 + case "low": + body["priority"] = -1 + case "lowest": + body["priority"] = -2 + } + } + if config.Retry != nil { + body["retry"] = *config.Retry + } + if config.Expire != nil { + body["expire"] = *config.Expire + } + jsonBody, err := json.Marshal(body) + if err != nil { + return err + } + res, err := http.Post(PUSHOVER_API_URL, "application/json", bytes.NewBuffer(jsonBody)) + if err != nil { + return err + } + if res.StatusCode < 200 || res.StatusCode >= 300 { + return fmt.Errorf("pushover: %s", res.Status) + } + return nil +} diff --git a/backends/syslog.go b/backends/syslog.go new file mode 100644 index 0000000..2d71782 --- /dev/null +++ b/backends/syslog.go @@ -0,0 +1,112 @@ +package backends + +import ( + "fmt" + "log/syslog" +) + +func init() { + backends["syslog"] = NewSyslog() +} + +type SyslogConfig struct { + Facility *string `mapstructure:"facility" validate:"omitempty,oneof=kern user mail daemon auth syslog lpr news uucp cron authpriv ftp local0 local1 local2 local3 local4 local5 local6 local7"` + Severity *string `mapstructure:"severity" validate:"omitempty,oneof=emerg alert crit err warning notice info debug"` +} + +type Syslog struct { +} + +func NewSyslog() BackendInterface { + return &Syslog{} +} + +func (*Syslog) GetConfig() interface{} { + return SyslogConfig{} +} + +func (*Syslog) Send(configIface interface{}, title string, message string, status *bool) error { + config, ok := configIface.(SyslogConfig) + if !ok { + return fmt.Errorf("invalid config") + } + var facility syslog.Priority + if config.Facility != nil { + switch *config.Facility { + case "kern": + facility = syslog.LOG_KERN + case "user": + facility = syslog.LOG_USER + case "mail": + facility = syslog.LOG_MAIL + case "daemon": + facility = syslog.LOG_DAEMON + case "auth": + facility = syslog.LOG_AUTH + case "syslog": + facility = syslog.LOG_SYSLOG + case "lpr": + facility = syslog.LOG_LPR + case "news": + facility = syslog.LOG_NEWS + case "uucp": + facility = syslog.LOG_UUCP + case "cron": + facility = syslog.LOG_CRON + case "authpriv": + facility = syslog.LOG_AUTHPRIV + case "ftp": + facility = syslog.LOG_FTP + case "local0": + facility = syslog.LOG_LOCAL0 + case "local1": + facility = syslog.LOG_LOCAL1 + case "local2": + facility = syslog.LOG_LOCAL2 + case "local3": + facility = syslog.LOG_LOCAL3 + case "local4": + facility = syslog.LOG_LOCAL4 + case "local5": + facility = syslog.LOG_LOCAL5 + case "local6": + facility = syslog.LOG_LOCAL6 + case "local7": + facility = syslog.LOG_LOCAL7 + } + } else { + facility = syslog.LOG_USER + } + var severity syslog.Priority + if config.Severity != nil { + switch *config.Severity { + case "emerg": + severity = syslog.LOG_EMERG + case "alert": + severity = syslog.LOG_ALERT + case "crit": + severity = syslog.LOG_CRIT + case "err": + severity = syslog.LOG_ERR + case "warning": + severity = syslog.LOG_WARNING + case "notice": + severity = syslog.LOG_NOTICE + case "info": + severity = syslog.LOG_INFO + case "debug": + severity = syslog.LOG_DEBUG + } + } else { + severity = syslog.LOG_INFO + } + + logger, err := syslog.New(facility|severity, "") + if err != nil { + return err + } + defer logger.Close() + logger.Info(message) + + return nil +} From 76f914e29efcbec4c8211a57667b87164ddb9a84 Mon Sep 17 00:00:00 2001 From: Takanori Hirano Date: Tue, 26 Jul 2022 01:29:46 +0900 Subject: [PATCH 5/5] v1.0.1 README --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e5f77b..6e0c56b 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ However, support for the backend type is poorer than ntfy. ```sh $ # for linux -$ sudo curl -L https://github.com/hrntknr/ntf/releases/download/v0.1.9/ntf-x86_64-unknown-linux-gnu -o /usr/local/bin/ntf +$ sudo curl -L https://github.com/hrntknr/ntf/releases/download/v1.0.1/ntf-x86_64-unknown-linux-gnu -o /usr/local/bin/ntf $ # for mac -$ # sudo curl -L https://github.com/hrntknr/ntf/releases/download/v0.1.9/ntf-x86_64-apple-darwin -o /usr/local/bin/ntf +$ # sudo curl -L https://github.com/hrntknr/ntf/releases/download/v1.0.1/ntf-x86_64-apple-darwin -o /usr/local/bin/ntf $ sudo chmod +x /usr/local/bin/ntf $ echo -e 'backends: ["pushover"]\npushover: {"user_key": "t0k3n"}' > ~/.ntf.yml @@ -38,6 +38,45 @@ $ echo 'export AUTO_NTF_DONE_LONGER_THAN=10' >> ~/.bashrc $ echo 'eval "$(ntf shell-integration)"' >> ~/.bashrc ``` +``` +> $ ntf --help +Usage: + [command] + +Available Commands: + done Execute the command and notify the message + help Help about any command + send send notification + shell-integration shell-integration + +Flags: + -h, --help help for this command + +Use " [command] --help" for more information about a command. + +> $ ntf send --help +send notification + +Usage: + send [flags] + +Flags: + --backends strings + -h, --help help for send + --line.token string + --pushbullet.token string + --pushover.device string + --pushover.expire int + --pushover.priority string + --pushover.retry int + --pushover.user_key string + --slack.color string + --slack.webhook string + --syslog.facility string + --syslog.severity string + -t, --title string override title +``` + ## Supported backend ### [slack: (webhook)](https://api.slack.com/messaging/webhooks) @@ -111,3 +150,8 @@ syslog: facility: 'user' #option severity: 'emerg' #option ``` + +## Custom backend + +You can add the custom backend you want by simply creating a struct that implements the interface in the backends folder. +Or, submit a backend request that you would like to use via Issue.