From 4b8695b1c07dadb85378ab43fb85a5815844369c Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Tue, 17 Sep 2024 12:46:36 -0400 Subject: [PATCH 01/28] feat: set auth token --- .gitignore | 1 + Cargo.lock | 854 ++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 12 + src/config.rs | 62 ++++ src/error.rs | 9 + src/main.rs | 46 +++ 6 files changed, 984 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/config.rs create mode 100644 src/error.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..388e946 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,854 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "config" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" +dependencies = [ + "async-trait", + "convert_case", + "json5", + "lazy_static", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde", + "serde_json", + "toml", + "yaml-rust", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[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 = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-multimap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" +dependencies = [ + "dlv-list", + "hashbrown 0.13.2", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pest" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64", + "bitflags", + "serde", + "serde_derive", +] + +[[package]] +name = "rust-ini" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "s2-cli" +version = "0.1.0" +dependencies = [ + "clap", + "config", + "dirs", + "serde", + "thiserror", + "toml", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +dependencies = [ + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[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 new file mode 100644 index 0000000..290446b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "s2-cli" +version = "0.1.0" +edition = "2021" + +[dependencies] +clap = { version = "4.5.17", features = ["derive"] } +config = "0.14.0" +dirs = "5.0.1" +serde = { version = "1.0.210", features = ["derive"] } +thiserror = "1.0.63" +toml = "0.8.19" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..2b1767c --- /dev/null +++ b/src/config.rs @@ -0,0 +1,62 @@ +use std::path::PathBuf; + +use config::{Config, FileFormat}; +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +use crate::error::S2CliError; + +#[derive(Debug, Deserialize, Serialize)] +pub struct S2Config { + token: String, +} + +/// Path to the configuration file +pub fn config_path() -> Result { + let mut path = dirs::config_dir().ok_or(S2ConfigError::ConfigDirNotFound)?; + path.push("s2"); + path.push("config.toml"); + Ok(path) +} + +#[allow(dead_code)] +pub fn load_config(path: &PathBuf) -> Result { + let cfg = Config::builder() + .add_source(config::File::new( + path.to_str().ok_or(S2ConfigError::ConfigPathError)?, + FileFormat::Toml, + )) + .build() + .map_err(|_| S2ConfigError::ConfigLoadError)?; + + Ok(cfg + .try_deserialize::() + .map_err(|_| S2ConfigError::ConfigLoadError)?) +} + +pub fn create_config(config_path: &PathBuf, token: &str) -> Result<(), S2ConfigError> { + let cfg = S2Config { + token: token.to_string(), + }; + + if let Some(parent) = config_path.parent() { + std::fs::create_dir_all(parent).map_err(|_| S2ConfigError::ConfigWriteError)?; + } + + let toml = toml::to_string(&cfg).unwrap(); + std::fs::write(config_path, toml).map_err(|_| S2ConfigError::ConfigWriteError)?; + + Ok(()) +} + +#[derive(Error, Debug)] +pub enum S2ConfigError { + #[error("Failed to find config directory")] + ConfigDirNotFound, + #[error("Failed to find config file")] + ConfigPathError, + #[error("Failed to load config file")] + ConfigLoadError, + #[error("Failed to write config file")] + ConfigWriteError, +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..b53d840 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + +use crate::config::S2ConfigError; + +#[derive(Error, Debug)] +pub enum S2CliError { + #[error(transparent)] + ConfigError(#[from] S2ConfigError), +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6fa428b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,46 @@ +use clap::{Parser, Subcommand}; +use config::{config_path, create_config}; +use error::S2CliError; + +mod config; +mod error; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand, Debug)] +enum Commands { + /// Manage s2 configuration + Config { + #[command(subcommand)] + action: ConfigActions, + }, +} + +#[derive(Subcommand, Debug)] +enum ConfigActions { + /// Set the authentication token + Set { + #[arg(short, long)] + token: String, + }, +} + +fn main() -> Result<(), S2CliError> { + let commands = Cli::parse(); + let config_path = config_path()?; + + match commands.command { + Commands::Config { action } => match action { + ConfigActions::Set { token } => { + create_config(&config_path, &token)?; + } + }, + } + + Ok(()) +} From 3d30a286873923948225579ca58c2c6db0520bf1 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Tue, 17 Sep 2024 18:51:12 -0400 Subject: [PATCH 02/28] colored --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + src/main.rs | 15 ++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 388e946..6537c85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,6 +138,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "config" version = "0.14.0" @@ -515,6 +525,7 @@ name = "s2-cli" version = "0.1.0" dependencies = [ "clap", + "colored", "config", "dirs", "serde", diff --git a/Cargo.toml b/Cargo.toml index 290446b..4691b87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] clap = { version = "4.5.17", features = ["derive"] } +colored = "2.1.0" config = "0.14.0" dirs = "5.0.1" serde = { version = "1.0.210", features = ["derive"] } diff --git a/src/main.rs b/src/main.rs index 6fa428b..72dda4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use clap::{Parser, Subcommand}; +use colored::*; use config::{config_path, create_config}; use error::S2CliError; @@ -30,7 +31,14 @@ enum ConfigActions { }, } -fn main() -> Result<(), S2CliError> { +fn main() { + if let Err(err) = run() { + eprintln!("{}", format!("✗ {}", err).red()); + std::process::exit(1); + } +} + +fn run() -> Result<(), S2CliError> { let commands = Cli::parse(); let config_path = config_path()?; @@ -38,6 +46,11 @@ fn main() -> Result<(), S2CliError> { Commands::Config { action } => match action { ConfigActions::Set { token } => { create_config(&config_path, &token)?; + println!("{}", "✓ Token set successfully".green().bold()); + println!( + " Configuration saved to: {}", + config_path.display().to_string().cyan() + ); } }, } From 573ca7a242bc89f41e4aa86dc76ba31239369359 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Tue, 17 Sep 2024 19:16:52 -0400 Subject: [PATCH 03/28] lint --- .github/workflows/ci.yml | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1d3f077 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: CI +permissions: + contents: read +'on': + pull_request: null + push: + branches: + - main +env: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + CLICOLOR: 1 + CARGO_INCREMENTAL: 0 +concurrency: + group: '${{ github.workflow }}-${{ github.ref }}' + cancel-in-progress: true +jobs: + ci: + permissions: + contents: none + name: CI + needs: lint + runs-on: ubuntu-latest + if: always() + steps: + - name: Failed + run: exit 1 + if: >- + contains(needs.*.result, 'failure') || contains(needs.*.result, + 'cancelled') || contains(needs.*.result, 'skipped') + lint: + name: clippy + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Protoc + uses: arduino/setup-protoc@v3 + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: 'clippy, rustfmt' + toolchain: stable + - name: Check format + run: cargo +nightly fmt --all -- --check + - name: Check clippy + run: >- + cargo clippy --workspace --all-features --all-targets -- -D warnings + --allow deprecated From d2b2235895c76988db6ea36a5dc8d6b995bbda11 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Tue, 17 Sep 2024 19:41:08 -0400 Subject: [PATCH 04/28] lint --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d3f077..7ec02e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,7 @@ jobs: with: components: 'clippy, rustfmt' toolchain: stable + target: x86_64-unknown-linux-gnu - name: Check format run: cargo +nightly fmt --all -- --check - name: Check clippy From 1c08115752fb3a805f85dbd491ed8a27837f4e38 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Tue, 17 Sep 2024 19:49:41 -0400 Subject: [PATCH 05/28] lint --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ec02e9..7dfdf17 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,8 +28,7 @@ jobs: if: >- contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') - lint: - name: clippy + lint: runs-on: ubuntu-latest steps: - name: Checkout repository @@ -43,7 +42,7 @@ jobs: toolchain: stable target: x86_64-unknown-linux-gnu - name: Check format - run: cargo +nightly fmt --all -- --check + run: cargo fmt --all -- --check - name: Check clippy run: >- cargo clippy --workspace --all-features --all-targets -- -D warnings From d43d3ddfb0d3d7678baeeb6a2d9b478bf069522a Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Tue, 17 Sep 2024 19:55:43 -0400 Subject: [PATCH 06/28] .. --- src/config.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/config.rs b/src/config.rs index 2b1767c..62b0315 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use config::{Config, FileFormat}; use serde::{Deserialize, Serialize}; @@ -13,25 +13,24 @@ pub struct S2Config { /// Path to the configuration file pub fn config_path() -> Result { - let mut path = dirs::config_dir().ok_or(S2ConfigError::ConfigDirNotFound)?; + let mut path = dirs::config_dir().ok_or(S2ConfigError::DirNotFound)?; path.push("s2"); path.push("config.toml"); Ok(path) } #[allow(dead_code)] -pub fn load_config(path: &PathBuf) -> Result { +pub fn load_config(path: &Path) -> Result { let cfg = Config::builder() .add_source(config::File::new( - path.to_str().ok_or(S2ConfigError::ConfigPathError)?, + path.to_str().ok_or(S2ConfigError::PathError)?, FileFormat::Toml, )) .build() - .map_err(|_| S2ConfigError::ConfigLoadError)?; + .map_err(|_| S2ConfigError::LoadError)?; - Ok(cfg - .try_deserialize::() - .map_err(|_| S2ConfigError::ConfigLoadError)?) + cfg.try_deserialize::() + .map_err(|_| S2ConfigError::LoadError) } pub fn create_config(config_path: &PathBuf, token: &str) -> Result<(), S2ConfigError> { @@ -40,11 +39,11 @@ pub fn create_config(config_path: &PathBuf, token: &str) -> Result<(), S2ConfigE }; if let Some(parent) = config_path.parent() { - std::fs::create_dir_all(parent).map_err(|_| S2ConfigError::ConfigWriteError)?; + std::fs::create_dir_all(parent).map_err(|_| S2ConfigError::WriteError)?; } let toml = toml::to_string(&cfg).unwrap(); - std::fs::write(config_path, toml).map_err(|_| S2ConfigError::ConfigWriteError)?; + std::fs::write(config_path, toml).map_err(|_| S2ConfigError::WriteError)?; Ok(()) } @@ -52,11 +51,11 @@ pub fn create_config(config_path: &PathBuf, token: &str) -> Result<(), S2ConfigE #[derive(Error, Debug)] pub enum S2ConfigError { #[error("Failed to find config directory")] - ConfigDirNotFound, + DirNotFound, #[error("Failed to find config file")] - ConfigPathError, + PathError, #[error("Failed to load config file")] - ConfigLoadError, + LoadError, #[error("Failed to write config file")] - ConfigWriteError, + WriteError, } From 3577eea1d2bd3cc5a28f9ce58cc9aa137693a243 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Wed, 18 Sep 2024 22:43:31 -0400 Subject: [PATCH 07/28] .. --- Cargo.lock | 1306 ++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 4 + src/account.rs | 78 +++ src/config.rs | 23 +- src/error.rs | 35 +- src/main.rs | 98 +++- 6 files changed, 1480 insertions(+), 64 deletions(-) create mode 100644 src/account.rs diff --git a/Cargo.lock b/Cargo.lock index 6537c85..b4ea25b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,30 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anstream" version = "0.6.15" @@ -51,6 +75,34 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.82" @@ -62,12 +114,112 @@ dependencies = [ "syn", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 1.0.1", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4fa97bb310c33c811334143cf64c5bb2b7b3c06e453db6b095d7061eff8f113" +dependencies = [ + "fastrand", + "gloo-timers", + "tokio", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "2.6.0" @@ -86,6 +238,24 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + [[package]] name = "cfg-if" version = "1.0.0" @@ -262,12 +432,85 @@ dependencies = [ "const-random", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -289,6 +532,49 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.5.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.13.2" @@ -307,6 +593,128 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.5.0" @@ -317,18 +725,42 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "is_ci" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "json5" version = "0.4.1" @@ -369,78 +801,214 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] -name = "memchr" -version = "2.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "linux-raw-sys" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] -name = "nom" -version = "7.1.3" +name = "lock_api" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ - "memchr", - "minimal-lexical", + "autocfg", + "scopeguard", ] [[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "option-ext" -version = "0.2.0" +name = "log" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] -name = "ordered-multimap" -version = "0.6.0" +name = "matchit" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" -dependencies = [ - "dlv-list", - "hashbrown 0.13.2", -] +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] -name = "pathdiff" -version = "0.2.1" +name = "memchr" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] -name = "pest" -version = "2.7.12" +name = "miette" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1" dependencies = [ - "memchr", + "backtrace", + "backtrace-ext", + "cfg-if", + "miette-derive", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size", + "textwrap", "thiserror", - "ucd-trie", + "unicode-width", ] [[package]] -name = "pest_derive" -version = "2.7.12" +name = "miette-derive" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" +checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c" dependencies = [ - "pest", - "pest_generator", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "pest_generator" +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[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.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "multimap" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-multimap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" +dependencies = [ + "dlv-list", + "hashbrown 0.13.2", +] + +[[package]] +name = "owo-colors" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[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.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" version = "2.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" @@ -463,6 +1031,67 @@ dependencies = [ "sha2", ] +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap 2.5.0", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -472,6 +1101,59 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" +dependencies = [ + "bytes", + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" +dependencies = [ + "prost", +] + [[package]] name = "quote" version = "1.0.37" @@ -481,6 +1163,45 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.6" @@ -492,13 +1213,42 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + [[package]] name = "ron" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64", + "base64 0.21.7", "bitflags", "serde", "serde_derive", @@ -514,12 +1264,53 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "s2" +version = "0.1.0" +source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=fromstr#3d0415946fae3afadce54ac713fa436fb6c3ce1b" +dependencies = [ + "backon", + "http", + "prost", + "prost-types", + "secrecy", + "thiserror", + "tonic", + "tonic-build", + "typed-builder", +] + [[package]] name = "s2-cli" version = "0.1.0" @@ -528,11 +1319,30 @@ dependencies = [ "colored", "config", "dirs", + "humantime", + "miette", + "s2", "serde", "thiserror", + "tokio", "toml", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + [[package]] name = "serde" version = "1.0.210" @@ -585,12 +1395,73 @@ dependencies = [ "digest", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "supports-color" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" +dependencies = [ + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0a1e5168041f5f3ff68ff7d95dcb9c8749df29f6e7e89ada40dd4c9de404ee" + +[[package]] +name = "supports-unicode" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" + [[package]] name = "syn" version = "2.0.77" @@ -602,6 +1473,52 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -631,6 +1548,59 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.19" @@ -658,13 +1628,145 @@ version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ - "indexmap", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", "winnow", ] +[[package]] +name = "tonic" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typed-builder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e14ed59dc8b7b26cacb2a92bad2e8b1f098806063898ab42a3bd121d7d45e75" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "typenum" version = "1.17.0" @@ -683,12 +1785,24 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-segmentation" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + [[package]] name = "utf8parse" version = "0.2.2" @@ -701,12 +1815,76 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + [[package]] name = "windows-sys" version = "0.48.0" @@ -725,6 +1903,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -863,3 +2050,30 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 4691b87..c8d9b46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,3 +11,7 @@ dirs = "5.0.1" serde = { version = "1.0.210", features = ["derive"] } thiserror = "1.0.63" toml = "0.8.19" +s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "fromstr" } +tokio = { version = "*", features = ["full"] } +humantime = "2.1.0" +miette = { version = "7.2.0", features = ["fancy"] } diff --git a/src/account.rs b/src/account.rs new file mode 100644 index 0000000..3224a5b --- /dev/null +++ b/src/account.rs @@ -0,0 +1,78 @@ +use s2::{ + client::Client, + service_error::{CreateBasinError, ServiceError}, + types::{ + BasinConfig, CreateBasinResponse, ListBasinsResponse, RetentionPolicy, StorageClass, + StreamConfig, + }, +}; + +pub struct AccountService { + client: Client, +} + +#[derive(Debug, thiserror::Error)] +pub enum AccountServiceError { + #[error("Failed to list basins: {0}")] + ListBasins(String), + + #[error("Failed to create basin")] + CreateBasin(#[from] ServiceError), +} + +impl AccountService { + pub fn new(client: Client) -> Self { + Self { client } + } + + pub async fn list_basins( + &self, + prefix: String, + start_after: String, + limit: u32, + ) -> Result { + let list_basins_req = s2::types::ListBasinsRequest { + prefix, + start_after, + limit, + }; + + self.client + .list_basins(list_basins_req) + .await + .map_err(|e| AccountServiceError::ListBasins(e.to_string())) + } + + pub async fn create_basin( + &self, + name: String, + storage_class: Option, + retention_policy: Option, + ) -> Result { + let basin_config = match (&storage_class, retention_policy) { + (Some(storage_class), Some(retention_policy)) => { + let stream_config = StreamConfig::builder() + .storage_class(*storage_class) + .retention_policy(RetentionPolicy::Age(*retention_policy)) + .build(); + + let basin_config = BasinConfig::builder() + .default_stream_config(Some(stream_config)) + .build(); + + Some(basin_config) + } + _ => None, + }; + + let create_basin_req = s2::types::CreateBasinRequest::builder() + .basin(name) + .config(basin_config) + .build(); + + self.client + .create_basin(create_basin_req) + .await + .map_err(AccountServiceError::CreateBasin) + } +} diff --git a/src/config.rs b/src/config.rs index 62b0315..247a009 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,7 @@ -use std::path::{Path, PathBuf}; +use std::{ + env, + path::{Path, PathBuf}, +}; use config::{Config, FileFormat}; use serde::{Deserialize, Serialize}; @@ -8,10 +11,9 @@ use crate::error::S2CliError; #[derive(Debug, Deserialize, Serialize)] pub struct S2Config { - token: String, + pub token: String, } -/// Path to the configuration file pub fn config_path() -> Result { let mut path = dirs::config_dir().ok_or(S2ConfigError::DirNotFound)?; path.push("s2"); @@ -21,19 +23,21 @@ pub fn config_path() -> Result { #[allow(dead_code)] pub fn load_config(path: &Path) -> Result { - let cfg = Config::builder() + if let Ok(env_token) = env::var("S2_AUTH_TOKEN") { + return Ok(S2Config { token: env_token }); + } + Config::builder() .add_source(config::File::new( path.to_str().ok_or(S2ConfigError::PathError)?, FileFormat::Toml, )) .build() - .map_err(|_| S2ConfigError::LoadError)?; - - cfg.try_deserialize::() + .map_err(|_| S2ConfigError::LoadError)? + .try_deserialize::() .map_err(|_| S2ConfigError::LoadError) } -pub fn create_config(config_path: &PathBuf, token: &str) -> Result<(), S2ConfigError> { +pub fn create_config(config_path: &PathBuf, token: String) -> Result<(), S2ConfigError> { let cfg = S2Config { token: token.to_string(), }; @@ -52,10 +56,13 @@ pub fn create_config(config_path: &PathBuf, token: &str) -> Result<(), S2ConfigE pub enum S2ConfigError { #[error("Failed to find config directory")] DirNotFound, + #[error("Failed to find config file")] PathError, + #[error("Failed to load config file")] LoadError, + #[error("Failed to write config file")] WriteError, } diff --git a/src/error.rs b/src/error.rs index b53d840..e3c8de0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,38 @@ +use colored::*; +use miette::Diagnostic; +use s2::client::ClientError; use thiserror::Error; -use crate::config::S2ConfigError; +use crate::{account::AccountServiceError, config::S2ConfigError}; -#[derive(Error, Debug)] +#[derive(Error, Debug, Diagnostic)] +#[diagnostic(help(r#" +{} + +► {} + {} + +► {} + {} + +► {} + {} +"#, +"Notice something wrong?".cyan().bold(), +"Open an issue:".green(), +"https://github.com/foo/issues".bold(), +"Reach out to us:".green(), +"hi@s2.dev".bold(), +"Join our community:".green(), +"Discord: https://discord.gg/s2".bold(), +))] pub enum S2CliError { #[error(transparent)] - ConfigError(#[from] S2ConfigError), + Config(#[from] S2ConfigError), + + #[error("Failed to connect to s2: {0}")] + Connection(#[from] ClientError), + + #[error(transparent)] + AccountService(#[from] AccountServiceError), } diff --git a/src/main.rs b/src/main.rs index 72dda4e..215ad96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,14 @@ +use account::AccountService; use clap::{Parser, Subcommand}; use colored::*; use config::{config_path, create_config}; use error::S2CliError; +use s2::{ + client::{Client, ClientConfig, Cloud}, + types::StorageClass, +}; +mod account; mod config; mod error; @@ -20,6 +26,12 @@ enum Commands { #[command(subcommand)] action: ConfigActions, }, + + /// Manage s2 account + Account { + #[command(subcommand)] + action: AccountActions, + }, } #[derive(Subcommand, Debug)] @@ -31,21 +43,62 @@ enum ConfigActions { }, } -fn main() { - if let Err(err) = run() { - eprintln!("{}", format!("✗ {}", err).red()); - std::process::exit(1); - } +#[derive(Subcommand, Debug)] +enum AccountActions { + /// List basins + ListBasins { + /// List basin names that begin with this prefix. + #[arg(short, long)] + prefix: String, + + /// List basins names that lexicographically start after this name. + #[arg(short, long)] + start_after: String, + + /// Number of results, upto a maximum of 1000. + #[arg(short, long)] + limit: u32, + }, + + /// Create a basin + CreateBasin { + /// Basin name, which must be globally unique. + #[arg(short, long)] + basin: String, + + /// Storage class for recent writes. + #[arg(short, long, requires_all = ["retention_policy"])] + storage_class: Option, + + /// Age threshold of oldest records in the stream, which can be automatically trimmed. + #[arg(short, long, requires_all = ["storage_class"])] + retention_policy: Option, + }, } -fn run() -> Result<(), S2CliError> { +async fn s2_client(token: String) -> Result { + let config = ClientConfig::builder() + .url(Cloud::Local) + .token(token.to_string()) + .build(); + + Ok(Client::connect(config).await?) +} + +#[tokio::main] +async fn main() -> miette::Result<()> { + run().await?; + Ok(()) +} + +async fn run() -> Result<(), S2CliError> { let commands = Cli::parse(); let config_path = config_path()?; match commands.command { Commands::Config { action } => match action { ConfigActions::Set { token } => { - create_config(&config_path, &token)?; + create_config(&config_path, token)?; println!("{}", "✓ Token set successfully".green().bold()); println!( " Configuration saved to: {}", @@ -53,6 +106,37 @@ fn run() -> Result<(), S2CliError> { ); } }, + + Commands::Account { action } => { + let cfg = config::load_config(&config_path)?; + let account_service = AccountService::new(s2_client(cfg.token).await?); + match action { + AccountActions::ListBasins { + prefix, + start_after, + limit, + } => { + let response = account_service + .list_basins(prefix, start_after, limit) + .await?; + + for basin_metadata in response.basins { + println!("{}", basin_metadata.name); + } + } + + AccountActions::CreateBasin { + basin, + storage_class, + retention_policy, + } => { + let response = account_service + .create_basin(basin, storage_class, retention_policy) + .await?; + println!("{:?}", response); + } + } + } } Ok(()) From 3b98813169c577afe93c27ba1ca7ffb6f5d57412 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Thu, 19 Sep 2024 09:44:02 -0400 Subject: [PATCH 08/28] .. --- src/account.rs | 21 ++++++++++++++------ src/config.rs | 7 +++++-- src/error.rs | 53 +++++++++++++++++++++++++++++++------------------- src/main.rs | 18 ++++++++++++++--- 4 files changed, 68 insertions(+), 31 deletions(-) diff --git a/src/account.rs b/src/account.rs index 3224a5b..7373f1b 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,6 +1,6 @@ use s2::{ client::Client, - service_error::{CreateBasinError, ServiceError}, + service_error::{CreateBasinError, DeleteBasinError, ServiceError}, types::{ BasinConfig, CreateBasinResponse, ListBasinsResponse, RetentionPolicy, StorageClass, StreamConfig, @@ -18,6 +18,9 @@ pub enum AccountServiceError { #[error("Failed to create basin")] CreateBasin(#[from] ServiceError), + + #[error("Failed to delete basin")] + DeleteBasin(#[from] ServiceError), } impl AccountService { @@ -31,11 +34,11 @@ impl AccountService { start_after: String, limit: u32, ) -> Result { - let list_basins_req = s2::types::ListBasinsRequest { - prefix, - start_after, - limit, - }; + let list_basins_req = s2::types::ListBasinsRequest::builder() + .prefix(prefix) + .start_after(start_after) + .limit(limit) + .build(); self.client .list_basins(list_basins_req) @@ -75,4 +78,10 @@ impl AccountService { .await .map_err(AccountServiceError::CreateBasin) } + + pub async fn delete_basin(&self, name: String) -> Result<(), AccountServiceError> { + let delete_basin_req = s2::types::DeleteBasinRequest { basin: name }; + self.client.delete_basin(delete_basin_req).await?; + Ok(()) + } } diff --git a/src/config.rs b/src/config.rs index 247a009..5468c52 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,6 +4,7 @@ use std::{ }; use config::{Config, FileFormat}; +use miette::Diagnostic; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -21,7 +22,6 @@ pub fn config_path() -> Result { Ok(path) } -#[allow(dead_code)] pub fn load_config(path: &Path) -> Result { if let Ok(env_token) = env::var("S2_AUTH_TOKEN") { return Ok(S2Config { token: env_token }); @@ -52,7 +52,7 @@ pub fn create_config(config_path: &PathBuf, token: String) -> Result<(), S2Confi Ok(()) } -#[derive(Error, Debug)] +#[derive(Error, Debug, Diagnostic)] pub enum S2ConfigError { #[error("Failed to find config directory")] DirNotFound, @@ -61,6 +61,9 @@ pub enum S2ConfigError { PathError, #[error("Failed to load config file")] + #[diagnostic(help( + "Did you run `s2 config set`? or use `S2_AUTH_TOKEN` environment variable." + ))] LoadError, #[error("Failed to write config file")] diff --git a/src/error.rs b/src/error.rs index e3c8de0..56afba7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,5 @@ +use std::sync::OnceLock; + use colored::*; use miette::Diagnostic; use s2::client::ClientError; @@ -5,34 +7,45 @@ use thiserror::Error; use crate::{account::AccountServiceError, config::S2ConfigError}; +static HELP: OnceLock = OnceLock::new(); + +fn get_help() -> &'static str { + HELP.get_or_init(|| { + format!( + r#" + {} + + ► {} + {} + + ► {} + {} + + ► {} + {} + "#, + "Notice something wrong?".cyan().bold(), + "Open an issue:".green(), + "https://github.com/s2-cli/issues".bold(), + "Reach out to us:".green(), + "hi@s2.dev".bold(), + "Join our community:".green(), + "Discord: https://discord.gg/s2".bold(), + ) + }) +} + #[derive(Error, Debug, Diagnostic)] -#[diagnostic(help(r#" -{} - -► {} - {} - -► {} - {} - -► {} - {} -"#, -"Notice something wrong?".cyan().bold(), -"Open an issue:".green(), -"https://github.com/foo/issues".bold(), -"Reach out to us:".green(), -"hi@s2.dev".bold(), -"Join our community:".green(), -"Discord: https://discord.gg/s2".bold(), -))] pub enum S2CliError { #[error(transparent)] + #[diagnostic(transparent)] Config(#[from] S2ConfigError), #[error("Failed to connect to s2: {0}")] + #[diagnostic(help("Are you connected to the internet?"))] Connection(#[from] ClientError), #[error(transparent)] + #[diagnostic(help("{}", get_help()))] AccountService(#[from] AccountServiceError), } diff --git a/src/main.rs b/src/main.rs index 215ad96..0f5cd1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,8 +62,7 @@ enum AccountActions { /// Create a basin CreateBasin { - /// Basin name, which must be globally unique. - #[arg(short, long)] + /// Basin name, which must be globally unique. basin: String, /// Storage class for recent writes. @@ -74,15 +73,24 @@ enum AccountActions { #[arg(short, long, requires_all = ["storage_class"])] retention_policy: Option, }, + + /// Delete a basin + DeleteBasin { + /// Basin name to delete. + basin: String, + }, } async fn s2_client(token: String) -> Result { let config = ClientConfig::builder() .url(Cloud::Local) .token(token.to_string()) + .connection_timeout(std::time::Duration::from_secs(5)) .build(); - Ok(Client::connect(config).await?) + let bro = Client::connect(config).await?; + println!("{:?}", bro); + Ok(bro) } #[tokio::main] @@ -135,6 +143,10 @@ async fn run() -> Result<(), S2CliError> { .await?; println!("{:?}", response); } + AccountActions::DeleteBasin { basin } => { + account_service.delete_basin(basin).await?; + println!("{}", "✓ Basin deleted successfully".green().bold()); + } } } } From 0c5d333d488f66afc121299d6b9074c7533c4f0f Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Thu, 19 Sep 2024 16:32:48 -0400 Subject: [PATCH 09/28] update --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/account.rs | 2 +- src/error.rs | 13 +------------ src/main.rs | 8 +++----- 5 files changed, 7 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b4ea25b..da80f73 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1298,7 +1298,7 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "s2" version = "0.1.0" -source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=fromstr#3d0415946fae3afadce54ac713fa436fb6c3ce1b" +source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=main#448a154072fbdc029f76b48e74c9bb4d80f4db29" dependencies = [ "backon", "http", diff --git a/Cargo.toml b/Cargo.toml index c8d9b46..546a346 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ dirs = "5.0.1" serde = { version = "1.0.210", features = ["derive"] } thiserror = "1.0.63" toml = "0.8.19" -s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "fromstr" } +s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "main" } tokio = { version = "*", features = ["full"] } humantime = "2.1.0" miette = { version = "7.2.0", features = ["fancy"] } diff --git a/src/account.rs b/src/account.rs index 7373f1b..207da28 100644 --- a/src/account.rs +++ b/src/account.rs @@ -80,7 +80,7 @@ impl AccountService { } pub async fn delete_basin(&self, name: String) -> Result<(), AccountServiceError> { - let delete_basin_req = s2::types::DeleteBasinRequest { basin: name }; + let delete_basin_req = s2::types::DeleteBasinRequest::builder().basin(name).build(); self.client.delete_basin(delete_basin_req).await?; Ok(()) } diff --git a/src/error.rs b/src/error.rs index 56afba7..e85d38b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,18 +12,7 @@ static HELP: OnceLock = OnceLock::new(); fn get_help() -> &'static str { HELP.get_or_init(|| { format!( - r#" - {} - - ► {} - {} - - ► {} - {} - - ► {} - {} - "#, + "\n{}\n\n ► {}\n{}\n\n ► {}\n{}\n\n ► {}\n{}", "Notice something wrong?".cyan().bold(), "Open an issue:".green(), "https://github.com/s2-cli/issues".bold(), diff --git a/src/main.rs b/src/main.rs index 0f5cd1d..513174c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use colored::*; use config::{config_path, create_config}; use error::S2CliError; use s2::{ - client::{Client, ClientConfig, Cloud}, + client::{Client, ClientConfig, HostCloud}, types::StorageClass, }; @@ -83,14 +83,12 @@ enum AccountActions { async fn s2_client(token: String) -> Result { let config = ClientConfig::builder() - .url(Cloud::Local) + .host_uri(HostCloud::Local) .token(token.to_string()) .connection_timeout(std::time::Duration::from_secs(5)) .build(); - let bro = Client::connect(config).await?; - println!("{:?}", bro); - Ok(bro) + Ok(Client::connect(config).await?) } #[tokio::main] From 6f8fd93acd9dca44298d3377fd8b68f1661d23b8 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Fri, 20 Sep 2024 00:52:34 -0400 Subject: [PATCH 10/28] . --- Cargo.lock | 52 +++++++++++++++++++++++++---------- Cargo.toml | 1 + src/account.rs | 2 +- src/basin.rs | 39 ++++++++++++++++++++++++++ src/error.rs | 6 +++- src/main.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 151 insertions(+), 23 deletions(-) create mode 100644 src/basin.rs diff --git a/Cargo.lock b/Cargo.lock index da80f73..5c6237d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,9 +252,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" [[package]] name = "cfg-if" @@ -302,6 +302,27 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "color-print" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ee543c60ff3888934877a5671f45494dd27ed4ba25c6670b9a7576b7ed7a8c0" +dependencies = [ + "color-print-proc-macro", +] + +[[package]] +name = "color-print-proc-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ff1a80c5f3cb1ca7c06ffdd71b6a6dd6d8f896c42141fbd43f50ed28dcdb93" +dependencies = [ + "nom", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "colorchoice" version = "1.0.2" @@ -988,9 +1009,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.12" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" dependencies = [ "memchr", "thiserror", @@ -999,9 +1020,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.12" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d" +checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0" dependencies = [ "pest", "pest_generator", @@ -1009,9 +1030,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.12" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe" +checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e" dependencies = [ "pest", "pest_meta", @@ -1022,9 +1043,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.7.12" +version = "2.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174" +checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f" dependencies = [ "once_cell", "pest", @@ -1298,7 +1319,7 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "s2" version = "0.1.0" -source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=main#448a154072fbdc029f76b48e74c9bb4d80f4db29" +source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=main#7c0283d7391a58d9d0e22c04fbb1ee5091f91367" dependencies = [ "backon", "http", @@ -1316,6 +1337,7 @@ name = "s2-cli" version = "0.1.0" dependencies = [ "clap", + "color-print", "colored", "config", "dirs", @@ -1443,9 +1465,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "supports-color" -version = "3.0.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" +checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77" dependencies = [ "is_ci", ] @@ -1799,9 +1821,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "utf8parse" diff --git a/Cargo.toml b/Cargo.toml index 546a346..988924d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,4 @@ s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "main" } tokio = { version = "*", features = ["full"] } humantime = "2.1.0" miette = { version = "7.2.0", features = ["fancy"] } +color-print = "0.3.6" diff --git a/src/account.rs b/src/account.rs index 207da28..fa27a9f 100644 --- a/src/account.rs +++ b/src/account.rs @@ -32,7 +32,7 @@ impl AccountService { &self, prefix: String, start_after: String, - limit: u32, + limit: usize, ) -> Result { let list_basins_req = s2::types::ListBasinsRequest::builder() .prefix(prefix) diff --git a/src/basin.rs b/src/basin.rs new file mode 100644 index 0000000..f2d8cd7 --- /dev/null +++ b/src/basin.rs @@ -0,0 +1,39 @@ +use s2::{ + client::BasinClient, + service_error::{ListStreamsError, ServiceError}, + types::ListStreamsResponse, +}; + +pub struct BasinService { + client: BasinClient, +} + +#[derive(Debug, thiserror::Error)] +pub enum BasinServiceError { + #[error("Failed to list streams: {0}")] + ListStreams(#[from] ServiceError), +} + +impl BasinService { + pub fn new(client: BasinClient) -> Self { + Self { client } + } + + pub async fn list_streams( + &self, + prefix: String, + start_after: String, + limit: usize, + ) -> Result { + let list_streams_req = s2::types::ListStreamsRequest::builder() + .prefix(prefix) + .start_after(start_after) + .limit(limit) + .build(); + + self.client + .list_streams(list_streams_req) + .await + .map_err(BasinServiceError::ListStreams) + } +} diff --git a/src/error.rs b/src/error.rs index e85d38b..8ae177a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,7 +5,7 @@ use miette::Diagnostic; use s2::client::ClientError; use thiserror::Error; -use crate::{account::AccountServiceError, config::S2ConfigError}; +use crate::{account::AccountServiceError, basin::BasinServiceError, config::S2ConfigError}; static HELP: OnceLock = OnceLock::new(); @@ -37,4 +37,8 @@ pub enum S2CliError { #[error(transparent)] #[diagnostic(help("{}", get_help()))] AccountService(#[from] AccountServiceError), + + #[error(transparent)] + #[diagnostic(help("{}", get_help()))] + BasinService(#[from] BasinServiceError), } diff --git a/src/main.rs b/src/main.rs index 513174c..d380906 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use account::AccountService; -use clap::{Parser, Subcommand}; +use basin::BasinService; +use clap::{builder::styling, Parser, Subcommand}; use colored::*; use config::{config_path, create_config}; use error::S2CliError; @@ -9,11 +10,25 @@ use s2::{ }; mod account; +mod basin; mod config; mod error; +const STYLES: styling::Styles = styling::Styles::styled() + .header(styling::AnsiColor::Green.on_default().bold()) + .usage(styling::AnsiColor::Green.on_default().bold()) + .literal(styling::AnsiColor::Blue.on_default().bold()) + .placeholder(styling::AnsiColor::Cyan.on_default()); + +const USAGE: &str = color_print::cstr!( + r#" + $ s2-cli config set --token ... + $ s2-cli account list-basins --prefix "bar" --start-after "foo" --limit 100 + "# +); + #[derive(Parser, Debug)] -#[command(version, about, long_about = None)] +#[command(version, about, override_usage = USAGE, styles = STYLES)] struct Cli { #[command(subcommand)] command: Commands, @@ -21,7 +36,7 @@ struct Cli { #[derive(Subcommand, Debug)] enum Commands { - /// Manage s2 configuration + /// Manage s2-cli configuration Config { #[command(subcommand)] action: ConfigActions, @@ -32,6 +47,12 @@ enum Commands { #[command(subcommand)] action: AccountActions, }, + + /// Manage s2 basins + Basins { + #[command(subcommand)] + action: BasinActions, + }, } #[derive(Subcommand, Debug)] @@ -57,7 +78,7 @@ enum AccountActions { /// Number of results, upto a maximum of 1000. #[arg(short, long)] - limit: u32, + limit: usize, }, /// Create a basin @@ -66,11 +87,11 @@ enum AccountActions { basin: String, /// Storage class for recent writes. - #[arg(short, long, requires_all = ["retention_policy"])] + #[arg(short, long)] storage_class: Option, /// Age threshold of oldest records in the stream, which can be automatically trimmed. - #[arg(short, long, requires_all = ["storage_class"])] + #[arg(short, long)] retention_policy: Option, }, @@ -81,6 +102,27 @@ enum AccountActions { }, } +#[derive(Subcommand, Debug)] +enum BasinActions { + /// List Streams + ListStreams { + /// Name of the basin to list streams from. + basin: String, + + /// List stream names that begin with this prefix. + #[arg(short, long)] + prefix: String, + + /// List stream names that lexicographically start after this name. + #[arg(short, long)] + start_after: String, + + /// Number of results, upto a maximum of 1000. + #[arg(short, long)] + limit: u32, + }, +} + async fn s2_client(token: String) -> Result { let config = ClientConfig::builder() .host_uri(HostCloud::Local) @@ -147,6 +189,26 @@ async fn run() -> Result<(), S2CliError> { } } } + Commands::Basins { action } => { + let cfg = config::load_config(&config_path)?; + let client = s2_client(cfg.token).await?; + match action { + BasinActions::ListStreams { + basin, + prefix, + start_after, + limit, + } => { + let basin_client = client.basin_client(basin).await?; + let response = BasinService::new(basin_client) + .list_streams(prefix, start_after, limit as usize) + .await?; + for stream in response.streams { + println!("{}", stream); + } + } + } + } } Ok(()) From bff795b6b40e184fe8a89c481b0f2068a28d9205 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Fri, 27 Sep 2024 00:27:23 -0400 Subject: [PATCH 11/28] .. --- Cargo.lock | 108 +++++++++++++++++++++++++++++----------------------- Cargo.toml | 2 +- src/main.rs | 11 +++++- 3 files changed, 71 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c6237d..362597b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -122,15 +122,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" dependencies = [ "async-trait", "axum-core", @@ -148,16 +148,16 @@ dependencies = [ "rustversion", "serde", "sync_wrapper 1.0.1", - "tower", + "tower 0.5.1", "tower-layer", "tower-service", ] [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" dependencies = [ "async-trait", "bytes", @@ -168,7 +168,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", ] @@ -264,9 +264,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" dependencies = [ "clap_builder", "clap_derive", @@ -274,9 +274,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" dependencies = [ "anstream", "anstyle", @@ -286,9 +286,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -708,9 +708,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" dependencies = [ "bytes", "futures-channel", @@ -721,7 +721,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -801,9 +800,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libredox" @@ -1124,9 +1123,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2ecbe40f08db5c006b5764a2645f7f3f141ce756412ac9e1dd6087e6d32995" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" dependencies = [ "bytes", "prost-derive", @@ -1134,9 +1133,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8650aabb6c35b860610e9cff5dc1af886c9e25073b7b1712a68972af4281302" +checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15" dependencies = [ "bytes", "heck", @@ -1155,9 +1154,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf0c195eebb4af52c752bec4f52f645da98b6e92077a04110c7f349477ae5ac" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" dependencies = [ "anyhow", "itertools", @@ -1168,9 +1167,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60caa6738c7369b940c3d49246a8d1749323674c65cb13010134f5c9bad5b519" +checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" dependencies = [ "prost", ] @@ -1216,9 +1215,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" dependencies = [ "bitflags", ] @@ -1319,7 +1318,7 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "s2" version = "0.1.0" -source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=main#7c0283d7391a58d9d0e22c04fbb1ee5091f91367" +source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=basinstate-disp#12d3927993c9064651498ca46350cb0ca236e0c3" dependencies = [ "backon", "http", @@ -1399,9 +1398,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -1543,18 +1542,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", @@ -1646,9 +1645,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.5.0", "serde", @@ -1659,9 +1658,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f6ba989e4b2c58ae83d862d3a3e27690b6e3ae630d0deb59f3697f32aa88ad" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", @@ -1681,7 +1680,7 @@ dependencies = [ "socket2", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -1689,13 +1688,14 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4ee8877250136bd7e3d2331632810a4df4ea5e004656990d8d66d2f5ee8a67" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" dependencies = [ "prettyplease", "proc-macro2", "prost-build", + "prost-types", "quote", "syn", ] @@ -1720,6 +1720,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -2057,9 +2071,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 988924d..9acfe88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ dirs = "5.0.1" serde = { version = "1.0.210", features = ["derive"] } thiserror = "1.0.63" toml = "0.8.19" -s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "main" } +s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "basinstate-disp" } tokio = { version = "*", features = ["full"] } humantime = "2.1.0" miette = { version = "7.2.0", features = ["fancy"] } diff --git a/src/main.rs b/src/main.rs index d380906..383368a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ use config::{config_path, create_config}; use error::S2CliError; use s2::{ client::{Client, ClientConfig, HostCloud}, - types::StorageClass, + types::{BasinMetadata, StorageClass}, }; mod account; @@ -169,7 +169,14 @@ async fn run() -> Result<(), S2CliError> { .await?; for basin_metadata in response.basins { - println!("{}", basin_metadata.name); + let BasinMetadata { name, state, .. } = basin_metadata; + + let state = match state { + s2::types::BasinState::Active => state.to_string().green(), + s2::types::BasinState::Deleting => state.to_string().red(), + _ => state.to_string().yellow(), + }; + println!("{} {}", name, state); } } From a74a97acd9a3fbb26c993d8f47ff89f0035237f2 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Fri, 27 Sep 2024 00:29:17 -0400 Subject: [PATCH 12/28] .. --- src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 383368a..678af4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -185,10 +185,11 @@ async fn run() -> Result<(), S2CliError> { storage_class, retention_policy, } => { - let response = account_service + account_service .create_basin(basin, storage_class, retention_policy) .await?; - println!("{:?}", response); + + println!("{}", "✓ Basin created successfully".green().bold()); } AccountActions::DeleteBasin { basin } => { account_service.delete_basin(basin).await?; From 07f689e9980e1eea39c6c2a69d17297d26a07d42 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Fri, 27 Sep 2024 11:06:36 -0400 Subject: [PATCH 13/28] stephen --- src/config.rs | 10 ++++++++++ src/main.rs | 12 ++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/config.rs b/src/config.rs index 5468c52..e04aff6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,6 +15,7 @@ pub struct S2Config { pub token: String, } +#[cfg(target_os = "windows")] pub fn config_path() -> Result { let mut path = dirs::config_dir().ok_or(S2ConfigError::DirNotFound)?; path.push("s2"); @@ -22,6 +23,15 @@ pub fn config_path() -> Result { Ok(path) } +#[cfg(not(target_os = "windows"))] +pub fn config_path() -> Result { + let mut path = dirs::home_dir().ok_or(S2ConfigError::DirNotFound)?; + path.push(".config"); + path.push("s2"); + path.push("config.toml"); + Ok(path) +} + pub fn load_config(path: &Path) -> Result { if let Ok(env_token) = env::var("S2_AUTH_TOKEN") { return Ok(S2Config { token: env_token }); diff --git a/src/main.rs b/src/main.rs index 678af4d..bb3b633 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,15 +70,15 @@ enum AccountActions { ListBasins { /// List basin names that begin with this prefix. #[arg(short, long)] - prefix: String, + prefix: Option, /// List basins names that lexicographically start after this name. #[arg(short, long)] - start_after: String, + start_after: Option, /// Number of results, upto a maximum of 1000. #[arg(short, long)] - limit: usize, + limit: Option, }, /// Create a basin @@ -165,7 +165,11 @@ async fn run() -> Result<(), S2CliError> { limit, } => { let response = account_service - .list_basins(prefix, start_after, limit) + .list_basins( + prefix.unwrap_or_default(), + start_after.unwrap_or_default(), + limit.unwrap_or_default(), + ) .await?; for basin_metadata in response.basins { From aaf0ff91dcc7c0c121e56c1e0249026bc2523e16 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Fri, 27 Sep 2024 11:22:39 -0400 Subject: [PATCH 14/28] .. --- Cargo.lock | 125 ++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + src/config.rs | 17 ++----- src/main.rs | 27 ++++++++--- 4 files changed, 149 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 362597b..9656140 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -842,6 +842,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -934,6 +943,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "object" version = "0.36.4" @@ -965,6 +984,12 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "4.1.0" @@ -1241,8 +1266,17 @@ checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1253,9 +1287,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.4", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.4" @@ -1347,6 +1387,8 @@ dependencies = [ "thiserror", "tokio", "toml", + "tracing", + "tracing-subscriber", ] [[package]] @@ -1416,6 +1458,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -1560,6 +1611,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -1775,6 +1836,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -1845,6 +1936,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.5" @@ -1921,6 +2018,28 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +[[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-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[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 = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 9acfe88..bbd0126 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,5 @@ tokio = { version = "*", features = ["full"] } humantime = "2.1.0" miette = { version = "7.2.0", features = ["fancy"] } color-print = "0.3.6" +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } diff --git a/src/config.rs b/src/config.rs index e04aff6..aed02c7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,4 @@ -use std::{ - env, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use config::{Config, FileFormat}; use miette::Diagnostic; @@ -12,7 +9,7 @@ use crate::error::S2CliError; #[derive(Debug, Deserialize, Serialize)] pub struct S2Config { - pub token: String, + pub auth_token: String, } #[cfg(target_os = "windows")] @@ -33,24 +30,20 @@ pub fn config_path() -> Result { } pub fn load_config(path: &Path) -> Result { - if let Ok(env_token) = env::var("S2_AUTH_TOKEN") { - return Ok(S2Config { token: env_token }); - } Config::builder() .add_source(config::File::new( path.to_str().ok_or(S2ConfigError::PathError)?, FileFormat::Toml, )) + .add_source(config::Environment::with_prefix("S2")) .build() .map_err(|_| S2ConfigError::LoadError)? .try_deserialize::() .map_err(|_| S2ConfigError::LoadError) } -pub fn create_config(config_path: &PathBuf, token: String) -> Result<(), S2ConfigError> { - let cfg = S2Config { - token: token.to_string(), - }; +pub fn create_config(config_path: &PathBuf, auth_token: String) -> Result<(), S2ConfigError> { + let cfg = S2Config { auth_token }; if let Some(parent) = config_path.parent() { std::fs::create_dir_all(parent).map_err(|_| S2ConfigError::WriteError)?; diff --git a/src/main.rs b/src/main.rs index bb3b633..36b08d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use s2::{ client::{Client, ClientConfig, HostCloud}, types::{BasinMetadata, StorageClass}, }; +use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt}; mod account; mod basin; @@ -60,7 +61,7 @@ enum ConfigActions { /// Set the authentication token Set { #[arg(short, long)] - token: String, + auth_token: String, }, } @@ -123,10 +124,10 @@ enum BasinActions { }, } -async fn s2_client(token: String) -> Result { +async fn s2_client(auth_token: String) -> Result { let config = ClientConfig::builder() .host_uri(HostCloud::Local) - .token(token.to_string()) + .token(auth_token.to_string()) .connection_timeout(std::time::Duration::from_secs(5)) .build(); @@ -143,10 +144,21 @@ async fn run() -> Result<(), S2CliError> { let commands = Cli::parse(); let config_path = config_path()?; + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .pretty() + .with_span_events(FmtSpan::NEW) + .compact() + .with_writer(std::io::stderr), + ) + .with(tracing_subscriber::EnvFilter::from_default_env()) + .init(); + match commands.command { Commands::Config { action } => match action { - ConfigActions::Set { token } => { - create_config(&config_path, token)?; + ConfigActions::Set { auth_token } => { + create_config(&config_path, auth_token)?; println!("{}", "✓ Token set successfully".green().bold()); println!( " Configuration saved to: {}", @@ -157,7 +169,8 @@ async fn run() -> Result<(), S2CliError> { Commands::Account { action } => { let cfg = config::load_config(&config_path)?; - let account_service = AccountService::new(s2_client(cfg.token).await?); + println!("cfg: {:?}", cfg); + let account_service = AccountService::new(s2_client(cfg.auth_token).await?); match action { AccountActions::ListBasins { prefix, @@ -203,7 +216,7 @@ async fn run() -> Result<(), S2CliError> { } Commands::Basins { action } => { let cfg = config::load_config(&config_path)?; - let client = s2_client(cfg.token).await?; + let client = s2_client(cfg.auth_token).await?; match action { BasinActions::ListStreams { basin, From 82c7918771ce8766dbd19889110ad143036302eb Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Fri, 27 Sep 2024 14:54:58 -0400 Subject: [PATCH 15/28] trying --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/account.rs | 18 +++++++++++++++--- src/main.rs | 30 +++++++++++++++++++++++++++++- 4 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9656140..8abf64d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1358,7 +1358,7 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "s2" version = "0.1.0" -source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=basinstate-disp#12d3927993c9064651498ca46350cb0ca236e0c3" +source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=main#153aa1b2d7e9eb328ce4ae50a1eeb972a780fdc9" dependencies = [ "backon", "http", diff --git a/Cargo.toml b/Cargo.toml index bbd0126..3483ee1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ dirs = "5.0.1" serde = { version = "1.0.210", features = ["derive"] } thiserror = "1.0.63" toml = "0.8.19" -s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "basinstate-disp" } +s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "main" } tokio = { version = "*", features = ["full"] } humantime = "2.1.0" miette = { version = "7.2.0", features = ["fancy"] } diff --git a/src/account.rs b/src/account.rs index fa27a9f..c72fc18 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,9 +1,9 @@ use s2::{ client::Client, - service_error::{CreateBasinError, DeleteBasinError, ServiceError}, + service_error::{CreateBasinError, DeleteBasinError, GetBasinConfigError, ServiceError}, types::{ - BasinConfig, CreateBasinResponse, ListBasinsResponse, RetentionPolicy, StorageClass, - StreamConfig, + BasinConfig, CreateBasinResponse, GetBasinConfigResponse, ListBasinsResponse, + RetentionPolicy, StorageClass, StreamConfig, }, }; @@ -21,6 +21,9 @@ pub enum AccountServiceError { #[error("Failed to delete basin")] DeleteBasin(#[from] ServiceError), + + #[error("Failed to get basin config")] + GetBasinConfig(#[from] ServiceError), } impl AccountService { @@ -84,4 +87,13 @@ impl AccountService { self.client.delete_basin(delete_basin_req).await?; Ok(()) } + + pub async fn get_basin_config(&self, name: String) -> Result { + let get_basin_config_req = s2::types::GetBasinConfigRequest::builder() + .basin(name) + .build(); + let GetBasinConfigResponse { config } = + self.client.get_basin_config(get_basin_config_req).await?; + Ok(config) + } } diff --git a/src/main.rs b/src/main.rs index 36b08d5..58877b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,6 +65,7 @@ enum ConfigActions { }, } +#[deny(missing_docs)] #[derive(Subcommand, Debug)] enum AccountActions { /// List basins @@ -101,6 +102,22 @@ enum AccountActions { /// Basin name to delete. basin: String, }, + + /// Get basin config + GetBasinConfig { + /// Basin name to get config for. + basin: String, + }, + + /// Reconfigure a basin + ReconfigureBasin { + /// Basin name to reconfigure. + basin: String, + + /// Configuration to apply. + #[arg(short, long)] + config: Vec, + }, } #[derive(Subcommand, Debug)] @@ -122,6 +139,9 @@ enum BasinActions { #[arg(short, long)] limit: u32, }, + + /// Create a stream + CreateStream {}, } async fn s2_client(auth_token: String) -> Result { @@ -169,7 +189,6 @@ async fn run() -> Result<(), S2CliError> { Commands::Account { action } => { let cfg = config::load_config(&config_path)?; - println!("cfg: {:?}", cfg); let account_service = AccountService::new(s2_client(cfg.auth_token).await?); match action { AccountActions::ListBasins { @@ -212,6 +231,14 @@ async fn run() -> Result<(), S2CliError> { account_service.delete_basin(basin).await?; println!("{}", "✓ Basin deleted successfully".green().bold()); } + + AccountActions::GetBasinConfig { basin } => { + let basin_config = account_service.get_basin_config(basin).await?; + println!("{:?}", basin_config); + } + AccountActions::ReconfigureBasin { basin, config } => { + unimplemented!() + } } } Commands::Basins { action } => { @@ -232,6 +259,7 @@ async fn run() -> Result<(), S2CliError> { println!("{}", stream); } } + BasinActions::CreateStream {} => todo!(), } } } From 74819953d53b253e7553da0ab27300f99dba228d Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Sat, 28 Sep 2024 19:31:08 -0400 Subject: [PATCH 16/28] fmly --- Cargo.lock | 30 ++++++++++++++++++------ Cargo.toml | 4 +++- src/error.rs | 9 ++++++++ src/main.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 95 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8abf64d..51bc708 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,9 +128,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -792,6 +792,18 @@ dependencies = [ "serde", ] +[[package]] +name = "json_dotpath" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbdcfef3cf5591f0cef62da413ae795e3d1f5a00936ccec0b2071499a32efd1a" +dependencies = [ + "serde", + "serde_derive", + "serde_json", + "thiserror", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1358,13 +1370,15 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "s2" version = "0.1.0" -source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=main#153aa1b2d7e9eb328ce4ae50a1eeb972a780fdc9" dependencies = [ "backon", "http", "prost", "prost-types", "secrecy", + "serde", + "serde_derive", + "serde_json", "thiserror", "tonic", "tonic-build", @@ -1381,9 +1395,11 @@ dependencies = [ "config", "dirs", "humantime", + "json_dotpath", "miette", "s2", "serde", + "serde_json", "thiserror", "tokio", "toml", @@ -1536,9 +1552,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "syn" -version = "2.0.77" +version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 3483ee1..23b8b6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,10 +11,12 @@ dirs = "5.0.1" serde = { version = "1.0.210", features = ["derive"] } thiserror = "1.0.63" toml = "0.8.19" -s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "main" } +s2 = { path = "../../s2.rs" } tokio = { version = "*", features = ["full"] } humantime = "2.1.0" miette = { version = "7.2.0", features = ["fancy"] } color-print = "0.3.6" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +serde_json = "1.0.128" +json_dotpath = "1.1.0" diff --git a/src/error.rs b/src/error.rs index 8ae177a..4945473 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,4 +41,13 @@ pub enum S2CliError { #[error(transparent)] #[diagnostic(help("{}", get_help()))] BasinService(#[from] BasinServiceError), + + #[error(transparent)] + InvalidConfigSubPath(#[from] json_dotpath::Error), + + #[error(transparent)] + InvalidConfig(#[from] serde_json::Error), + + #[error("Path: {0} not found!")] + PathKeyNotFound(String), } diff --git a/src/main.rs b/src/main.rs index 58877b1..2b8a664 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,17 @@ +use std::{error::Error, time::Duration}; + use account::AccountService; use basin::BasinService; use clap::{builder::styling, Parser, Subcommand}; use colored::*; use config::{config_path, create_config}; use error::S2CliError; +use json_dotpath::DotPaths; use s2::{ client::{Client, ClientConfig, HostCloud}, - types::{BasinMetadata, StorageClass}, + types::{BasinConfig, BasinMetadata, RetentionPolicy, StorageClass, StreamConfig}, }; +use serde_json::Value; use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt}; mod account; @@ -115,8 +119,8 @@ enum AccountActions { basin: String, /// Configuration to apply. - #[arg(short, long)] - config: Vec, + #[arg(short, long, value_parser = parse_key_val::, num_args = 1..)] + config: Vec<(String, String)>, }, } @@ -154,6 +158,19 @@ async fn s2_client(auth_token: String) -> Result { Ok(Client::connect(config).await?) } +fn parse_key_val(s: &str) -> Result<(T, U), Box> +where + T: std::str::FromStr, + T::Err: Error + Send + Sync + 'static, + U: std::str::FromStr, + U::Err: Error + Send + Sync + 'static, +{ + let pos = s + .find('=') + .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?; + Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) +} + #[tokio::main] async fn main() -> miette::Result<()> { run().await?; @@ -237,7 +254,46 @@ async fn run() -> Result<(), S2CliError> { println!("{:?}", basin_config); } AccountActions::ReconfigureBasin { basin, config } => { - unimplemented!() + // dummy basin config for full path matching + let basin_config = BasinConfig::builder() + .default_stream_config(Some( + StreamConfig::builder() + .storage_class(StorageClass::Unspecified) + .retention_policy(RetentionPolicy::Age(Duration::from_secs(60))) + .build(), + )) + .build(); + + let mut json_config: Value = serde_json::to_value(basin_config) + .expect("Failed to convert basin_config to Value"); + + for (key, value) in config { + match value.as_str() { + "null" => { + json_config.dot_remove(&key)?; + } + _ => { + let parsed_value = match humantime::parse_duration(&value) { + Ok(duration) => serde_json::json!({ + "secs": duration.as_secs(), + "nanos": duration.subsec_nanos() + }), + Err(_) => Value::String(value.clone()), + }; + + match json_config.dot_has_checked(&key) { + Ok(true) => { + json_config.dot_set(&key, parsed_value)?; + } + _ => { + Err(S2CliError::PathKeyNotFound(key.clone()))?; + } + } + } + } + } + + let basin_config: BasinConfig = serde_json::from_value(json_config)?; } } } From b035467154a1788d85038e5436d7aede49b81429 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Sun, 29 Sep 2024 02:30:46 -0400 Subject: [PATCH 17/28] .. --- Cargo.lock | 39 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/account.rs | 23 ++++++++++++++++++++++- src/error.rs | 22 ++++++++++++++++++++++ src/main.rs | 42 +++++++++++++++++++++++++++++++++--------- 5 files changed, 117 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51bc708..7a0df16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,6 +359,19 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + [[package]] name = "const-random" version = "0.1.18" @@ -413,6 +426,19 @@ dependencies = [ "typenum", ] +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror", + "zeroize", +] + [[package]] name = "digest" version = "0.10.7" @@ -459,6 +485,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "equivalent" version = "1.0.1" @@ -1393,6 +1425,7 @@ dependencies = [ "color-print", "colored", "config", + "dialoguer", "dirs", "humantime", "json_dotpath", @@ -1483,6 +1516,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "signal-hook-registry" version = "1.4.2" diff --git a/Cargo.toml b/Cargo.toml index 23b8b6a..93f93be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } serde_json = "1.0.128" json_dotpath = "1.1.0" +dialoguer = "0.11.0" diff --git a/src/account.rs b/src/account.rs index c72fc18..2b30b60 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,6 +1,9 @@ use s2::{ client::Client, - service_error::{CreateBasinError, DeleteBasinError, GetBasinConfigError, ServiceError}, + service_error::{ + CreateBasinError, DeleteBasinError, GetBasinConfigError, ReconfigureBasinError, + ServiceError, + }, types::{ BasinConfig, CreateBasinResponse, GetBasinConfigResponse, ListBasinsResponse, RetentionPolicy, StorageClass, StreamConfig, @@ -24,6 +27,9 @@ pub enum AccountServiceError { #[error("Failed to get basin config")] GetBasinConfig(#[from] ServiceError), + + #[error("Failed to reconfigure basin")] + ReconfigureBasin(#[from] ServiceError), } impl AccountService { @@ -96,4 +102,19 @@ impl AccountService { self.client.get_basin_config(get_basin_config_req).await?; Ok(config) } + + pub async fn reconfigure_basin( + &self, + basin: String, + basin_config: BasinConfig, + mask: Vec, + ) -> Result<(), AccountServiceError> { + let reconfigure_basin_req = s2::types::ReconfigureBasinRequest::builder() + .basin(basin) + .config(basin_config) + .mask(mask) + .build(); + self.client.reconfigure_basin(reconfigure_basin_req).await?; + Ok(()) + } } diff --git a/src/error.rs b/src/error.rs index 4945473..fad2f8f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,6 +24,23 @@ fn get_help() -> &'static str { }) } +const INVALID_PATH_HELP: &str = color_print::cstr!( + r#" +A valid basin configuration looks like this: + +{ + "basin": "my-basin-1", + "config": { + "defaultStreamConfig": { + "storageClass": "STORAGE_CLASS_STANDARD", + } + } +} + +And a path like default_stream_config.storage_class would be valid. +"# +); + #[derive(Error, Debug, Diagnostic)] pub enum S2CliError { #[error(transparent)] @@ -49,5 +66,10 @@ pub enum S2CliError { InvalidConfig(#[from] serde_json::Error), #[error("Path: {0} not found!")] + #[diagnostic(help("{}", INVALID_PATH_HELP))] PathKeyNotFound(String), + + #[error("Failed to interact for confirmation!")] + #[diagnostic(help("{}", get_help()))] + ConfirmationError(#[from] dialoguer::Error), } diff --git a/src/main.rs b/src/main.rs index 2b8a664..aab330e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use basin::BasinService; use clap::{builder::styling, Parser, Subcommand}; use colored::*; use config::{config_path, create_config}; +use dialoguer::Confirm; use error::S2CliError; use json_dotpath::DotPaths; use s2::{ @@ -265,15 +266,15 @@ async fn run() -> Result<(), S2CliError> { .build(); let mut json_config: Value = serde_json::to_value(basin_config) - .expect("Failed to convert basin_config to Value"); + .expect("Failed to convert basin_config to Value"); - for (key, value) in config { + for (key, value) in &config { match value.as_str() { - "null" => { - json_config.dot_remove(&key)?; + "null" | "" => { + json_config.dot_remove(key)?; } _ => { - let parsed_value = match humantime::parse_duration(&value) { + let parsed_value = match humantime::parse_duration(value) { Ok(duration) => serde_json::json!({ "secs": duration.as_secs(), "nanos": duration.subsec_nanos() @@ -281,9 +282,9 @@ async fn run() -> Result<(), S2CliError> { Err(_) => Value::String(value.clone()), }; - match json_config.dot_has_checked(&key) { + match json_config.dot_has_checked(key) { Ok(true) => { - json_config.dot_set(&key, parsed_value)?; + json_config.dot_set(key, parsed_value)?; } _ => { Err(S2CliError::PathKeyNotFound(key.clone()))?; @@ -291,9 +292,32 @@ async fn run() -> Result<(), S2CliError> { } } } - } + } + + let basin_config: BasinConfig = serde_json::from_value(json_config)?; - let basin_config: BasinConfig = serde_json::from_value(json_config)?; + let confirmation = Confirm::new() + .with_prompt(color_print::cformat!( + "Are you sure you want to reconfigure basin {}?", + basin, + )) + .interact()?; + + match confirmation { + true => { + account_service + .reconfigure_basin( + basin, + basin_config, + config.iter().map(|(k, _)| k.clone()).collect(), + ) + .await?; + println!("{}", "✓ Basin reconfigured successfully".green().bold()); + } + false => { + println!("{}", "✗ Reconfigure cancelled".red().bold()); + } + } } } } From 93062bfe063b1ae9a64cb2f06778b10e5164261e Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Mon, 30 Sep 2024 16:14:56 -0400 Subject: [PATCH 18/28] .. --- Cargo.lock | 13 ---- Cargo.toml | 1 - src/basin.rs | 84 +++++++++++++++++++-- src/error.rs | 24 ------ src/main.rs | 202 ++++++++++++++++++++++++++++++--------------------- src/types.rs | 81 +++++++++++++++++++++ 6 files changed, 278 insertions(+), 127 deletions(-) create mode 100644 src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 7a0df16..eb6c8d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -824,18 +824,6 @@ dependencies = [ "serde", ] -[[package]] -name = "json_dotpath" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbdcfef3cf5591f0cef62da413ae795e3d1f5a00936ccec0b2071499a32efd1a" -dependencies = [ - "serde", - "serde_derive", - "serde_json", - "thiserror", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1428,7 +1416,6 @@ dependencies = [ "dialoguer", "dirs", "humantime", - "json_dotpath", "miette", "s2", "serde", diff --git a/Cargo.toml b/Cargo.toml index 93f93be..332814e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,5 +19,4 @@ color-print = "0.3.6" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } serde_json = "1.0.128" -json_dotpath = "1.1.0" dialoguer = "0.11.0" diff --git a/src/basin.rs b/src/basin.rs index f2d8cd7..bd49550 100644 --- a/src/basin.rs +++ b/src/basin.rs @@ -1,7 +1,10 @@ use s2::{ client::BasinClient, - service_error::{ListStreamsError, ServiceError}, - types::ListStreamsResponse, + service_error::{ + CreateStreamError, DeleteStreamError, GetStreamConfigError, ListStreamsError, + ReconfigureStreamError, ServiceError, + }, + types::{GetStreamConfigResponse, ListStreamsResponse, StreamConfig}, }; pub struct BasinService { @@ -12,6 +15,18 @@ pub struct BasinService { pub enum BasinServiceError { #[error("Failed to list streams: {0}")] ListStreams(#[from] ServiceError), + + #[error("Failed to create stream")] + CreateStream(#[from] ServiceError), + + #[error("Failed to delete stream")] + DeleteStream(#[from] ServiceError), + + #[error("Failed to get stream config")] + GetStreamConfig(#[from] ServiceError), + + #[error("Failed to reconfigure stream")] + ReconfigureStream(#[from] ServiceError), } impl BasinService { @@ -24,16 +39,73 @@ impl BasinService { prefix: String, start_after: String, limit: usize, - ) -> Result { + ) -> Result, BasinServiceError> { let list_streams_req = s2::types::ListStreamsRequest::builder() .prefix(prefix) .start_after(start_after) .limit(limit) .build(); + let ListStreamsResponse { streams, .. } = + self.client.list_streams(list_streams_req).await?; + + Ok(streams) + } + + pub async fn create_stream( + &self, + stream_name: String, + config: Option, + ) -> Result<(), BasinServiceError> { + let create_stream_req = s2::types::CreateStreamRequest::builder() + .stream(stream_name) + .config(config) + .build(); + + self.client.create_stream(create_stream_req).await?; + + Ok(()) + } + + pub async fn delete_stream(&self, stream_name: String) -> Result<(), BasinServiceError> { + let delete_stream_req = s2::types::DeleteStreamRequest::builder() + .stream(stream_name) + .build(); + + self.client.delete_stream(delete_stream_req).await?; + + Ok(()) + } + + pub async fn get_stream_config( + &self, + stream: String, + ) -> Result { + let get_stream_config_req = s2::types::GetStreamConfigRequest::builder() + .stream(stream) + .build(); + + let GetStreamConfigResponse { config } = + self.client.get_stream_config(get_stream_config_req).await?; + Ok(config) + } + + pub async fn reconfigure_stream( + &self, + stream: String, + config: StreamConfig, + mask: Vec, + ) -> Result<(), BasinServiceError> { + let reconfigure_stream_req = s2::types::ReconfigureStreamRequest::builder() + .stream(stream) + .config(config) + .mask(mask) + .build(); + self.client - .list_streams(list_streams_req) - .await - .map_err(BasinServiceError::ListStreams) + .reconfigure_stream(reconfigure_stream_req) + .await?; + + Ok(()) } } diff --git a/src/error.rs b/src/error.rs index fad2f8f..184866e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,23 +24,6 @@ fn get_help() -> &'static str { }) } -const INVALID_PATH_HELP: &str = color_print::cstr!( - r#" -A valid basin configuration looks like this: - -{ - "basin": "my-basin-1", - "config": { - "defaultStreamConfig": { - "storageClass": "STORAGE_CLASS_STANDARD", - } - } -} - -And a path like default_stream_config.storage_class would be valid. -"# -); - #[derive(Error, Debug, Diagnostic)] pub enum S2CliError { #[error(transparent)] @@ -59,16 +42,9 @@ pub enum S2CliError { #[diagnostic(help("{}", get_help()))] BasinService(#[from] BasinServiceError), - #[error(transparent)] - InvalidConfigSubPath(#[from] json_dotpath::Error), - #[error(transparent)] InvalidConfig(#[from] serde_json::Error), - #[error("Path: {0} not found!")] - #[diagnostic(help("{}", INVALID_PATH_HELP))] - PathKeyNotFound(String), - #[error("Failed to interact for confirmation!")] #[diagnostic(help("{}", get_help()))] ConfirmationError(#[from] dialoguer::Error), diff --git a/src/main.rs b/src/main.rs index aab330e..8415531 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,21 @@ -use std::{error::Error, time::Duration}; - use account::AccountService; use basin::BasinService; use clap::{builder::styling, Parser, Subcommand}; use colored::*; use config::{config_path, create_config}; -use dialoguer::Confirm; use error::S2CliError; -use json_dotpath::DotPaths; use s2::{ client::{Client, ClientConfig, HostCloud}, - types::{BasinConfig, BasinMetadata, RetentionPolicy, StorageClass, StreamConfig}, + types::{BasinMetadata, StorageClass}, }; -use serde_json::Value; use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt}; +use types::{BasinConfig, StreamConfig}; mod account; mod basin; mod config; mod error; +mod types; const STYLES: styling::Styles = styling::Styles::styled() .header(styling::AnsiColor::Green.on_default().bold()) @@ -119,9 +116,9 @@ enum AccountActions { /// Basin name to reconfigure. basin: String, - /// Configuration to apply. - #[arg(short, long, value_parser = parse_key_val::, num_args = 1..)] - config: Vec<(String, String)>, + /// Configuration to apply. + #[command(flatten)] + config: BasinConfig, }, } @@ -146,7 +143,48 @@ enum BasinActions { }, /// Create a stream - CreateStream {}, + CreateStream { + /// Name of the basin to create a stream in. + basin: String, + + /// Name of the stream to create. + stream: String, + + /// Configuration to apply. + #[command(flatten)] + config: Option, + }, + + /// Delete a stream + DeleteStream { + /// Name of the basin to delete a stream from. + basin: String, + + /// Name of the stream to delete. + stream: String, + }, + + /// Get stream config + GetStreamConfig { + /// Name of the basin to get stream config from. + basin: String, + + /// Name of the stream to get config for. + stream: String, + }, + + /// Reconfigure a stream + ReconfigureStream { + /// Name of the basin to reconfigure a stream in. + basin: String, + + /// Name of the stream to reconfigure. + stream: String, + + /// Configuration to apply. + #[command(flatten)] + config: StreamConfig, + }, } async fn s2_client(auth_token: String) -> Result { @@ -159,19 +197,6 @@ async fn s2_client(auth_token: String) -> Result { Ok(Client::connect(config).await?) } -fn parse_key_val(s: &str) -> Result<(T, U), Box> -where - T: std::str::FromStr, - T::Err: Error + Send + Sync + 'static, - U: std::str::FromStr, - U::Err: Error + Send + Sync + 'static, -{ - let pos = s - .find('=') - .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{s}`"))?; - Ok((s[..pos].parse()?, s[pos + 1..].parse()?)) -} - #[tokio::main] async fn main() -> miette::Result<()> { run().await?; @@ -255,69 +280,29 @@ async fn run() -> Result<(), S2CliError> { println!("{:?}", basin_config); } AccountActions::ReconfigureBasin { basin, config } => { - // dummy basin config for full path matching - let basin_config = BasinConfig::builder() - .default_stream_config(Some( - StreamConfig::builder() - .storage_class(StorageClass::Unspecified) - .retention_policy(RetentionPolicy::Age(Duration::from_secs(60))) - .build(), - )) - .build(); - - let mut json_config: Value = serde_json::to_value(basin_config) - .expect("Failed to convert basin_config to Value"); - - for (key, value) in &config { - match value.as_str() { - "null" | "" => { - json_config.dot_remove(key)?; + let mut mask = Vec::new(); + match &config.default_stream_config { + Some(config) => { + match config.storage_class { + Some(_) => { + mask.push("default_stream_config.storage_class".to_string()); + } + None => {} } - _ => { - let parsed_value = match humantime::parse_duration(value) { - Ok(duration) => serde_json::json!({ - "secs": duration.as_secs(), - "nanos": duration.subsec_nanos() - }), - Err(_) => Value::String(value.clone()), - }; - - match json_config.dot_has_checked(key) { - Ok(true) => { - json_config.dot_set(key, parsed_value)?; - } - _ => { - Err(S2CliError::PathKeyNotFound(key.clone()))?; - } + + match config.retention_policy { + Some(_) => { + mask.push("default_stream_config.retention_policy".to_string()); } + None => {} } } + None => {} } - let basin_config: BasinConfig = serde_json::from_value(json_config)?; - - let confirmation = Confirm::new() - .with_prompt(color_print::cformat!( - "Are you sure you want to reconfigure basin {}?", - basin, - )) - .interact()?; - - match confirmation { - true => { - account_service - .reconfigure_basin( - basin, - basin_config, - config.iter().map(|(k, _)| k.clone()).collect(), - ) - .await?; - println!("{}", "✓ Basin reconfigured successfully".green().bold()); - } - false => { - println!("{}", "✗ Reconfigure cancelled".red().bold()); - } - } + account_service + .reconfigure_basin(basin, config.into(), mask) + .await?; } } } @@ -332,14 +317,65 @@ async fn run() -> Result<(), S2CliError> { limit, } => { let basin_client = client.basin_client(basin).await?; - let response = BasinService::new(basin_client) + let streams = BasinService::new(basin_client) .list_streams(prefix, start_after, limit as usize) .await?; - for stream in response.streams { + for stream in streams { println!("{}", stream); } } - BasinActions::CreateStream {} => todo!(), + BasinActions::CreateStream { + basin, + stream, + config, + } => { + let basin_client = client.basin_client(basin).await?; + BasinService::new(basin_client) + .create_stream(stream, config.map(Into::into)) + .await?; + println!("{}", "✓ Stream created successfully".green().bold()); + } + BasinActions::DeleteStream { basin, stream } => { + let basin_client = client.basin_client(basin).await?; + BasinService::new(basin_client) + .delete_stream(stream) + .await?; + println!("{}", "✓ Stream deleted successfully".green().bold()); + } + BasinActions::GetStreamConfig { basin, stream } => { + let basin_client = client.basin_client(basin).await?; + let config = BasinService::new(basin_client) + .get_stream_config(stream) + .await?; + println!("{:?}", serde_json::to_string_pretty(&config)?); + } + BasinActions::ReconfigureStream { + basin, + stream, + config, + } => { + let basin_client = client.basin_client(basin).await?; + let mut mask = Vec::new(); + match config.storage_class { + Some(_) => { + mask.push("storage_class".to_string()); + } + None => {} + }; + + match config.retention_policy { + Some(_) => { + mask.push("retention_policy".to_string()); + } + None => {} + }; + + BasinService::new(basin_client) + .reconfigure_stream(stream, config.into(), mask) + .await?; + + println!("{}", "✓ Stream reconfigured successfully".green().bold()); + } } } } diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..5a8ce45 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,81 @@ +use clap::{Parser, ValueEnum}; +use std::time::Duration; + +#[derive(Parser, Debug, Clone)] +pub struct BasinConfig { + #[clap(flatten)] + pub default_stream_config: Option, +} + +#[derive(Parser, Debug, Clone)] +pub struct StreamConfig { + #[arg(short, long)] + /// Storage class for a stream. + pub storage_class: Option, + #[arg(short, long, help("Example: 1d, 1w, 1y"))] + /// Retention policy for a stream. + pub retention_policy: Option, +} + +#[derive(ValueEnum, Debug, Clone)] +pub enum StorageClass { + Unspecified, + Standard, + Express, +} + +#[derive(Clone, Debug)] +pub enum RetentionPolicy { + #[allow(dead_code)] + Age(Duration), +} + +impl From<&str> for RetentionPolicy { + fn from(s: &str) -> Self { + match humantime::parse_duration(s) { + Ok(d) => RetentionPolicy::Age(d), + Err(_) => RetentionPolicy::Age(Duration::from_secs(0)), + } + } +} + +impl From for s2::types::BasinConfig { + fn from(config: BasinConfig) -> Self { + let default_stream_config = config.default_stream_config.map(|c| c.into()); + s2::types::BasinConfig::builder() + .default_stream_config(default_stream_config) + .build() + } +} + +impl From for s2::types::StreamConfig { + fn from(config: StreamConfig) -> Self { + let storage_class = config + .storage_class + .map(s2::types::StorageClass::from) + .unwrap_or(s2::types::StorageClass::Unspecified); + let retention_policy = config.retention_policy.map(|r| r.into()); + s2::types::StreamConfig::builder() + .storage_class(storage_class) + .retention_policy(retention_policy) + .build() + } +} + +impl From for s2::types::StorageClass { + fn from(class: StorageClass) -> Self { + match class { + StorageClass::Unspecified => s2::types::StorageClass::Unspecified, + StorageClass::Standard => s2::types::StorageClass::Standard, + StorageClass::Express => s2::types::StorageClass::Express, + } + } +} + +impl From for s2::types::RetentionPolicy { + fn from(policy: RetentionPolicy) -> Self { + match policy { + RetentionPolicy::Age(d) => s2::types::RetentionPolicy::Age(d), + } + } +} From 5f474c4f5a79da9b89966e1de4792412447eb0d3 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Tue, 1 Oct 2024 11:56:21 -0400 Subject: [PATCH 19/28] .. --- Cargo.lock | 54 +++++++++++++++++++++++-- src/account.rs | 10 ++--- src/basin.rs | 6 +-- src/error.rs | 47 +++++++++------------ src/main.rs | 108 +++++++++++++++++++++++++++++++------------------ src/stream.rs | 24 +++++++++++ src/types.rs | 50 +++++++++++++++++++++-- 7 files changed, 216 insertions(+), 83 deletions(-) create mode 100644 src/stream.rs diff --git a/Cargo.lock b/Cargo.lock index eb6c8d8..7aef720 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -525,6 +525,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.30" @@ -532,6 +547,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -540,6 +556,34 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -558,10 +602,16 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ + "futures-channel", "futures-core", + "futures-io", + "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -1392,13 +1442,11 @@ name = "s2" version = "0.1.0" dependencies = [ "backon", + "futures", "http", "prost", "prost-types", "secrecy", - "serde", - "serde_derive", - "serde_json", "thiserror", "tonic", "tonic-build", diff --git a/src/account.rs b/src/account.rs index 2b30b60..7078f01 100644 --- a/src/account.rs +++ b/src/account.rs @@ -5,8 +5,7 @@ use s2::{ ServiceError, }, types::{ - BasinConfig, CreateBasinResponse, GetBasinConfigResponse, ListBasinsResponse, - RetentionPolicy, StorageClass, StreamConfig, + BasinConfig, BasinMetadata, ListBasinsResponse, RetentionPolicy, StorageClass, StreamConfig, }, }; @@ -60,7 +59,7 @@ impl AccountService { name: String, storage_class: Option, retention_policy: Option, - ) -> Result { + ) -> Result { let basin_config = match (&storage_class, retention_policy) { (Some(storage_class), Some(retention_policy)) => { let stream_config = StreamConfig::builder() @@ -98,9 +97,8 @@ impl AccountService { let get_basin_config_req = s2::types::GetBasinConfigRequest::builder() .basin(name) .build(); - let GetBasinConfigResponse { config } = - self.client.get_basin_config(get_basin_config_req).await?; - Ok(config) + + Ok(self.client.get_basin_config(get_basin_config_req).await?) } pub async fn reconfigure_basin( diff --git a/src/basin.rs b/src/basin.rs index bd49550..c9c1fd5 100644 --- a/src/basin.rs +++ b/src/basin.rs @@ -4,7 +4,7 @@ use s2::{ CreateStreamError, DeleteStreamError, GetStreamConfigError, ListStreamsError, ReconfigureStreamError, ServiceError, }, - types::{GetStreamConfigResponse, ListStreamsResponse, StreamConfig}, + types::{ListStreamsResponse, StreamConfig}, }; pub struct BasinService { @@ -85,9 +85,7 @@ impl BasinService { .stream(stream) .build(); - let GetStreamConfigResponse { config } = - self.client.get_stream_config(get_stream_config_req).await?; - Ok(config) + Ok(self.client.get_stream_config(get_stream_config_req).await?) } pub async fn reconfigure_stream( diff --git a/src/error.rs b/src/error.rs index 184866e..33fd449 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,28 +1,21 @@ -use std::sync::OnceLock; - -use colored::*; use miette::Diagnostic; use s2::client::ClientError; use thiserror::Error; -use crate::{account::AccountServiceError, basin::BasinServiceError, config::S2ConfigError}; - -static HELP: OnceLock = OnceLock::new(); - -fn get_help() -> &'static str { - HELP.get_or_init(|| { - format!( - "\n{}\n\n ► {}\n{}\n\n ► {}\n{}\n\n ► {}\n{}", - "Notice something wrong?".cyan().bold(), - "Open an issue:".green(), - "https://github.com/s2-cli/issues".bold(), - "Reach out to us:".green(), - "hi@s2.dev".bold(), - "Join our community:".green(), - "Discord: https://discord.gg/s2".bold(), - ) - }) -} +use crate::{ + account::AccountServiceError, basin::BasinServiceError, config::S2ConfigError, + stream::StreamServiceError, +}; + +const HELP: &str = color_print::cstr!( + "\nNotice something wrong?\n\n\ + ► Open an issue:\n\ + https://github.com/s2-cli/issues\n\n\ + ► Reach out to us:\n\ + hi@s2.dev\n\n\ + ► Join our community:\n\ + Discord: https://discord.gg/s2" +); #[derive(Error, Debug, Diagnostic)] pub enum S2CliError { @@ -35,17 +28,17 @@ pub enum S2CliError { Connection(#[from] ClientError), #[error(transparent)] - #[diagnostic(help("{}", get_help()))] + #[diagnostic(help("{}", HELP))] AccountService(#[from] AccountServiceError), #[error(transparent)] - #[diagnostic(help("{}", get_help()))] + #[diagnostic(help("{}", HELP))] BasinService(#[from] BasinServiceError), #[error(transparent)] - InvalidConfig(#[from] serde_json::Error), + #[diagnostic(help("{}", HELP))] + StreamService(#[from] StreamServiceError), - #[error("Failed to interact for confirmation!")] - #[diagnostic(help("{}", get_help()))] - ConfirmationError(#[from] dialoguer::Error), + #[error(transparent)] + InvalidConfig(#[from] serde_json::Error), } diff --git a/src/main.rs b/src/main.rs index 8415531..6024f30 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,16 +5,18 @@ use colored::*; use config::{config_path, create_config}; use error::S2CliError; use s2::{ - client::{Client, ClientConfig, HostCloud}, + client::{BasinClient, Client, ClientConfig, HostCloud, StreamClient}, types::{BasinMetadata, StorageClass}, }; +use stream::StreamService; use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt}; -use types::{BasinConfig, StreamConfig}; +use types::{BasinConfig, StreamConfig, RETENTION_POLICY_PATH, STORAGE_CLASS_PATH}; mod account; mod basin; mod config; mod error; +mod stream; mod types; const STYLES: styling::Styles = styling::Styles::styled() @@ -23,7 +25,7 @@ const STYLES: styling::Styles = styling::Styles::styled() .literal(styling::AnsiColor::Blue.on_default().bold()) .placeholder(styling::AnsiColor::Cyan.on_default()); -const USAGE: &str = color_print::cstr!( +const GENERAL_USAGE: &str = color_print::cstr!( r#" $ s2-cli config set --token ... $ s2-cli account list-basins --prefix "bar" --start-after "foo" --limit 100 @@ -31,7 +33,7 @@ const USAGE: &str = color_print::cstr!( ); #[derive(Parser, Debug)] -#[command(version, about, override_usage = USAGE, styles = STYLES)] +#[command(version, about, override_usage = GENERAL_USAGE, styles = STYLES)] struct Cli { #[command(subcommand)] command: Commands, @@ -52,10 +54,16 @@ enum Commands { }, /// Manage s2 basins - Basins { + Basin { #[command(subcommand)] action: BasinActions, }, + + /// Manage s2 streams + Stream { + #[command(subcommand)] + action: StreamActions, + }, } #[derive(Subcommand, Debug)] @@ -187,14 +195,26 @@ enum BasinActions { }, } -async fn s2_client(auth_token: String) -> Result { - let config = ClientConfig::builder() +#[derive(Subcommand, Debug)] +enum StreamActions { + /// Get the next sequence number that will be assigned by a stream. + GetNextSeqNum { + /// Name of the basin to get the next sequence number from. + basin: String, + + /// Name of the stream to get the next sequence number for. + stream: String, + }, + + +} + +fn s2_config(auth_token: String) -> ClientConfig { + ClientConfig::builder() .host_uri(HostCloud::Local) .token(auth_token.to_string()) .connection_timeout(std::time::Duration::from_secs(5)) - .build(); - - Ok(Client::connect(config).await?) + .build() } #[tokio::main] @@ -232,7 +252,8 @@ async fn run() -> Result<(), S2CliError> { Commands::Account { action } => { let cfg = config::load_config(&config_path)?; - let account_service = AccountService::new(s2_client(cfg.auth_token).await?); + let account_service = + AccountService::new(Client::connect(s2_config(cfg.auth_token)).await?); match action { AccountActions::ListBasins { prefix, @@ -270,6 +291,7 @@ async fn run() -> Result<(), S2CliError> { println!("{}", "✓ Basin created successfully".green().bold()); } + AccountActions::DeleteBasin { basin } => { account_service.delete_basin(basin).await?; println!("{}", "✓ Basin deleted successfully".green().bold()); @@ -277,24 +299,20 @@ async fn run() -> Result<(), S2CliError> { AccountActions::GetBasinConfig { basin } => { let basin_config = account_service.get_basin_config(basin).await?; - println!("{:?}", basin_config); + let basin_config: BasinConfig = basin_config.into(); + println!("{:?}", serde_json::to_string_pretty(&basin_config)?); } + AccountActions::ReconfigureBasin { basin, config } => { let mut mask = Vec::new(); match &config.default_stream_config { Some(config) => { - match config.storage_class { - Some(_) => { - mask.push("default_stream_config.storage_class".to_string()); - } - None => {} + if config.storage_class.is_some() { + mask.push(STORAGE_CLASS_PATH.to_string()); } - match config.retention_policy { - Some(_) => { - mask.push("default_stream_config.retention_policy".to_string()); - } - None => {} + if config.retention_policy.is_some() { + mask.push(RETENTION_POLICY_PATH.to_string()); } } None => {} @@ -306,9 +324,10 @@ async fn run() -> Result<(), S2CliError> { } } } - Commands::Basins { action } => { + + Commands::Basin { action } => { let cfg = config::load_config(&config_path)?; - let client = s2_client(cfg.auth_token).await?; + let basin_config = s2_config(cfg.auth_token); match action { BasinActions::ListStreams { basin, @@ -316,7 +335,7 @@ async fn run() -> Result<(), S2CliError> { start_after, limit, } => { - let basin_client = client.basin_client(basin).await?; + let basin_client = BasinClient::connect(basin_config, basin).await?; let streams = BasinService::new(basin_client) .list_streams(prefix, start_after, limit as usize) .await?; @@ -324,50 +343,50 @@ async fn run() -> Result<(), S2CliError> { println!("{}", stream); } } + BasinActions::CreateStream { basin, stream, config, } => { - let basin_client = client.basin_client(basin).await?; + let basin_client = BasinClient::connect(basin_config, basin).await?; BasinService::new(basin_client) .create_stream(stream, config.map(Into::into)) .await?; println!("{}", "✓ Stream created successfully".green().bold()); } + BasinActions::DeleteStream { basin, stream } => { - let basin_client = client.basin_client(basin).await?; + let basin_client = BasinClient::connect(basin_config, basin).await?; BasinService::new(basin_client) .delete_stream(stream) .await?; println!("{}", "✓ Stream deleted successfully".green().bold()); } + BasinActions::GetStreamConfig { basin, stream } => { - let basin_client = client.basin_client(basin).await?; + let basin_client = BasinClient::connect(basin_config, basin).await?; let config = BasinService::new(basin_client) .get_stream_config(stream) .await?; + let config: StreamConfig = config.into(); println!("{:?}", serde_json::to_string_pretty(&config)?); } + BasinActions::ReconfigureStream { basin, stream, config, } => { - let basin_client = client.basin_client(basin).await?; + let basin_client = BasinClient::connect(basin_config, basin).await?; let mut mask = Vec::new(); - match config.storage_class { - Some(_) => { - mask.push("storage_class".to_string()); - } - None => {} + + if config.storage_class.is_some() { + mask.push("storage_class".to_string()); }; - match config.retention_policy { - Some(_) => { - mask.push("retention_policy".to_string()); - } - None => {} + if config.retention_policy.is_some() { + mask.push("retention_policy".to_string()); }; BasinService::new(basin_client) @@ -378,6 +397,17 @@ async fn run() -> Result<(), S2CliError> { } } } + Commands::Stream { action } => { + let cfg = config::load_config(&config_path)?; + let s2_config = s2_config(cfg.auth_token); + match action { + StreamActions::GetNextSeqNum { basin, stream } => { + let stream_client = StreamClient::connect(s2_config, basin, stream).await?; + let seq_num = StreamService::new(stream_client).get_next_seq_num().await?; + println!("{}", seq_num); + } + } + } } Ok(()) diff --git a/src/stream.rs b/src/stream.rs new file mode 100644 index 0000000..4a20c0f --- /dev/null +++ b/src/stream.rs @@ -0,0 +1,24 @@ +use s2::{ + client::StreamClient, + service_error::{GetNextSeqNumError, ServiceError}, +}; + +pub struct StreamService { + client: StreamClient, +} + +#[derive(Debug, thiserror::Error)] +pub enum StreamServiceError { + #[error("Failed to get next sequence number")] + GetNextSeqNumError(#[from] ServiceError), +} + +impl StreamService { + pub fn new(client: StreamClient) -> Self { + Self { client } + } + + pub async fn get_next_seq_num(&self) -> Result { + Ok(self.client.get_next_seq_num().await?) + } +} diff --git a/src/types.rs b/src/types.rs index 5a8ce45..2929fd0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,13 +1,19 @@ +//! Types for Basin configuration that directly map to s2::types. + use clap::{Parser, ValueEnum}; +use serde::Serialize; use std::time::Duration; -#[derive(Parser, Debug, Clone)] +pub const STORAGE_CLASS_PATH: &str = "default_stream_config.storage_class"; +pub const RETENTION_POLICY_PATH: &str = "default_stream_config.retention_policy"; + +#[derive(Parser, Debug, Clone, Serialize)] pub struct BasinConfig { #[clap(flatten)] pub default_stream_config: Option, } -#[derive(Parser, Debug, Clone)] +#[derive(Parser, Debug, Clone, Serialize)] pub struct StreamConfig { #[arg(short, long)] /// Storage class for a stream. @@ -17,14 +23,14 @@ pub struct StreamConfig { pub retention_policy: Option, } -#[derive(ValueEnum, Debug, Clone)] +#[derive(ValueEnum, Debug, Clone, Serialize)] pub enum StorageClass { Unspecified, Standard, Express, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub enum RetentionPolicy { #[allow(dead_code)] Age(Duration), @@ -72,6 +78,16 @@ impl From for s2::types::StorageClass { } } +impl From for StorageClass { + fn from(class: s2::types::StorageClass) -> Self { + match class { + s2::types::StorageClass::Unspecified => StorageClass::Unspecified, + s2::types::StorageClass::Standard => StorageClass::Standard, + s2::types::StorageClass::Express => StorageClass::Express, + } + } +} + impl From for s2::types::RetentionPolicy { fn from(policy: RetentionPolicy) -> Self { match policy { @@ -79,3 +95,29 @@ impl From for s2::types::RetentionPolicy { } } } + +impl From for RetentionPolicy { + fn from(policy: s2::types::RetentionPolicy) -> Self { + match policy { + s2::types::RetentionPolicy::Age(d) => RetentionPolicy::Age(d), + } + } +} + +impl From for BasinConfig { + fn from(config: s2::types::BasinConfig) -> Self { + let default_stream_config = config.default_stream_config.map(|c| c.into()); + BasinConfig { + default_stream_config, + } + } +} + +impl From for StreamConfig { + fn from(config: s2::types::StreamConfig) -> Self { + StreamConfig { + storage_class: Some(config.storage_class.into()), + retention_policy: config.retention_policy.map(|r| r.into()), + } + } +} From ad02f7735c7b06c17b3e89db0d8759fe299ec103 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Wed, 2 Oct 2024 01:50:51 -0400 Subject: [PATCH 20/28] .. --- Cargo.lock | 12 ++++++++++++ Cargo.toml | 1 + src/error.rs | 3 +++ src/main.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++- src/stream.rs | 25 ++++++++++++++++++++++++- src/types.rs | 35 ++++++++++++++++++++++++++++++++++- 6 files changed, 121 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7aef720..0502d02 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -874,6 +874,17 @@ dependencies = [ "serde", ] +[[package]] +name = "jsonl" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abae98f45234fc1980c198798166a80ad6b35eb5b7db4caa7bc72ff919e6b80" +dependencies = [ + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1464,6 +1475,7 @@ dependencies = [ "dialoguer", "dirs", "humantime", + "jsonl", "miette", "s2", "serde", diff --git a/Cargo.toml b/Cargo.toml index 332814e..3c9d212 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } serde_json = "1.0.128" dialoguer = "0.11.0" +jsonl = "4.0.1" diff --git a/src/error.rs b/src/error.rs index 33fd449..c3aff1b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -41,4 +41,7 @@ pub enum S2CliError { #[error(transparent)] InvalidConfig(#[from] serde_json::Error), + + #[error("Failed to record file: {0}")] + RecordFileReadError(String), } diff --git a/src/main.rs b/src/main.rs index 6024f30..fca369f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::{fs::File, io::BufReader, path::PathBuf}; + use account::AccountService; use basin::BasinService; use clap::{builder::styling, Parser, Subcommand}; @@ -206,7 +208,25 @@ enum StreamActions { stream: String, }, - + /// Append a batch of records to a stream. + Append { + /// Name of the basin. + basin: String, + + /// Name of the stream. + stream: String, + + /// Enforce that the sequence number issued to the first record matches. + #[arg(short, long)] + match_seq_num: Option, + + /// Enforce a fencing token which must have been previously set by a `fence` command record. + #[arg(short, long)] + fencing_token: Option, + + /// Path to the file containing the records to append. + file: PathBuf, + }, } fn s2_config(auth_token: String) -> ClientConfig { @@ -406,6 +426,32 @@ async fn run() -> Result<(), S2CliError> { let seq_num = StreamService::new(stream_client).get_next_seq_num().await?; println!("{}", seq_num); } + + StreamActions::Append { + basin, + stream, + match_seq_num, + fencing_token, + file, + } => { + let stream_client = StreamClient::connect(s2_config, basin, stream).await?; + let stream_service = StreamService::new(stream_client); + + let record_file = File::open(file.clone()) + .map_err(|_| S2CliError::RecordFileReadError(file.display().to_string()))?; + + let records = jsonl::read::, Vec>( + BufReader::new(record_file), + ) + .map_err(|_| { + S2CliError::RecordFileReadError("Failed to parse records".to_string()) + })?; + + stream_service + .append(records, match_seq_num, fencing_token) + .await?; + println!("{}", "✓ Records appended successfully".green().bold()); + } } } } diff --git a/src/stream.rs b/src/stream.rs index 4a20c0f..30939c4 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -1,8 +1,11 @@ use s2::{ client::StreamClient, - service_error::{GetNextSeqNumError, ServiceError}, + service_error::{AppendError, GetNextSeqNumError, ServiceError}, + types::AppendInput, }; +use crate::types::AppendRecord; + pub struct StreamService { client: StreamClient, } @@ -11,6 +14,9 @@ pub struct StreamService { pub enum StreamServiceError { #[error("Failed to get next sequence number")] GetNextSeqNumError(#[from] ServiceError), + + #[error("Failed to append records")] + AppendError(#[from] ServiceError), } impl StreamService { @@ -21,4 +27,21 @@ impl StreamService { pub async fn get_next_seq_num(&self) -> Result { Ok(self.client.get_next_seq_num().await?) } + + pub async fn append( + &self, + records: Vec, + match_seq_num: Option, + fencing_token: Option, + ) -> Result<(), StreamServiceError> { + let append_req = AppendInput::builder() + .records(records.into_iter().map(Into::into).collect()) + .match_seq_num(match_seq_num) + .fencing_token(fencing_token.map(|t| t.into())) + .build(); + + self.client.append(append_req).await?; + + Ok(()) + } } diff --git a/src/types.rs b/src/types.rs index 2929fd0..b09c8be 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ //! Types for Basin configuration that directly map to s2::types. use clap::{Parser, ValueEnum}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::time::Duration; pub const STORAGE_CLASS_PATH: &str = "default_stream_config.storage_class"; @@ -121,3 +121,36 @@ impl From for StreamConfig { } } } + +#[derive(Serialize, Deserialize)] +pub struct AppendRecord { + /// Series of name-value pairs for this record. + pub headers: Vec
, + /// Body of the record. + pub body: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct Header { + pub name: Vec, + pub value: Vec, +} + +impl From for s2::types::AppendRecord { + fn from(record: AppendRecord) -> Self { + let headers = record.headers.into_iter().map(|h| h.into()).collect(); + s2::types::AppendRecord::builder() + .headers(headers) + .body(record.body) + .build() + } +} + +impl From
for s2::types::Header { + fn from(header: Header) -> Self { + s2::types::Header::builder() + .name(header.name) + .value(header.value) + .build() + } +} From 564520f6efe60c28dc77e455959bd17e5e7fcbc1 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Wed, 2 Oct 2024 12:10:16 -0400 Subject: [PATCH 21/28] . --- Cargo.lock | 21 ------------ src/account.rs | 57 +++++++++++++++---------------- src/basin.rs | 52 ++++++++++++----------------- src/error.rs | 12 +------ src/main.rs | 91 +++----------------------------------------------- src/stream.rs | 47 -------------------------- src/types.rs | 58 ++++++++------------------------ 7 files changed, 68 insertions(+), 270 deletions(-) delete mode 100644 src/stream.rs diff --git a/Cargo.lock b/Cargo.lock index 0502d02..d66f234 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1461,7 +1461,6 @@ dependencies = [ "thiserror", "tonic", "tonic-build", - "typed-builder", ] [[package]] @@ -1976,26 +1975,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "typed-builder" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e14ed59dc8b7b26cacb2a92bad2e8b1f098806063898ab42a3bd121d7d45e75" -dependencies = [ - "typed-builder-macro", -] - -[[package]] -name = "typed-builder-macro" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "typenum" version = "1.17.0" diff --git a/src/account.rs b/src/account.rs index 7078f01..b36f6ac 100644 --- a/src/account.rs +++ b/src/account.rs @@ -5,7 +5,8 @@ use s2::{ ServiceError, }, types::{ - BasinConfig, BasinMetadata, ListBasinsResponse, RetentionPolicy, StorageClass, StreamConfig, + BasinConfig, BasinMetadata, CreateBasinRequest, DeleteBasinRequest, ListBasinsRequest, + ListBasinsResponse, ReconfigureBasinRequest, RetentionPolicy, StorageClass, StreamConfig, }, }; @@ -42,11 +43,10 @@ impl AccountService { start_after: String, limit: usize, ) -> Result { - let list_basins_req = s2::types::ListBasinsRequest::builder() - .prefix(prefix) - .start_after(start_after) - .limit(limit) - .build(); + let list_basins_req = ListBasinsRequest::new() + .with_prefix(prefix) + .with_start_after(start_after) + .with_limit(limit); self.client .list_basins(list_basins_req) @@ -56,30 +56,28 @@ impl AccountService { pub async fn create_basin( &self, - name: String, + basin: String, storage_class: Option, retention_policy: Option, ) -> Result { let basin_config = match (&storage_class, retention_policy) { (Some(storage_class), Some(retention_policy)) => { - let stream_config = StreamConfig::builder() - .storage_class(*storage_class) - .retention_policy(RetentionPolicy::Age(*retention_policy)) - .build(); + let stream_config = StreamConfig::new() + .with_storage_class(*storage_class) + .with_retention_policy(RetentionPolicy::Age(*retention_policy)); - let basin_config = BasinConfig::builder() - .default_stream_config(Some(stream_config)) - .build(); + let basin_config = BasinConfig::with_default_stream_config(stream_config); Some(basin_config) } _ => None, }; - let create_basin_req = s2::types::CreateBasinRequest::builder() - .basin(name) - .config(basin_config) - .build(); + let mut create_basin_req = CreateBasinRequest::new(basin); + + if let Some(basin_config) = basin_config { + create_basin_req = create_basin_req.with_config(basin_config) + }; self.client .create_basin(create_basin_req) @@ -87,18 +85,17 @@ impl AccountService { .map_err(AccountServiceError::CreateBasin) } - pub async fn delete_basin(&self, name: String) -> Result<(), AccountServiceError> { - let delete_basin_req = s2::types::DeleteBasinRequest::builder().basin(name).build(); + pub async fn delete_basin(&self, basin: String) -> Result<(), AccountServiceError> { + let delete_basin_req = DeleteBasinRequest::new(basin); self.client.delete_basin(delete_basin_req).await?; Ok(()) } - pub async fn get_basin_config(&self, name: String) -> Result { - let get_basin_config_req = s2::types::GetBasinConfigRequest::builder() - .basin(name) - .build(); - - Ok(self.client.get_basin_config(get_basin_config_req).await?) + pub async fn get_basin_config( + &self, + basin: String, + ) -> Result { + Ok(self.client.get_basin_config(basin).await?) } pub async fn reconfigure_basin( @@ -107,11 +104,9 @@ impl AccountService { basin_config: BasinConfig, mask: Vec, ) -> Result<(), AccountServiceError> { - let reconfigure_basin_req = s2::types::ReconfigureBasinRequest::builder() - .basin(basin) - .config(basin_config) - .mask(mask) - .build(); + let reconfigure_basin_req = ReconfigureBasinRequest::new(basin) + .with_config(basin_config) + .with_mask(mask); self.client.reconfigure_basin(reconfigure_basin_req).await?; Ok(()) } diff --git a/src/basin.rs b/src/basin.rs index c9c1fd5..897138c 100644 --- a/src/basin.rs +++ b/src/basin.rs @@ -4,7 +4,10 @@ use s2::{ CreateStreamError, DeleteStreamError, GetStreamConfigError, ListStreamsError, ReconfigureStreamError, ServiceError, }, - types::{ListStreamsResponse, StreamConfig}, + types::{ + CreateStreamRequest, DeleteStreamRequest, ListStreamsRequest, ListStreamsResponse, + ReconfigureStreamRequest, StreamConfig, + }, }; pub struct BasinService { @@ -40,11 +43,10 @@ impl BasinService { start_after: String, limit: usize, ) -> Result, BasinServiceError> { - let list_streams_req = s2::types::ListStreamsRequest::builder() - .prefix(prefix) - .start_after(start_after) - .limit(limit) - .build(); + let list_streams_req = ListStreamsRequest::new() + .with_prefix(prefix) + .with_start_after(start_after) + .with_limit(limit); let ListStreamsResponse { streams, .. } = self.client.list_streams(list_streams_req).await?; @@ -54,26 +56,23 @@ impl BasinService { pub async fn create_stream( &self, - stream_name: String, + stream: String, config: Option, ) -> Result<(), BasinServiceError> { - let create_stream_req = s2::types::CreateStreamRequest::builder() - .stream(stream_name) - .config(config) - .build(); + let mut create_stream_req = CreateStreamRequest::new(stream); - self.client.create_stream(create_stream_req).await?; + if let Some(config) = config { + create_stream_req = create_stream_req.with_config(config); + }; + self.client.create_stream(create_stream_req).await?; Ok(()) } - pub async fn delete_stream(&self, stream_name: String) -> Result<(), BasinServiceError> { - let delete_stream_req = s2::types::DeleteStreamRequest::builder() - .stream(stream_name) - .build(); - - self.client.delete_stream(delete_stream_req).await?; - + pub async fn delete_stream(&self, stream: String) -> Result<(), BasinServiceError> { + self.client + .delete_stream(DeleteStreamRequest::new(stream)) + .await?; Ok(()) } @@ -81,11 +80,7 @@ impl BasinService { &self, stream: String, ) -> Result { - let get_stream_config_req = s2::types::GetStreamConfigRequest::builder() - .stream(stream) - .build(); - - Ok(self.client.get_stream_config(get_stream_config_req).await?) + Ok(self.client.get_stream_config(stream).await?) } pub async fn reconfigure_stream( @@ -94,16 +89,13 @@ impl BasinService { config: StreamConfig, mask: Vec, ) -> Result<(), BasinServiceError> { - let reconfigure_stream_req = s2::types::ReconfigureStreamRequest::builder() - .stream(stream) - .config(config) - .mask(mask) - .build(); + let reconfigure_stream_req = ReconfigureStreamRequest::new(stream) + .with_config(config) + .with_mask(mask); self.client .reconfigure_stream(reconfigure_stream_req) .await?; - Ok(()) } } diff --git a/src/error.rs b/src/error.rs index c3aff1b..c2e9e23 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,10 +2,7 @@ use miette::Diagnostic; use s2::client::ClientError; use thiserror::Error; -use crate::{ - account::AccountServiceError, basin::BasinServiceError, config::S2ConfigError, - stream::StreamServiceError, -}; +use crate::{account::AccountServiceError, basin::BasinServiceError, config::S2ConfigError}; const HELP: &str = color_print::cstr!( "\nNotice something wrong?\n\n\ @@ -35,13 +32,6 @@ pub enum S2CliError { #[diagnostic(help("{}", HELP))] BasinService(#[from] BasinServiceError), - #[error(transparent)] - #[diagnostic(help("{}", HELP))] - StreamService(#[from] StreamServiceError), - #[error(transparent)] InvalidConfig(#[from] serde_json::Error), - - #[error("Failed to record file: {0}")] - RecordFileReadError(String), } diff --git a/src/main.rs b/src/main.rs index fca369f..500aec5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -use std::{fs::File, io::BufReader, path::PathBuf}; - use account::AccountService; use basin::BasinService; use clap::{builder::styling, Parser, Subcommand}; @@ -7,18 +5,17 @@ use colored::*; use config::{config_path, create_config}; use error::S2CliError; use s2::{ - client::{BasinClient, Client, ClientConfig, HostCloud, StreamClient}, + client::{BasinClient, Client, ClientConfig, HostCloud}, types::{BasinMetadata, StorageClass}, }; -use stream::StreamService; use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt}; use types::{BasinConfig, StreamConfig, RETENTION_POLICY_PATH, STORAGE_CLASS_PATH}; mod account; mod basin; + mod config; mod error; -mod stream; mod types; const STYLES: styling::Styles = styling::Styles::styled() @@ -60,12 +57,6 @@ enum Commands { #[command(subcommand)] action: BasinActions, }, - - /// Manage s2 streams - Stream { - #[command(subcommand)] - action: StreamActions, - }, } #[derive(Subcommand, Debug)] @@ -197,44 +188,10 @@ enum BasinActions { }, } -#[derive(Subcommand, Debug)] -enum StreamActions { - /// Get the next sequence number that will be assigned by a stream. - GetNextSeqNum { - /// Name of the basin to get the next sequence number from. - basin: String, - - /// Name of the stream to get the next sequence number for. - stream: String, - }, - - /// Append a batch of records to a stream. - Append { - /// Name of the basin. - basin: String, - - /// Name of the stream. - stream: String, - - /// Enforce that the sequence number issued to the first record matches. - #[arg(short, long)] - match_seq_num: Option, - - /// Enforce a fencing token which must have been previously set by a `fence` command record. - #[arg(short, long)] - fencing_token: Option, - - /// Path to the file containing the records to append. - file: PathBuf, - }, -} - fn s2_config(auth_token: String) -> ClientConfig { - ClientConfig::builder() - .host_uri(HostCloud::Local) - .token(auth_token.to_string()) - .connection_timeout(std::time::Duration::from_secs(5)) - .build() + ClientConfig::new(auth_token.to_string()) + .with_host_uri(HostCloud::Local) + .with_connection_timeout(std::time::Duration::from_secs(5)) } #[tokio::main] @@ -417,44 +374,6 @@ async fn run() -> Result<(), S2CliError> { } } } - Commands::Stream { action } => { - let cfg = config::load_config(&config_path)?; - let s2_config = s2_config(cfg.auth_token); - match action { - StreamActions::GetNextSeqNum { basin, stream } => { - let stream_client = StreamClient::connect(s2_config, basin, stream).await?; - let seq_num = StreamService::new(stream_client).get_next_seq_num().await?; - println!("{}", seq_num); - } - - StreamActions::Append { - basin, - stream, - match_seq_num, - fencing_token, - file, - } => { - let stream_client = StreamClient::connect(s2_config, basin, stream).await?; - let stream_service = StreamService::new(stream_client); - - let record_file = File::open(file.clone()) - .map_err(|_| S2CliError::RecordFileReadError(file.display().to_string()))?; - - let records = jsonl::read::, Vec>( - BufReader::new(record_file), - ) - .map_err(|_| { - S2CliError::RecordFileReadError("Failed to parse records".to_string()) - })?; - - stream_service - .append(records, match_seq_num, fencing_token) - .await?; - println!("{}", "✓ Records appended successfully".green().bold()); - } - } - } } - Ok(()) } diff --git a/src/stream.rs b/src/stream.rs deleted file mode 100644 index 30939c4..0000000 --- a/src/stream.rs +++ /dev/null @@ -1,47 +0,0 @@ -use s2::{ - client::StreamClient, - service_error::{AppendError, GetNextSeqNumError, ServiceError}, - types::AppendInput, -}; - -use crate::types::AppendRecord; - -pub struct StreamService { - client: StreamClient, -} - -#[derive(Debug, thiserror::Error)] -pub enum StreamServiceError { - #[error("Failed to get next sequence number")] - GetNextSeqNumError(#[from] ServiceError), - - #[error("Failed to append records")] - AppendError(#[from] ServiceError), -} - -impl StreamService { - pub fn new(client: StreamClient) -> Self { - Self { client } - } - - pub async fn get_next_seq_num(&self) -> Result { - Ok(self.client.get_next_seq_num().await?) - } - - pub async fn append( - &self, - records: Vec, - match_seq_num: Option, - fencing_token: Option, - ) -> Result<(), StreamServiceError> { - let append_req = AppendInput::builder() - .records(records.into_iter().map(Into::into).collect()) - .match_seq_num(match_seq_num) - .fencing_token(fencing_token.map(|t| t.into())) - .build(); - - self.client.append(append_req).await?; - - Ok(()) - } -} diff --git a/src/types.rs b/src/types.rs index b09c8be..8585431 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,7 +1,7 @@ //! Types for Basin configuration that directly map to s2::types. use clap::{Parser, ValueEnum}; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use std::time::Duration; pub const STORAGE_CLASS_PATH: &str = "default_stream_config.storage_class"; @@ -47,10 +47,11 @@ impl From<&str> for RetentionPolicy { impl From for s2::types::BasinConfig { fn from(config: BasinConfig) -> Self { - let default_stream_config = config.default_stream_config.map(|c| c.into()); - s2::types::BasinConfig::builder() - .default_stream_config(default_stream_config) - .build() + if let Some(default_stream_config) = config.default_stream_config.map(|c| c.into()) { + s2::types::BasinConfig::with_default_stream_config(default_stream_config) + } else { + s2::types::BasinConfig::default() + } } } @@ -61,10 +62,13 @@ impl From for s2::types::StreamConfig { .map(s2::types::StorageClass::from) .unwrap_or(s2::types::StorageClass::Unspecified); let retention_policy = config.retention_policy.map(|r| r.into()); - s2::types::StreamConfig::builder() - .storage_class(storage_class) - .retention_policy(retention_policy) - .build() + let stream_config = s2::types::StreamConfig::new().with_storage_class(storage_class); + + if let Some(retention_policy) = retention_policy { + stream_config.with_retention_policy(retention_policy) + } else { + stream_config + } } } @@ -106,9 +110,8 @@ impl From for RetentionPolicy { impl From for BasinConfig { fn from(config: s2::types::BasinConfig) -> Self { - let default_stream_config = config.default_stream_config.map(|c| c.into()); BasinConfig { - default_stream_config, + default_stream_config: config.default_stream_config.map(Into::into), } } } @@ -121,36 +124,3 @@ impl From for StreamConfig { } } } - -#[derive(Serialize, Deserialize)] -pub struct AppendRecord { - /// Series of name-value pairs for this record. - pub headers: Vec
, - /// Body of the record. - pub body: Vec, -} - -#[derive(Serialize, Deserialize)] -pub struct Header { - pub name: Vec, - pub value: Vec, -} - -impl From for s2::types::AppendRecord { - fn from(record: AppendRecord) -> Self { - let headers = record.headers.into_iter().map(|h| h.into()).collect(); - s2::types::AppendRecord::builder() - .headers(headers) - .body(record.body) - .build() - } -} - -impl From
for s2::types::Header { - fn from(header: Header) -> Self { - s2::types::Header::builder() - .name(header.name) - .value(header.value) - .build() - } -} From 3d478b69ae04c8fb69d7fecdb5e74cc2dff6c4b0 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Wed, 2 Oct 2024 12:12:29 -0400 Subject: [PATCH 22/28] .. --- Cargo.lock | 52 +--------------------------------------------------- Cargo.toml | 4 +--- 2 files changed, 2 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d66f234..3e522f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,19 +359,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "console" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.52.0", -] - [[package]] name = "const-random" version = "0.1.18" @@ -426,19 +413,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "dialoguer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" -dependencies = [ - "console", - "shell-words", - "tempfile", - "thiserror", - "zeroize", -] - [[package]] name = "digest" version = "0.10.7" @@ -485,12 +459,6 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - [[package]] name = "equivalent" version = "1.0.1" @@ -874,17 +842,6 @@ dependencies = [ "serde", ] -[[package]] -name = "jsonl" -version = "4.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abae98f45234fc1980c198798166a80ad6b35eb5b7db4caa7bc72ff919e6b80" -dependencies = [ - "serde", - "serde_json", - "thiserror", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1451,6 +1408,7 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "s2" version = "0.1.0" +source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=vrongmeal/stream-service#b7dbf80e93428e5fd35a0af00a9c531b20140806" dependencies = [ "backon", "futures", @@ -1471,10 +1429,8 @@ dependencies = [ "color-print", "colored", "config", - "dialoguer", "dirs", "humantime", - "jsonl", "miette", "s2", "serde", @@ -1562,12 +1518,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shell-words" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" - [[package]] name = "signal-hook-registry" version = "1.4.2" diff --git a/Cargo.toml b/Cargo.toml index 3c9d212..9952f03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ dirs = "5.0.1" serde = { version = "1.0.210", features = ["derive"] } thiserror = "1.0.63" toml = "0.8.19" -s2 = { path = "../../s2.rs" } +s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "vrongmeal/stream-service" } tokio = { version = "*", features = ["full"] } humantime = "2.1.0" miette = { version = "7.2.0", features = ["fancy"] } @@ -19,5 +19,3 @@ color-print = "0.3.6" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } serde_json = "1.0.128" -dialoguer = "0.11.0" -jsonl = "4.0.1" From 61d88bd079ef18ecb3fc1bea4faae6abcd50ce98 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Wed, 2 Oct 2024 12:14:48 -0400 Subject: [PATCH 23/28] . --- .github/workflows/ci.yml | 49 ---------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 7dfdf17..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: CI -permissions: - contents: read -'on': - pull_request: null - push: - branches: - - main -env: - RUST_BACKTRACE: 1 - CARGO_TERM_COLOR: always - CLICOLOR: 1 - CARGO_INCREMENTAL: 0 -concurrency: - group: '${{ github.workflow }}-${{ github.ref }}' - cancel-in-progress: true -jobs: - ci: - permissions: - contents: none - name: CI - needs: lint - runs-on: ubuntu-latest - if: always() - steps: - - name: Failed - run: exit 1 - if: >- - contains(needs.*.result, 'failure') || contains(needs.*.result, - 'cancelled') || contains(needs.*.result, 'skipped') - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Protoc - uses: arduino/setup-protoc@v3 - - name: Install Rust - uses: actions-rust-lang/setup-rust-toolchain@v1 - with: - components: 'clippy, rustfmt' - toolchain: stable - target: x86_64-unknown-linux-gnu - - name: Check format - run: cargo fmt --all -- --check - - name: Check clippy - run: >- - cargo clippy --workspace --all-features --all-targets -- -D warnings - --allow deprecated From 3b872aa3454278cf089508e47be5429285af16a9 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Wed, 2 Oct 2024 12:29:41 -0400 Subject: [PATCH 24/28] .. --- src/basin.rs | 101 ---------------------------------- src/error.rs | 6 +-- src/main.rs | 150 +-------------------------------------------------- 3 files changed, 3 insertions(+), 254 deletions(-) delete mode 100644 src/basin.rs diff --git a/src/basin.rs b/src/basin.rs deleted file mode 100644 index 897138c..0000000 --- a/src/basin.rs +++ /dev/null @@ -1,101 +0,0 @@ -use s2::{ - client::BasinClient, - service_error::{ - CreateStreamError, DeleteStreamError, GetStreamConfigError, ListStreamsError, - ReconfigureStreamError, ServiceError, - }, - types::{ - CreateStreamRequest, DeleteStreamRequest, ListStreamsRequest, ListStreamsResponse, - ReconfigureStreamRequest, StreamConfig, - }, -}; - -pub struct BasinService { - client: BasinClient, -} - -#[derive(Debug, thiserror::Error)] -pub enum BasinServiceError { - #[error("Failed to list streams: {0}")] - ListStreams(#[from] ServiceError), - - #[error("Failed to create stream")] - CreateStream(#[from] ServiceError), - - #[error("Failed to delete stream")] - DeleteStream(#[from] ServiceError), - - #[error("Failed to get stream config")] - GetStreamConfig(#[from] ServiceError), - - #[error("Failed to reconfigure stream")] - ReconfigureStream(#[from] ServiceError), -} - -impl BasinService { - pub fn new(client: BasinClient) -> Self { - Self { client } - } - - pub async fn list_streams( - &self, - prefix: String, - start_after: String, - limit: usize, - ) -> Result, BasinServiceError> { - let list_streams_req = ListStreamsRequest::new() - .with_prefix(prefix) - .with_start_after(start_after) - .with_limit(limit); - - let ListStreamsResponse { streams, .. } = - self.client.list_streams(list_streams_req).await?; - - Ok(streams) - } - - pub async fn create_stream( - &self, - stream: String, - config: Option, - ) -> Result<(), BasinServiceError> { - let mut create_stream_req = CreateStreamRequest::new(stream); - - if let Some(config) = config { - create_stream_req = create_stream_req.with_config(config); - }; - - self.client.create_stream(create_stream_req).await?; - Ok(()) - } - - pub async fn delete_stream(&self, stream: String) -> Result<(), BasinServiceError> { - self.client - .delete_stream(DeleteStreamRequest::new(stream)) - .await?; - Ok(()) - } - - pub async fn get_stream_config( - &self, - stream: String, - ) -> Result { - Ok(self.client.get_stream_config(stream).await?) - } - - pub async fn reconfigure_stream( - &self, - stream: String, - config: StreamConfig, - mask: Vec, - ) -> Result<(), BasinServiceError> { - let reconfigure_stream_req = ReconfigureStreamRequest::new(stream) - .with_config(config) - .with_mask(mask); - - self.client - .reconfigure_stream(reconfigure_stream_req) - .await?; - Ok(()) - } -} diff --git a/src/error.rs b/src/error.rs index c2e9e23..d3d689b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use miette::Diagnostic; use s2::client::ClientError; use thiserror::Error; -use crate::{account::AccountServiceError, basin::BasinServiceError, config::S2ConfigError}; +use crate::{account::AccountServiceError, config::S2ConfigError}; const HELP: &str = color_print::cstr!( "\nNotice something wrong?\n\n\ @@ -28,10 +28,6 @@ pub enum S2CliError { #[diagnostic(help("{}", HELP))] AccountService(#[from] AccountServiceError), - #[error(transparent)] - #[diagnostic(help("{}", HELP))] - BasinService(#[from] BasinServiceError), - #[error(transparent)] InvalidConfig(#[from] serde_json::Error), } diff --git a/src/main.rs b/src/main.rs index 500aec5..5b3aa8c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,18 +1,16 @@ use account::AccountService; -use basin::BasinService; use clap::{builder::styling, Parser, Subcommand}; use colored::*; use config::{config_path, create_config}; use error::S2CliError; use s2::{ - client::{BasinClient, Client, ClientConfig, HostCloud}, + client::{Client, ClientConfig, HostCloud}, types::{BasinMetadata, StorageClass}, }; use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt}; -use types::{BasinConfig, StreamConfig, RETENTION_POLICY_PATH, STORAGE_CLASS_PATH}; +use types::{BasinConfig, RETENTION_POLICY_PATH, STORAGE_CLASS_PATH}; mod account; -mod basin; mod config; mod error; @@ -51,12 +49,6 @@ enum Commands { #[command(subcommand)] action: AccountActions, }, - - /// Manage s2 basins - Basin { - #[command(subcommand)] - action: BasinActions, - }, } #[derive(Subcommand, Debug)] @@ -123,71 +115,6 @@ enum AccountActions { }, } -#[derive(Subcommand, Debug)] -enum BasinActions { - /// List Streams - ListStreams { - /// Name of the basin to list streams from. - basin: String, - - /// List stream names that begin with this prefix. - #[arg(short, long)] - prefix: String, - - /// List stream names that lexicographically start after this name. - #[arg(short, long)] - start_after: String, - - /// Number of results, upto a maximum of 1000. - #[arg(short, long)] - limit: u32, - }, - - /// Create a stream - CreateStream { - /// Name of the basin to create a stream in. - basin: String, - - /// Name of the stream to create. - stream: String, - - /// Configuration to apply. - #[command(flatten)] - config: Option, - }, - - /// Delete a stream - DeleteStream { - /// Name of the basin to delete a stream from. - basin: String, - - /// Name of the stream to delete. - stream: String, - }, - - /// Get stream config - GetStreamConfig { - /// Name of the basin to get stream config from. - basin: String, - - /// Name of the stream to get config for. - stream: String, - }, - - /// Reconfigure a stream - ReconfigureStream { - /// Name of the basin to reconfigure a stream in. - basin: String, - - /// Name of the stream to reconfigure. - stream: String, - - /// Configuration to apply. - #[command(flatten)] - config: StreamConfig, - }, -} - fn s2_config(auth_token: String) -> ClientConfig { ClientConfig::new(auth_token.to_string()) .with_host_uri(HostCloud::Local) @@ -301,79 +228,6 @@ async fn run() -> Result<(), S2CliError> { } } } - - Commands::Basin { action } => { - let cfg = config::load_config(&config_path)?; - let basin_config = s2_config(cfg.auth_token); - match action { - BasinActions::ListStreams { - basin, - prefix, - start_after, - limit, - } => { - let basin_client = BasinClient::connect(basin_config, basin).await?; - let streams = BasinService::new(basin_client) - .list_streams(prefix, start_after, limit as usize) - .await?; - for stream in streams { - println!("{}", stream); - } - } - - BasinActions::CreateStream { - basin, - stream, - config, - } => { - let basin_client = BasinClient::connect(basin_config, basin).await?; - BasinService::new(basin_client) - .create_stream(stream, config.map(Into::into)) - .await?; - println!("{}", "✓ Stream created successfully".green().bold()); - } - - BasinActions::DeleteStream { basin, stream } => { - let basin_client = BasinClient::connect(basin_config, basin).await?; - BasinService::new(basin_client) - .delete_stream(stream) - .await?; - println!("{}", "✓ Stream deleted successfully".green().bold()); - } - - BasinActions::GetStreamConfig { basin, stream } => { - let basin_client = BasinClient::connect(basin_config, basin).await?; - let config = BasinService::new(basin_client) - .get_stream_config(stream) - .await?; - let config: StreamConfig = config.into(); - println!("{:?}", serde_json::to_string_pretty(&config)?); - } - - BasinActions::ReconfigureStream { - basin, - stream, - config, - } => { - let basin_client = BasinClient::connect(basin_config, basin).await?; - let mut mask = Vec::new(); - - if config.storage_class.is_some() { - mask.push("storage_class".to_string()); - }; - - if config.retention_policy.is_some() { - mask.push("retention_policy".to_string()); - }; - - BasinService::new(basin_client) - .reconfigure_stream(stream, config.into(), mask) - .await?; - - println!("{}", "✓ Stream reconfigured successfully".green().bold()); - } - } - } } Ok(()) } From b5d35a16182c57a9fe53fe47e14a1f3f3456cbd9 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Thu, 3 Oct 2024 09:54:08 -0400 Subject: [PATCH 25/28] .. --- src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5b3aa8c..7bc8084 100644 --- a/src/main.rs +++ b/src/main.rs @@ -146,8 +146,8 @@ async fn run() -> Result<(), S2CliError> { Commands::Config { action } => match action { ConfigActions::Set { auth_token } => { create_config(&config_path, auth_token)?; - println!("{}", "✓ Token set successfully".green().bold()); - println!( + eprintln!("{}", "✓ Token set successfully".green().bold()); + eprintln!( " Configuration saved to: {}", config_path.display().to_string().cyan() ); @@ -193,12 +193,12 @@ async fn run() -> Result<(), S2CliError> { .create_basin(basin, storage_class, retention_policy) .await?; - println!("{}", "✓ Basin created successfully".green().bold()); + eprintln!("{}", "✓ Basin created successfully".green().bold()); } AccountActions::DeleteBasin { basin } => { account_service.delete_basin(basin).await?; - println!("{}", "✓ Basin deleted successfully".green().bold()); + eprintln!("{}", "✓ Basin deleted successfully".green().bold()); } AccountActions::GetBasinConfig { basin } => { From 3bcddfecd45bd78c3d4e39d9f4cc8e067e3afc49 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Thu, 3 Oct 2024 11:34:50 -0400 Subject: [PATCH 26/28] .. --- Cargo.lock | 81 ++++++++++++++++++++++++++++---------------------- src/account.rs | 30 ++++++++----------- src/error.rs | 16 ++++++---- src/main.rs | 30 +++++++++---------- 4 files changed, 83 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e522f0..c2e2fe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,9 +83,9 @@ checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -264,9 +264,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.5.18" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" dependencies = [ "clap_builder", "clap_derive", @@ -274,9 +274,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.18" +version = "4.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" dependencies = [ "anstream", "anstyle", @@ -633,7 +633,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -654,9 +654,9 @@ checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "heck" @@ -706,9 +706,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -787,12 +787,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -1014,9 +1014,12 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] [[package]] name = "option-ext" @@ -1133,7 +1136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.6.0", ] [[package]] @@ -1168,6 +1171,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -1290,9 +1299,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags", ] @@ -1310,14 +1319,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -1331,13 +1340,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -1348,9 +1357,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "ron" @@ -1408,7 +1417,7 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "s2" version = "0.1.0" -source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=vrongmeal/stream-service#b7dbf80e93428e5fd35a0af00a9c531b20140806" +source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=vrongmeal/stream-service#b17a9bddcc76d928d2189068b4368ac5fc8b8dbf" dependencies = [ "backon", "futures", @@ -1610,9 +1619,9 @@ checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -1761,7 +1770,7 @@ version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_spanned", "toml_datetime", @@ -1933,9 +1942,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" diff --git a/src/account.rs b/src/account.rs index b36f6ac..c0354de 100644 --- a/src/account.rs +++ b/src/account.rs @@ -6,7 +6,7 @@ use s2::{ }, types::{ BasinConfig, BasinMetadata, CreateBasinRequest, DeleteBasinRequest, ListBasinsRequest, - ListBasinsResponse, ReconfigureBasinRequest, RetentionPolicy, StorageClass, StreamConfig, + ListBasinsResponse, ReconfigureBasinRequest, StreamConfig, }, }; @@ -57,27 +57,21 @@ impl AccountService { pub async fn create_basin( &self, basin: String, - storage_class: Option, - retention_policy: Option, + storage_class: Option, + retention_policy: Option, ) -> Result { - let basin_config = match (&storage_class, retention_policy) { - (Some(storage_class), Some(retention_policy)) => { - let stream_config = StreamConfig::new() - .with_storage_class(*storage_class) - .with_retention_policy(RetentionPolicy::Age(*retention_policy)); + let mut stream_config = StreamConfig::new(); - let basin_config = BasinConfig::with_default_stream_config(stream_config); + if let Some(storage_class) = storage_class { + stream_config = stream_config.with_storage_class(storage_class); + } - Some(basin_config) - } - _ => None, - }; + if let Some(retention_policy) = retention_policy { + stream_config = stream_config.with_retention_policy(retention_policy.into()); + } - let mut create_basin_req = CreateBasinRequest::new(basin); - - if let Some(basin_config) = basin_config { - create_basin_req = create_basin_req.with_config(basin_config) - }; + let create_basin_req = CreateBasinRequest::new(basin) + .with_config(BasinConfig::with_default_stream_config(stream_config)); self.client .create_basin(create_basin_req) diff --git a/src/error.rs b/src/error.rs index d3d689b..88b32d5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,12 +6,17 @@ use crate::{account::AccountServiceError, config::S2ConfigError}; const HELP: &str = color_print::cstr!( "\nNotice something wrong?\n\n\ - ► Open an issue:\n\ + > Open an issue:\n\ https://github.com/s2-cli/issues\n\n\ - ► Reach out to us:\n\ - hi@s2.dev\n\n\ - ► Join our community:\n\ - Discord: https://discord.gg/s2" + > Reach out to us:\n\ + hi@s2.dev" +); + +const BUG_HELP: &str = color_print::cstr!( + "\nLooks like you may have encountered a bug!\n\n\ + > Report this issue here: \n\ + https://github.com/s2-cli/issues +" ); #[derive(Error, Debug, Diagnostic)] @@ -29,5 +34,6 @@ pub enum S2CliError { AccountService(#[from] AccountServiceError), #[error(transparent)] + #[diagnostic(help("{}", BUG_HELP))] InvalidConfig(#[from] serde_json::Error), } diff --git a/src/main.rs b/src/main.rs index 7bc8084..e051f75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use config::{config_path, create_config}; use error::S2CliError; use s2::{ client::{Client, ClientConfig, HostCloud}, - types::{BasinMetadata, StorageClass}, + types::BasinMetadata, }; use tracing_subscriber::{fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt}; use types::{BasinConfig, RETENTION_POLICY_PATH, STORAGE_CLASS_PATH}; @@ -24,7 +24,7 @@ const STYLES: styling::Styles = styling::Styles::styled() const GENERAL_USAGE: &str = color_print::cstr!( r#" - $ s2-cli config set --token ... + $ s2-cli config set --auth-token ... $ s2-cli account list-basins --prefix "bar" --start-after "foo" --limit 100 "# ); @@ -83,13 +83,8 @@ enum AccountActions { /// Basin name, which must be globally unique. basin: String, - /// Storage class for recent writes. - #[arg(short, long)] - storage_class: Option, - - /// Age threshold of oldest records in the stream, which can be automatically trimmed. - #[arg(short, long)] - retention_policy: Option, + #[command(flatten)] + config: BasinConfig, }, /// Delete a basin @@ -123,6 +118,7 @@ fn s2_config(auth_token: String) -> ClientConfig { #[tokio::main] async fn main() -> miette::Result<()> { + miette::set_panic_hook(); run().await?; Ok(()) } @@ -184,11 +180,15 @@ async fn run() -> Result<(), S2CliError> { } } - AccountActions::CreateBasin { - basin, - storage_class, - retention_policy, - } => { + AccountActions::CreateBasin { basin, config } => { + let (storage_class, retention_policy) = match &config.default_stream_config { + Some(config) => { + let storage_class = config.storage_class.clone(); + let retention_policy = config.retention_policy.clone(); + (storage_class, retention_policy) + } + None => (None, None), + }; account_service .create_basin(basin, storage_class, retention_policy) .await?; @@ -204,7 +204,7 @@ async fn run() -> Result<(), S2CliError> { AccountActions::GetBasinConfig { basin } => { let basin_config = account_service.get_basin_config(basin).await?; let basin_config: BasinConfig = basin_config.into(); - println!("{:?}", serde_json::to_string_pretty(&basin_config)?); + println!("{}", serde_json::to_string_pretty(&basin_config)?); } AccountActions::ReconfigureBasin { basin, config } => { From 995514b4da899c5b054a9539a002be79e7ce6e39 Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Thu, 3 Oct 2024 11:42:27 -0400 Subject: [PATCH 27/28] .. --- src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index aed02c7..ac37df5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -65,7 +65,7 @@ pub enum S2ConfigError { #[error("Failed to load config file")] #[diagnostic(help( - "Did you run `s2 config set`? or use `S2_AUTH_TOKEN` environment variable." + "Did you run `s2-cli config set`? or use `S2_AUTH_TOKEN` environment variable." ))] LoadError, From 4ecfad93b63c6774b9ac3c76135922dd8036882a Mon Sep 17 00:00:00 2001 From: Mehul Arora Date: Thu, 3 Oct 2024 12:13:46 -0400 Subject: [PATCH 28/28] update --- Cargo.lock | 34 ++++++++++++++++---------------- Cargo.toml | 2 +- src/account.rs | 2 +- src/error.rs | 2 +- src/main.rs | 6 +++--- src/types.rs | 53 +++++++++++++++++++++++++------------------------- 6 files changed, 50 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c2e2fe0..53e0056 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1414,22 +1414,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "s2" -version = "0.1.0" -source = "git+ssh://git@github.com/s2-streamstore/s2.rs.git?branch=vrongmeal/stream-service#b17a9bddcc76d928d2189068b4368ac5fc8b8dbf" -dependencies = [ - "backon", - "futures", - "http", - "prost", - "prost-types", - "secrecy", - "thiserror", - "tonic", - "tonic-build", -] - [[package]] name = "s2-cli" version = "0.1.0" @@ -1441,9 +1425,9 @@ dependencies = [ "dirs", "humantime", "miette", - "s2", "serde", "serde_json", + "streamstore", "thiserror", "tokio", "toml", @@ -1567,6 +1551,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "streamstore" +version = "0.1.0" +source = "git+ssh://git@github.com/s2-streamstore/s2-rust-sdk.git?branch=vrongmeal/stream-service#b6907833a7a8dce0db8e1d7538d4d432effd8f47" +dependencies = [ + "backon", + "futures", + "http", + "prost", + "prost-types", + "secrecy", + "thiserror", + "tonic", + "tonic-build", +] + [[package]] name = "strsim" version = "0.11.1" diff --git a/Cargo.toml b/Cargo.toml index 9952f03..4b1b061 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ dirs = "5.0.1" serde = { version = "1.0.210", features = ["derive"] } thiserror = "1.0.63" toml = "0.8.19" -s2 = { git = "ssh://git@github.com/s2-streamstore/s2.rs.git", branch = "vrongmeal/stream-service" } +streamstore = { git = "ssh://git@github.com/s2-streamstore/s2-rust-sdk.git", branch = "vrongmeal/stream-service" } tokio = { version = "*", features = ["full"] } humantime = "2.1.0" miette = { version = "7.2.0", features = ["fancy"] } diff --git a/src/account.rs b/src/account.rs index c0354de..d1cdcdf 100644 --- a/src/account.rs +++ b/src/account.rs @@ -1,4 +1,4 @@ -use s2::{ +use streamstore::{ client::Client, service_error::{ CreateBasinError, DeleteBasinError, GetBasinConfigError, ReconfigureBasinError, diff --git a/src/error.rs b/src/error.rs index 88b32d5..0fcba66 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,5 @@ use miette::Diagnostic; -use s2::client::ClientError; +use streamstore::client::ClientError; use thiserror::Error; use crate::{account::AccountServiceError, config::S2ConfigError}; diff --git a/src/main.rs b/src/main.rs index e051f75..4705177 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use clap::{builder::styling, Parser, Subcommand}; use colored::*; use config::{config_path, create_config}; use error::S2CliError; -use s2::{ +use streamstore::{ client::{Client, ClientConfig, HostCloud}, types::BasinMetadata, }; @@ -172,8 +172,8 @@ async fn run() -> Result<(), S2CliError> { let BasinMetadata { name, state, .. } = basin_metadata; let state = match state { - s2::types::BasinState::Active => state.to_string().green(), - s2::types::BasinState::Deleting => state.to_string().red(), + streamstore::types::BasinState::Active => state.to_string().green(), + streamstore::types::BasinState::Deleting => state.to_string().red(), _ => state.to_string().yellow(), }; println!("{} {}", name, state); diff --git a/src/types.rs b/src/types.rs index 8585431..1d79e3c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,4 @@ -//! Types for Basin configuration that directly map to s2::types. +//! Types for Basin configuration that directly map to streamstore::types. use clap::{Parser, ValueEnum}; use serde::Serialize; @@ -45,24 +45,25 @@ impl From<&str> for RetentionPolicy { } } -impl From for s2::types::BasinConfig { +impl From for streamstore::types::BasinConfig { fn from(config: BasinConfig) -> Self { if let Some(default_stream_config) = config.default_stream_config.map(|c| c.into()) { - s2::types::BasinConfig::with_default_stream_config(default_stream_config) + streamstore::types::BasinConfig::with_default_stream_config(default_stream_config) } else { - s2::types::BasinConfig::default() + streamstore::types::BasinConfig::default() } } } -impl From for s2::types::StreamConfig { +impl From for streamstore::types::StreamConfig { fn from(config: StreamConfig) -> Self { let storage_class = config .storage_class - .map(s2::types::StorageClass::from) - .unwrap_or(s2::types::StorageClass::Unspecified); + .map(streamstore::types::StorageClass::from) + .unwrap_or(streamstore::types::StorageClass::Unspecified); let retention_policy = config.retention_policy.map(|r| r.into()); - let stream_config = s2::types::StreamConfig::new().with_storage_class(storage_class); + let stream_config = + streamstore::types::StreamConfig::new().with_storage_class(storage_class); if let Some(retention_policy) = retention_policy { stream_config.with_retention_policy(retention_policy) @@ -72,52 +73,52 @@ impl From for s2::types::StreamConfig { } } -impl From for s2::types::StorageClass { +impl From for streamstore::types::StorageClass { fn from(class: StorageClass) -> Self { match class { - StorageClass::Unspecified => s2::types::StorageClass::Unspecified, - StorageClass::Standard => s2::types::StorageClass::Standard, - StorageClass::Express => s2::types::StorageClass::Express, + StorageClass::Unspecified => streamstore::types::StorageClass::Unspecified, + StorageClass::Standard => streamstore::types::StorageClass::Standard, + StorageClass::Express => streamstore::types::StorageClass::Express, } } } -impl From for StorageClass { - fn from(class: s2::types::StorageClass) -> Self { +impl From for StorageClass { + fn from(class: streamstore::types::StorageClass) -> Self { match class { - s2::types::StorageClass::Unspecified => StorageClass::Unspecified, - s2::types::StorageClass::Standard => StorageClass::Standard, - s2::types::StorageClass::Express => StorageClass::Express, + streamstore::types::StorageClass::Unspecified => StorageClass::Unspecified, + streamstore::types::StorageClass::Standard => StorageClass::Standard, + streamstore::types::StorageClass::Express => StorageClass::Express, } } } -impl From for s2::types::RetentionPolicy { +impl From for streamstore::types::RetentionPolicy { fn from(policy: RetentionPolicy) -> Self { match policy { - RetentionPolicy::Age(d) => s2::types::RetentionPolicy::Age(d), + RetentionPolicy::Age(d) => streamstore::types::RetentionPolicy::Age(d), } } } -impl From for RetentionPolicy { - fn from(policy: s2::types::RetentionPolicy) -> Self { +impl From for RetentionPolicy { + fn from(policy: streamstore::types::RetentionPolicy) -> Self { match policy { - s2::types::RetentionPolicy::Age(d) => RetentionPolicy::Age(d), + streamstore::types::RetentionPolicy::Age(d) => RetentionPolicy::Age(d), } } } -impl From for BasinConfig { - fn from(config: s2::types::BasinConfig) -> Self { +impl From for BasinConfig { + fn from(config: streamstore::types::BasinConfig) -> Self { BasinConfig { default_stream_config: config.default_stream_config.map(Into::into), } } } -impl From for StreamConfig { - fn from(config: s2::types::StreamConfig) -> Self { +impl From for StreamConfig { + fn from(config: streamstore::types::StreamConfig) -> Self { StreamConfig { storage_class: Some(config.storage_class.into()), retention_policy: config.retention_policy.map(|r| r.into()),