From 5d21fb3c5f2353a37d6208155b9457fcf156316a Mon Sep 17 00:00:00 2001 From: Sven Kanoldt Date: Sun, 6 Nov 2022 00:30:38 +0100 Subject: [PATCH] feat(functions): implement `jwt` template function (#8) - fixes #6 - auto add a `exp` claim, with expiry of +15min, in case none was provided - refactor cli and structure - refactor test boiler plate into a builder - add docs and changelog --- .github/workflows/build.yml | 1 - CHANGELOG.md | 3 + Cargo.lock | 1448 +++++++++++++++++--- Cargo.toml | 11 +- README.md | 41 +- examples/README.md | 8 + src/curlz/cli/commands/request.rs | 79 +- src/curlz/cli/header_args.rs | 61 + src/curlz/cli/mod.rs | 2 + src/curlz/data/request/http_headers.rs | 57 + src/curlz/interactive.rs | 38 +- src/curlz/lib.rs | 3 + src/curlz/ops/run_curl.rs | 2 +- src/curlz/template/functions/jwt.rs | 130 ++ src/curlz/template/functions/mod.rs | 2 + src/curlz/template/functions/prompt.rs | 26 + src/curlz/{template.rs => template/mod.rs} | 6 +- src/curlz/test_utils.rs | 54 + tests/basics.rs | 70 +- 19 files changed, 1733 insertions(+), 309 deletions(-) create mode 100644 examples/README.md create mode 100644 src/curlz/cli/header_args.rs create mode 100644 src/curlz/template/functions/jwt.rs create mode 100644 src/curlz/template/functions/mod.rs create mode 100644 src/curlz/template/functions/prompt.rs rename src/curlz/{template.rs => template/mod.rs} (88%) create mode 100644 src/curlz/test_utils.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f15c638..dcc270a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,7 +65,6 @@ jobs: - insta test runs-on: ${{ matrix.version }} continue-on-error: ${{ matrix.rust == 'nightly' }} - environment: build steps: - uses: actions/checkout@v2 - name: setup | rust diff --git a/CHANGELOG.md b/CHANGELOG.md index 9507944..ab14602 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [Unreleased]: https://github.com/curlz-rs/curlz/compare/v0.1.0-alpha.1...HEAD ### Added +- special template function `jwt` for json web tokens [see #6](https://github.com/curlz-rs/curlz/issues/6) + ### Changed ### Deprecated ### Removed ### Fixed ### Security ### Contributors +- [@sassman](https://github.com/sassman) ## [0.1.0-alpha.2] - 2022-08-16 [0.1.0-alpha.2]: https://github.com/curlz-rs/curlz/compare/v0.1.0-alpha.1..v0.1.0-alpha.2 diff --git a/Cargo.lock b/Cargo.lock index cbcac8c..d6edc25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,24 +4,43 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anyhow" -version = "1.0.61" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" + +[[package]] +name = "assert-json-diff" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] [[package]] name = "assert_cmd" -version = "2.0.4" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ae1ddd39efd67689deb1979d80bad3bf7f2b09c6e6117c8d1f2443b5e2f83e" +checksum = "ba45b8163c49ab5f972e59a8a5a03b6d2972619d486e19ec9fe744f7c2753d3c" dependencies = [ "bstr", "doc-comment", @@ -31,6 +50,28 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "async-channel" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-trait" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atty" version = "0.2.14" @@ -48,6 +89,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bitflags" version = "1.3.2" @@ -56,26 +103,66 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bstr" -version = "0.2.17" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd" dependencies = [ - "lazy_static", "memchr", + "once_cell", "regex-automata", + "serde", ] +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[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.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time 0.1.44", + "wasm-bindgen", + "winapi", +] + [[package]] name = "clap" -version = "3.2.17" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "bitflags", "clap_derive", @@ -97,9 +184,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.17" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13547f7012c01ab4a0e8f8967730ada8f9fdf419e8b6c792788f39cf4e46eefa" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck", "proc-macro-error", @@ -117,15 +204,34 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "concurrent-queue" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +dependencies = [ + "cache-padded", +] + [[package]] name = "console" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847" +checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", "terminal_size", "unicode-width", "winapi", @@ -133,9 +239,18 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.5.0" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a24b1aaf0fd0ce8b45161144d6f42cd91677fd5940fd431183eb023b3a2b8" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "curlz" @@ -143,6 +258,7 @@ version = "0.1.0-alpha.2" dependencies = [ "anyhow", "assert_cmd", + "chrono", "clap", "clap-verbosity-flag", "convert_case", @@ -151,51 +267,99 @@ dependencies = [ "env_logger", "filenamify", "insta", + "jsonwebtoken", "log", "minijinja", "predicates", + "rstest", "serde", + "serde_json", "serde_yaml", "tempfile", + "tokio", + "wiremock", ] [[package]] -name = "dialoguer" -version = "0.10.2" +name = "cxx" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1" +checksum = "6b7d4e43b25d3c994662706a1d4fcfc32aaa6afd287502c111b237093bb23f3a" dependencies = [ - "console", - "tempfile", - "zeroize", + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", ] [[package]] -name = "difflib" -version = "0.4.0" +name = "cxx-build" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" +checksum = "84f8829ddc213e2c1368e51a2564c552b65a8cb6a28f31e576270ac81d5e5827" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e72537424b474af1460806647c41d4b6d35d09ef7fe031c5c2fa5766047cc56a" [[package]] -name = "dirs" -version = "4.0.0" +name = "cxxbridge-macro" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "309e4fb93eed90e1e14bea0da16b209f81813ba9fc7830c20ed151dd7bc0a4d7" dependencies = [ - "dirs-sys", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "dirs-sys" -version = "0.3.7" +name = "deadpool" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" dependencies = [ - "libc", - "redox_users", - "winapi", + "async-trait", + "deadpool-runtime", + "num_cpus", + "retain_mut", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaa37046cc0f6c3cc6090fbdbf73ef0b8ef4cfcc37f6befc0020f63e8cf121e1" + +[[package]] +name = "dialoguer" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1" +dependencies = [ + "console", + "tempfile", + "zeroize", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "doc-comment" version = "0.3.3" @@ -204,18 +368,15 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "dotenvy" -version = "0.15.1" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e851a83c30366fd01d75b913588e95e74a1705c1ecc5d58b1f8e1a6d556525f" -dependencies = [ - "dirs", -] +checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0" [[package]] name = "either" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "encode_unicode" @@ -225,9 +386,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "env_logger" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" dependencies = [ "atty", "humantime", @@ -236,6 +397,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "fastrand" version = "1.8.0" @@ -264,201 +431,586 @@ dependencies = [ ] [[package]] -name = "getrandom" -version = "0.2.7" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "hashbrown" -version = "0.12.3" +name = "form_urlencoded" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] [[package]] -name = "heck" -version = "0.4.0" +name = "futures" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] [[package]] -name = "hermit-abi" -version = "0.1.19" +name = "futures-channel" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ - "libc", + "futures-core", + "futures-sink", ] [[package]] -name = "humantime" -version = "2.1.0" +name = "futures-core" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] -name = "indexmap" -version = "1.9.1" +name = "futures-executor" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ - "autocfg", - "hashbrown", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "insta" -version = "1.18.2" +name = "futures-io" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a9aec10c9a062ef0454fd49ebaefa59239f836d1b30891d9cc2289978dd970" -dependencies = [ - "console", - "linked-hash-map", - "once_cell", - "serde", - "similar", - "yaml-rust", -] +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] -name = "instant" -version = "0.1.12" +name = "futures-lite" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" dependencies = [ - "cfg-if", + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", ] [[package]] -name = "itertools" -version = "0.10.3" +name = "futures-macro" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ - "either", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "itoa" -version = "1.0.3" +name = "futures-sink" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] -name = "lazy_static" -version = "1.4.0" +name = "futures-task" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] -name = "libc" -version = "0.2.131" +name = "futures-timer" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "futures-util" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] [[package]] -name = "log" -version = "0.4.17" +name = "getrandom" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] -name = "memchr" -version = "2.5.0" +name = "h2" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] [[package]] -name = "minijinja" -version = "0.17.0" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccff60140963c1feb2858b2e3250187397a8aaaba14b36841ec00889039a97c3" -dependencies = [ - "serde", -] +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] -name = "normalize-line-endings" -version = "0.3.0" +name = "heck" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" [[package]] -name = "num-traits" -version = "0.2.15" +name = "hermit-abi" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ - "autocfg", + "libc", ] [[package]] -name = "once_cell" -version = "1.13.0" +name = "http" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa", +] [[package]] -name = "os_str_bytes" -version = "6.3.0" +name = "http-body" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] [[package]] -name = "predicates" -version = "2.1.1" +name = "http-types" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c" +checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" dependencies = [ - "difflib", - "float-cmp", - "itertools", - "normalize-line-endings", - "predicates-core", - "regex", + "anyhow", + "async-channel", + "base64", + "futures-lite", + "http", + "infer", + "pin-project-lite", + "rand", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "url", ] [[package]] -name = "predicates-core" -version = "1.0.3" +name = "httparse" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] -name = "predicates-tree" -version = "1.0.5" +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfba89e19b959ca163c7752ba59d737c1ceea53a5d31a149c805446fc958064" dependencies = [ - "predicates-core", - "termtree", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", ] [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "iana-time-zone" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "infer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" + +[[package]] +name = "insta" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581d4e3314cae4536e5d22ffd23189d4a374696c5ef733eadafae0ed273fd303" +dependencies = [ + "console", + "lazy_static", + "linked-hash-map", + "similar", + "yaml-rust", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa4b4af834c6cfd35d8763d359661b90f2e45d8f750a0849156c7f4671af09c" +dependencies = [ + "base64", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[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.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "link-cplusplus" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369" +dependencies = [ + "cc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minijinja" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8602d7e47eed0a7026af5666ff0d4500548afe1f7567a351ada0bb61bd250a9e" +dependencies = [ + "serde", +] + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "os_str_bytes" +version = "6.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3baf96e39c5359d2eb0dd6ccb42c62b91d9678aa68160d261b9e0ccbf9e9dea9" + +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "pem" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +dependencies = [ + "base64", +] + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "predicates" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab68289ded120dcbf9d571afcf70163233229052aec9b08ab09532f698d0e1e6" +dependencies = [ + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6e7125585d872860e9955ca571650b27a4979c5823084168c5ed5bbfb016b56" + +[[package]] +name = "predicates-tree" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad3f7fa8d61e139cbc7c3edfebf3b6678883a53f5ffac65d1259329a93ee43a5" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", ] [[package]] @@ -474,9 +1026,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -491,30 +1043,60 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "rand" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "bitflags", + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "rand_chacha" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ "getrandom", - "redox_syscall", - "thiserror", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" dependencies = [ "aho-corasick", "memchr", @@ -529,17 +1111,72 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "retain_mut" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rstest" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "e9c9dc66cc29792b663ffb5269be669f1613664e69ad56441fdb895c2347b930" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5015e68a0685a95ade3eee617ff7101ab6a3fc689203101ca16ebc16f2b89c66" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "rustc_version" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "winapi", + "semver", ] [[package]] @@ -548,31 +1185,78 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +[[package]] +name = "scratch" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898" + +[[package]] +name = "semver" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" + [[package]] name = "serde" -version = "1.0.143" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" +checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.143" +version = "1.0.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" +checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_json" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45" +dependencies = [ + "indexmap", + "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 = "serde_yaml" -version = "0.9.9" +version = "0.9.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50845f68d5c693aac7d72a25415ddd21cb8182c04eafe447b73af55a05f9e1b" +checksum = "6d232d893b10de3eb7258ff01974d6ee20663d8e833263c99409d4b13a0209da" dependencies = [ "indexmap", "itoa", @@ -587,11 +1271,48 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803" +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time 0.3.16", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "syn" -version = "1.0.99" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d" dependencies = [ "proc-macro2", "quote", @@ -633,53 +1354,221 @@ dependencies = [ [[package]] name = "termtree" -version = "0.2.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b" +checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" [[package]] name = "textwrap" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.32" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.32" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca" +dependencies = [ + "itoa", + "libc", + "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bb801831d812c562ae7d2bfb531f26e66e4e1f6b17307ba4149c5064710e5b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unsafe-libyaml" -version = "0.2.2" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931179334a56395bcf64ba5e0ff56781381c1a5832178280c7d7f91d1679aeb0" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] [[package]] name = "version_check" @@ -696,12 +1585,104 @@ dependencies = [ "libc", ] +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "web-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -733,6 +1714,85 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + +[[package]] +name = "wiremock" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "249dc68542861d17eae4b4e5e8fb381c2f9e8f255a84f6771d5fdf8b6c03ce3c" +dependencies = [ + "assert-json-diff", + "async-trait", + "base64", + "deadpool", + "futures", + "futures-timer", + "http-types", + "hyper", + "log", + "once_cell", + "regex", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index 3b0c4ec..f7bb5f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,17 +16,24 @@ serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.9" dotenvy = { version = "0.15" } anyhow = "1.0" -convert_case = "0.5" +convert_case = "0.6" dialoguer = "0.10" filenamify = "0.1" +chrono = "0.4" -minijinja = "0.17.0" +minijinja = "0.25" + +jsonwebtoken = "8.1" +serde_json = { version = "1.0", features = ["preserve_order"] } [dev-dependencies] insta = "1.17" tempfile = "3.3" assert_cmd = "2.0" predicates = "2.1" +rstest = "0.15" +wiremock = "0.5" +tokio = { version = "1.21", features = ["rt", "macros"], default-features = false } [[bin]] name = "curlz" diff --git a/README.md b/README.md index 0dcc474..597bb22 100644 --- a/README.md +++ b/README.md @@ -13,31 +13,32 @@ ## Features -- ☑️ .env files -- ☑️ .yaml env files -- ☑️ placeholder evaluation, with the [minijinja](https://docs.rs/minijinja/latest/minijinja/) template engine +- ☑️.env files +- ☑️.yaml env files +- ☑️placeholder evaluation, with the [minijinja](https://docs.rs/minijinja/latest/minijinja/) template engine - in urls - in http headers (`-H | --header` arguments) - in every other passed curl parameter -- ☑️ save request as a bookmark, containing +- ☑️save request as a bookmark, containing - curl arguments - http headers - http method - placeholders -- ☑️ pass all arguments after `--` to curl, that makes drop-in-replacement possible -- ☑️ execute a bookmarked request -- ☑️ special placeholder variables that would interact with the user - - ☑️ prompt for a password as `{{ prompt_password() }}` +- ☑️pass all arguments after `--` to curl, that makes drop-in-replacement possible +- ☑️execute a bookmarked request +- ☑️special placeholder variables that would interact with the user + - ☑️prompt for a password as `{{ prompt_password() }}` `curlz r https://api.github.com/user -- -u "{{ username }}:{{ prompt_password() }}"` - - ☑️ prompt for interactive input with a label as `{{ prompt_for("Username") }}` or `{{ prompt_for("Birthdate") }}` + - ☑️prompt for interactive input with a label as `{{ prompt_for("Username") }}` or `{{ prompt_for("Birthdate") }}` `curlz -- -u "{{ prompt_for("Username") }}:{{ prompt_password() }}" https://api.github.com/user` +- ☑️special placeholder for developers, like for Json Web Tokens (JWT) + - example: `{{ jwt(claims, signing_key) }}`, where `claims` and `signing_key` are looked up at the environment file or can be directly provided map and string + ```shell + curlz r -H 'Authorization: Bearer {{ jwt({"uid": "1234"}, "000") }}' https://httpbin.org/bearer -- -vvv + ``` ## TODOs - [] evaluate placeholders at the beginning of an url -- [] special placeholder for developers, like `jwt_token` or `mfa_token` - - example: `{{ jwt_token(signin_key, signin_secret) }}`, where `signin_key` and `signin_secret` are first looked up - at the environment file as variable or else taken then as given. - `curlz -H "Authorization: Bearer {{ jwt_token(signin_key, signin_secret) }}" -X POST https://api.github.com/user/repos -d '{ "name": "{{ repo_name }}" }'` ## Example #1 @@ -47,11 +48,19 @@ In this example we're going to download a pre-configured `.gitignore` for a give - the same with curlz: `curlz r https://api.github.com/gitignore/templates/Rust` - Add a placeholder that is interactively requested `curlz r 'https://api.github.com/gitignore/templates/{{ prompt_for("Language") | title }}'` -- Now lets bookmark this request: +- Now let's bookmark this request: ```sh curlz r --bookmark 'https://api.github.com/gitignore/templates/{{ prompt_for("Language") | title }}' Language: rust Please enter a bookmark name: gitignore ``` -- Finally, we can keep using the bookmark from now on - `curlz r gitignore` +- Finally, we can keep using the bookmark from now on: `curlz r gitignore` + +## Template functions + +### Json Web Token - `jwt(claims: map, [signing_key: string])` +- arguments: + - `claims`: to be a map of key value pairs like `{"uid": "1234"}` that are the payload of the JWT + - `signing_key`: to be a string, this is optional and can be provided at the environment file with a variable named `jwt_signing_key` +- output: string is a Json Web Token (JWT) +- notes: the hash algorithm is `HS256` and the JWT header is `{"alg": "HS256", "typ": "JWT"}` diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..4276849 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,8 @@ +# Examples + +Note: for testing you can `cargo install echo-server` and run `echo-server` + +- a request with a JWT authorization header, that has custom claims + ```sh + curlz r 'http://localhost:8080/' -- -vvv --header 'authorization: Baerer {{ jwt({"foo": "bar"}) }}' + ``` \ No newline at end of file diff --git a/src/curlz/cli/commands/request.rs b/src/curlz/cli/commands/request.rs index 8c54480..f6188ec 100644 --- a/src/curlz/cli/commands/request.rs +++ b/src/curlz/cli/commands/request.rs @@ -6,6 +6,7 @@ use crate::ops::{ use crate::utils::parse_pairs; use crate::variables::Placeholder; +use crate::cli::HeaderArgs; use anyhow::Context; use clap::Parser; use clap_verbosity_flag::{InfoLevel, Verbosity}; @@ -44,7 +45,7 @@ pub struct RequestCli { /// curlz -H "User-Agent: yes-please/2000" https://example.com /// curlz -H "Host:" https://example.com /// ``` - #[clap(long = "header", short = 'H', number_of_values = 1, value_parser)] + #[clap(long = "header", short = 'H', value_parser)] pub headers: Vec, #[clap(long = "json", action)] @@ -84,7 +85,10 @@ impl MutOperation for RequestCli { let method = extract_method(&mut raw) .unwrap_or_else(|| HttpMethod::from_str(self.http_method.as_str()))?; - let mut headers = extract_headers(&mut raw); + let mut headers: HttpHeaders = self.headers.as_slice().into(); + let (mut raw, headers_args) = extract_headers(&raw); + let headers_raw: HttpHeaders = headers_args.into(); + headers.merge(&headers_raw); if self.json { headers.push("Content-Type", "application/json"); headers.push("Accept", "application/json"); @@ -146,40 +150,31 @@ impl MutOperation for RequestCli { /// checks if a string is a URL fn is_url(potential_url: impl AsRef) -> bool { - potential_url - .as_ref() - .trim_start_matches('\'') - .starts_with("http") + let trimmed_url = potential_url.as_ref().trim_start_matches('\''); + + trimmed_url.starts_with("http") || trimmed_url.starts_with("{{") } -/// Extracts the http headers from the command line arguments +/// Extracts the http headers from command line arguments /// If a header `-H | --header` is found, it's removed from the `raw_args` -fn extract_headers(raw_args: &mut Vec) -> HttpHeaders { - let mut headers = HttpHeaders::default(); - raw_args - .clone() - .iter() - .enumerate() - .step_by(2) - .zip(raw_args.clone().iter().enumerate().skip(1).step_by(2)) - .filter_map(|((ik, key), (iv, value))| match key.as_str() { - "-H" | "--header" => Some(((ik, key), (iv, value))), - _ => None, - }) - // now it gets ugly #1 - .rev() - .for_each(|((ki, _key), (kv, value))| { - // ugly the #2 - raw_args.swap_remove(kv); - raw_args.swap_remove(ki); - let (key, value) = value.split_once(':').unwrap(); - headers.push(key.trim(), value.trim()); - }); - - // ugly the #3 - headers.reverse(); +fn extract_headers(raw_args: &Vec) -> (Vec, HeaderArgs) { + let headers = HeaderArgs::from(raw_args); + + let mut non_header_args = vec![]; + let mut i = 0_usize; + while i < raw_args.len() { + match raw_args.get(i).unwrap().as_str() { + "-H" | "--header" => { + i += 2; + } + v => { + non_header_args.push(v.to_string()); + i += 1; + } + } + } - headers + (non_header_args, headers) } /// Extracts the http method from the command line arguments @@ -262,25 +257,31 @@ mod tests { #[test] fn should_extract_headers() { - let mut args = vec![ + let args = vec![ + "-vvv", "-H", "foo: bar", "--header", "Accept: application/json", + "--header", + r#"Authorization: Baerer {{ jwt({"foo": "bar"}) }}"#, "http://example.com", ] .iter() - .map(|s| s.to_string()) + .map(ToString::to_string) .collect(); - let headers = extract_headers(&mut args); + let (args, headers) = extract_headers(&args); assert_eq!( headers.as_ref(), &[ - ("foo".to_string(), "bar".to_string()), - ("Accept".to_string(), "application/json".to_string()) + "foo: bar".to_string(), + "Accept: application/json".to_string(), + r#"Authorization: Baerer {{ jwt({"foo": "bar"}) }}"#.to_string(), ] ); - assert_eq!(args.len(), 1); - assert_eq!(args.pop(), Some("http://example.com".to_string())); + // TODO: it's unclear why this here fails: + assert_eq!(args.len(), 2); + assert_eq!(args.first(), Some(&"-vvv".to_string())); + assert_eq!(args.last(), Some(&"http://example.com".to_string())); } } diff --git a/src/curlz/cli/header_args.rs b/src/curlz/cli/header_args.rs new file mode 100644 index 0000000..aab4276 --- /dev/null +++ b/src/curlz/cli/header_args.rs @@ -0,0 +1,61 @@ +pub struct HeaderArgs(Vec); + +impl AsRef<[String]> for HeaderArgs { + fn as_ref(&self) -> &[String] { + self.0.as_slice() + } +} + +impl From<&Vec> for HeaderArgs { + fn from(raw_headers: &Vec) -> Self { + let mut headers = vec![]; + let mut i = 0_usize; + while i < raw_headers.len() { + match raw_headers.get(i).unwrap().as_str() { + "-H" | "--header" => { + headers.push(raw_headers.get(i + 1).unwrap().to_string()); + i += 2; + } + _ => { + i += 1; + } + } + } + + HeaderArgs(headers) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_parse_the_header_arg_flag_away() { + let mut args = vec![ + "-H", + "foo: bar", + "--header", + "Accept: application/json", + "--header", + r#"Authorization: Baerer {{ jwt({"foo": "bar"}) }}"#, + "http://example.com", + ] + .iter() + .map(|s| s.to_string()) + .collect::>(); + + let headers: HeaderArgs = (&args).into(); + + assert_eq!( + headers.as_ref(), + &[ + "foo: bar".to_string(), + "Accept: application/json".to_string(), + r#"Authorization: Baerer {{ jwt({"foo": "bar"}) }}"#.to_string() + ] + ); + assert_eq!(args.len(), 7); + assert_eq!(args.pop(), Some("http://example.com".to_string())); + } +} diff --git a/src/curlz/cli/mod.rs b/src/curlz/cli/mod.rs index 9c99244..2a45f8a 100644 --- a/src/curlz/cli/mod.rs +++ b/src/curlz/cli/mod.rs @@ -1,10 +1,12 @@ pub mod commands; +mod header_args; pub mod main; use clap::Parser; use clap_verbosity_flag::{InfoLevel, Verbosity}; pub use commands::*; +pub use header_args::HeaderArgs; #[derive(Clone, Debug, Parser)] #[clap(author, version, about, long_about = None, arg_required_else_help(true))] diff --git a/src/curlz/data/request/http_headers.rs b/src/curlz/data/request/http_headers.rs index 4d84b9e..ed279ab 100644 --- a/src/curlz/data/request/http_headers.rs +++ b/src/curlz/data/request/http_headers.rs @@ -1,3 +1,4 @@ +use crate::cli::HeaderArgs; use serde::{Deserialize, Serialize}; #[derive(Default, Debug, Serialize, Deserialize, Clone)] @@ -27,3 +28,59 @@ impl AsRef<[(String, String)]> for HttpHeaders { self.0.as_slice() } } + +impl From for HttpHeaders { + fn from(raw_headers: HeaderArgs) -> Self { + raw_headers.as_ref().into() + } +} + +impl From<&[String]> for HttpHeaders { + fn from(headers: &[String]) -> Self { + Self( + headers + .iter() + .map(|value| { + let (key, value) = value.split_once(':').unwrap(); + (key.trim().to_string(), value.trim().to_string()) + }) + .collect(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_parse_the_header_arg_flag_away() { + let args = vec![ + "-H", + "foo: bar", + "--header", + "Accept: application/json", + "--header", + r#"Authorization: Baerer {{ jwt({"foo": "bar"}) }}"#, + "http://example.com", + ] + .iter() + .map(ToString::to_string) + .collect::>(); + + let headers: HeaderArgs = (&args).into(); + let headers: HttpHeaders = headers.into(); + + assert_eq!( + headers.as_ref(), + &[ + ("foo".to_string(), "bar".to_string()), + ("Accept".to_string(), "application/json".to_string()), + ( + "Authorization".to_string(), + r#"Baerer {{ jwt({"foo": "bar"}) }}"#.to_string() + ) + ] + ); + } +} diff --git a/src/curlz/interactive.rs b/src/curlz/interactive.rs index 06b38ce..e1cd3f1 100644 --- a/src/curlz/interactive.rs +++ b/src/curlz/interactive.rs @@ -1,8 +1,6 @@ use crate::Result; -use dialoguer::{Input, Password}; -use minijinja::value::Value; -use minijinja::{Error, ErrorKind}; +use dialoguer::Input; pub fn user_question(prompt: &str, default: &Option) -> Result { let mut i = Input::::new(); @@ -12,37 +10,3 @@ pub fn user_question(prompt: &str, default: &Option) -> Result { } i.interact().map_err(Into::::into) } - -/// prompt for a password, to be used in a minijinja template -pub fn prompt_password( - _state: &minijinja::State, - _args: Vec, -) -> std::result::Result { - Password::new() - .with_prompt("Password") - .allow_empty_password(true) - .interact() - .map_err(|e| { - Error::new( - ErrorKind::ImpossibleOperation, - "cannot read password from stdin", - ) - .with_source(e) - }) -} - -/// prompt for something that has a name, to be used in a minijinja template -pub fn prompt_for(_state: &minijinja::State, prompt: Value) -> std::result::Result { - let prompt = prompt.to_string(); - Input::new() - .with_prompt(prompt) - .allow_empty(true) - .interact() - .map_err(|e| { - Error::new( - ErrorKind::ImpossibleOperation, - "cannot read prompt from stdin", - ) - .with_source(e) - }) -} diff --git a/src/curlz/lib.rs b/src/curlz/lib.rs index 9f2339b..c730a1d 100644 --- a/src/curlz/lib.rs +++ b/src/curlz/lib.rs @@ -7,4 +7,7 @@ pub mod utils; pub mod variables; pub mod workspace; +#[cfg(test)] +pub mod test_utils; + pub type Result = anyhow::Result; diff --git a/src/curlz/ops/run_curl.rs b/src/curlz/ops/run_curl.rs index 2480e12..069fe3e 100644 --- a/src/curlz/ops/run_curl.rs +++ b/src/curlz/ops/run_curl.rs @@ -30,7 +30,7 @@ impl<'a> MutOperation for RunCurlCommand<'a> { if context.verbosity.eq(&Verbosity::Silent) { cmd.arg("-s"); } - cmd.args(&["-X", &method]) + cmd.args(["-X", &method]) .args( &self .request diff --git a/src/curlz/template/functions/jwt.rs b/src/curlz/template/functions/jwt.rs new file mode 100644 index 0000000..b026523 --- /dev/null +++ b/src/curlz/template/functions/jwt.rs @@ -0,0 +1,130 @@ +use chrono::{Duration, Timelike, Utc}; +use jsonwebtoken::{EncodingKey, Header}; +use minijinja::value::Value; +use minijinja::{Error, ErrorKind, State}; +use std::collections::HashMap; +use std::ops::{Add, Not}; + +const CLAIM_EXPIRY: &str = "exp"; +const CLAIM_ISSUED_AT: &str = "iat"; + +/// generates a jwt token based on some given claims +pub fn jwt(state: &State, claims: Value, signing_key: Option) -> Result { + let mut claims: HashMap = + serde_json::from_str(claims.to_string().as_str()).unwrap(); + + // in case expiry is missing, we expire it in 15min + if claims.contains_key(CLAIM_EXPIRY).not() { + let expire_in = Utc::now() + .add(Duration::minutes(15)) + .with_second(0) + .unwrap() + .timestamp(); + claims.insert(CLAIM_EXPIRY.to_string(), serde_json::Value::from(expire_in)); + } + + claims.insert( + CLAIM_ISSUED_AT.to_string(), + serde_json::Value::from(Utc::now().with_second(0).unwrap().timestamp()), + ); + + let signing_key = signing_key + .or_else(|| state.lookup("jwt_signing_key")) + .ok_or_else(|| { + Error::new( + ErrorKind::MissingArgument, + "The variable `jwt_signing_key` was not defined.", + ) + })?; + + let token = jsonwebtoken::encode( + &Header::default(), + &claims, + &EncodingKey::from_secret(signing_key.as_bytes().unwrap()), + ); + + token.map_err(|e| { + Error::new( + ErrorKind::UndefinedError, + "jsonwebtoken failed to encode the token.", + ) + .with_source(e) + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::RenderBuilder; + use chrono::{Duration, Timelike}; + use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; + use rstest::rstest; + use serde::Deserialize; + + #[test] + #[should_panic(expected = "The variable `jwt_signing_key` was not defined.")] + fn should_throw_when_signing_key_is_missing() { + RenderBuilder::new() + .with_function("jwt", jwt) + .render(r#"Bearer {{ jwt({"sub": "b@b.com"}) }}"#); + } + + #[rstest] + #[case( + r#"Bearer {{ jwt({"sub": "b@b.com"}, "000") }}"#, + RenderBuilder::new().with_function("jwt", jwt) + )] + #[case( + r#"Bearer {{ jwt({"sub": "b@b.com"}) }}"#, + RenderBuilder::new().with_function("jwt", jwt) + .with_env_var("jwt_signing_key", "000") + )] + #[case( + r#"Bearer {{ jwt({"sub": "b@b.com", "iat": 666}) }}"#, + RenderBuilder::new().with_function("jwt", jwt) + .with_env_var("jwt_signing_key", "000") + )] + #[case( + r#"Bearer {{ jwt(jwt_claims) }}"#, + RenderBuilder::new().with_function("jwt", jwt) + .with_env_var("jwt_signing_key", "000") + .with_env_var("jwt_claims", r#"{"sub": "b@b.com", "iat": 666}"#) + )] + fn should_set_expiry_when_missing(#[case] token: &str, #[case] builder: RenderBuilder) { + let now = Utc::now(); + let jwt = builder.render(token); + + let token_message = { + #[derive(Deserialize)] + struct Claims { + sub: String, + exp: i64, + iat: i64, + } + + let jwt = jwt.as_str().split(' ').last().unwrap(); + decode::( + jwt, + &DecodingKey::from_secret(b"000"), + &Validation::new(Algorithm::HS256), + ) + .unwrap() + }; + + assert_eq!(token_message.claims.sub.as_str(), "b@b.com"); + assert_eq!( + token_message.claims.exp, + now.add(Duration::minutes(15)) + .with_second(0) + .unwrap() + .timestamp(), + "token should expire in 15min" + ); + dbg!(token_message.claims.iat); + assert_eq!( + token_message.claims.iat, + now.with_second(0).unwrap().timestamp(), + "token should be issued now-ish" + ); + } +} diff --git a/src/curlz/template/functions/mod.rs b/src/curlz/template/functions/mod.rs new file mode 100644 index 0000000..3518897 --- /dev/null +++ b/src/curlz/template/functions/mod.rs @@ -0,0 +1,2 @@ +pub mod jwt; +pub mod prompt; diff --git a/src/curlz/template/functions/prompt.rs b/src/curlz/template/functions/prompt.rs new file mode 100644 index 0000000..f79590d --- /dev/null +++ b/src/curlz/template/functions/prompt.rs @@ -0,0 +1,26 @@ +use dialoguer::{Input, Password}; +use minijinja::value::Value; +use minijinja::{Error, ErrorKind}; + +/// prompt for a password, to be used in a minijinja template +pub fn prompt_password(_state: &minijinja::State, _args: Vec) -> Result { + Password::new() + .with_prompt("Password") + .allow_empty_password(true) + .interact() + .map_err(|e| { + Error::new(ErrorKind::UndefinedError, "cannot read password from stdin").with_source(e) + }) +} + +/// prompt for something that has a name, to be used in a minijinja template +pub fn prompt_for(_state: &minijinja::State, prompt: Value) -> Result { + let prompt = prompt.to_string(); + Input::new() + .with_prompt(prompt) + .allow_empty(true) + .interact() + .map_err(|e| { + Error::new(ErrorKind::UndefinedError, "cannot read prompt from stdin").with_source(e) + }) +} diff --git a/src/curlz/template.rs b/src/curlz/template/mod.rs similarity index 88% rename from src/curlz/template.rs rename to src/curlz/template/mod.rs index 6c20979..69ff1c4 100644 --- a/src/curlz/template.rs +++ b/src/curlz/template/mod.rs @@ -1,4 +1,7 @@ -use crate::interactive::{prompt_for, prompt_password}; +mod functions; + +use functions::jwt::jwt; +use functions::prompt::{prompt_for, prompt_password}; use minijinja::value::Value; use minijinja::Environment; @@ -20,6 +23,7 @@ impl<'source> Renderer<'source> { let mut env = Environment::new(); env.add_function("prompt_password", prompt_password); env.add_function("prompt_for", prompt_for); + env.add_function("jwt", jwt); Self { env, ctx } } diff --git a/src/curlz/test_utils.rs b/src/curlz/test_utils.rs new file mode 100644 index 0000000..61b70c2 --- /dev/null +++ b/src/curlz/test_utils.rs @@ -0,0 +1,54 @@ +use minijinja::functions::Function; +use minijinja::value::{FunctionArgs, FunctionResult, Value}; +use minijinja::Environment; + +/// [`RenderBuilder`] simplifies test case creation +pub struct RenderBuilder<'source> { + env: Environment<'source>, +} + +impl<'source> RenderBuilder<'source> { + pub fn with_env_var(mut self, name: &'source str, value: impl Into) -> Self { + self.env.add_global(name, value.into()); + + self + } +} + +impl<'source> Default for RenderBuilder<'source> { + fn default() -> Self { + Self { + env: Environment::empty(), + } + } +} + +impl<'source> RenderBuilder<'source> { + /// creates a new fresh builder + pub fn new() -> Self { + Self::default() + } + + /// registers a template filter function + pub fn with_function(mut self, name: &'source str, f: F) -> Self + where + // the crazy bounds here exist to enable borrowing in closures + F: Function + for<'a> Function>::Output>, + Rv: FunctionResult, + Args: for<'a> FunctionArgs<'a>, + { + self.env.add_function(name, f); + + self + } + + /// it renders a given template + pub fn render(mut self, template: &'source str) -> String { + let name = "something"; + self.env.add_template(name, template).unwrap(); + let template = self.env.get_template(name).unwrap(); + + let ctx = Value::default(); + template.render(&ctx).unwrap() + } +} diff --git a/tests/basics.rs b/tests/basics.rs index d60e158..a3816b4 100644 --- a/tests/basics.rs +++ b/tests/basics.rs @@ -1,9 +1,13 @@ +use assert_cmd::assert::Assert; use assert_cmd::prelude::*; use dotenvy::dotenv; use predicates::prelude::*; +use std::process::Command; +use wiremock::matchers::{method, path}; +use wiremock::{Mock, MockServer, ResponseTemplate}; -fn binary() -> Result { - std::process::Command::cargo_bin(env!("CARGO_PKG_NAME")) +fn binary() -> Result { + Command::cargo_bin(env!("CARGO_PKG_NAME")) } #[test] @@ -15,21 +19,51 @@ fn should_show_usage_when_no_args_passed() { .stderr(predicate::str::contains("USAGE:")); } -#[test] -fn should_request_a_url() { - dotenv().ok(); +#[tokio::test] +async fn should_request_a_url() { + CurlzTest::new() + .await + .with_url("/gitignore/templates/Rust") + .request() + .await; +} - let token = std::env::var("GITHUB_TOKEN").unwrap(); - binary() - .unwrap() - .args(&[ - "r", - "https://api.github.com/gitignore/templates/Rust", - "--", - "-H", - format!("Authorization: Bearer {token}").as_str(), - ]) - .assert() - .success() - .stdout(predicate::str::contains("# Generated by Cargo")); +struct CurlzTest { + mock_server: MockServer, + url_part: String, + payload: String, +} + +impl CurlzTest { + pub async fn new() -> Self { + dotenv().ok(); + Self { + mock_server: MockServer::start().await, + url_part: "".to_string(), + payload: "# curlz rocks".to_string(), + } + } + + /// sets the target url to be requested + pub fn with_url(mut self, url_part: &str) -> Self { + self.url_part = url_part.to_string(); + self + } + + /// runs curlz and requests the given url + pub async fn request(self) -> Assert { + Mock::given(method("GET")) + .and(path(self.url_part.as_str())) + .respond_with(ResponseTemplate::new(200).set_body_string(&self.payload)) + .mount(&self.mock_server) + .await; + + let url = format!("{}{}", &self.mock_server.uri(), self.url_part); + binary() + .unwrap() + .args(["r", url.as_str()]) + .assert() + .success() + .stdout(predicate::str::contains(&self.payload)) + } }