diff --git a/.config/nextest.toml b/.config/nextest.toml
new file mode 100644
index 0000000..a747759
--- /dev/null
+++ b/.config/nextest.toml
@@ -0,0 +1,126 @@
+[profile.default]
+default-filter = "all()"
+
+retries = 2
+test-threads = "num-cpus"
+
+status-level = "retry"
+final-status-level = "pass"
+failure-output = "immediate"
+
+[test-groups]
+# Read-only tests that can run with high parallelism
+read_only = { max-threads = 16 }
+# Tests that create isolated virtual hosts
+isolated_vhosts = { max-threads = 8 }
+# Tests that modify users/permissions (global state)
+user_management = { max-threads = 1 }
+# Tests that modify virtual hosts (global state)
+vhost_management = { max-threads = 1 }
+# Tests that modify runtime parameters
+runtime_params = { max-threads = 1 }
+# Tests requiring complete isolation
+sequential = { max-threads = 1 }
+
+[[profile.default.overrides]]
+filter = 'test(list)'
+priority = 60
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_federation)'
+priority = 56
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_policies)'
+priority = 55
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_memory_breakdown)'
+priority = 50
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_export)'
+priority = 40
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_import)'
+priority = 30
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(test_shovel)'
+priority = 20
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'test(deprecated_features)'
+priority = 10
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'binary(federation_upstream_uri_modification_tests)'
+priority = 25
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'binary(shovel_source_uri_modification_tests)'
+priority = 24
+test-group = 'sequential'
+
+[[profile.default.overrides]]
+filter = 'binary(shovel_destination_uri_modification_tests)'
+priority = 23
+test-group = 'sequential'
+
+# Read-only tests that can run with high parallelism
+[[profile.default.overrides]]
+filter = 'binary(help_tests) or binary(health_check_tests) or binary(nodes_tests) or binary(feature_flag_tests)'
+priority = 80
+test-group = 'read_only'
+
+# Tests that create isolated virtual hosts
+[[profile.default.overrides]]
+filter = 'binary(queues_tests) or binary(exchanges_tests) or binary(bindings_tests) or binary(streams_tests) or binary(vhost_limits_tests) or binary(channels_tests) or binary(connections_tests)'
+priority = 70
+test-group = 'isolated_vhosts'
+
+# User management tests (global state)
+[[profile.default.overrides]]
+filter = 'binary(users_tests) or binary(permissions_tests) or binary(user_limits_tests)'
+priority = 65
+test-group = 'user_management'
+
+# Runtime parameter tests (excluding already sequential ones)
+[[profile.default.overrides]]
+filter = 'binary(runtime_parameters_tests)'
+priority = 26
+test-group = 'runtime_params'
+
+# Virtual host management (global vhost operations)
+[[profile.default.overrides]]
+filter = 'binary(vhosts_tests)'
+priority = 62
+test-group = 'vhost_management'
+
+# Virtual host delete_multiple tests (global vhost operations, can be flaky due to race conditions)
+[[profile.default.overrides]]
+filter = 'binary(vhosts_delete_multiple_tests)'
+priority = 61
+test-group = 'vhost_management'
+
+# Combined integration tests (creates global users)
+[[profile.default.overrides]]
+filter = 'binary(combined_integration_tests)'
+priority = 61
+test-group = 'user_management'
+
+# Tests that can run with moderate parallelism (no major conflicts)
+[[profile.default.overrides]]
+filter = 'binary(deprecated_feature_tests) or binary(test_commands_recommended_against_tests) or binary(feature_flag_management_tests)'
+priority = 75
+test-group = 'isolated_vhosts'
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..90a0d1c
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+version: 2
+updates:
+ - package-ecosystem: "cargo"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 10
+ reviewers:
+ - "michaelklishin"
+ assignees:
+ - "michaelklishin"
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index ab76f65..dfa8f55 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -1,6 +1,15 @@
name: CI
-on: [push, pull_request]
+on:
+ push:
+ paths:
+ - ".github/workflows/ci.yaml"
+ - ".config/nextest.toml"
+ - "src/**"
+ - "tests/**"
+ - "Cargo.toml"
+ - "Cargo.lock"
+ pull_request: {}
env:
RUSTFLAGS: -D warnings
@@ -10,10 +19,15 @@ env:
jobs:
lint:
name: Lint
- runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ runner:
+ - "ubuntu-22.04"
+ - "ubuntu-24.04"
+ runs-on: ${{ matrix.runner }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
@@ -25,15 +39,23 @@ jobs:
run: cargo fmt --all --check
build:
- name: Build and test
- runs-on: ubuntu-latest
strategy:
matrix:
- rust-version: [ stable ]
+ rabbitmq-series:
+ - "4.0"
+ - "4.1"
+ rust-version:
+ - stable
+ - beta
+ runner:
+ - "ubuntu-22.04"
+ - "ubuntu-24.04"
+ # - "ubuntu-24.04-arm"
+ runs-on: ${{ matrix.runner }}
services:
rabbitmq:
- image: rabbitmq:4-management
+ image: rabbitmq:${{ matrix.rabbitmq-series }}-management
ports:
- 15672:15672
- 5672:5672
@@ -54,4 +76,4 @@ jobs:
run: RUST_HTTP_API_CLIENT_RABBITMQCTL=DOCKER:${{job.services.rabbitmq.id}} bin/ci/before_build.sh
- name: Run tests
- run: RUST_BACKTRACE=1 NEXTEST_RETRIES=4 cargo nextest run --workspace --no-fail-fast --all-features
+ run: RUST_BACKTRACE=1 NEXTEST_RETRIES=2 cargo nextest run --workspace --no-fail-fast --all-features
\ No newline at end of file
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index a75b34f..ddc7608 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -27,12 +27,16 @@ jobs:
os: macos-latest
target_rustflags: ''
# Windoze, x86-64
- - target: x86_64-pc-windows-gnu
+ - target: x86_64-pc-windows-msvc
os: windows-latest
target_rustflags: ''
# Linux, x86-64
- target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
+ os: ubuntu-22.04
+ target_rustflags: ''
+ # Linux, aarch64
+ - target: aarch64-unknown-linux-gnu
+ os: ubuntu-24.04-arm
target_rustflags: ''
permissions:
@@ -69,26 +73,44 @@ jobs:
if: ${{ matrix.os == 'macos-latest' }}
uses: actions/upload-artifact@v4
with:
- name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}.tar.gz"
- path: "${{ github.workspace }}/target/${{ matrix.target }}/release/rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}.tar.gz"
+ name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}"
+ path: "${{ github.workspace }}/target/${{ matrix.target }}/release/rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}"
+ retention-days: 2
+
+ - name: Build an rabbitmqadmin release archive on x86-64 Linux
+ id: release-build-amd64-linux
+ if: ${{ matrix.os == 'ubuntu-24.04' || matrix.os == 'ubuntu-22.04' }}
+ run: nu ./scripts/release-linux.nu
+ env:
+ OS: ${{ matrix.os }}
+ TARGET: ${{ matrix.target }}
+ TARGET_RUSTFLAGS: ${{ matrix.target_rustflags }}
+ SRC: ${{ github.workspace }}
+ - name: Store x86-64 Linux build artifact
+ id: upload-amd64-linux-artifact
+ if: ${{ matrix.os == 'ubuntu-24.04' || matrix.os == 'ubuntu-22.04' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}"
+ path: "${{ github.workspace }}/target/${{ matrix.target }}/release/rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}"
retention-days: 2
- - name: Build an rabbitmqadmin release archive on Linux
- id: release-build-linux
- if: ${{ matrix.os == 'ubuntu-latest' }}
+ - name: Build an rabbitmqadmin release archive on aarch64 Linux
+ id: release-build-aarch64-linux
+ if: ${{ matrix.os == 'ubuntu-24.04-arm' }}
run: nu ./scripts/release-linux.nu
env:
OS: ${{ matrix.os }}
TARGET: ${{ matrix.target }}
TARGET_RUSTFLAGS: ${{ matrix.target_rustflags }}
SRC: ${{ github.workspace }}
- - name: Store Linux build artifact
- id: upload-linux-artifact
- if: ${{ matrix.os == 'ubuntu-latest' }}
+ - name: Store aarch64 Linux build artifact
+ id: upload-aarch64-linux-artifact
+ if: ${{ matrix.os == 'ubuntu-24.04-arm' }}
uses: actions/upload-artifact@v4
with:
- name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}.tar.gz"
- path: "${{ github.workspace }}/target/${{ matrix.target }}/release/rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}.tar.gz"
+ name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}"
+ path: "${{ github.workspace }}/target/${{ matrix.target }}/release/rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}"
retention-days: 2
- name: Build an rabbitmqadmin release archive on Windows
@@ -105,28 +127,33 @@ jobs:
if: ${{ matrix.os == 'windows-latest' }}
uses: actions/upload-artifact@v4
with:
- name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}.zip"
- path: "${{ github.workspace }}/target/${{ matrix.target }}/release/rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}.zip"
+ name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}.exe"
+ path: "${{ github.workspace }}/target/${{ matrix.target }}/release/rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-${{ matrix.target }}.exe"
retention-days: 2
create_gh_release:
needs: [release]
- runs-on: ubuntu-latest
+ runs-on: ubuntu-24.04
steps:
- name: Fetch macOS archive
uses: actions/download-artifact@v4
with:
- name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-aarch64-apple-darwin.tar.gz"
+ name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-aarch64-apple-darwin"
+ path: ./artifacts
+ - name: Fetch amd64 Linux archive
+ uses: actions/download-artifact@v4
+ with:
+ name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-x86_64-unknown-linux-gnu"
path: ./artifacts
- - name: Fetch Linux archive
+ - name: Fetch aarch64 Linux archive
uses: actions/download-artifact@v4
with:
- name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-x86_64-unknown-linux-gnu.tar.gz"
+ name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-aarch64-unknown-linux-gnu"
path: ./artifacts
- name: Fetch Windows archive
uses: actions/download-artifact@v4
with:
- name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-x86_64-pc-windows-gnu.zip"
+ name: "rabbitmqadmin-${{ vars.NEXT_RELESE_VERSION }}-x86_64-pc-windows-msvc.exe"
path: ./artifacts
- name: Publish rabbitmqadmin release archive
uses: softprops/action-gh-release@v2
diff --git a/.gitignore b/.gitignore
index d26635b..556b5fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
.idea/*
.fleet/*
profile.json
+.tool-versions
+*.proptest-regressions
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..45df4d3
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,1157 @@
+# rabbitmqadmin-ng Change Log
+
+## v2.17.0 (in development)
+
+No changes yet.
+
+
+## v2.16.0 (Oct 20, 2025)
+
+### Enhancements
+
+* `plugins` is a new command group for listing enabled plugins:
+
+ ```shell
+ # List plugins across all cluster nodes
+ rabbitmqadmin plugins list_all
+
+ # List plugins on a specific node
+ rabbitmqadmin plugins list_on_node --node rabbit@hostname
+ ```
+
+* Errors now include the `error` or `reason` field from the API response (if they were present there)
+
+* `--timeout` is a new global option limits HTTP API request execution timeout. The value is in seconds and defaults
+ to 60s:
+
+ ```shell
+ rabbitmqadmin --timeout 15 queues list
+ ```
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.66.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.66.0)
+
+
+## v2.15.0 (Sep 30, 2025)
+
+### Enhancements
+
+* `permissions` is a new command group for operations on user permissions:
+
+ ```shell
+ rabbitmqadmin permissions list
+
+ rabbitmqadmin permissions declare --user "user1" --configure ".*" --read ".*" --write ".*"
+
+ rabbitmqadmin permissions delete --user "user1"
+ ```
+
+* `user_limits` is a new command group for operations on [per-user limits](https://www.rabbitmq.com/docs/user-limits):
+
+ ```shell
+ rabbitmqadmin user_limits list
+
+ rabbitmqadmin user_limits declare --user "user1" --name "max-connections" --value "100"
+
+ rabbitmqadmin user_limits delete --user "user1" --name "max-connections"
+ ```
+
+* `vhost_limits` is a new command group for operations on [virtual host limits](https://www.rabbitmq.com/docs/vhosts#limits):
+
+ ```shell
+ rabbitmqadmin vhost_limits list
+
+ rabbitmqadmin vhost_limits declare --name "max-connections" --value "1000"
+
+ rabbitmqadmin vhost_limits delete --name "max-connections"
+ ```
+
+### Deprecations
+
+* "Verb" command groups (`list [object]`, `declare [object]`, `delete [object]`) are now deprecated in favor of the "noun" group commands (such as `users [operation]` or `permissions [operation]`).
+
+
+## v2.14.0 (Sep 30, 2025)
+
+### Enhancements
+
+* Several commands now have minimalistic progress indicators: `federation disable_tls_peer_verification_for_all_upstreams`, `federation enable_tls_peer_verification_for_all_upstreams`, `shovels disable_tls_peer_verification_for_all_source_uris`, `shovels disable_tls_peer_verification_for_all_destination_uris`, `shovels enable_tls_peer_verification_for_all_source_uris`, and `shovels enable_tls_peer_verification_for_all_destination_uris`
+
+* `vhosts delete_multiple` is a new command that deletes multiple virtual hosts matching a regular expression pattern:
+
+ ```shell
+ # Delete all virtual hosts matching a pattern (requires explicit approval)
+ rabbitmqadmin vhosts delete_multiple --name-pattern "test-.*" --approve
+
+ # Dry-run to see what would be deleted without actually deleting
+ rabbitmqadmin vhosts delete_multiple --name-pattern "staging-.*" --dry-run
+
+ # Non-interactive mode (no --approve flag needed)
+ rabbitmqadmin --non-interactive vhosts delete_multiple --name-pattern "temp-.*"
+ ```
+
+ One virtual host — named `/`, that is, the default one — is always skipped to preserve
+ at least one functional virtual host at all times.
+
+ **Important**: this command is **very destructive** and should be used with caution. Always test with `--dry-run` first.
+
+* `vhosts enable_deletion_protection` and `vhosts disable_deletion_protection` are two new commands
+ for managing [virtual host deletion protection](https://www.rabbitmq.com/docs/vhosts#deletion-protection):
+
+ ```shell
+ # Enable deletion protection for a virtual host
+ rabbitmqadmin vhosts enable_deletion_protection --name "production-vhost"
+
+ # Disable deletion protection for a virtual host
+ rabbitmqadmin vhosts disable_deletion_protection --name "production-vhost"
+ ```
+
+ Protected virtual hosts cannot be deleted, either individually using `vhosts delete` or
+ as part of bulk operations using `vhosts delete_multiple`. To delete a protected
+ virtual host, its protection must be lifted first.
+
+## v2.13.0 (Sep 26, 2025)
+
+### Enhancements
+
+* Memory breakdown commands (`show memory_breakdown_in_bytes` and `show memory_breakdown_in_percent`) now gracefully handle
+ cases where memory breakdown stats are not yet available on the target node
+
+* `shovel enable_tls_peer_verification_for_all_source_uris` is a new command that enables TLS peer verification
+ for all shovel source URIs:
+
+ ```shell
+ # The certificate and private key paths below refer
+ # to the files deployed to the target RabbitMQ node(s), not to the
+ # local files.
+ #
+ # As such, these arguments are command-specific and should not be confused
+ # with the global `--tls-ca-cert-file`, `--tls-cert-file`, and `--tls-key-file`
+ # arguments that are used by `rabbitmqadmin` itself to connect to the target node
+ # over the HTTP API.
+ rabbitmqadmin shovels enable_tls_peer_verification_for_all_source_uris \
+ --node-local-ca-certificate-bundle-path /path/to/node/local/ca_bundle.pem \
+ --node-local-client-certificate-file-path /path/to/node/local/client_certificate.pem \
+ --node-local-client-private-key-file-path /path/to/node/local/client_private_key.pem
+ ```
+
+ See [TLS guide](https://www.rabbitmq.com/docs/ssl#peer-verification) and [Shovel guide](https://www.rabbitmq.com/docs/shovel#tls) to learn more.
+
+* `shovel enable_tls_peer_verification_for_all_destination_uris` is a new command that enables TLS peer verification
+ for all shovel destination URIs:
+
+ ```shell
+ # Ditto, the certificate and private key paths below refer
+ # to the files deployed to the target RabbitMQ node(s), not to the
+ # local files.
+ rabbitmqadmin shovels enable_tls_peer_verification_for_all_destination_uris \
+ --node-local-ca-certificate-bundle-path /path/to/node/local/ca_bundle.pem \
+ --node-local-client-certificate-file-path /path/to/node/local/client_certificate.pem \
+ --node-local-client-private-key-file-path /path/to/node/local/client_private_key.pem
+ ```
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.59.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.59.0)
+
+
+## v2.12.0 (Sep 23, 2025)
+
+### Enhancements
+
+* `federation enable_tls_peer_verification_for_all_upstreams` is a new command that enables TLS peer verification
+ for all federation upstreams:
+
+ ```shell
+ # Note that the certificate and private key paths below refer
+ # to the files deployed to the target RabbitMQ node(s), not to the
+ # local files.
+ #
+ # As such, these arguments are command-specific and should not be confused
+ # with the global `--tls-ca-cert-file`, `--tls-cert-file`, and `--tls-key-file`
+ # arguments that are used by `rabbitmqadmin` itself to connect to the target node
+ # over the HTTP API.
+ rabbitmqadmin federation enable_tls_peer_verification_for_all_upstreams \
+ --node-local-ca-certificate-bundle-path /path/to/node/local/ca_bundle.pem \
+ --node-local-client-certificate-file-path /path/to/node/local/client_certificate.pem \
+ --node-local-client-private-key-file-path /path/to/node/local/client_private_key.pem
+ ```
+
+ See [TLS guide](https://www.rabbitmq.com/docs/ssl#peer-verification) and [Federation guide](https://www.rabbitmq.com/docs/federation#tls-connections) to learn more.
+
+ * `shovel disable_tls_peer_verification_for_all_source_uris` is a new command that disables TLS peer verification
+ for all shovel source URIs.
+
+ **Important**: this command should **only** be used to undo incorrect shovel source URIs, after a bad deployment, for example,
+ if [peer verification](https://www.rabbitmq.com/docs/ssl#peer-verification) was enabled before certificates and keys were
+ deployed.
+
+ * `shovel disable_tls_peer_verification_for_all_source_uris` is a new command that disables TLS peer verification
+ for all shovel source URIs.
+
+ **Important**: this command should **only** be used to undo incorrect shovel destination URIs (see above).
+
+* All `delete_*` and `clear_*` commands now support the `--idempotently` flag (previously it was just a few):
+ - `bindings delete`
+ - `close connection`
+ - `close user_connections`
+ - `connections close`
+ - `connections close_of_user`
+ - `exchanges delete`
+ - `exchanges unbind`
+ - `federation delete_upstream`
+ - `global_parameters clear`
+ - `operator_policies delete`
+ - `parameters clear`
+ - `policies delete`
+ - `queues delete`
+ - `shovels delete`
+ - `streams delete`
+ - `users delete`
+ - `vhosts delete`
+
+* Updated `delete_binding` to use the new `BindingDeletionParams` struct API
+
+## v2.11.0 (Sep 22, 2025)
+
+### Enhancements
+
+* `federation disable_tls_peer_verification_for_all_upstreams` is a new command that disables TLS peer verification
+ for all federation upstreams.
+
+ **Important**: this command should **only** be used to correct federation upstream URI after a bad deployment, for example,
+ if [peer verification](https://www.rabbitmq.com/docs/ssl#peer-verification) was enabled before certificates and keys were
+ deployed.
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.57.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.57.0)
+
+
+
+## v2.10.0 (Sep 18, 2025)
+
+### Enhancements
+
+* `definitions export_from_vhost` now supports `--transformations`:
+
+ ```shell
+ # previously only 'definitions export' supported --transformations
+ rabbitmqadmin --vhost "my-vhost" definitions export_from_vhost \
+ --transformations prepare_for_quorum_queue_migration,drop_empty_policies \
+ --file "my-vhost.definitions.json"
+ ```
+
+### Bug Fixes
+
+ * The `prepare_for_quorum_queue_migration` transformation did not remove CMQ-related keys
+ such as `x-ha-mode` from [optional queue arguments](https://www.rabbitmq.com/docs/queues#optional-arguments)
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.52.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.52.0)
+
+
+## v2.9.0 (Aug 25, 2025)
+
+### Enhancements
+
+ * RabbitMQ 4.2 forward compatibility: `shovels list_all` and `shovels list` now can render
+ [local shovel](https://github.com/rabbitmq/rabbitmq-server/pull/14256) rows
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.44.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.44.0)
+
+
+## v2.8.2 (Aug 19, 2025)
+
+### Enhancements
+
+ * `definitions export` is now compatible with RabbitMQ 3.10.0, a series that has
+ reached end of life (EOL) in late 2023
+
+### Upgrades
+
+ * RabbitMQ HTTP API client was upgraded to [`0.43.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.43.0)
+
+
+## v2.8.1 (Aug 14, 2025)
+
+### Bug Fixes
+
+ * `shovels list` and `shovels list_all` panicked when target cluster had at least one
+ static shovel
+
+### Upgrades
+
+ * RabbitMQ HTTP API client was upgraded to [`0.42.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.42.0)
+
+
+## v2.8.0 (Aug 11, 2025)
+
+### Bug Fixes
+
+ * `shovels list_all` panicked when one of the shovels was in the `terminated` state
+
+### Enhancements
+
+ * `shovels list` is a new command that lists shovels in a particular virtual host
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.41.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.41.0)
+
+
+## v2.7.2 (Aug 6, 2025)
+
+### Bug Fixes
+
+ * `shovels declare_amqp091` panicked when the `--source-exchange` argument was not provided,
+ even if `--source-queue` was
+
+
+## v2.7.1 (Jul 17, 2025)
+
+### Bug Fixes
+
+ * Improved handling of missing or impossible to load/parse `--tls-ca-cert-file` on the command line.
+
+ The tool now properly handles cases where a [CA certificate](https://www.rabbitmq.com/docs/ssl#peer-verification) file path is not provided, making
+ CA certificate loading optional rather than required, which prevents crashes when TLS is used
+ without a custom CA certificate bundle
+
+ * `show overview` could panic when run against a freshly booted RabbitMQ node that did not have certain
+ metrics/rates initialized and available. Now those metrics will use the default values for their types,
+ such as `0` and `0.0` for the counters, gauges, rates
+
+### Upgrades
+
+* RabbitMQ HTTP API client was upgraded to [`0.40.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.40.0)
+
+
+## v2.7.0 (Jul 15, 2025)
+
+### Enhancements
+
+ * `rabbitmqadmin.conf` now supports more TLS-related settings: `ca_certificate_bundle_path` (corresponds to `--tls-ca-cert-file` on the command line),
+ `client_certificate_file_path` (corresponds to `--tls-cert-file`), and `client_private_key_file_path` (corresponds to `--tls-key-file`).
+
+ As the names suggest, they are used to configure the CA certificate bundle file path, the client certificate file path,
+ and the client private key file path, respectively:
+
+ ```toml
+ [production]
+ hostname = "(redacted)"
+ port = 15671
+ username = "user-efe1f4d763f6"
+ password = "(redacted)"
+ tls = true
+ ca_certificate_bundle_path = "/path/to/ca_certificate.pem"
+ client_certificate_file_path = "/path/to/client_certificate.pem"
+ client_private_key_file_path = "/path/to/client_key.pem"
+ ```
+
+ To learn more, see [RabbitMQ's TLS guide](https://www.rabbitmq.com/docs/ssl).
+
+### Bug Fixes
+
+ * Tool version was unintentionally missing from `-h` output (but present in its long counterpart, `--help`)
+ * The `tls` setting in `rabbitmqadmin.conf`, a `--use-tls` equivalent, was not respected when connecting to a node
+ in certain cases
+
+## v2.6.0 (Jul 12, 2025)
+
+### Enhancements
+
+ * New command, `passwords salt_and_hash`, that implements the [password salting and hashing algorithm](https://www.rabbitmq.com/docs/passwords#computing-password-hash)
+ used by RabbitMQ's internal authentication backend:
+
+ ```shell
+ rabbitmqadmin passwords salt_and_hash "sEkr37^va1ue"
+ # => ┌───────────────┬──────────────────────────────────────────────────┐
+ # => │ Result │
+ # => ├───────────────┼──────────────────────────────────────────────────┤
+ # => │ key │ value │
+ # => ├───────────────┼──────────────────────────────────────────────────┤
+ # => │ password hash │ vRZC0bF0Ut4+6pmcQRSu87S/wRXdHRalgY5DV/5KDd5SzK69 │
+ # => └───────────────┴──────────────────────────────────────────────────┘
+ ```
+
+ This value can be passed as a `--password-hash` when creating a user with the `users declare`
+ command.
+
+ * `users declare` now supports a new argument, `--hashing-algorithm`, that accepts two
+ possible values: `sha256` (the default) and `sha512`:
+
+ ```shell
+ # RabbitMQ nodes must also be configured to use SHA-512 password hashing,
+ # or this user won't be able to authenticate against them
+ rabbitmqadmin users declare --username "username43742" --password "example_%^4@8s7" --hashing-algorithm "sha512"
+ ```
+
+ Target RabbitMQ nodes must be [configured](https://www.rabbitmq.com/docs/passwords#changing-algorithm) to use the same hashing algorithm (SHA-256 is
+ used by default).
+
+
+## v2.5.0 (Jul 11, 2025)
+
+### Enhancements
+
+ * `definitions export` now supports a new transformation: `prepare_for_quorum_queue_migration`.
+
+ ```shell
+ rabbitmqadmin definitions export --transformations prepare_for_quorum_queue_migration,drop_empty_policies --stdout
+ ```
+
+ This one not only strips off the CMQ-related keys
+ but also handles an incompatible `"overflow"`/`"x-overflow"` key value
+ and `"queue-mode"`/`"x-queue-mode"` keys, both not supported
+ by quorum queues.
+
+### Bug Fixes
+
+ * `export definitions` CLI interface was unintentionally different from that of `definitions export`.
+ Note that `export definitions` only exists for better backwards compatibility with `rabbitmqadmin` v1,
+ use `definitions export` when possible.
+
+
+## v2.4.0 (Jul 4, 2025)
+
+### Bug Fixes
+
+ * `connections list` failed to deserialize a list of connections that included direct connections
+ (as in the Erlang AMQP 0-9-1 client), namely local connections of shovels and federation links.
+
+ GitHub issue: [#68](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/68)
+
+### Upgrades
+
+ * RabbitMQ HTTP API client was upgraded to [`0.36.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.36.0)
+
+
+## v2.3.0 (Jun 30, 2025)
+
+ * RabbitMQ HTTP API client was upgraded to [`0.35.0`](https://github.com/michaelklishin/rabbitmq-http-api-rs/releases/tag/v0.35.0) to fix a `connections list` command
+ panic.
+
+
+## v2.2.1 (Jun 20, 2025)
+
+### Bug Fixes
+
+ * Several `rabbitmqadmin.conf` settings were not merged correctly with
+ the command line arguments.
+
+ GitHub issue: [#58](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/58)
+
+
+## v2.2.0 (Jun 12, 2025)
+
+### Enhancements
+
+ * `connections` is a new command group for operations on connections
+ * `channels` is a new command group for operations on channels
+ * `operator_policies` is a new command group for working with operator policies.
+ It matches the `policies` group but acts on [operator policies](https://www.rabbitmq.com/docs/policies#operator-policies)
+ * `policies set` and `policies update` are two new aliases for `policies declare`. The former follows the naming
+ used by `rabbitmqctl` and the latter reflects the fact that the command can be used to update an existing policy,
+ in particular, to override its definition
+ * `policies patch` is a new command that updates a policy definition by merging the provided definition with the existing one
+ * `policies delete_definition_keys` is a new command that removes keys from a policy definition
+ * `policies delete_definition_keys_from_all_in` is a new command that removes definition keys from all policies in a virtual host
+ * `policies update_definition` is a new command that updates a policy definition key; for multi-key updates, see `policies patch
+ * `policies update_definitions_of_all_in` is a new command that updates a definition key for all policies in a virtual host
+ * `policies declare_override` is a new command that declares a policy that overrides another policy
+ * `policies declare_blanket` is a new command that declares a low priority policy that matches all objects not matched
+ by any other policies
+ * `parameters list_all` is a new command that lists all runtime parameters across all virtual hosts
+ * `parameters list_in` is a new command that lists runtime parameters of a given component (type)
+ in a specific virtual host
+
+
+## v2.1.0 (May 8, 2025)
+
+### Enhancements
+
+ * `bindings` is a new command group for operations on bindings
+ * `exchanges` is a new command group for operations on exchanges
+ * `global_parameters` is a new command group for operations on [global runtime parameters](https://www.rabbitmq.com/docs/parameters)
+ * `nodes` is a new command group for operations on nodes
+ * `parameters` is a new command group for operations on [runtime parameters](https://www.rabbitmq.com/docs/parameters)
+ * `queues` is a new command group for operations on queues
+ * `streams` is a new command group for operations on streams
+ * `users` is a new command group for operations on users
+ * `vhosts` is a new command group for operations on virtual hosts
+ * Command groups are now ordered alphabetically
+
+### Bug Fixes
+
+ * Both `-h` and `--help` now display relevant doc guide URLs.
+ Previously it was only the case for `--help`
+
+### Other Changes
+
+ * `vhosts declare` no longer has a default value for `--default-queue-type`.
+ Instead, the default will be controlled exclusively by RabbitMQ
+
+
+## v2.0.0 (Mar 31, 2025)
+
+### Enhancements
+
+#### Subcommand and Long Option Inference
+
+If the `RABBITMQADMIN_NON_INTERACTIVE_MODE` is not set to `true`, this tool
+now can infer subcommand and --long-option names.
+
+This means that a subcommand can be referenced with its unique prefix,
+that is,
+
+* 'del queue' will be inferred as 'delete queue'
+* 'del q --nam "a.queue"' will be inferred as 'delete queue --name "a.queue"'
+
+To enable each feature, set the following environment variables to
+'true':
+
+* `RABBITMQADMIN_INFER_SUBCOMMANDS`
+* `RABBITMQADMIN_INFER_LONG_OPTIONS`
+
+This feature is only meant to be used interactively. For non-interactive
+use, it can be potentially too dangerous to allow.
+
+#### Intentionally Restricted Environment Variable Support
+
+Environment variables have a number of serious downsides compared to a `rabbitmqadmin.conf`
+and the regular `--long-options` on the command line:
+
+1. Non-existent support for value types and validation ("everything is a string")
+2. Subprocess inheritance restrictions that can be very time-consuming to debug
+3. Different syntax for setting them between the classic POSIX-era shells (such as `bash`, `zsh`) and modern ones (such as [`nushell`](https://www.nushell.sh/))
+
+For these reasons and others, `rabbitmqadmin` v2 intentionally uses the configuration file and the
+CLI options over the environment variables.
+
+`rabbitmqadmin` v2 does, however, supports a number of environment variables for a few
+global settings that cannot be configured any other way (besides a CLI option),
+or truly represent an environment characteristic, e.g. either the non-interactive mode
+should be enabled.
+
+These environment variables are as follows:
+
+| Environment variable | Type | When used | Description |
+|--------------------------------------|---------------------------------------------------|---------------------------------------|--------------------------------------------------------------|
+| `RABBITMQADMIN_CONFIG_FILE_PATH` | Local filesystem path | Pre-flight (before command execution) | Same meaning as the global `--confg-file` argument |
+| `RABBITMQADMIN_NON_INTERACTIVE_MODE` | Boolean | Command execution | Enables the non-interactive mode.
Same meaning as the global `--non-interactive` argument |
+| `RABBITMQADMIN_QUIET_MODE` | Boolean | Command execution | Instructs the tool to produce less output.
Same meaning as the global `--quiet` argument |
+| `RABBITMQADMIN_INFER_SUBCOMMANDS` | Boolean | Pre-flight (before command execution) | Enables inference (completion of partial names) of subcommands. Does not apply to the non-interactive mode. |
+| `RABBITMQADMIN_INFER_LONG_OPTIONS` | Boolean | Pre-flight (before command execution) | Enables inference (completion of partial names) of `--long-options`. Does not apply to the non-interactive mode. |
+| `RABBITMQADMIN_NODE_ALIAS` | String | Command execution | Same meaning as the global `--node` argument |
+| `RABBITMQADMIN_TARGET_HOST` | String | Command execution | Same meaning as the global `--host` argument |
+| `RABBITMQADMIN_TARGET_PORT` | Positive integer | Command execution | Same meaning as the global `--port` argument |
+| `RABBITMQADMIN_API_PATH_PREFIX` | String | Command execution | Same meaning as the global `--path-prefix` argument |
+| `RABBITMQADMIN_TARGET_VHOST` | String | Command execution | Same meaning as the global `--vhost` argument |
+| `RABBITMQADMIN_BASE_URI` | String | Command execution | Same meaning as the global `--base-uri` argument |
+| `RABBITMQADMIN_USE_TLS` | Boolean | Command execution | Same meaning as the global `--tls` argument |
+| `RABBITMQADMIN_USERNAME` | String | Command execution | Same meaning as the global `--username` argument |
+| `RABBITMQADMIN_PASSWORD` | String | Command execution | Same meaning as the global `--password` argument |
+| `RABBITMQADMIN_TABLE_STYLE` | Enum, see `--table-style` in `rabbitmqadmin help` | Command execution | Same meaning as the global `--table-style` argument |
+
+## v0.29.0 (Mar 23, 2025)
+
+### Breaking Changes
+
+ * `definitions export`'s special `--file` value of `-` for "standard input" is deprecated. Use `--stdout` instead:
+
+ ```shell
+ rabbitmqadmin definitions export --stdout > definitions.json
+ ```
+
+ ```shell
+ # exports 3.x-era definitions that might contain classic queue mirroring keys, transforms
+ # them to not use any CMQ policies, injects an explicit queue type into the matched queues,
+ # and drops all the policies that had nothing beyond the CMQ keys,
+ # then passes the result to the standard input of
+ # 'rabbitmqadmin definitions import --stdin'
+ rabbitmqadmin --node "source.node" definitions export --transformations strip_cmq_keys_from_policies,drop_empty_policies --stdout | rabbitmqadmin --node "destination.node" definitions import --stdin
+ ```
+
+### Enhancements
+
+ * `definitions import` now supports reading definitions from the standard input instead of a file.
+ For that, pass `--stdin` instead of `--file "/path/to/definitions.json"`.
+
+ ```shell
+ rabbitmqadmin definitions import --stdin < definitions.json
+ ```
+
+ ```shell
+ cat definitions.json | rabbitmqadmin definitions import --stdin
+ ```
+
+ ```shell
+ # exports 3.x-era definitions that might contain classic queue mirroring keys, transforms
+ # them to not use any CMQ policies, injects an explicit queue type into the matched queues,
+ # and drops all the policies that had nothing beyond the CMQ keys,
+ # then passes the result to the standard input of
+ # 'rabbitmqadmin definitions import --stdin'
+ rabbitmqadmin --node "source.node" definitions export --transformations strip_cmq_keys_from_policies,drop_empty_policies --stdout | rabbitmqadmin --node "destination.node" definitions import --stdin
+ ```
+
+
+## v0.28.0 (Mar 23, 2025)
+
+### Enhancements
+
+ * New command group: `federation`, see
+
+ ```shell
+ rabbitmqadmin federation help
+ ```
+
+* New command: `federation declare_upstream_for_queues` for declaring upstreams that will exclusively be used for queue
+ federation. This command does not support any options related to exchange federation.
+
+ ```shell
+ rabbitmqadmin federation --vhost "local.vhost" declare_upstream_for_queues \
+ --name "dc.vancouver" \
+ --uri "amqp://192.168.0.25/demote.vhost" \
+ --ack-mode "on-confirm"
+ ```
+
+* New command: `federation declare_upstream_for_exchanges` for declaring upstreams that will exclusively be used exchange
+ federation. This command does not support any options related to queue federation.
+
+ ```shell
+ rabbitmqadmin federation --vhost "local.vhost" declare_upstream_for_exchanges \
+ --name "dc.vancouver" \
+ --uri "amqp://192.168.0.25/demote.vhost" \
+ --ack-mode "on-confirm"
+ ```
+
+ * New command: `federation declare_upstream` for declaring upstreams that can be used for either queue or exchange
+ federation. This command supports the whole spectrum of federation upstream options, that is, both the settings
+ of queue and exchange federation.
+
+ ```shell
+ rabbitmqadmin federation --vhost "local.vhost" declare_upstream \
+ --name "dc.canada.bc.vancouver" \
+ --uri "amqp://192.168.0.25/demote.vhost" \
+ --ack-mode "on-confirm"
+ ```
+
+ * New command: `federation list_all_upstreams` for listing all upstreams (that is, upstreams across all the virtual hosts in the cluster).
+
+ ```shell
+ rabbitmqadmin federation list_all_upstreams
+ ```
+
+ * New command: `federation list_all_links` for listing all links (that is, links across all the virtual hosts in the cluster).
+
+ ```shell
+ rabbitmqadmin federation list_all_links
+ ```
+
+ * New command: `federation delete_upstream`. As the name suggests, it deletes an upstream.
+
+ ```shell
+ rabbitmqadmin federation delete_upstream --name "dc.canada.bc.vancouver"
+ ```
+
+ * New definitions export `--transformations` value, `obfuscate_usernames`, changes usernames to dummy values
+ (e.g. so that definitions could be shared safely with external teams)
+ * New definitions export `--transformations` value, `exclude_users`, removes users from the result
+ (also for safe sharing)
+ * New definitions export `--transformations` value, `exclude_permissions`, removes all permissions
+ (also for safe sharing)
+ * New definitions export `--transformations` value, `exclude_runtime_parameters`, removes all runtime parameters
+ * New definitions export `--transformations` value, `exclude_policies`, removes all policies
+ * New definitions export `--transformations` value, `no_op`, applies no transformation
+
+
+## v0.27.0 (Mar 10, 2025)
+
+### Enhancements
+
+ * `definitions export` now supports a new option, `--transformations`, a comma-separated list of
+ supported operations to apply to the definitions.
+
+ ```shell
+ rabbitmqadmin definitions export --transformations strip_cmq_keys_from_policies,drop_empty_policies
+ ```
+
+ The command above applies two transformations named `strip_cmq_keys_from_policies` and `drop_empty_policies`
+ that will strip all classic queue mirroring-related policy keys that RabbitMQ 3.13 nodes supported,
+ then removes the policies that did not have any keys left (ended up having an empty definition).
+
+ * When `--non-interactive` mode is used, newlines in table cells are now replaced with comma-separated lists
+
+### Bug Fixes
+
+ * 'declare queue's `--type` option values that the tool does not recognize are now passed as is to
+ the HTTP API
+
+
+## v0.26.0 (Mar 3, 2025)
+
+### Enhancements
+
+ * `policies` is a new command group for policy operations:
+
+ ```shell
+ rabbitmqadmin help policies
+
+ # an equivalent of 'declare policy'
+ rabbitmqadmin policies declare --name "policy-name" --pattern '^matching\..+' --apply-to "quorum_queues" \
+ --priority 10 \
+ --definition '{"max-length": 10000}'
+
+ # an equivalent of 'list policies'
+ rabbitmqadmin policies list
+
+ # an equivalent of 'delete policy'
+ rabbitmqadmin policies delete --name "policy-name"
+ ```
+ * `policies list_in` is a new command that lists policies in a specific virtual host:
+
+ ```shell
+ rabbitmqadmin --vhost "a.vhost" policies list_in
+ ```
+
+ ```shell
+ rabbitmqadmin --vhost "streams.vhost" policies list_in --apply-to "streams"
+ ```
+
+ * `policies list_matching_object` is a new command that lists all policies that
+ would match an object (a queue, a stream, an exchange)
+ with a given name:
+
+ ```shell
+ rabbitmqadmin --vhost "a.vhost" policies list_matching_object --name 'audit.events' --type queues
+ ```
+
+### Bug Fixes
+
+ * `declare policy`'s `--apply-to` argument value was ignored
+
+
+
+## v0.25.0 (Mar 2, 2025)
+
+### Enhancements
+
+ * Binary packages for 8x86-64 Linux are now produced on an older `glibc` version, `2.35`,
+ for compatibility with Debian Bookworm and Ubuntu 22.04
+
+ * `shovels declare_amqp10` is a new command that declares a dynamic Shovel that will use
+ AMQP 1.0 for both source and destination:
+
+ ```shell
+ rabbitmqadmin --vhost "shovels" shovels declare_amqp10 \
+ --name "shovel-2" \
+ --source-uri amqp://localhost:5672 \
+ --destination-uri amqp://localhost:5672 \
+ --source-address "/queue/src.q" --destination-address "/queue/dest.q"
+ ```
+
+ * `shovels declare_amqp091` is a new command that declares a dynamic Shovel that will use
+ AMQP 0-9-1 for both source and destination:
+
+ ```shell
+ rabbitmqadmin --vhost "shovels" shovels declare_amqp091 \
+ --name "shovel-2" \
+ --source-uri amqp://localhost:5672 \
+ --destination-uri amqp://localhost:5672 \
+ --source-queue "src.q" --destination-exchange "amq.fanout"
+ ```
+
+ * `shovels delete` is a new command that deletes a dynamic shovel:
+
+ ```shell
+ rabbitmqadmin --vhost "shovels" shovels delete --name "shovel-2"
+ ```
+
+
+## v0.24.0 (Feb 8, 2025)
+
+### Enhancements
+
+ * `definitions export_from_vhost` is a new command that exports definitions from a single virtual host
+ (as opposed to definitions for the entire cluster)
+
+ * `definitions import_into_vhost` is a new command that imports virtual host-specific definitions
+ (as opposed to definitions for the entire cluster)
+
+
+## v0.23.0 (Feb 2, 2025)
+
+### Enhancements
+
+ * `list user_connections` is a new command that lists connections of a specific user:
+
+ ```
+ rabbitmqadmin --vhost="/" list user_connections --username "monitoring.1"
+
+ rabbitmqadmin --vhost="production" list user_connections --username "web.45cf7dc28"
+ ```
+
+* `close user_connections` is a new command that closes connections of a specific user:
+
+ ```
+ rabbitmqadmin --vhost="/" close user_connections --username "monitoring.2"
+
+ rabbitmqadmin --vhost="production" close user_connections --username "web.94ee67772"
+ ```
+
+ * New general option `--table-style`, can be used to change output table styling.
+
+ By default, the following style is used:
+
+ ```shell
+ rabbitmqadmin --table-style=modern show overview
+ ```
+
+ An equivalent of `--non-interactive` in terms of styling is
+
+ ```shell
+ rabbitmqadmin --table-style=borderless show overview
+ ```
+
+ More available styles:
+
+ ```shell
+ rabbitmqadmin --table-style=ascii show overview
+ ```
+
+ ```shell
+ rabbitmqadmin --table-style=psql show overview
+ ```
+
+ ```shell
+ rabbitmqadmin --table-style=markdown show overview
+ ```
+
+ ```shell
+ rabbitmqadmin --table-style=dots show overview
+ ```
+
+
+## v0.22.0 (Feb 1, 2025)
+
+### Naming
+
+* `tanzu sds enable` was renamed to `tanzu sds enable_on_node`.
+
+ This breaking change only applies to a command specific to
+ Tanzu RabbitMQ 4.1, a series currently in development.
+
+* `tanzu sds disable` was renamed to `tanzu sds disable_on_node`.
+
+ This breaking change only applies to a command specific to
+ Tanzu RabbitMQ 4.1, a series currently in development.
+
+### Enhancements
+
+* `tanzu sds enable_cluster_wide` is a new command that disables SDS on all cluster nodes.
+
+ This command is specific to Tanzu RabbitMQ 4.1, a series currently in development.
+
+* `tanzu sds disable_cluster_wide` is a new command that disables SDS on all cluster nodes.
+
+ This command is specific to Tanzu RabbitMQ 4.1, a series currently in development.
+
+
+
+## v0.21.0 (Feb 1, 2025)
+
+### Bug Fixes
+
+ * `list connections` now correctly handles RabbitMQ Stream Protocol
+ connections that do not have the `channel_max` metric set
+
+### Enhancements
+
+ * `declare stream` is a new command that accepts stream-specific
+ arguments:
+
+ ```shell
+ rabbitmqadmin --vhost "vh1" declare stream --name "streams.1" --expiration "8h" \
+ --arguments '{"x-initial-cluster-size": 3}'
+ ```
+
+ * `delete stream` is an alias for `delete queue` that makes more sense for
+ environments where [streams](https://www.rabbitmq.com/docs/streams)
+ are used more often than queues:
+
+ ```shell
+ rabbitmqadmin --vhost "vh1" delete stream --name "streams.1"
+ ```
+
+
+## v0.20.0 (Jan 28, 2025)
+
+### Enhancements
+
+ * Initial support for Tanzu RabbitMQ Schema Definition Sync (SDS).
+
+ ```
+ rabbitmqadmin help tanzu sds
+
+ rabbitmqadmin tanzu sds status
+ ```
+
+ * Initial support for Tanzu RabbitMQ Warm Standby Replication (WSR).
+
+ ```
+ rabbitmqadmin help tanzu wsr
+
+ rabbitmqadmin tanzu wsr status
+ ```
+
+
+## v0.19.0 (Jan 5, 2025)
+
+### Enhancements
+
+* Two new commands for reasoning about target [node's memory footprint](https://www.rabbitmq.com/docs/memory-use):
+
+ ```shell
+ # displays a breakdown in bytes
+ rabbitmqadmin show memory_breakdown_in_bytes --node 'rabbit@hostname'
+ ```
+
+ ```shell
+ # displays a breakdown in percent
+ rabbitmqadmin show memory_breakdown_in_percent --node 'rabbit@hostname'
+ ```
+
+ Example output of `show memory_breakdown_in_percent`:
+
+ ```
+ ┌────────────────────────────────────────┬────────────┐
+ │ key │ percentage │
+ ├────────────────────────────────────────┼────────────┤
+ │ total │ 100% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Binary heap │ 45.10% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Allocated but unused │ 23.45% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Quorum queue ETS tables │ 23.05% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Other processes │ 5.32% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Other (used by the runtime) │ 4.98% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Code │ 4.54% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Client connections: others processes │ 3.64% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Management stats database │ 3.48% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Client connections: reader processes │ 3.22% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Plugins and their data │ 3.12% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Other (ETS tables) │ 1.55% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Metrics data │ 0.66% │
+ ├────────────────────────────────────────┼────────────┤
+ │ AMQP 0-9-1 channels │ 0.40% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Message store indices │ 0.27% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Atom table │ 0.24% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Client connections: writer processes │ 0.19% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Quorum queue replica processes │ 0.10% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Stream replica processes │ 0.07% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Mnesia │ 0.02% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Metadata store │ 0.02% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Stream coordinator processes │ 0.02% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Classic queue processes │ 0.00% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Metadata store ETS tables │ 0.00% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Stream replica reader processes │ 0.00% │
+ ├────────────────────────────────────────┼────────────┤
+ │ Reserved by the kernel but unallocated │ 0.00% │
+ └────────────────────────────────────────┴────────────┘
+ ```
+
+ Note that there are [two different supported strategies](https://www.rabbitmq.com/docs/memory-use#strategies) for computing memory footprint of a node. RabbitMQ uses both and takes the greater value for 100% when computing the relative
+ share in percent for each category. Other factors that can affect the precision of percentage values reported are [runtime allocator](https://www.rabbitmq.com/docs/memory-use#preallocated-memory) behavior nuances and the [kernel page cache](https://www.rabbitmq.com/docs/memory-use#page-cache).
+
+
+
+## v0.18.0 (Jan 1, 2025)
+
+### Enhancements
+
+ * Client identity support: `--tls-cert-file` and `--tls-key-file` are the (re-introduced)
+ options that allow the user to pass in a public certificate (key) and private key pair
+ for x.509 [peer verification](https://www.rabbitmq.com/docs/ssl#peer-verification):
+
+ ```shell
+ rabbitmqadmin --use-tls --host 'target.domain' --port 15671 \
+ --tls-ca-cert-file '/path/to/ca_certificate.pem' \
+ --tls-cert-file '/path/to/client_certificate.pem' \
+ --tls-key-file '/path/to/client_key.pem' \
+ list connections
+ ```
+
+ GitHub issue: [#26](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/26)
+
+ * Initial cross-platform support for loading of [trusted CA certificates](https://www.rabbitmq.com/docs/ssl#peer-verification-trusted-certificates)
+ from system locations.
+
+ This behavior is enabled automatically. The certificates in a PEM file passed in
+ via `--tls-ca-cert-file` are merged with the list of CA certificates discovered in
+ the platform-specific stores.
+
+ GitHub issue: [#42](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/42)
+
+ * `rabbitmqadmin show memory_breakdown` is a new command that outputs [a breakdown of target node's memory footprint](https://www.rabbitmq.com/docs/memory-use#breakdown)
+
+
+## v0.17.0 (Dec 31, 2024)
+
+### Enhancements
+
+ * New [health checks](https://www.rabbitmq.com/docs/monitoring#health-checks):
+
+ ```shell
+ # To see help: 'rabbitmqadmin health_check help port_listener'
+ rabbitmqadmin health_check port_listener --port [port]
+ ```
+
+ ```shell
+ # To see help: 'rabbitmqadmin health_check help protocol_listener'
+ rabbitmqadmin health_check protoocl_listener --protocol [protocol]
+ ```
+
+
+## v0.16.0 (Dec 29, 2024)
+
+### Enhancements
+
+ * `rabbitmqadmin feature_flags list` (also available as `rabbitmqadmin list feature_flags`) is a new command
+ that lists [feature flags](https://www.rabbitmq.com/docs/feature-flags) and their cluster state.
+
+ GitHub issue: [#38](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/38)
+
+* `rabbitmqadmin feature_flags enable --name {feature flag}` and `rabbitmqadmin feature_flags enable_all` are new commands
+ that enable feature flags.
+
+ Just like its `rabbitmqctl` counterpart, `rabbitmqadmin feature_flags enable_all` will only enable
+ the stable [feature flags](https://www.rabbitmq.com/docs/feature-flags) and will skip the experimental ones.
+
+ GitHub issues: [#41](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/41)
+
+ * `rabbitmqadmin deprecated_features list` (also available as `rabbitmqadmin list deprecated_features`) is a new
+ function that lists all [deprecated features](https://www.rabbitmq.com/docs/deprecated-features).
+
+ GitHub issue: [#39](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/39)
+
+* `rabbitmqadmin deprecated_features list_used` (also available as `rabbitmqadmin list deprecated_features_in_use`) is a new
+ function that lists the [deprecated features](https://www.rabbitmq.com/docs/deprecated-features) that are found to be
+ used in the cluster.
+
+ GitHub issue: [#40](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/40)
+
+
+## v0.15.0 (Dec 26, 2024)
+
+### Enhancements
+
+ * Improved error reporting.
+
+ A failed HTTP API request now prints the request URL (this does NOT include the credentials),
+ and the response body, making it easier to identify the problem without consulting [RabbitMQ node logs](https://www.rabbitmq.com/docs/management#http-logging).
+
+ * CLI interface `help` message polishing.
+
+ More commands now provide links to the relevant documentation guides,
+ use (a reasonable amount of) coloring and recommend against features such as that are
+ [polling message consumption]((https://www.rabbitmq.com/docs/consumers#polling)) that were never designed or intended to be used in production
+
+ * README documentation improvements
+
+
+
+## v0.14.0 (Dec 22, 2024)
+
+### Breaking Changes
+
+ * Multi-word command line flags now use the more common `--snake-case[=]{value}` format
+ instead of `rabbitmqadmin` v1's `lower_case={value}`.
+
+### Enhancements
+
+ * New command category: `health_check` which provides access to the [health check endpoints](https://rabbitmq.com/docs/monitoring#health-checks).
+
+ Currently, only the three (arguably) most important health checks are implemented: `local_alarms`, `cluster_wide_alarms`, and `node_is_quorum_critical`.
+
+ GitHub issues: [#33](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/33), [#34](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/34).
+
+ * `health_check help` now includes a link to the respective RabbitMQ documentation guide.
+
+### Bug Fixes
+
+* Configuration paths with a tilda (`~`), including the default configuration file at `~/.rabbitmqadmin.conf`,
+ were not loaded correctly.
+
+
+## v0.13.0 (Dec 21, 2024)
+
+### Enhancements
+
+ * Several key `delete` commands, namely `delete vhost`, `delete user`, `delete queue` and `delete exchange` now support a new flag, `--idempotently`.
+ When this flag is used, 404 Not Found responses from the HTTP
+
+ GitHub issue: [#32](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/32)
+
+ * `--non-interactive` is a new global flag. When used, this flag will instruct `rabbitmqadmin` to not produce table border formatting,
+ and generally try to use output that'd be easier to consume from scripts
+
+ * Initial work on improving error reporting
+
+## Bug Fixes
+
+ * `declare exchange` produced an incorrect API request payload
+ when target exchange `--type` was an `x-*` type (a plugin provided-type), such as `x-local-random` or `x-consistent-hash`
+
+
+## v0.12.0 (Dec 8, 2024)
+
+### Enhancements
+
+ * Implement support for configuration files. Instead of `.ini` files used by
+ `rabbitmqadmin` v1, this version uses [TOML](https://toml.io/en/).
+
+ GitHub issue: [#28](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/28)
+
+ * Implement `show overview`
+
+ GitHub issue: [#25](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/25)
+
+ * `declare queue` no longer requires a `--queue-type`. If not type is specified,
+ it will not be included into the request and the queue will be declared
+ with the [default queue type of the target virtual host](https://rabbitmq.com/docs/vhosts#default-queue-type).
+
+ GitHub issue: [#29](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/29)
+
+### Releases
+
+Release artifacts are no longer distributed as single file archives. Instead,
+the release now includes "naked" binaries that can be downloaded and executed
+without un-archiving.
+
+GitHub issue: [#31](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/31)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 487c500..f9a0002 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,12 +3,12 @@
## Running Tests
While tests support the standard `cargo test` option, another option
-for running tests is []().
+for running tests is [`cargo nextest`](https://nexte.st/).
### Run All Tests
``` bash
-NEXTEST_RETRIES=3 cargo nextest
+NEXTEST_RETRIES=3 cargo nextest run --all-features
```
### Run a Specific Test
diff --git a/Cargo.lock b/Cargo.lock
index 06b3e43..deea646 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,18 +4,18 @@ version = 4
[[package]]
name = "addr2line"
-version = "0.24.2"
+version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
-version = "2.0.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
@@ -26,12 +26,6 @@ dependencies = [
"memchr",
]
-[[package]]
-name = "android-tzdata"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
-
[[package]]
name = "android_system_properties"
version = "0.1.5"
@@ -43,9 +37,9 @@ dependencies = [
[[package]]
name = "anstream"
-version = "0.6.18"
+version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -58,43 +52,44 @@ dependencies = [
[[package]]
name = "anstyle"
-version = "1.0.10"
+version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.1.2"
+version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.6"
+version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
+checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
dependencies = [
"anstyle",
- "windows-sys 0.59.0",
+ "once_cell_polyfill",
+ "windows-sys 0.60.2",
]
[[package]]
name = "assert_cmd"
-version = "2.0.16"
+version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
+checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66"
dependencies = [
"anstyle",
"bstr",
@@ -114,15 +109,38 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
-version = "1.4.0"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "aws-lc-rs"
+version = "1.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d"
+dependencies = [
+ "aws-lc-sys",
+ "zeroize",
+]
+
+[[package]]
+name = "aws-lc-sys"
+version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c"
+dependencies = [
+ "bindgen",
+ "cc",
+ "cmake",
+ "dunce",
+ "fs_extra",
+]
[[package]]
name = "backtrace"
-version = "0.3.74"
+version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
+checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
dependencies = [
"addr2line",
"cfg-if",
@@ -130,7 +148,7 @@ dependencies = [
"miniz_oxide",
"object",
"rustc-demangle",
- "windows-targets",
+ "windows-link 0.2.1",
]
[[package]]
@@ -139,17 +157,52 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+[[package]]
+name = "bindgen"
+version = "0.72.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "itertools",
+ "log",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+ "syn",
+]
+
+[[package]]
+name = "bit-set"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+
[[package]]
name = "bitflags"
-version = "2.6.0"
+version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]]
name = "bstr"
-version = "1.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22"
+checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"regex-automata",
@@ -158,70 +211,91 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.16.0"
+version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytecount"
-version = "0.6.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
-
-[[package]]
-name = "byteorder"
-version = "1.5.0"
+version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
[[package]]
name = "bytes"
-version = "1.9.0"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
-version = "1.2.1"
+version = "1.2.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47"
+checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
dependencies = [
+ "find-msvc-tools",
+ "jobserver",
+ "libc",
"shlex",
]
+[[package]]
+name = "cexpr"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
+dependencies = [
+ "nom",
+]
+
[[package]]
name = "cfg-if"
-version = "1.0.0"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chrono"
-version = "0.4.38"
+version = "0.4.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
dependencies = [
- "android-tzdata",
"iana-time-zone",
"num-traits",
- "windows-targets",
+ "windows-link 0.2.1",
+]
+
+[[package]]
+name = "clang-sys"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading",
]
[[package]]
name = "clap"
-version = "4.5.21"
+version = "4.5.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
+checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623"
dependencies = [
"clap_builder",
- "clap_derive",
]
[[package]]
name = "clap_builder"
-version = "4.5.21"
+version = "4.5.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
+checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0"
dependencies = [
"anstream",
"anstyle",
@@ -230,22 +304,19 @@ dependencies = [
]
[[package]]
-name = "clap_derive"
-version = "4.5.18"
+name = "clap_lex"
+version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
-dependencies = [
- "heck 0.5.0",
- "proc-macro2",
- "quote",
- "syn 2.0.89",
-]
+checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
-name = "clap_lex"
-version = "0.7.3"
+name = "cmake"
+version = "0.1.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
+checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
+dependencies = [
+ "cc",
+]
[[package]]
name = "color-print"
@@ -265,14 +336,27 @@ dependencies = [
"nom",
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
]
[[package]]
name = "colorchoice"
-version = "1.0.3"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+
+[[package]]
+name = "console"
+version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4"
+dependencies = [
+ "encode_unicode",
+ "libc",
+ "once_cell",
+ "unicode-width",
+ "windows-sys 0.61.2",
+]
[[package]]
name = "core-foundation"
@@ -284,6 +368,16 @@ dependencies = [
"libc",
]
+[[package]]
+name = "core-foundation"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
@@ -292,9 +386,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crossbeam-deque"
-version = "0.8.5"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
@@ -311,9 +405,19 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
-version = "0.8.20"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "deranged"
+version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071"
+dependencies = [
+ "powerfmt",
+ "serde_core",
+]
[[package]]
name = "difflib"
@@ -321,6 +425,27 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
+[[package]]
+name = "dirs"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "displaydoc"
version = "0.2.5"
@@ -329,7 +454,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
]
[[package]]
@@ -338,11 +463,23 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
+[[package]]
+name = "dunce"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+
[[package]]
name = "either"
-version = "1.13.0"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "encode_unicode"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "encoding_rs"
@@ -355,31 +492,37 @@ dependencies = [
[[package]]
name = "equivalent"
-version = "1.0.1"
+version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
-version = "0.3.10"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
name = "fastrand"
-version = "2.2.0"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
+checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
[[package]]
name = "float-cmp"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
+checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8"
dependencies = [
"num-traits",
]
@@ -407,13 +550,19 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
-version = "1.2.1"
+version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
+[[package]]
+name = "fs_extra"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
+
[[package]]
name = "futures-channel"
version = "0.3.31"
@@ -466,26 +615,48 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"wasi",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "libc",
+ "r-efi",
+ "wasip2",
+ "wasm-bindgen",
]
[[package]]
name = "gimli"
-version = "0.31.1"
+version = "0.32.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
+
+[[package]]
+name = "glob"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "h2"
-version = "0.4.7"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
+checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
dependencies = [
"atomic-waker",
"bytes",
@@ -502,15 +673,9 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.15.2"
+version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
-
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]]
name = "heck"
@@ -518,17 +683,11 @@ 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"
+version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
+checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
dependencies = [
"bytes",
"fnv",
@@ -547,12 +706,12 @@ dependencies = [
[[package]]
name = "http-body-util"
-version = "0.1.2"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
- "futures-util",
+ "futures-core",
"http",
"http-body",
"pin-project-lite",
@@ -560,25 +719,27 @@ dependencies = [
[[package]]
name = "httparse"
-version = "1.9.5"
+version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "hyper"
-version = "1.5.1"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f"
+checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
dependencies = [
+ "atomic-waker",
"bytes",
"futures-channel",
- "futures-util",
+ "futures-core",
"h2",
"http",
"http-body",
"httparse",
"itoa",
"pin-project-lite",
+ "pin-utils",
"smallvec",
"tokio",
"want",
@@ -586,15 +747,15 @@ dependencies = [
[[package]]
name = "hyper-rustls"
-version = "0.27.3"
+version = "0.27.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
+checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
dependencies = [
- "futures-util",
"http",
"hyper",
"hyper-util",
"rustls",
+ "rustls-native-certs",
"rustls-pki-types",
"tokio",
"tokio-rustls",
@@ -619,33 +780,41 @@ dependencies = [
[[package]]
name = "hyper-util"
-version = "0.1.10"
+version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
+checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
dependencies = [
+ "base64",
"bytes",
"futures-channel",
+ "futures-core",
"futures-util",
"http",
"http-body",
"hyper",
+ "ipnet",
+ "libc",
+ "percent-encoding",
"pin-project-lite",
"socket2",
+ "system-configuration",
"tokio",
"tower-service",
"tracing",
+ "windows-registry",
]
[[package]]
name = "iana-time-zone"
-version = "0.1.61"
+version = "0.1.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
+ "log",
"wasm-bindgen",
"windows-core",
]
@@ -661,21 +830,22 @@ dependencies = [
[[package]]
name = "icu_collections"
-version = "1.5.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47"
dependencies = [
"displaydoc",
+ "potential_utf",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
-name = "icu_locid"
-version = "1.5.0"
+name = "icu_locale_core"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a"
dependencies = [
"displaydoc",
"litemap",
@@ -684,31 +854,11 @@ dependencies = [
"zerovec",
]
-[[package]]
-name = "icu_locid_transform"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_locid_transform_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
-
[[package]]
name = "icu_normalizer"
-version = "1.5.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979"
dependencies = [
"displaydoc",
"icu_collections",
@@ -716,72 +866,59 @@ dependencies = [
"icu_properties",
"icu_provider",
"smallvec",
- "utf16_iter",
- "utf8_iter",
- "write16",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
-version = "1.5.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3"
[[package]]
name = "icu_properties"
-version = "1.5.1"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b"
dependencies = [
"displaydoc",
"icu_collections",
- "icu_locid_transform",
+ "icu_locale_core",
"icu_properties_data",
"icu_provider",
- "tinystr",
+ "potential_utf",
+ "zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
-version = "1.5.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632"
[[package]]
name = "icu_provider"
-version = "1.5.0"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af"
dependencies = [
"displaydoc",
- "icu_locid",
- "icu_provider_macros",
+ "icu_locale_core",
"stable_deref_trait",
"tinystr",
"writeable",
"yoke",
"zerofrom",
+ "zerotrie",
"zerovec",
]
-[[package]]
-name = "icu_provider_macros"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.89",
-]
-
[[package]]
name = "idna"
-version = "1.0.3"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
@@ -790,9 +927,9 @@ dependencies = [
[[package]]
name = "idna_adapter"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
"icu_normalizer",
"icu_properties",
@@ -800,19 +937,42 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.6.0"
+version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
+checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
dependencies = [
"equivalent",
"hashbrown",
]
+[[package]]
+name = "indicatif"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2e0ddd45fe8e09ee1a607920b12271f8a5528a41ecaf6e1d1440d6493315b6b"
+dependencies = [
+ "console",
+ "portable-atomic",
+ "unicode-width",
+ "unit-prefix",
+ "web-time",
+]
+
[[package]]
name = "ipnet"
-version = "2.10.1"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
+
+[[package]]
+name = "iri-string"
+version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
+checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2"
+dependencies = [
+ "memchr",
+ "serde",
+]
[[package]]
name = "is_terminal_polyfill"
@@ -820,66 +980,112 @@ 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.14"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "jobserver"
+version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+dependencies = [
+ "getrandom 0.3.4",
+ "libc",
+]
[[package]]
name = "js-sys"
-version = "0.3.72"
+version = "0.3.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
+checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
dependencies = [
+ "once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
-version = "0.2.166"
+version = "0.2.177"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
+
+[[package]]
+name = "libloading"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36"
+checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
+dependencies = [
+ "cfg-if",
+ "windows-link 0.2.1",
+]
[[package]]
name = "libmimalloc-sys"
-version = "0.1.39"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23aa6811d3bd4deb8a84dde645f943476d13b248d818edcf8ce0b2f37f036b44"
+checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870"
dependencies = [
"cc",
"libc",
]
[[package]]
-name = "linux-raw-sys"
-version = "0.4.14"
+name = "libredox"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
-
-[[package]]
+checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
+dependencies = [
+ "bitflags",
+ "libc",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
+
+[[package]]
name = "litemap"
-version = "0.7.4"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
+checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
[[package]]
name = "log"
-version = "0.4.22"
+version = "0.4.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
+
+[[package]]
+name = "lru-slab"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "memchr"
-version = "2.7.4"
+version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "mimalloc"
-version = "0.1.43"
+version = "0.1.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68914350ae34959d83f732418d51e2427a794055d0b9529f48259ac07af65633"
+checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8"
dependencies = [
"libmimalloc-sys",
]
@@ -908,30 +1114,29 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
-version = "0.8.0"
+version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
-version = "1.0.2"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
+checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
dependencies = [
- "hermit-abi",
"libc",
"wasi",
- "windows-sys 0.52.0",
+ "windows-sys 0.61.2",
]
[[package]]
name = "native-tls"
-version = "0.2.12"
+version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
+checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
dependencies = [
"libc",
"log",
@@ -939,7 +1144,7 @@ dependencies = [
"openssl-probe",
"openssl-sys",
"schannel",
- "security-framework",
+ "security-framework 2.11.1",
"security-framework-sys",
"tempfile",
]
@@ -960,6 +1165,12 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -971,24 +1182,30 @@ dependencies = [
[[package]]
name = "object"
-version = "0.36.5"
+version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e"
+checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
-version = "1.20.2"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "openssl"
-version = "0.10.68"
+version = "0.10.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
+checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654"
dependencies = [
"bitflags",
"cfg-if",
@@ -1007,20 +1224,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
]
[[package]]
name = "openssl-probe"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
[[package]]
name = "openssl-sys"
-version = "0.9.104"
+version = "0.9.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
+checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2"
dependencies = [
"cc",
"libc",
@@ -1028,11 +1245,26 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "ordered-float"
+version = "2.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "papergrid"
-version = "0.13.0"
+version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2b0f8def1f117e13c895f3eda65a7b5650688da29d6ad04635f61bc7b92eebd"
+checksum = "6978128c8b51d8f4080631ceb2302ab51e32cc6e8615f735ee2f83fd269ae3f1"
dependencies = [
"bytecount",
"fnv",
@@ -1041,15 +1273,15 @@ dependencies = [
[[package]]
name = "percent-encoding"
-version = "2.3.1"
+version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pin-project-lite"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
@@ -1059,24 +1291,45 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
-version = "0.3.31"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "portable-atomic"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
+
+[[package]]
+name = "potential_utf"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
-version = "0.2.20"
+version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "predicates"
-version = "3.1.2"
+version = "3.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97"
+checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
dependencies = [
"anstyle",
"difflib",
@@ -1088,20 +1341,30 @@ dependencies = [
[[package]]
name = "predicates-core"
-version = "1.0.8"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931"
+checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
[[package]]
name = "predicates-tree"
-version = "1.0.11"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13"
+checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
dependencies = [
"predicates-core",
"termtree",
]
+[[package]]
+name = "prettyplease"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
+dependencies = [
+ "proc-macro2",
+ "syn",
+]
+
[[package]]
name = "proc-macro-error-attr2"
version = "2.0.0"
@@ -1121,35 +1384,125 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
]
[[package]]
name = "proc-macro2"
-version = "1.0.92"
+version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
+[[package]]
+name = "proptest"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40"
+dependencies = [
+ "bit-set",
+ "bit-vec",
+ "bitflags",
+ "num-traits",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+ "regex-syntax",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quinn"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
+dependencies = [
+ "bytes",
+ "cfg_aliases",
+ "pin-project-lite",
+ "quinn-proto",
+ "quinn-udp",
+ "rustc-hash",
+ "rustls",
+ "socket2",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.11.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
+dependencies = [
+ "bytes",
+ "getrandom 0.3.4",
+ "lru-slab",
+ "rand",
+ "ring",
+ "rustc-hash",
+ "rustls",
+ "rustls-pki-types",
+ "slab",
+ "thiserror",
+ "tinyvec",
+ "tracing",
+ "web-time",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.5.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
+dependencies = [
+ "cfg_aliases",
+ "libc",
+ "once_cell",
+ "socket2",
+ "tracing",
+ "windows-sys 0.60.2",
+]
+
[[package]]
name = "quote"
-version = "1.0.37"
+version = "1.0.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
dependencies = [
"proc-macro2",
]
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
[[package]]
name = "rabbitmq_http_client"
-version = "0.9.0"
-source = "git+https://github.com/michaelklishin/rabbitmq-http-api-rs#8c6454142ea2605029b973db687e96e1863668c1"
+version = "0.66.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f429603d727775bf2761cfc7f2f75d9cfb1ad99ff059e4458f95040ce9ed7b7"
dependencies = [
+ "backtrace",
+ "log",
"percent-encoding",
"rand",
"rbase64",
+ "regex",
"reqwest",
"ring",
"serde",
@@ -1157,20 +1510,30 @@ dependencies = [
"serde_json",
"tabled",
"thiserror",
+ "time",
+ "tokio",
+ "url",
+ "urlencoding",
]
[[package]]
name = "rabbitmqadmin"
-version = "0.11.0"
+version = "2.16.0"
dependencies = [
"assert_cmd",
"clap",
"color-print",
+ "indicatif",
+ "log",
"predicates",
+ "proptest",
"rabbitmq_http_client",
+ "regex",
"reqwest",
+ "rustls",
"serde",
"serde_json",
+ "shellexpand",
"sysexits",
"tabled",
"thiserror",
@@ -1180,20 +1543,19 @@ dependencies = [
[[package]]
name = "rand"
-version = "0.8.5"
+version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
- "libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
-version = "0.3.1"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
@@ -1201,18 +1563,27 @@ dependencies = [
[[package]]
name = "rand_core"
-version = "0.6.4"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
+dependencies = [
+ "getrandom 0.3.4",
+]
+
+[[package]]
+name = "rand_xorshift"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
dependencies = [
- "getrandom",
+ "rand_core",
]
[[package]]
name = "rayon"
-version = "1.10.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
dependencies = [
"either",
"rayon-core",
@@ -1220,9 +1591,9 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.12.1"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
@@ -1238,11 +1609,22 @@ dependencies = [
"rayon",
]
+[[package]]
+name = "redox_users"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
+dependencies = [
+ "getrandom 0.2.16",
+ "libredox",
+ "thiserror",
+]
+
[[package]]
name = "regex"
-version = "1.11.1"
+version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
@@ -1252,9 +1634,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.9"
+version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
@@ -1263,15 +1645,15 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.8.5"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "reqwest"
-version = "0.12.9"
+version = "0.12.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
+checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
dependencies = [
"base64",
"bytes",
@@ -1287,72 +1669,82 @@ dependencies = [
"hyper-rustls",
"hyper-tls",
"hyper-util",
- "ipnet",
"js-sys",
"log",
"mime",
"mime_guess",
"native-tls",
- "once_cell",
"percent-encoding",
"pin-project-lite",
- "rustls-pemfile",
+ "quinn",
+ "rustls",
+ "rustls-native-certs",
+ "rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
- "system-configuration",
"tokio",
"tokio-native-tls",
+ "tokio-rustls",
+ "tower",
+ "tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
- "windows-registry",
]
[[package]]
name = "ring"
-version = "0.17.8"
+version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
- "getrandom",
+ "getrandom 0.2.16",
"libc",
- "spin",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rustc-demangle"
-version = "0.1.24"
+version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
+
+[[package]]
+name = "rustc-hash"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustix"
-version = "0.38.41"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
+checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.52.0",
+ "windows-sys 0.61.2",
]
[[package]]
name = "rustls"
-version = "0.23.19"
+version = "0.23.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1"
+checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7"
dependencies = [
+ "aws-lc-rs",
+ "log",
"once_cell",
+ "ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
@@ -1360,44 +1752,70 @@ dependencies = [
]
[[package]]
-name = "rustls-pemfile"
-version = "2.2.0"
+name = "rustls-native-certs"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
+checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923"
dependencies = [
+ "openssl-probe",
"rustls-pki-types",
+ "schannel",
+ "security-framework 3.5.1",
]
[[package]]
name = "rustls-pki-types"
-version = "1.10.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b"
+checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
+dependencies = [
+ "web-time",
+ "zeroize",
+]
[[package]]
name = "rustls-webpki"
-version = "0.102.8"
+version = "0.103.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
+checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf"
dependencies = [
+ "aws-lc-rs",
"ring",
"rustls-pki-types",
"untrusted",
]
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "rusty-fork"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
+]
+
[[package]]
name = "ryu"
-version = "1.0.18"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "schannel"
-version = "0.1.27"
+version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
+checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1407,7 +1825,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags",
- "core-foundation",
+ "core-foundation 0.9.4",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework"
+version = "3.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
+dependencies = [
+ "bitflags",
+ "core-foundation 0.10.1",
"core-foundation-sys",
"libc",
"security-framework-sys",
@@ -1415,9 +1846,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "2.12.1"
+version = "2.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2"
+checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0"
dependencies = [
"core-foundation-sys",
"libc",
@@ -1425,54 +1856,76 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.215"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
+ "serde_core",
"serde_derive",
]
[[package]]
name = "serde-aux"
-version = "4.5.0"
+version = "4.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d2e8bfba469d06512e11e3311d4d051a4a387a5b42d010404fecf3200321c95"
+checksum = "207f67b28fe90fb596503a9bf0bf1ea5e831e21307658e177c5dfcdfc3ab8a0a"
dependencies = [
"chrono",
"serde",
+ "serde-value",
"serde_json",
]
+[[package]]
+name = "serde-value"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
+dependencies = [
+ "ordered-float",
+ "serde",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
[[package]]
name = "serde_derive"
-version = "1.0.215"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
]
[[package]]
name = "serde_json"
-version = "1.0.133"
+version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
+checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
+ "serde_core",
]
[[package]]
name = "serde_spanned"
-version = "0.6.8"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392"
dependencies = [
- "serde",
+ "serde_core",
]
[[package]]
@@ -1487,6 +1940,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "shellexpand"
+version = "3.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb"
+dependencies = [
+ "dirs",
+]
+
[[package]]
name = "shlex"
version = "1.3.0"
@@ -1495,40 +1957,31 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "slab"
-version = "0.4.9"
+version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
-dependencies = [
- "autocfg",
-]
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "smallvec"
-version = "1.13.2"
+version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
-version = "0.5.8"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
+checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
dependencies = [
"libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.60.2",
]
-[[package]]
-name = "spin"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-
[[package]]
name = "stable_deref_trait"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "strsim"
@@ -1544,20 +1997,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
-version = "1.0.109"
+version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.89"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
+checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
@@ -1575,20 +2017,20 @@ dependencies = [
[[package]]
name = "synstructure"
-version = "0.13.1"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
]
[[package]]
name = "sysexits"
-version = "0.8.5"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a70e00b0ea6c3e7154dfc48fee004bf61bdc154fd99b7e17c05318503b9660b"
+checksum = "bf9154bb31a0b747e214520c60cb9a842df871bf6a5fea5be4352b59d6c432ab"
[[package]]
name = "system-configuration"
@@ -1597,7 +2039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags",
- "core-foundation",
+ "core-foundation 0.9.4",
"system-configuration-sys",
]
@@ -1613,89 +2055,144 @@ dependencies = [
[[package]]
name = "tabled"
-version = "0.17.0"
+version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6709222f3973137427ce50559cd564dc187a95b9cfe01613d2f4e93610e510a"
+checksum = "e39a2ee1fbcd360805a771e1b300f78cc88fec7b8d3e2f71cd37bbf23e725c7d"
dependencies = [
"papergrid",
"tabled_derive",
+ "testing_table",
]
[[package]]
name = "tabled_derive"
-version = "0.9.0"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "931be476627d4c54070a1f3a9739ccbfec9b36b39815106a20cce2243bbcefe1"
+checksum = "0ea5d1b13ca6cff1f9231ffd62f15eefd72543dab5e468735f1a456728a02846"
dependencies = [
- "heck 0.4.1",
+ "heck",
"proc-macro-error2",
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn",
]
[[package]]
name = "tempfile"
-version = "3.14.0"
+version = "3.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
+checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
dependencies = [
- "cfg-if",
"fastrand",
+ "getrandom 0.3.4",
"once_cell",
"rustix",
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
]
[[package]]
name = "termtree"
-version = "0.4.1"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
+checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
+
+[[package]]
+name = "testing_table"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f8daae29995a24f65619e19d8d31dea5b389f3d853d8bf297bbf607cd0014cc"
+dependencies = [
+ "unicode-width",
+]
[[package]]
name = "thiserror"
-version = "2.0.3"
+version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa"
+checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "2.0.3"
+version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568"
+checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
+]
+
+[[package]]
+name = "time"
+version = "0.3.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
+
+[[package]]
+name = "time-macros"
+version = "0.2.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
+dependencies = [
+ "num-conv",
+ "time-core",
]
[[package]]
name = "tinystr"
-version = "0.7.6"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b"
dependencies = [
"displaydoc",
"zerovec",
]
+[[package]]
+name = "tinyvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
[[package]]
name = "tokio"
-version = "1.41.1"
+version = "1.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33"
+checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
dependencies = [
- "backtrace",
"bytes",
"libc",
"mio",
"pin-project-lite",
"socket2",
- "windows-sys 0.52.0",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -1710,20 +2207,19 @@ dependencies = [
[[package]]
name = "tokio-rustls"
-version = "0.26.0"
+version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
dependencies = [
"rustls",
- "rustls-pki-types",
"tokio",
]
[[package]]
name = "tokio-util"
-version = "0.7.12"
+version = "0.7.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a"
+checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
dependencies = [
"bytes",
"futures-core",
@@ -1734,38 +2230,82 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.8.19"
+version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8"
dependencies = [
- "serde",
+ "indexmap",
+ "serde_core",
"serde_spanned",
"toml_datetime",
- "toml_edit",
+ "toml_parser",
+ "toml_writer",
+ "winnow",
]
[[package]]
name = "toml_datetime"
-version = "0.6.8"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533"
dependencies = [
- "serde",
+ "serde_core",
]
[[package]]
-name = "toml_edit"
-version = "0.22.22"
+name = "toml_parser"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
+checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e"
dependencies = [
- "indexmap",
- "serde",
- "serde_spanned",
- "toml_datetime",
"winnow",
]
+[[package]]
+name = "toml_writer"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
+
+[[package]]
+name = "tower"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
+dependencies = [
+ "bitflags",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "iri-string",
+ "pin-project-lite",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[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"
@@ -1784,9 +2324,9 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.33"
+version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
dependencies = [
"once_cell",
]
@@ -1797,23 +2337,35 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
[[package]]
name = "unicase"
-version = "2.8.0"
+version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
+checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
-version = "1.0.14"
+version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]]
name = "unicode-width"
-version = "0.2.0"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
+
+[[package]]
+name = "unit-prefix"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817"
[[package]]
name = "untrusted"
@@ -1823,20 +2375,21 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
-version = "2.5.4"
+version = "2.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
+ "serde",
]
[[package]]
-name = "utf16_iter"
-version = "1.0.5"
+name = "urlencoding"
+version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "utf8_iter"
@@ -1858,9 +2411,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "wait-timeout"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
+checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
dependencies = [
"libc",
]
@@ -1876,53 +2429,64 @@ dependencies = [
[[package]]
name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.1+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
+dependencies = [
+ "wit-bindgen",
+]
[[package]]
name = "wasm-bindgen"
-version = "0.2.95"
+version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
+checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
dependencies = [
"cfg-if",
"once_cell",
+ "rustversion",
"wasm-bindgen-macro",
+ "wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.95"
+version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
+checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
dependencies = [
"bumpalo",
"log",
- "once_cell",
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.45"
+version = "0.4.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b"
+checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c"
dependencies = [
"cfg-if",
"js-sys",
+ "once_cell",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.95"
+version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
+checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1930,28 +2494,41 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.95"
+version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
+checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.95"
+version = "0.2.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
+checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
+dependencies = [
+ "unicode-ident",
+]
[[package]]
name = "web-sys"
-version = "0.3.72"
+version = "0.3.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -1959,41 +2536,96 @@ dependencies = [
[[package]]
name = "windows-core"
-version = "0.52.0"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link 0.2.1",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
- "windows-targets",
+ "proc-macro2",
+ "quote",
+ "syn",
]
+[[package]]
+name = "windows-link"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
[[package]]
name = "windows-registry"
-version = "0.2.0"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
+checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
dependencies = [
- "windows-result",
- "windows-strings",
- "windows-targets",
+ "windows-link 0.1.3",
+ "windows-result 0.3.4",
+ "windows-strings 0.4.2",
]
[[package]]
name = "windows-result"
-version = "0.2.0"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
- "windows-targets",
+ "windows-link 0.2.1",
]
[[package]]
name = "windows-strings"
-version = "0.1.0"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
+dependencies = [
+ "windows-link 0.1.3",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
- "windows-result",
- "windows-targets",
+ "windows-link 0.2.1",
]
[[package]]
@@ -2002,16 +2634,25 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
]
[[package]]
name = "windows-sys"
-version = "0.59.0"
+version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
- "windows-targets",
+ "windows-link 0.2.1",
]
[[package]]
@@ -2020,14 +2661,31 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "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-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link 0.2.1",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
]
[[package]]
@@ -2036,74 +2694,119 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
[[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_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
[[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_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
[[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_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
[[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_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
[[package]]
name = "winnow"
-version = "0.6.20"
+version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
-dependencies = [
- "memchr",
-]
+checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
[[package]]
-name = "write16"
-version = "1.0.0"
+name = "wit-bindgen"
+version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "writeable"
-version = "0.5.5"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
[[package]]
name = "yoke"
-version = "0.7.5"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc"
dependencies = [
"serde",
"stable_deref_trait",
@@ -2113,69 +2816,79 @@ dependencies = [
[[package]]
name = "yoke-derive"
-version = "0.7.5"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
"synstructure",
]
[[package]]
name = "zerocopy"
-version = "0.7.35"
+version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
- "byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.7.35"
+version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
]
[[package]]
name = "zerofrom"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
"synstructure",
]
[[package]]
name = "zeroize"
-version = "1.8.1"
+version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+
+[[package]]
+name = "zerotrie"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
[[package]]
name = "zerovec"
-version = "0.10.4"
+version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
dependencies = [
"yoke",
"zerofrom",
@@ -2184,11 +2897,11 @@ dependencies = [
[[package]]
name = "zerovec-derive"
-version = "0.10.3"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.89",
+ "syn",
]
diff --git a/Cargo.toml b/Cargo.toml
index 6b6421c..718ce4c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,25 +1,46 @@
[package]
name = "rabbitmqadmin"
-version = "0.11.0"
-edition = "2021"
+version = "2.16.0"
+edition = "2024"
+
+description = "rabbitmqadmin v2 is a modern CLI tool for the RabbitMQ HTTP API"
+license = "MIT OR Apache-2.0"
[dependencies]
-clap = { version = "4.5", features = ["derive", "help", "color", "cargo"] }
+clap = { version = "4", features = ["help", "color", "cargo", "env"] }
url = "2"
-sysexits = "0.8.1"
+sysexits = "0.10"
reqwest = { version = "0.12", features = [
+ "blocking",
"json",
"multipart",
- "default-tls"
+ "__rustls",
+ "rustls-tls-native-roots",
+] }
+rabbitmq_http_client = { version = "0.66.0", features = [
+ "blocking",
+ "tabled",
] }
-rabbitmq_http_client = { git = "https://github.com/michaelklishin/rabbitmq-http-api-rs", features = ["core", "tabled"] }
serde = { version = "1.0", features = ["derive", "std"] }
serde_json = "1"
-tabled = "0.17"
-toml = "0.8"
+tabled = "0.20"
+toml = "0.9"
color-print = "0.3"
thiserror = "2"
+shellexpand = "3.1"
+regex = "1"
+indicatif = "0.18"
+
+log = "0.4"
+rustls = { version = "0.23", features = ["aws_lc_rs"] }
+# fern = "0.7"
+# humantime = "2.1.0"
+# backtrace = "0.3"
[dev-dependencies]
assert_cmd = "2.0"
predicates = "3.1"
+proptest = "1.9"
+
+[lints.clippy]
+uninlined_format_args = "allow"
diff --git a/README.md b/README.md
index edb848f..c2b8950 100644
--- a/README.md
+++ b/README.md
@@ -1,62 +1,249 @@
-# rabbitmqadmin v2
+# rabbitmqadmin v2: a Modern Command Line Client for the [RabbitMQ HTTP API](https://www.rabbitmq.com/docs/management#http-api)
-`rabbitmqadmin` v2 is a major revision of one of the RabbitMQ's CLI tools.
+`rabbitmqadmin` v2 is a major revision of `rabbitmqadmin`, one of the [RabbitMQ CLI tools](https://www.rabbitmq.com/docs/cli)
+that target the [HTTP API](https://www.rabbitmq.com/docs/management#http-api).
-If you are migrating from the original `rabbitqadmin`, please see [CLI Changes](#cli-changes)
-to learn about a few breaking change in the interface.
+If you are migrating from the original `rabbitqadmin`, please see [Breaking or Potentially Breaking Changes](#breaking-or-potentially-breaking-changes)
+to learn about the breaking changes in the command line interface.
+
+The general "shape and feel" of the interface is still very similar to `rabbitmqadmin` v1. However, this generation
+is significantly more powerful, in particular, when it comes to [Blue-Green Deployment upgrades and migrations](https://www.rabbitmq.com/blog/2025/07/29/latest-benefits-of-rmq-and-migrating-to-qq-along-the-way)
+from RabbitMQ 3.13.x to 4.x.
+
+
+## Supported RabbitMQ Series
+
+`rabbitmqadmin` v2 targets
+
+ * Open source RabbitMQ `4.x`
+ * Open source RabbitMQ `3.13.x` (specifically for the command groups and commands related to upgrades)
+ * Tanzu RabbitMQ `4.x`
+ * Tanzu RabbitMQ `3.13.x`
+
+
+## Getting Started
+
+### Installation
+
+#### Binary Releases
To download a binary build, see [Releases](https://github.com/rabbitmq/rabbitmqadmin-ng/releases).
-For usage documentation, see [Usage](#usage).
+#### Building from Source with `cargo install`
+On platforms not covered by the binary builds, `rabbitmqadmin` v2 can be installed with [Cargo](https://doc.rust-lang.org/cargo/commands/cargo-install.html):
-## Project Goals
+```shell
+cargo install rabbitmqadmin
+```
-This version of `rabbitmqadmin` has a few ideas in mind:
+### Documentation
- * This is a major version bump. Therefore, breaking changes are OK. `rabbitmqadmin` hasn't seen a revision in thirteen years
- * `rabbitmqadmin` should be standalone binary. There are very few reasons not to build and distribute it that way
- * v2 should be a distributed via GitHub releases and not a special `rabbitmq_management` endpoint
- * There is a lot of room to improve validation of flags and arguments, since breaking changes are OK for v2
- * Output should be revisited: what columns are output by default, whether columns should be selectable
- * Support for JSON and CSV was a popular addition in `rabbitmqctl`, `rabbitmq-diagnostics`, etc. Perhaps `rabbitmqadmin` should consider supporting them, too?
+For usage documentation, see the [dedicated RabbitMQ doc guide](https://www.rabbitmq.com/docs/management-cli) and/or [Usage](#usage) below.
+
+
+## Getting Help
+
+Please use GitHub Discussions in this repository and [RabbitMQ community Discord server](https://rabbitmq.com/discord/).
## Project Maturity
This version of `rabbitmqadmin` should be considered reasonably mature to be used.
-Before migrating, please see [CLI Changes](#cli-changes) to learn about a few breaking change in the interface.
+Before migrating, please see [Breaking or Potentially Breaking Changes](#breaking-or-potentially-breaking-changes) to learn about a few breaking change in the interface.
-### Known Limitations
-The following `rabbitmqadmin` v1 features are not currently implemented:
+## Usage
-* [Configuration file support](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/28)
-* Support for TLS client (x.509, HTTPS) [certificate and private key](https://github.com/rabbitmq/rabbitmqadmin-ng/issues/26)
+### Exploring Available Command Groups and Sub-commands
+To explore what command groups are available, use
-## Usage
+```shell
+rabbitmqadmin help
+```
-### Getting Help
+which will output a list of command groups:
-To learn about what command groups and specific commands are available, run
+```
+Usage: rabbitmqadmin [OPTIONS]
-``` shell
-rabbitmqadmin --help
+Commands:
+ bindings Operations on bindings
+ channels Operations on channels
+ close Closes connections
+ connections Operations on connections
+ declare Creates or declares objects
+ definitions Operations on definitions (everything except for messages: virtual hosts, queues, streams, exchanges, bindings, users, etc)
+ delete Deletes objects
+ deprecated_features Operations on deprecated features
+ exchanges Operations on exchanges
+ export See 'definitions export'
+ feature_flags Operations on feature flags
+ federation Operations on federation upstreams and links
+ get Fetches message(s) from a queue or stream via polling. Only suitable for development and test environments.
+ global_parameters Operations on global runtime parameters
+ health_check Runs health checks
+ import See 'definitions import'
+ list Lists objects
+ nodes Node operations
+ operator_policies Operations on operator policies
+ parameters Operations on runtime parameters
+ passwords Operations on passwords
+ policies Operations on policies
+ publish Publishes (inefficiently) message(s) to a queue or a stream. Only suitable for development and test environments.
+ purge Purges queues
+ queues Operations on queues
+ rebalance Rebalancing of leader replicas
+ show Overview, memory footprint breakdown, and more
+ shovels Operations on shovels
+ streams Operations on streams
+ tanzu Tanzu RabbitMQ-specific commands
+ users Operations on users
+ vhosts Virtual host operations
+ help Print this message or the help of the given subcommand(s)
```
-Note that in this version, **global flags must precede the command category (e.g. `list`) and the command itself**:
+To explore commands in a specific group, use
```shell
-rabbitmqadmin --vhost "events" declare queue --name "target.quorum.queue.name" --type "quorum" --durable true
+rabbitmqadmin {group name} help
```
-The same command will display global flags. To learn about a specific command, append
-`--help` to it:
+### Exploring the CLI with `help`, `--help`
+
+To learn about what command groups and specific commands are available, run
``` shell
+rabbitmqadmin help
+```
+
+This flag can be appended to a command or subcommand to get command-specific documentation:
+
+```shell
rabbitmqadmin declare queue --help
+# => creates or declares things
+# =>
+# => Usage: rabbitmqadmin declare [object]
+# => ...
+```
+
+Alternatively, the `help` subcommand can be given a command name. It's the equivalent
+of tagging on `--help` at the end of command name:
+
+```shell
+rabbitmqadmin declare help queue
+# => declares a queue or a stream
+# =>
+# => Usage: rabbitmqadmin declare queue [OPTIONS] --name
+```
+
+More specific examples are covered in the Examples section below.
+
+
+### Interactive vs. Use in Scripts
+
+Like the original version, `rabbitmqadmin` v2 is first and foremost built for interactive use
+by humans. Many commands will output formatted tables, for example:
+
+```shell
+rabbitmqadmin show overview
+```
+
+will output a table that looks like this:
+
+```
+┌───────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────┐
+│ Overview │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ key │ value │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Product name │ RabbitMQ │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Product version │ 4.1.2 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ RabbitMQ version │ 4.1.2 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Erlang version │ 27.3.4 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Erlang details │ Erlang/OTP 27 [erts-15.2.5] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit] │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Connections (total) │ 4 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ AMQP 0-9-1 channels (total) │ 4 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Queues and streams (total) │ 4 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Consumers (total) │ 4 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Messages (total) │ 222 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Messages ready for delivery (total) │ 2 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Messages delivered but unacknowledged by consumers (total) │ 220 │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Publishing (ingress) rate (global) │ │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Publishing confirm rate (global) │ │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Consumer delivery (egress) rate (global) │ │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Consumer delivery in automatic acknowledgement mode rate (global) │ │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Consumer acknowledgement rate (global) │ │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Unroutable messages: returned-to-publisher rate (global) │ │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Unroutable messages: dropped rate (global) │ │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Cluster tags │ "az": "us-east-3" │
+│ │ "environment": "production" │
+│ │ "region": "us-east" │
+│ │ │
+├───────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Node tags │ "environment": "production" │
+│ │ "instance": "xlarge.m3" │
+│ │ │
+└───────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────┘
+```
+
+As it is easy to observe, parsing such output in a script will be challenging.
+
+For this reason, `rabbitmqadmin` v2 can render results in a way that would be much more friendly
+for scripting if the `--non-interactive` flag is passed. It is a global flag so it must be
+passed before the command and subcommand name:
+
+```shell
+rabbitmqadmin --non-interactive show overview
+```
+
+The output of the above command will not include any table borders and will is much easier to parse
+as a result:
+
+```
+key
+Product name RabbitMQ
+Product version 4.1.2
+RabbitMQ version 4.1.2
+Erlang version 27.3.4
+Erlang details Erlang/OTP 27 [erts-15.2.7] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit]
+Connections (total) 0
+AMQP 0-9-1 channels (total) 0
+Queues and streams (total) 3
+Consumers (total) 0
+Messages (total) 0
+Messages ready for delivery (total) 0
+Messages delivered but unacknowledged by consumers (total) 0
+Publishing (ingress) rate (global)
+Publishing confirm rate (global)
+Consumer delivery (egress) rate (global)
+Consumer delivery in automatic acknowledgement mode rate (global)
+Consumer acknowledgement rate (global)
+Unroutable messages: returned-to-publisher rate (global)
+Unroutable messages: dropped rate (global)
+Cluster tags "az": "us-east-3","environment": "production","region": "us-east",
+Node tags "environment": "production","instance": "xlarge.m3",
```
### Retrieving Basic Node Information
@@ -65,12 +252,14 @@ rabbitmqadmin declare queue --help
rabbitmqadmin show overview
```
+will display essential node information in tabular form.
+
### Retrieving Connection, Queue/Stream, Channel Churn Information
-Helps assess connection, queue/stream, channel [churn metrics](https://www.rabbitmq.com/docs/connections#high-connection-churn) in the cluster.
+Helps assess connection, queue/stream, channel [churn metrics](https://rabbitmq.com/docs/connections#high-connection-churn) in the cluster.
``` shell
-rabbitmqadmin show overview
+rabbitmqadmin show churn
```
### Listing cluster nodes
@@ -121,7 +310,25 @@ rabbitmqadmin list bindings
rabbitmqadmin --vhost "events" list bindings
```
-### Declare a queue
+### Create a Virtual Host
+
+```shell
+rabbitmqadmin declare vhost --name "vh-789" --default-queue-type "quorum" --description "Used to reproduce issue #789"
+```
+
+### Delete a Virtual Host
+
+```shell
+rabbitmqadmin delete vhost --name "vh-789"
+```
+
+```shell
+# --idempotently means that 404 Not Found responses will not be considered errors
+rabbitmqadmin delete vhost --name "vh-789" --idempotently
+```
+
+
+### Declare a Queue
```shell
rabbitmqadmin --vhost "events" declare queue --name "target.quorum.queue.name" --type "quorum" --durable true
@@ -132,7 +339,13 @@ rabbitmqadmin --vhost "events" declare queue --name "target.stream.name" --type
```
```shell
-rabbitmqadmin --vhost "events" declare queue --name "target.classic.queue.name" --type "classic" --durable false --auto_delete true
+rabbitmqadmin --vhost "events" declare queue --name "target.classic.queue.name" --type "classic" --durable true --auto-delete false
+```
+
+### Purge a queue
+
+```
+rabbitmqadmin --vhost "events" purge queue --name "target.queue.name"
```
### Delete a queue
@@ -141,18 +354,821 @@ rabbitmqadmin --vhost "events" declare queue --name "target.classic.queue.name"
rabbitmqadmin --vhost "events" delete queue --name "target.queue.name"
```
-## Differences from `rabbitmqadmin` v1
+``` shell
+# --idempotently means that 404 Not Found responses will not be considered errors
+rabbitmqadmin --vhost "events" delete queue --name "target.queue.name" --idempotently
+```
+
+### Declare an Exchange
+
+```shell
+rabbitmqadmin --vhost "events" declare exchange --name "events.all_types.topic" --type "topic" --durable true
+```
+
+```shell
+rabbitmqadmin --vhost "events" declare exchange --name "events.all_type.uncategorized" --type "fanout" --durable true --auto-delete false
+```
+
+```shell
+rabbitmqadmin --vhost "events" declare exchange --name "local.random.c60bda92" --type "x-local-random" --durable true
+```
+
+### Delete an exchange
+
+``` shell
+rabbitmqadmin --vhost "events" delete exchange --name "target.exchange.name"
+```
+
+``` shell
+# --idempotently means that 404 Not Found responses will not be considered errors
+rabbitmqadmin --vhost "events" delete exchange --name "target.exchange.name" --idempotently
+```
+
+### Inspecting Node Memory Breakdown
+
+There are two commands for reasoning about target [node's memory footprint](https://rabbitmq.com/docs/memory-use):
+
+```shell
+# displays a breakdown in bytes
+rabbitmqadmin show memory_breakdown_in_bytes --node 'rabbit@hostname'
+```
+
+```shell
+# displays a breakdown in percent
+rabbitmqadmin show memory_breakdown_in_percent --node 'rabbit@hostname'
+```
+
+Example output of `show memory_breakdown_in_percent`:
+
+```
+┌────────────────────────────────────────┬────────────┐
+│ key │ percentage │
+├────────────────────────────────────────┼────────────┤
+│ total │ 100% │
+├────────────────────────────────────────┼────────────┤
+│ Binary heap │ 45.10% │
+├────────────────────────────────────────┼────────────┤
+│ Allocated but unused │ 23.45% │
+├────────────────────────────────────────┼────────────┤
+│ Quorum queue ETS tables │ 23.05% │
+├────────────────────────────────────────┼────────────┤
+│ Other processes │ 5.32% │
+├────────────────────────────────────────┼────────────┤
+│ Other (used by the runtime) │ 4.98% │
+├────────────────────────────────────────┼────────────┤
+│ Code │ 4.54% │
+├────────────────────────────────────────┼────────────┤
+│ Client connections: others processes │ 3.64% │
+├────────────────────────────────────────┼────────────┤
+│ Management stats database │ 3.48% │
+├────────────────────────────────────────┼────────────┤
+│ Client connections: reader processes │ 3.22% │
+├────────────────────────────────────────┼────────────┤
+│ Plugins and their data │ 3.12% │
+├────────────────────────────────────────┼────────────┤
+│ Other (ETS tables) │ 1.55% │
+├────────────────────────────────────────┼────────────┤
+│ Metrics data │ 0.66% │
+├────────────────────────────────────────┼────────────┤
+│ AMQP 0-9-1 channels │ 0.40% │
+├────────────────────────────────────────┼────────────┤
+│ Message store indices │ 0.27% │
+├────────────────────────────────────────┼────────────┤
+│ Atom table │ 0.24% │
+├────────────────────────────────────────┼────────────┤
+│ Client connections: writer processes │ 0.19% │
+├────────────────────────────────────────┼────────────┤
+│ Quorum queue replica processes │ 0.10% │
+├────────────────────────────────────────┼────────────┤
+│ Stream replica processes │ 0.07% │
+├────────────────────────────────────────┼────────────┤
+│ Mnesia │ 0.02% │
+├────────────────────────────────────────┼────────────┤
+│ Metadata store │ 0.02% │
+├────────────────────────────────────────┼────────────┤
+│ Stream coordinator processes │ 0.02% │
+├────────────────────────────────────────┼────────────┤
+│ Classic queue processes │ 0.00% │
+├────────────────────────────────────────┼────────────┤
+│ Metadata store ETS tables │ 0.00% │
+├────────────────────────────────────────┼────────────┤
+│ Stream replica reader processes │ 0.00% │
+├────────────────────────────────────────┼────────────┤
+│ Reserved by the kernel but unallocated │ 0.00% │
+└────────────────────────────────────────┴────────────┘
+```
+
+Note that there are [two different supported strategies](https://rabbitmq.com/docs/memory-use#strategies)
+for computing memory footprint of a node. `rabbitmqadmin` will use the greater value
+for 100% when computing the relative share in percent for each category.
+
+Other factors that can affect the precision of percentage values reported
+are [runtime allocator](https://rabbitmq.com/docs/memory-use#preallocated-memory)
+behavior nuances and the [kernel page cache](https://rabbitmq.com/docs/memory-use#page-cache).
+
+### List feature flags and their state
+
+```shell
+rabbitmqadmin feature_flags list
+```
+
+```shell
+# same command as above
+rabbitmqadmin list feature_flags
+```
+
+### Enable a feature flag
+
+```shell
+rabbitmqadmin feature_flags enable rabbitmq_4.0.0
+```
+
+### Enable all stable feature flags
+
+```shell
+rabbitmqadmin feature_flags enable_all
+```
+
+### List deprecated features in use in the cluster
+
+```shell
+rabbitmqadmin deprecated_features list_used
+```
+
+### List all deprecated features
+
+```shell
+rabbitmqadmin deprecated_features list
+```
+
+```shell
+# same command as above
+rabbitmqadmin list deprecated_features
+```
+
+### Export Definitions
+
+To export [definitions](https://www.rabbitmq.com/docs/definitions) to standard output, use `definitions export --stdout`:
+
+```shell
+rabbitmqadmin definitions export --stdout
+```
+
+To export definitions to a file, use `definitions export --file /path/to/definitions.file.json`:
+
+```shell
+rabbitmqadmin definitions export --file /path/to/definitions.file.json
+```
+
+### Export and Transform Definitions
+
+`definitions export` can transform the exported JSON definitions file it gets from the
+target node. This is done by applying one or more transformations to the exported
+JSON file.
+
+This can be useful to remove classic queue mirroring-related keys (such as `ha-mode`) from a definitions
+set originating from a 3.13.x node, or to obfuscate usernames and passwords, or exclude certain definitions file
+sections entirely.
+
+To specify what transformations should be applied, use the `--transformations` options,
+which takes a comma-separated list of supported operation names.
+
+The following table explains what transformations are available and what they do:
+
+| Transformation name | Description |
+|--------------------------------|--------------------------------------------------------------|
+| `strip_cmq_keys_from_policies` | Deletes all classic queue mirroring-related keys (such as `ha-mode`) from all exported policies.
Must be followed by `drop_empty_policies` to strip off the policies whose definition has become empty (and thus invalid at import time) after the removal of all classic queue mirroring-related keys |
+| `drop_empty_policies` | Should be used after `strip_cmq_keys_from_policies` to strip off the policies whose definition has become empty (and thus invalid at import time) after the removal of all classic queue mirroring-related keys |
+| `obfuscate_usernames` | Replaces usernames and passwords with dummy values.
For usernames the values used are: `obfuscated-username-1`, `obfuscated-username-2`, and so on.
For passwords the values generated are: `password-1`, `password-2`, and so forth.
This transformations updates both the users and the permissions sections, consistently |
+| `exclude_users` | Removes all users from the result. Commonly used together with `exclude_permissions` |
+| `exclude_permissions` | Removes all permissions from the result. Commonly used together with `exclude_users` |
+| `exclude_runtime_parameters` | Removes all runtime parameters (including federation upstreams, shovels, WSR and SDS settings in Tanzu RabbitMQ) from the result |
+| `exclude_policies` | Removes all policies from the result |
+| `no_op` | Does nothing. Can be used as the default in dynamically computed transformation lists, e.g. in scripts |
+
+#### Examples
+
+The following command applies two transformations named `strip_cmq_keys_from_policies` and `drop_empty_policies`
+that will strip all classic queue mirroring-related policy keys that RabbitMQ 3.13 nodes supported,
+then removes the policies that did not have any keys left (ended up having an empty definition):
+
+```shell
+# strips classic mirrored queue-related policy keys from the exported definitions, then prints them
+# to the standard output stream
+rabbitmqadmin definitions export --stdout --transformations strip_cmq_keys_from_policies,drop_empty_policies
+```
+
+The following example exports definitions without users and permissions:
+
+```shell
+# removes users and user permissions from the exported definitions, then prints them
+# to the standard output stream
+rabbitmqadmin definitions export --stdout --transformations exclude_users,exclude_permissions
+```
+
+To export definitions with usernames replaced by dummy values (usernames: `obfuscated-username-1`, `obfuscated-username-2`, and so on;
+passwords: `password-1`, `password-2`, and so forth), use the `obfuscate_usernames` transformation:
+
+```shell
+rabbitmqadmin definitions export --file /path/to/definitions.file.json --transformations obfuscate_usernames
+```
+
+### Declare a Policy
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies declare \
+ --name "policy-name-1" \
+ --pattern '^cq.1\..+' \
+ --apply-to "queues" \
+ --priority 10 \
+ --definition '{"max-length": 1000000}'
+```
+
+### Delete a Policy
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies delete --name "policy-name-1"
+```
+
+### List All Policies
+
+```shell
+rabbitmqadmin policies list
+```
+
+### List Policies in A Virtual Host
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies list_in
+```
+
+### List Policies Matching an Object
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies list_matching_object --name "cq.1" --type "classic_queue"
+
+rabbitmqadmin --vhost "vh-1" policies list_matching_object --name "qq.1" --type "quorum_queue"
+
+rabbitmqadmin --vhost "vh-1" policies list_matching_object --name "topics.events" --type "exchange"
+```
+
+### Patch (Perform a Partial Update on) a Policy
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies patch \
+ --name "policy-name-1" \
+ --definition '{"max-length": 7777777, "max-length-bytes": 3333333333}'
+```
+
+### Remove One Or More Policy Definition Keys
+
+```shell
+rabbitmqadmin policies delete_definition_keys \
+ --name "policy-name-2" \
+ --definition-keys max-length-bytes,max-length
+```
+
+### Declare an [Override Policy](https://www.rabbitmq.com/docs/policies#override)
+
+[Override policies](https://www.rabbitmq.com/docs/policies#override) are temporarily declared
+policies that match the same objects as an existing policy but have a higher priority
+and a slightly different definition.
+
+This is a potentially safer alternative to patching policies, say, during [Blue-Green deployment migrations](https://www.rabbitmq.com/docs/blue-green-upgrade).
+
+Override policies are meant to be relatively short lived.
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies declare_override \
+ --name "policy-name-1" \
+ --override-name "tmp.overrides.policy-name-1" \
+ --apply-to "queues" \
+ --definition '{"federation-upstream-set": "all"}'
+```
+
+### Declare a [Blanket Policy](https://www.rabbitmq.com/docs/policies#blanket)
+
+A [blanket policy](https://www.rabbitmq.com/docs/policies#blanket) is a policy with a negative priority that
+matches all names. That is, it is a policy that matches everything not matched by other policies (that usually
+will have positive priorities).
+
+Blanket policies are most useful in combination with override policies
+covered above during [Blue-Green deployment migrations](https://www.rabbitmq.com/docs/blue-green-upgrade).
+
+Blanket policies are meant to be relatively short lived.
+
+```shell
+rabbitmqadmin --vhost "vh-1" policies declare_blanket \
+ --name "blanket-queuues" \
+ --apply-to "queues" \
+ --definition '{"federation-upstream-set": "all"}'
+```
+
+
+### Import Definition
+
+To import definitions from the standard input, use `definitions import --stdin`:
+
+```shell
+cat /path/to/definitions.file.json | rabbitmqadmin definitions import --stdin
+```
+
+To import definitions from a file, use `definitions import --file /path/to/definitions.file.json`:
+
+```shell
+rabbitmqadmin definitions import --file /path/to/definitions.file.json
+```
+
+### Declare an AMQP 0-9-1 Shovel
+
+To declare a [dynamic shovel](https://www.rabbitmq.com/docs/shovel-dynamic) that uses AMQP 0-9-1 for both source and desitnation, use
+`shovel declare_amqp091`:
+
+```shell
+rabbitmqadmin shovel declare_amqp091 --name my-amqp091-shovel \
+ --source-uri amqp://username:s3KrE7@source.hostname:5672 \
+ --destination-uri amqp://username:s3KrE7@source.hostname:5672 \
+ --ack-mode "on-confirm" \
+ --source-queue "src.queue" \
+ --destination-queue "dest.queue" \
+ --predeclared-source false \
+ --predeclared-destination false
+```
+
+### Declare an AMQP 1.0 Shovel
+
+To declare a [dynamic shovel](https://www.rabbitmq.com/docs/shovel-dynamic) that uses AMQP 1.0 for both source and desitnation, use
+`shovel declare_amqp10`.
+
+Note that
+
+1. With AMQP 1.0 shovels, credentials in the URI are mandatory (there are no defaults)
+2. With AMQP 1.0 shovels, the topology must be pre-declared (an equivalent of `--predeclared-source true` and `--predeclared-destination true` for AMQP 0-9-1 shovels)
+2. AMQP 1.0 shovels should use [AMQP 1.0 addresses v2](https://www.rabbitmq.com/docs/amqp#addresses)
+
+```shell
+rabbitmqadmin shovel declare_amqp10 --name my-amqp1.0-shovel \
+ --source-uri "amqp://username:s3KrE7@source.hostname:5672?hostname=vhost:src-vhost" \
+ --destination-uri "amqp://username:s3KrE7@source.hostname:5672?hostname=vhost:dest-vhost" \
+ --ack-mode "on-confirm" \
+ --source-address "/queues/src.queue" \
+ --destination-address "/queues/dest.queue"
+```
+
+### List Shovels
+
+To list shovels across all virtual hosts, use `shovel list_all`:
+
+```shell
+rabbitmqadmin shovel list_all
+```
+
+### Delete a Shovel
+
+To delete a shovel, use `shovel delete --name`:
+
+```shell
+rabbitmqadmin shovel delete --name my-amqp091-shovel
+```
+
+### List Federation Upstreams
+
+To list [federation upstreams](https://www.rabbitmq.com/docs/federation) across all virtual hosts, use `federation list_all_upstreams`:
+
+```shell
+rabbitmqadmin federation list_all_upstreams
+```
+
+### Create a Federation Upstream for Exchange Federation
+
+To create a [federation upstream](https://www.rabbitmq.com/docs/federated-exchanges), use `federation declare_upstream_for_exchanges`.
+This command provides a reduced set of options, only those that are relevant
+specifically to exchange federation.
+
+```shell
+rabbitmqadmin --vhost "local-vhost" federation declare_upstream_for_exchanges --name "pollux" \
+ --uri "amqp://pollux.eng.megacorp.local:5672/remote-vhost" \
+ --ack-mode 'on-publish' \
+ --prefetch-count 2000 \
+ --exchange-name "overridden.name" \
+ --queue-type quorum \
+ --bind-using-nowait true
+```
+
+### Create a Federation Upstream for Queue Federation
+
+To create a [federation upstream](https://www.rabbitmq.com/docs/federated-queues), use `declare_upstream_for_queues`.
+This command provides a reduced set of options, only those that are relevant
+specifically to queue federation.
+
+```shell
+rabbitmqadmin --vhost "local-vhost" federation declare_upstream_for_queues --name "clusters.sirius" \
+ --uri "amqp://sirius.eng.megacorp.local:5672/remote-vhost" \
+ --ack-mode 'on-publish' \
+ --prefetch-count 2000 \
+ --queue-name "overridden.name" \
+ --consumer-tag "overriden.ctag"
+```
+
+### Create a Universal Federation Upstream
+
+To create a [federation upstream](https://www.rabbitmq.com/docs/federation) that will be (or can be)
+used for federating both queues and exchanges, use `declare_upstream`. It combines
+[all the federation options](https://www.rabbitmq.com/docs/federation-reference), that is,
+the options of both `declare_upstream_for_queues` and `declare_upstream_for_exchanges`.
+
+```shell
+rabbitmqadmin --vhost "local-vhost" federation declare_upstream --name "pollux" \
+ --uri "amqp://pollux.eng.megacorp.local:5672/remove-vhost" \
+ --ack-mode 'on-publish' \
+ --prefetch-count 2000 \
+ --queue-name "overridden.name" \
+ --consumer-tag "overriden.ctag" \
+ --exchange-name "overridden.name" \
+ --queue-type quorum \
+ --bind-using-nowait true
+```
+
+### Delete a Federation Upstream
+
+To delete a [federation upstream](https://www.rabbitmq.com/docs/federation), use 'federation delete_upstream',
+which takes a virtual host and an upstream name:
+
+```shell
+rabbitmqadmin --vhost "local-vhost" federation delete_upstream --name "upstream.to.delete"
+```
+
+### List Federation Links
+
+To list all [federation links](https://www.rabbitmq.com/docs/federation) across all virtual hosts, use `federation list_all_links`:
+
+```shell
+rabbitmqadmin federation list_all_links
+```
+
+### Create a User
+
+```shell
+# Salt and hash a cleartext password value, and output the resultign hash.
+# See https://www.rabbitmq.com/docs/passwords to learn more.
+rabbitmqadmin passwords salt_and_hash "cleartext value"
+```
+
+```shell
+rabbitmqadmin users declare --name "new-user" --password "secure-password" --tags "monitoring,management"
+```
+
+```shell
+# Create user with administrator tag using pre-hashed password
+# (use 'rabbitmqadmin passwords salt_and_hash' to generate the hash)
+rabbitmqadmin users declare --name "admin-user" --password-hash "{value produced by 'rabbitmqadmin passwords salt_and_hash'}" --tags "administrator"
+```
+
+```shell
+# If RabbitMQ nodes are configured to use SHA512 for passwords, add `--hashing-algorithm`.
+# See https://www.rabbitmq.com/docs/passwords to learn more.
+rabbitmqadmin users declare --name "secure-user" --password-hash "{SHA512-hashed-password}" --hashing-algorithm "SHA512" --tags "monitoring"
+```
+
+### Delete a User
+
+```shell
+rabbitmqadmin users delete --name "user-to-delete"
+```
+
+```shell
+# Idempotent deletion (won't fail if user doesn't exist)
+rabbitmqadmin users delete --name "user-to-delete" --idempotently
+```
+
+### Grant Permissions to a User
+
+```shell
+rabbitmqadmin users permissions --name "app-user" --configure ".*" --write ".*" --read ".*"
+```
+
+```shell
+rabbitmqadmin --vhost "production" users permissions --name "app-user" --configure "^amq\.gen.*|^aliveness-test$" --write ".*" --read ".*"
+```
+
+### Create a Binding
+
+```shell
+rabbitmqadmin --vhost "events" bindings declare --source "events.topic" --destination-type "queue" --destination "events.processing" --routing-key "user.created"
+```
+
+```shell
+rabbitmqadmin --vhost "events" bindings declare --source "events.fanout" --destination-type "exchange" --destination "events.archived" --routing-key "" --arguments '{"x-match": "all"}'
+```
+
+### Delete a Binding
+
+```shell
+rabbitmqadmin --vhost "events" bindings delete --source "events.topic" --destination-type "queue" --destination "events.processing" --routing-key "user.created"
+```
+
+### List Connections
+
+```shell
+rabbitmqadmin connections list
+```
+
+```shell
+# List connections for a specific user
+rabbitmqadmin connections list --user "app-user"
+```
+
+### Close Connections
+
+```shell
+# Close a specific connection by name
+rabbitmqadmin connections close --name "connection-name"
+```
+
+```shell
+# Close all connections from a specific user
+rabbitmqadmin connections close --user "problem-user" --reason "Maintenance window"
+```
+
+### List Channels
+
+```shell
+rabbitmqadmin channels list
+```
+
+```shell
+# List channels in a specific virtual host
+rabbitmqadmin --vhost "production" channels list
+```
+
+### Run Health Checks
+
+```shell
+# Check for local alarms
+rabbitmqadmin health_check local_alarms
+```
+
+```shell
+# Check for cluster-wide alarms
+rabbitmqadmin health_check cluster_wide_alarms
+```
+
+```shell
+# Check if node is quorum critical
+rabbitmqadmin health_check node_is_quorum_critical
+```
+
+```shell
+# Check for deprecated features in use
+rabbitmqadmin health_check deprecated_features_in_use
+```
+
+```shell
+# Check if a port listener is running
+rabbitmqadmin health_check port_listener --port 5672
+```
+
+```shell
+# Check if a protocol listener is running
+rabbitmqadmin health_check protocol_listener --protocol "amqp"
+```
+
+### Set Runtime Parameters
+
+```shell
+rabbitmqadmin --vhost "events" parameters declare --component "federation-upstream" --name "upstream-1" --value '{"uri": "amqp://remote-server", "ack-mode": "on-publish"}'
+```
+
+```shell
+rabbitmqadmin parameters delete --component "federation-upstream" --name "upstream-1"
+```
+
+### Set Global Parameters
+
+```shell
+rabbitmqadmin global_parameters declare --name "cluster_name" --value '"production-cluster"'
+```
+
+```shell
+rabbitmqadmin global_parameters delete --name "cluster_name"
+```
+
+### Declare Operator Policies
+
+```shell
+rabbitmqadmin --vhost "production" operator_policies declare --name "ha-policy" --pattern "^ha\." --definition '{"ha-mode": "exactly", "ha-params": 3}' --priority 1 --apply-to "queues"
+```
+
+### List Operator Policies
+
+```shell
+rabbitmqadmin operator_policies list
+```
+
+### Delete Operator Policies
+
+```shell
+rabbitmqadmin --vhost "production" operator_policies delete --name "ha-policy"
+```
+
+### Manage Passwords
+
+```shell
+# Change user password
+rabbitmqadmin passwords change --name "app-user" --new-password "new-secure-password"
+```
+
+### Rebalance Quorum Queue Leaders
+
+```shell
+# Rebalances leader members (replicas) for all quorum queue
+rabbitmqadmin rebalance all
+```
+
+### Stream Operations
+
+```shell
+# List streams
+rabbitmqadmin streams list
+```
+
+```shell
+# Declare a stream
+rabbitmqadmin --vhost "logs" streams declare --name "application.logs" --max-age "7d" --max-length-bytes "10GB"
+```
+
+```shell
+# Delete a stream
+rabbitmqadmin --vhost "logs" streams delete --name "old.stream"
+```
+
+### Node Operations
+
+```shell
+# List cluster nodes
+rabbitmqadmin nodes list
+```
+
+```shell
+# Show node information
+rabbitmqadmin nodes show --name "rabbit@server1"
+```
+
+
+## Subcommand and Long Option Inference
+
+This feature is available only in the `main` branch
+at the moment.
+
+If the `RABBITMQADMIN_NON_INTERACTIVE_MODE` is not set to `true`, this tool
+now can infer subcommand and --long-option names.
-Compared to the original `rabbitmqadmin`, this version:
+This means that a subcommand can be referenced with its unique prefix,
+that is,
- * Is distributed as a standalone binary and does not depend on Python
- * Uses much stricter CLI argument validation and has (relatively minor) breaking changes in the CLI
- * Is better documented
- * May choose to use different defaults where it makes sense
+* 'del queue' will be inferred as 'delete queue'
+* 'del q --nam "a.queue"' will be inferred as 'delete queue --name "a.queue"'
-### CLI Changes
+To enable each feature, set the following environment variables to
+'true':
-#### Global Arguments Come First
+* `RABBITMQADMIN_INFER_SUBCOMMANDS`
+* `RABBITMQADMIN_INFER_LONG_OPTIONS`
+
+This feature is only meant to be used interactively. For non-interactive
+use, it can be potentially too dangerous to allow.
+
+
+## Configuration Files
+
+`rabbitmqadmin` v2 supports [TOML](https://toml.io/en/)-based configuration files
+stores groups of HTTP API connection settings under aliases ("node names" in original `rabbitmqadmin` speak).
+
+Here is an example `rabbitmqadmin` v2 configuration file:
+
+```toml
+[local]
+hostname = "localhost"
+port = 15672
+username = "lolz"
+password = "lolz"
+vhost = '/'
+
+[staging]
+hostname = "192.168.20.31"
+port = 15672
+username = "staging-2387a72329"
+password = "staging-1d20cfbd9d"
+
+[production]
+hostname = "(redacted)"
+port = 15671
+username = "user-2ca6bae15ff6b79e92"
+password = "user-92ee4c479ae604cc72"
+```
+
+Instead of specifying `--hostname` or `--username` on the command line to connect to
+a cluster (or specific node) called `staging`, a `--node` alias can be specified instead:
+
+```shell
+# will use the settings from the section called [staging]
+rabbitmqadmin --node staging show churn
+```
+
+Default configuration file path is at `$HOME/.rabbitmqadmin.conf`, as it was in
+the original version of `rabbitmqadmin`. It can be overridden on the command line:
+
+```shell
+# will use the settings from the section called [staging]
+rabbitmqadmin --config $HOME/.configuration/rabbitmqadmin.conf --node staging show churn
+```
+
+## Intentionally Restricted Environment Variable Support
+
+Environment variables have a number of serious downsides compared to a `rabbitmqadmin.conf`
+and the regular `--long-options` on the command line:
+
+1. Non-existent support for value types and validation ("everything is a string")
+2. Subprocess inheritance restrictions that can be very time-consuming to debug
+3. Different syntax for setting them between the classic POSIX-era shells (such as `bash`, `zsh`) and modern ones (such as [`nushell`](https://www.nushell.sh/))
+
+For these reasons and others, `rabbitmqadmin` v2 intentionally uses the configuration file and the
+CLI options over the environment variables.
+
+`rabbitmqadmin` v2 does, however, supports a number of environment variables for a few
+global settings that cannot be configured any other way (besides a CLI option),
+or truly represent an environment characteristic, e.g. either the non-interactive mode
+should be enabled.
+
+These environment variables are as follows:
+
+| Environment variable | Type | When used | Description |
+|--------------------------------------|---------------------------------------------------|---------------------------------------|--------------------------------------------------------------|
+| `RABBITMQADMIN_CONFIG_FILE_PATH` | Local filesystem path | Pre-flight (before command execution) | Same meaning as the global `--confg-file` argument |
+| `RABBITMQADMIN_NON_INTERACTIVE_MODE` | Boolean | Command execution | Enables the non-interactive mode.
Same meaning as the global `--non-interactive` argument |
+| `RABBITMQADMIN_QUIET_MODE` | Boolean | Command execution | Instructs the tool to produce less output.
Same meaning as the global `--quiet` argument |
+| `RABBITMQADMIN_NODE_ALIAS` | String | Command execution | Same meaning as the global `--node` argument |
+| `RABBITMQADMIN_TARGET_HOST` | String | Command execution | Same meaning as the global `--host` argument |
+| `RABBITMQADMIN_TARGET_PORT` | Positive integer | Command execution | Same meaning as the global `--port` argument |
+| `RABBITMQADMIN_API_PATH_PREFIX` | String | Command execution | Same meaning as the global `--path-prefix` argument |
+| `RABBITMQADMIN_TARGET_VHOST` | String | Command execution | Same meaning as the global `--vhost` argument |
+| `RABBITMQADMIN_BASE_URI` | String | Command execution | Same meaning as the global `--base-uri` argument |
+| `RABBITMQADMIN_USE_TLS` | Boolean | Command execution | Same meaning as the global `--tls` argument |
+| `RABBITMQADMIN_USERNAME` | String | Command execution | Same meaning as the global `--username` argument |
+| `RABBITMQADMIN_PASSWORD` | String | Command execution | Same meaning as the global `--password` argument |
+| `RABBITMQADMIN_TABLE_STYLE` | Enum, see `--table-style` in `rabbitmqadmin help` | Command execution | Same meaning as the global `--table-style` argument |
+
+
+
+## Project Goals Compared to `rabbitmqadmin` v1
+
+This version of `rabbitmqadmin` has a few ideas in mind:
+
+* This is a major version bump. Therefore, reasonable breaking changes are OK. `rabbitmqadmin` hasn't seen a revision in fifteen years
+* Some features in `rabbitmqadmin` v1 arguably should never have been built-ins,
+ external tools for data processing and [modern shells](https://www.nushell.sh/) can manipulate tabular data
+ better than `rabbitmqadmin` ever would
+* `rabbitmqadmin` should be standalone binary. There are very few reasons not to build and distribute it that way
+* Standalone project, not an obscure feature: `rabbitmqadmin` should be a standalone tool, not a relatively unknown "feature" of
+ the RabbitMQ management plugin, and should be developed as such, not tied completely to the development
+ environment, practices and release schedule of RabbitMQ itself
+* v2 should be a distributed via GitHub releases and not a special `rabbitmq_management` endpoint
+* There is a lot of room to improve validation of flags and arguments, since breaking changes are OK for v2
+* This tool should strive to be as free as practically possible from CVEs in other projects that show up on security scans.
+ CVEs from older Python versions should not plague OCI images that choose to include `rabbitmqadmin` v2
+
+
+## Breaking or Potentially Breaking Changes
+
+### Some Non-Essential Features Were Dropped
+
+`rabbitmqadmin` v2 does not support
+
+ * Sorting of results. Instead, use `--non-interactive` and parse the spaces-separated
+ output. Many modern tools for working with data parse it into a table, sort the data set,
+ filter the results, and son. In fact, these features for data processing are ready available [in some shells](https://www.nushell.sh/)
+ * Column selection. This feature may be reintroduced
+ * JSON output for arbitrary commands (with the exception of `definitions` commands).
+ Use the HTTP API directly if you need to work with JSON
+ * CSV output for arbitrary commands. This format may be reintroduced
+
+### --snake-case for Command Options
+
+`rabbitmqadmin` v1 used `lower_case` for named command arguments, for example:
+
+```shell
+# Note: auto_delete
+rabbitmqadmin-v1 --vhost "vh-2" declare queue name="qq.1" type="quorum" durable=true auto_delete=false
+```
+
+`rabbitmqadmin` v2 uses a more typical `--snake-case` format for the same arguments:
+
+```shell
+# Note: --auto-delete
+rabbitmqadmin --vhost "vh-2" declare queue --name "qq.1" --type "quorum" --durable true --auto-delete false
+```
+
+### Global Arguments Come First
Global flags in `rabbitmqadmin` v2 must precede the command category (e.g. `list`) and the command itself,
namely various HTTP API endpoint options and `--vhost`:
@@ -161,9 +1177,52 @@ namely various HTTP API endpoint options and `--vhost`:
rabbitmqadmin --vhost "events" declare queue --name "target.quorum.queue.name" --type "quorum" --durable true
```
-### Getting Help
+### --prefix Overrides API Path Prefix
-Please use GitHub Discussions in this repository and [RabbitMQ community Discord server](https://rabbitmq.com/discord/).
+In `rabbitmqadmin` v1, `--path-prefix` appended to the default [API path prefix](https://rabbitmq.com/docs/management#path-prefix).
+In this version, the value passed to `--path-prefix` will be used as given, in other words,
+it replaces the default prefix, `/api`.
+
+### Configuration File Format Moved to TOML
+
+`rabbitmqadmin` v1 supported ini configuration files that allowed
+the user to group a number of command line values under a name, e.g. a cluster or node nickname.
+
+Due to the "no dependencies other than Python" design goal of `rabbitmqadmin` v1, this feature was not really tested,
+and the specific syntax (that of ini files, supported by Python's [`ConfigParser`](https://docs.python.org/3/library/configparser.html)) linting, parsing or generation tools were not really available.
+
+`rabbitmqadmin` v2 replaces this format with [TOML](https://toml.io/en/), a popular configuration standard
+with [verification and linting tools](https://www.toml-lint.com/), as well as very mature parser
+that is not at all specific to `rabbitmqadmin` v2.
+
+Here is an example `rabbitmqadmin` v2 configuration file:
+
+```toml
+[local]
+hostname = "localhost"
+port = 15672
+username = "lolz"
+password = "lolz"
+vhost = '/'
+
+[staging]
+hostname = "192.168.20.31"
+port = 15672
+username = "staging-2387a72329"
+password = "staging-1d20cfbd9d"
+
+[production]
+hostname = "(redacted)"
+port = 15671
+
+username = "user-efe1f4d763f6"
+password = "(redacted)"
+
+tls = true
+ca_certificate_bundle_path = "/path/to/ca_certificate.pem"
+client_certificate_file_path = "/path/to/client_certificate.pem"
+client_private_key_file_path = "/path/to/client_key.pem"
+```
## License
diff --git a/bin/ci/before_build.sh b/bin/ci/before_build.sh
index 64c78c8..3ab4af5 100755
--- a/bin/ci/before_build.sh
+++ b/bin/ci/before_build.sh
@@ -22,6 +22,9 @@ $CTL add_vhost /
$CTL add_user guest guest
$CTL set_permissions -p / guest ".*" ".*" ".*"
+cargo -q run '--' vhosts delete_multiple --name-pattern "^rabbitmqadmin" --dry-run --table-style modern
+cargo -q run '--' --non-interactive vhosts delete_multiple --name-pattern "^rabbitmqadmin"
+
$CTL add_vhost "rust/rabbitmqadmin"
$CTL set_permissions -p "rust/rabbitmqadmin" guest ".*" ".*" ".*"
@@ -30,12 +33,14 @@ $CTL set_cluster_name rabbitmq@localhost
$CTL enable_feature_flag all
-# Enable shovel plugin
+# Enable the plugins
$PLUGINS enable rabbitmq_shovel
$PLUGINS enable rabbitmq_shovel_management
-# Enable federation plugin
$PLUGINS enable rabbitmq_federation
$PLUGINS enable rabbitmq_federation_management
+$PLUGINS enable rabbitmq_stream
+$PLUGINS enable rabbitmq_stream_management
+
true
diff --git a/scripts/release-linux.nu b/scripts/release-linux.nu
index c5d4621..c97302e 100755
--- a/scripts/release-linux.nu
+++ b/scripts/release-linux.nu
@@ -30,11 +30,10 @@ if $os in ['ubuntu', 'ubuntu-latest'] {
print "Building on Ubuntu..."
if $target == 'aarch64-unknown-linux-gnu' {
sudo apt-get install -y gcc-aarch64-linux-gnu
- $env.CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = 'aarch64-linux-gnu-gcc'
build-with-cargo
} else if $target == 'armv7-unknown-linux-gnueabihf' {
sudo apt-get install pkg-config gcc-arm-linux-gnueabihf -y
- $env.CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = "arm-linux-gnueabihf-gcc"
+ # $env.CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = "arm-linux-gnueabihf-gcc"
build-with-cargo
} else {
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
@@ -47,11 +46,10 @@ if $os in ['fedora', 'fedora-latest'] {
print "Building on Fedora..."
if $target == 'aarch64-unknown-linux-gnu' {
sudo dnf install -y gcc-aarch64-linux-gnu
- $env.CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER = 'aarch64-linux-gnu-gcc'
build-with-cargo
} else if $target == 'armv7-unknown-linux-gnueabihf' {
sudo dnf install pkg-config gcc-arm-linux-gnueabihf -y
- $env.CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
+ # $env.CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER = 'arm-linux-gnueabihf-gcc'
build-with-cargo
}
}
@@ -70,15 +68,18 @@ cp -r LICENSE* $release_dir
cp -r README* $release_dir
cd $release_dir
-ls $release_dir
-print "Compiling a release archive..."
+let artifact_filename = $'($binary)-($version)-($target)'
-let archive_filename = $'($binary)-($version)-($target).tar.gz'
-print $'Release archive name: ($archive_filename)'
-tar --verbose -C $release_dir -czf $archive_filename $binary
-print $'Release archive at ($archive_filename) is ready'
-echo $'archive=($archive_filename)' | save --append $env.GITHUB_OUTPUT
+print $"Renaming release artifact to ($artifact_filename)..."
+chmod +x $binary
+cp -v $binary $'($release_dir)/($artifact_filename)'
+
+print $'Release artifact at ($artifact_filename) is ready'
+print $"Release directory: ($release_dir)"
+ls $release_dir | print
+
+echo $'artifact=($artifact_filename)' | save --append $env.GITHUB_OUTPUT
def 'build-with-cargo' [] {
cargo rustc --bin $binary --target $target --release
diff --git a/scripts/release-macos.nu b/scripts/release-macos.nu
index c113c2a..c08d070 100755
--- a/scripts/release-macos.nu
+++ b/scripts/release-macos.nu
@@ -35,16 +35,19 @@ cp -r LICENSE* $release_dir
cp -r README* $release_dir
cd $release_dir
-ls $release_dir
-print "Compiling a release archive..."
+let artifact_filename = $'($binary)-($version)-($target)'
-let archive_filename = $'($binary)-($version)-($target).tar.gz'
-print $'Release archive name: ($archive_filename)'
-tar --verbose -czf $'($release_dir)/($archive_filename)' $binary
-print $'Release archive at ($archive_filename) is ready'
-echo $'archive=($archive_filename)' | save --append $env.GITHUB_OUTPUT
+print $"Renaming release artifact to ($artifact_filename)..."
+chmod +x $binary
+cp -v $binary $'($release_dir)/($artifact_filename)'
+
+print $'Release artifact at ($artifact_filename) is ready'
+print $"Release directory: ($release_dir)"
+ls $release_dir | print
+
+echo $'artifact=($artifact_filename)' | save --append $env.GITHUB_OUTPUT
def 'build-with-cargo' [] {
- cargo rustc --bin $binary --target $target --release
+ cargo rustc -q --bin $binary --target $target --release
}
\ No newline at end of file
diff --git a/scripts/release-windows-msi.nu b/scripts/release-windows-msi.nu
old mode 100644
new mode 100755
diff --git a/scripts/release-windows.nu b/scripts/release-windows.nu
index bcf3b8d..e6468f9 100755
--- a/scripts/release-windows.nu
+++ b/scripts/release-windows.nu
@@ -20,7 +20,7 @@ rm -rf $release_dir
mkdir $release_dir
print $'Building on Windows in ($src)...'
-cargo rustc --bin $binary --target $target --release
+cargo rustc -q --bin $binary --target $target --release
#
# Release packaging
@@ -35,22 +35,16 @@ cp -r LICENSE* $release_dir
cp -r README* $release_dir
cd $release_dir
-ls $release_dir
-
-let archive_filename = $'($binary)-($version)-($target).zip'
-print $'Release archive name: ($archive_filename)'
-7z a $archive_filename $binary_filename
-
-print $'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls | print
-let archive = $'($release_dir)/($archive_filename).zip'
-7z a $archive ...(glob *)
-let pkg = (ls -f $archive | get name)
-if not ($pkg | is-empty) {
- # Workaround for https://github.com/softprops/action-gh-release/issues/280
- let archive = ($pkg | get 0 | str replace --all '\' '/')
- print $'Zip archive path: ($archive)'
- echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
-}
+
+let artifact_filename = $'($binary)-($version)-($target).exe'
+
+print $'(char nl)(ansi g)Build artifacts:(ansi reset)'; hr-line; ls | print
+print $'Release artifact name: ($artifact_filename)'
+
+cp $binary_filename $artifact_filename
+
+print $'Artifact ($artifact_filename) is ready'
+echo $'artifact=($artifact_filename)' | save --append $env.GITHUB_OUTPUT
# Print a horizontal line marker
diff --git a/src/cli.rs b/src/cli.rs
index 5d97866..a2e90ae 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023-2024 RabbitMQ Core Team (teamrabbitmq@gmail.com)
+// Copyright (C) 2023-2025 RabbitMQ Core Team (teamrabbitmq@gmail.com)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -14,23 +14,427 @@
use std::path::PathBuf;
use super::constants::*;
-use clap::{Arg, ArgAction, Command};
-use rabbitmq_http_client::commons::{BindingDestinationType, QueueType};
+use super::static_urls::*;
+use super::tanzu_cli::tanzu_subcommands;
+use crate::config::PreFlightSettings;
+use crate::output::TableStyle;
+use clap::{Arg, ArgAction, ArgGroup, Command, crate_name, crate_version, value_parser};
+use rabbitmq_http_client::commons::{
+ BindingDestinationType, ChannelUseMode, ExchangeType, MessageTransferAcknowledgementMode,
+ PolicyTarget, QueueType, SupportedProtocol,
+};
+use rabbitmq_http_client::password_hashing::HashingAlgorithm;
+use rabbitmq_http_client::requests::FederationResourceCleanupMode;
-pub fn parser() -> Command {
- let after_help: &'static str = color_print::cstr!(
+pub fn parser(pre_flight_settings: PreFlightSettings) -> Command {
+ let after_help = color_print::cformat!(
r#"
-Getting Help
- RabbitMQ docs: https://rabbitmq.com/docs/
- Discord server: https://rabbitmq.com/discord/
- GitHub Discussions: https://github.com/rabbitmq/rabbitmq-server/discussions"#
+Documentation and Community Resources
+
+ rabbitmqadmin docs: {}
+ RabbitMQ docs: {}
+ GitHub Discussions: {}
+ Discord server: {}
+
+Contribute
+
+ On GitHub: {}"#,
+ RABBITMQADMIN_DOC_GUIDE_URL,
+ RABBITMQ_DOC_GUIDES_URL,
+ GITHUB_DISCUSSIONS_URL,
+ DISCORD_SERVER_INVITATION_URL,
+ GITHUB_REPOSITORY_URL
);
- Command::new("rabbitmqadmin")
- .version(clap::crate_version!())
- .author("RabbitMQ Core Team")
- .about("rabbitmqadmin gen 2")
- .long_about("RabbitMQ CLI that uses the HTTP API")
+ let bindings_group = Command::new("bindings")
+ .about("Operations on bindings")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .subcommand_value_name("binding")
+ .arg_required_else_help(true)
+ .subcommands(binding_subcommands(pre_flight_settings.clone()));
+ let channels_group = Command::new("channels")
+ .about("Operations on channels")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .arg_required_else_help(true)
+ .subcommands(channels_subcommands(pre_flight_settings.clone()));
+ let close_group = Command::new("close")
+ .about("Closes connections")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .arg_required_else_help(true)
+ .subcommands(close_subcommands(pre_flight_settings.clone()));
+ let connections_group = Command::new("connections")
+ .about("Operations on connections")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .arg_required_else_help(true)
+ .subcommands(connections_subcommands(pre_flight_settings.clone()));
+ let declare_group = Command::new("declare")
+ .about("Creates or declares objects")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .arg_required_else_help(true)
+ .subcommands(declare_subcommands(pre_flight_settings.clone()));
+ let definitions_group = Command::new("definitions")
+ .about("Operations on definitions (everything except for messages: virtual hosts, queues, streams, exchanges, bindings, users, etc)")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEFINITION_GUIDE_URL
+ ))
+ .subcommand_value_name("export")
+ .arg_required_else_help(true)
+ .subcommands(definitions_subcommands(pre_flight_settings.clone()));
+ let delete_group = Command::new("delete")
+ .about("Deletes objects")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .arg_required_else_help(true)
+ .subcommands(delete_subcommands(pre_flight_settings.clone()));
+ let deprecated_features_group = Command::new("deprecated_features")
+ .about("Operations on deprecated features")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEPRECATED_FEATURE_GUIDE_URL
+ ))
+ .subcommand_value_name("deprecated feature")
+ .arg_required_else_help(true)
+ .subcommands(deprecated_features_subcommands(pre_flight_settings.clone()));
+ let exchanges_group = Command::new("exchanges")
+ .about("Operations on exchanges")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .subcommand_value_name("exchange")
+ .arg_required_else_help(true)
+ .subcommands(exchanges_subcommands(pre_flight_settings.clone()));
+ let export_group = Command::new("export")
+ .about("See 'definitions export'")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEFINITION_GUIDE_URL
+ ))
+ .subcommand_value_name("definitions")
+ .arg_required_else_help(true)
+ .subcommands(export_subcommands(pre_flight_settings.clone()));
+ let feature_flags_group = Command::new("feature_flags")
+ .about("Operations on feature flags")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ FEATURE_FLAG_GUIDE_URL
+ ))
+ .subcommand_value_name("feature flag")
+ .arg_required_else_help(true)
+ .subcommands(feature_flags_subcommands(pre_flight_settings.clone()));
+ let federation_group = Command::new("federation")
+ .about("Operations on federation upstreams and links")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}
+ * {}"#,
+ FEDERATION_GUIDE_URL,
+ FEDERATED_EXCHANGES_GUIDE_URL,
+ FEDERATED_QUEUES_GUIDE_URL,
+ FEDERATION_REFERENCE_URL
+ ))
+ .arg_required_else_help(true)
+ .subcommands(federation_subcommands(pre_flight_settings.clone()));
+ let get_group = Command::new("get")
+ .about(color_print::cstr!("Fetches message(s) from a queue or stream via polling. Only suitable for development and test environments."))
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!("Doc guide: {}", POLLING_CONSUMER_GUIDE_URL))
+ .subcommand_value_name("message")
+ .arg_required_else_help(true)
+ .subcommands(get_subcommands(pre_flight_settings.clone()));
+ let global_parameters_group = Command::new("global_parameters")
+ .about("Operations on global runtime parameters")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ))
+ .subcommand_value_name("runtime_parameter")
+ .arg_required_else_help(true)
+ .subcommands(global_parameters_subcommands(pre_flight_settings.clone()));
+ let health_check_group = Command::new("health_check")
+ .about("Runs health checks")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .subcommand_value_name("check")
+ .arg_required_else_help(true)
+ .subcommands(health_check_subcommands(pre_flight_settings.clone()))
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}"#,
+ HEALTH_CHECK_GUIDE_URL,
+ DEPRECATED_FEATURE_GUIDE_URL
+ ));
+ let import_group = Command::new("import")
+ .about("See 'definitions import'")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEFINITION_GUIDE_URL
+ ))
+ .subcommand_value_name("definitions")
+ .arg_required_else_help(true)
+ .subcommands(import_subcommands(pre_flight_settings.clone()));
+ let list_group = Command::new("list")
+ .about("Lists objects")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .arg_required_else_help(true)
+ .subcommands(list_subcommands(pre_flight_settings.clone()));
+ let nodes_group = Command::new("nodes")
+ .about("Node operations")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .arg_required_else_help(true)
+ .subcommands(nodes_subcommands(pre_flight_settings.clone()));
+ let operator_policies_group = Command::new("operator_policies")
+ .about("Operations on operator policies")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ POLICY_GUIDE_URL
+ ))
+ .subcommand_value_name("operator policy")
+ .arg_required_else_help(true)
+ .subcommands(operator_policies_subcommands(pre_flight_settings.clone()));
+ let parameters_group = Command::new("parameters")
+ .about("Operations on runtime parameters")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ))
+ .subcommand_value_name("runtime_parameter")
+ .arg_required_else_help(true)
+ .subcommands(parameters_subcommands(pre_flight_settings.clone()));
+ let passwords_group = Command::new("passwords")
+ .about("Operations on passwords")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ PASSWORD_GUIDE_URL
+ ))
+ .arg_required_else_help(true)
+ .subcommands(passwords_subcommands(pre_flight_settings.clone()));
+ let permissions_group = Command::new("permissions")
+ .about("Operations on user permissions")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ ACCESS_CONTROL_GUIDE_URL
+ ))
+ .subcommand_value_name("permission")
+ .arg_required_else_help(true)
+ .subcommands(permissions_subcommands(pre_flight_settings.clone()));
+ let plugins_group = Command::new("plugins")
+ .about("List enabled plugins")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ PLUGIN_GUIDE_URL
+ ))
+ .subcommand_value_name("plugin")
+ .arg_required_else_help(true)
+ .subcommands(plugins_subcommands(pre_flight_settings.clone()));
+ let policies_group = Command::new("policies")
+ .about("Operations on policies")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ POLICY_GUIDE_URL
+ ))
+ .subcommand_value_name("policy")
+ .arg_required_else_help(true)
+ .subcommands(policies_subcommands(pre_flight_settings.clone()));
+ let publish_group = Command::new("publish")
+ .about(color_print::cstr!("Publishes (inefficiently) message(s) to a queue or a stream. Only suitable for development and test environments."))
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!("Doc guide: {}", PUBLISHER_GUIDE_URL))
+ .subcommand_value_name("message")
+ .arg_required_else_help(true)
+ .subcommands(publish_subcommands(pre_flight_settings.clone()));
+ let purge_group = Command::new("purge")
+ .about("Purges queues")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .subcommand_value_name("queue")
+ .arg_required_else_help(true)
+ .subcommands(purge_subcommands(pre_flight_settings.clone()));
+ let queues_group = Command::new("queues")
+ .about("Operations on queues")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .subcommand_value_name("queue")
+ .arg_required_else_help(true)
+ .subcommands(queues_subcommands(pre_flight_settings.clone()));
+ let rebalance_group = Command::new("rebalance")
+ .about("Rebalancing of leader replicas")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ QUORUM_QUEUE_GUIDE_URL
+ ))
+ .subcommand_value_name("queues")
+ .arg_required_else_help(true)
+ .subcommands(rebalance_subcommands(pre_flight_settings.clone()));
+ let show_group = Command::new("show")
+ .about("Overview, memory footprint breakdown, and more")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ MONITORING_GUIDE_URL
+ ))
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .arg_required_else_help(true)
+ .subcommands(show_subcommands(pre_flight_settings.clone()));
+ let shovels_group = Command::new("shovels")
+ .about("Operations on shovels")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ SHOVEL_GUIDE_URL
+ ))
+ .subcommand_value_name("shovels")
+ .arg_required_else_help(true)
+ .subcommands(shovel_subcommands(pre_flight_settings.clone()));
+ let streams_group = Command::new("streams")
+ .about("Operations on streams")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .subcommand_value_name("stream")
+ .arg_required_else_help(true)
+ .subcommands(streams_subcommands(pre_flight_settings.clone()));
+ let tanzu_group = Command::new("tanzu")
+ .about("Tanzu RabbitMQ-specific commands")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ COMMERCIAL_OFFERINGS_GUIDE_URL
+ ))
+ .subcommand_value_name("subcommand")
+ .arg_required_else_help(true)
+ .subcommands(tanzu_subcommands());
+ let users_group = Command::new("users")
+ .about("Operations on users")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ ACCESS_CONTROL_GUIDE_URL
+ ))
+ .subcommand_value_name("subcommand")
+ .arg_required_else_help(true)
+ .subcommands(users_subcommands(pre_flight_settings.clone()));
+ let user_limits_group = Command::new("user_limits")
+ .about("Operations on per-user (resource) limits")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ USER_LIMIT_GUIDE_URL
+ ))
+ .subcommand_value_name("user_limit")
+ .arg_required_else_help(true)
+ .subcommands(user_limits_subcommands(pre_flight_settings.clone()));
+ let vhosts_group = Command::new("vhosts")
+ .about("Virtual host operations")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .arg_required_else_help(true)
+ .subcommands(vhosts_subcommands(pre_flight_settings.clone()));
+ let vhost_limits_group = Command::new("vhost_limits")
+ .about("Operations on virtual host (resource) limits")
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ VIRTUAL_HOST_LIMIT_GUIDE_URL
+ ))
+ .subcommand_value_name("vhost_limit")
+ .arg_required_else_help(true)
+ .subcommands(vhost_limits_subcommands(pre_flight_settings.clone()));
+
+ let command_groups = [
+ bindings_group,
+ channels_group,
+ close_group,
+ connections_group,
+ declare_group,
+ definitions_group,
+ delete_group,
+ deprecated_features_group,
+ exchanges_group,
+ export_group,
+ feature_flags_group,
+ federation_group,
+ get_group,
+ global_parameters_group,
+ health_check_group,
+ import_group,
+ list_group,
+ nodes_group,
+ operator_policies_group,
+ parameters_group,
+ passwords_group,
+ permissions_group,
+ plugins_group,
+ policies_group,
+ publish_group,
+ purge_group,
+ queues_group,
+ rebalance_group,
+ show_group,
+ shovels_group,
+ streams_group,
+ tanzu_group,
+ users_group,
+ user_limits_group,
+ vhosts_group,
+ vhost_limits_group,
+ ];
+
+ Command::new(crate_name!())
+ .version(crate_version!())
+ .author("The RabbitMQ Core Team")
+ .about(format!("{} gen 2, version: {}", crate_name!(), crate_version!()))
+ .long_about(format!(
+ "RabbitMQ CLI that uses the HTTP API. Version: {}",
+ crate_version!()
+ ))
+ .infer_subcommands(pre_flight_settings.infer_subcommands)
+ .infer_long_args(pre_flight_settings.infer_long_options)
.after_help(after_help)
.disable_version_flag(true)
// --config-file
@@ -38,7 +442,8 @@ pub fn parser() -> Command {
Arg::new("config_file_path")
.short('c')
.long("config")
- .value_parser(clap::value_parser!(PathBuf))
+ .env("RABBITMQADMIN_CONFIG_FILE_PATH")
+ .value_parser(value_parser!(PathBuf))
.default_value(DEFAULT_CONFIG_FILE_PATH),
)
// --node
@@ -48,6 +453,7 @@ pub fn parser() -> Command {
Arg::new("node_alias")
.short('N')
.long("node")
+ .env("RABBITMQADMIN_NODE_ALIAS")
.required(false)
.default_value(DEFAULT_NODE_ALIAS),
)
@@ -56,9 +462,9 @@ pub fn parser() -> Command {
Arg::new("host")
.short('H')
.long("host")
- .help("HTTP API hostname to use when connecting")
- .required(false)
- .default_value(DEFAULT_HOST),
+ .alias("hostname")
+ .env("RABBITMQADMIN_TARGET_HOST")
+ .help("HTTP API hostname to use when connecting"),
)
.visible_alias("hostname")
// --port
@@ -66,16 +472,17 @@ pub fn parser() -> Command {
Arg::new("port")
.short('P')
.long("port")
+ .env("RABBITMQADMIN_TARGET_PORT")
.help("HTTP API port to use when connecting")
.required(false)
- .value_parser(clap::value_parser!(u16))
- .default_value(DEFAULT_PORT_STR),
+ .value_parser(value_parser!(u16)),
)
// --base-uri
.arg(
Arg::new("base_uri")
.short('U')
.long("base-uri")
+ .env("RABBITMQADMIN_BASE_URI")
.help("base HTTP API endpoint URI")
.required(false)
.conflicts_with_all(["host", "port"]),
@@ -84,34 +491,39 @@ pub fn parser() -> Command {
.arg(
Arg::new("path_prefix")
.long("path-prefix")
- .required(false)
- .default_value(DEFAULT_PATH_PREFIX),
+ .env("RABBITMQADMIN_API_PATH_PREFIX")
+ .help("use if target node uses a path prefix. Defaults to '/api'"),
)
// --vhost
.arg(
Arg::new("vhost")
.short('V')
.long("vhost")
- .help("target virtual host")
- .required(false)
- .default_value(DEFAULT_VHOST),
+ // IMPORTANT: this means that subcommands won't be able to override --vhost or -V,
+ // otherwise the parser will panic. MK.
+ .global(true)
+ .env("RABBITMQADMIN_TARGET_VHOST")
+ .help("target virtual host. Defaults to '/'"),
)
// --username
.arg(
Arg::new("username")
.short('u')
.long("username")
- .required(false)
- .default_value(DEFAULT_USERNAME),
+ .env("RABBITMQADMIN_USERNAME")
+ .help(format!(
+ "this user must have the permissions for HTTP API access, see {}",
+ HTTP_API_ACCESS_PERMISSIONS_GUIDE_URL
+ )),
)
// --password
.arg(
Arg::new("password")
.short('p')
.long("password")
- .required(false)
- .default_value(DEFAULT_PASSWORD)
- .requires("username"),
+ .env("RABBITMQADMIN_PASSWORD")
+ .requires("username")
+ .help("requires username to be specified via --username or in the config file"),
)
// --insecure
.arg(
@@ -120,7 +532,7 @@ pub fn parser() -> Command {
.long("insecure")
.required(false)
.help("disables TLS peer (certificate chain) verification")
- .value_parser(clap::value_parser!(bool))
+ .value_parser(value_parser!(bool))
.action(ArgAction::SetTrue),
)
// --tls
@@ -128,810 +540,3503 @@ pub fn parser() -> Command {
Arg::new("tls")
.long("use-tls")
.help("use TLS (HTTPS) for HTTP API requests ")
- .required(false)
- .value_parser(clap::value_parser!(bool))
- .default_value("false")
- .action(ArgAction::SetFalse),
+ .env("RABBITMQADMIN_USE_TLS")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue),
)
// --tls-ca-cert-file
.arg(
- Arg::new("tls-ca-cert-file")
+ Arg::new("ca_certificate_bundle_path")
.long("tls-ca-cert-file")
.required(false)
- .help("TLS CA certificate PEM file path")
- .value_parser(clap::value_parser!(PathBuf)),
+ .help("Local path to a CA certificate file in the PEM format")
+ .value_parser(value_parser!(PathBuf)),
+ )
+ // --tls-cert-file
+ .arg(
+ Arg::new("client_certificate_file_path")
+ .long("tls-cert-file")
+ .required(false)
+ .requires("tls")
+ .help("Local path to a client certificate file in the PEM format")
+ .value_parser(value_parser!(PathBuf)),
+ )
+ // --tls-key-file
+ .arg(
+ Arg::new("client_private_key_file_path")
+ .long("tls-key-file")
+ .required(false)
+ .requires("tls")
+ .help("Local path to a client private key file in the PEM format")
+ .value_parser(value_parser!(PathBuf)),
+ )
+ // --timeout
+ .arg(
+ Arg::new("timeout")
+ .long("timeout")
+ .env("RABBITMQADMIN_TIMEOUT")
+ .help("HTTP API request timeout in seconds. Must be greater than 0")
+ .required(false)
+ .default_value("60")
+ .value_parser(value_parser!(u64).range(1..)),
)
// --quiet
.arg(
Arg::new("quiet")
.short('q')
.long("quiet")
+ .env("RABBITMQADMIN_QUIET_MODE")
.help("produce less output")
.required(false)
- .value_parser(clap::value_parser!(bool))
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue),
+ )
+ // --non-interactive
+ .arg(
+ Arg::new("non_interactive")
+ .long("non-interactive")
+ .global(true)
+ .env("RABBITMQADMIN_NON_INTERACTIVE_MODE")
+ .help("pass when invoking from scripts")
+ .conflicts_with("table_style")
+ .required(false)
+ .value_parser(value_parser!(bool))
.action(ArgAction::SetTrue),
)
+ // --table-style
+ .arg(
+ Arg::new("table_style")
+ .long("table-style")
+ .global(true)
+ .env("RABBITMQADMIN_TABLE_STYLE")
+ .help("style preset to apply to output tables: modern, borderless, ascii, dots, psql, markdown, sharp")
+ .conflicts_with("non_interactive")
+ .required(false)
+ .value_parser(value_parser!(TableStyle))
+ )
.subcommand_required(true)
- .subcommand_value_name("command")
- .subcommands([
- Command::new("show")
- .about("overview")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://www.rabbitmq.com/docs/monitoring"
- ))
- .subcommand_value_name("summary")
- .subcommands(show_subcomands()),
- Command::new("list")
- .about("lists objects by type")
- .subcommand_value_name("objects")
- .subcommands(list_subcommands()),
- Command::new("declare")
- .about("creates or declares things")
- .subcommand_value_name("object")
- .subcommands(declare_subcommands()),
- Command::new("delete")
- .about("deletes objects")
- .subcommand_value_name("object")
- .subcommands(delete_subcommands()),
- Command::new("purge")
- .about("purges queues")
- .subcommand_value_name("queue")
- .subcommands(purge_subcommands()),
- Command::new("close")
- .about("closes connections")
- .subcommand_value_name("connection")
- .subcommands(close_subcommands()),
- Command::new("rebalance")
- .about("rebalances queue leaders")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://www.rabbitmq.com/docs/quorum-queues"
- ))
- .subcommand_value_name("queues")
- .subcommands(rebalance_subcommands()),
- Command::new("definitions")
- .about("operations on definitions")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/docs/definitions"
- ))
- .subcommand_value_name("export")
- .subcommands(definitions_subcommands()),
- Command::new("export")
- .about("see 'definitions export'")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/docs/definitions"
- ))
- .subcommand_value_name("definitions")
- .subcommands(export_subcommands()),
- Command::new("import")
- .about("see 'definitions import'")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/docs/definitions"
- ))
- .subcommand_value_name("definitions")
- .subcommands(import_subcommands()),
- Command::new("publish")
- .about("publish a message")
- .subcommand_value_name("message")
- .subcommands(publish_subcommands()),
- Command::new("get")
- .about("get message(s) from a queue")
- .subcommand_value_name("message")
- .subcommands(get_subcommands()),
- ])
-}
-
-fn list_subcommands() -> [Command; 15] {
- // duplicate this very common global argument so that
- // it can be passed as the end of argument list
- let vhost_arg = Arg::new("vhost")
- .short('V')
- .long("vhost")
- .help("target virtual host")
- .required(false)
- .default_value(DEFAULT_VHOST);
-
- [
- Command::new("nodes").long_about("Lists cluster members"),
- Command::new("users").long_about("Lists users in the internal database"),
- Command::new("vhosts")
- .long_about("Lists virtual hosts")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/vhosts.html"
- )),
- Command::new("permissions")
- .arg(vhost_arg.clone())
- .long_about("Lists user permissions")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/access-control.html"
- )),
- Command::new("connections")
- .arg(vhost_arg.clone())
- .long_about("Lists client connections")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/connections.html"
- )),
- Command::new("channels")
- .arg(vhost_arg.clone())
- .long_about("Lists AMQP 0-9-1 channels")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/channels.html"
- )),
- Command::new("queues")
- .arg(vhost_arg.clone())
- .long_about("Lists queues")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/queues.html"
- )),
- Command::new("exchanges").arg(vhost_arg.clone()),
- Command::new("bindings").arg(vhost_arg.clone()),
- Command::new("consumers")
- .arg(vhost_arg.clone())
- .long_about("Lists consumers")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/consumers.html"
- )),
- Command::new("parameters")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("component")
- .long("component")
- .help("component (for example: federation-upstream, vhost-limits)")
- .required(false),
- )
- .long_about("Lists runtime parameters")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/parameters.html"
- )),
- Command::new("policies")
- .arg(vhost_arg.clone())
- .long_about("Lists policies")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/parameters.html"
- )),
- Command::new("operator_policies")
- .arg(vhost_arg.clone())
- .long_about("Lists operator policies")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/parameters.html"
- )),
- Command::new("vhost_limits")
- .arg(vhost_arg.clone())
- .long_about("Lists virtual host (resource) limits")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/vhosts.html"
- )),
- Command::new("user_limits")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("user")
- .long("user")
- .help("username")
- .required(false),
- )
- .long_about("Lists per-user (resource) limits")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/user-limits.html"
- )),
- ]
-}
-
-fn declare_subcommands() -> [Command; 11] {
- // duplicate this very common global argument so that
- // it can be passed as the end of argument list
- let vhost_arg = Arg::new("vhost")
- .short('V')
- .long("vhost")
- .help("target virtual host")
- .required(false)
- .default_value(DEFAULT_VHOST);
-
- [
- Command::new("user")
- .about("creates a user")
- .arg(
- Arg::new("name")
- .long("name")
- .help("username")
- .required(true),
- )
- .arg(
- Arg::new("password_hash")
- .help("salted password hash, see https://rabbitmq.com/passwords.html")
- .long("password_hash")
- .required(false)
- .default_value(""),
- )
- .arg(
- Arg::new("password")
- .long("password")
- .help("prefer providing a hash, see https://rabbitmq.com/passwords.html")
- .required(false)
- .default_value(""),
- )
- .arg(
- Arg::new("tags")
- .long("tags")
- .help("a list of comma-separated tags")
- .default_value(""),
- ),
- Command::new("vhost")
- .about("creates a virtual host")
- .arg(
- Arg::new("name")
- .long("name")
- .help("virtual host name")
- .required(true),
- )
- .arg(
- Arg::new("default_queue_type")
- .long("default-queue-type")
- .required(false)
- .default_value(DEFAULT_QUEUE_TYPE)
- .help("default queue type, one of: classic, quorum, stream"),
- )
- .arg(
- Arg::new("description")
- .long("description")
- .required(false)
- .help("a brief description of this virtual host"),
- )
- .arg(
- Arg::new("tracing")
- .long("tracing")
- .required(false)
- .action(ArgAction::SetTrue)
- .help("should tracing be enabled for this virtual host?"),
- ),
- Command::new("permissions")
- .about("grants permissions to a user")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("user")
- .long("user")
- .help("username")
- .required(true),
- )
- .arg(
- Arg::new("configure")
- .long("configure")
- .help("name pattern for configuration access")
- .required(true),
- )
- .arg(
- Arg::new("read")
- .long("read")
- .help("name pattern for read access")
- .required(true),
- )
- .arg(
- Arg::new("write")
- .long("write")
- .help("name pattern for write access")
- .required(true),
- ),
- Command::new("queue")
- .about("declares a queue")
- .arg(vhost_arg.clone())
- .arg(Arg::new("name").long("name").required(true).help("name"))
- .arg(
- Arg::new("type")
- .long("type")
- .help("queue type")
- .value_parser(clap::value_parser!(QueueType))
- .required(true),
- )
- .arg(
- Arg::new("durable")
- .long("durable")
- .help("should it persist after a restart")
- .required(false)
- .value_parser(clap::value_parser!(bool)),
- )
- .arg(
- Arg::new("auto_delete")
- .long("auto_delete")
- .help("should it be deleted when the last consumer disconnects")
- .required(false)
- .value_parser(clap::value_parser!(bool)),
- )
- .arg(
- Arg::new("arguments")
- .long("arguments")
- .help("additional exchange arguments")
- .required(false)
- .default_value("{}")
- .value_parser(clap::value_parser!(String)),
- ),
- Command::new("exchange")
- .about("declares an exchange")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("exchange name")
- .required(true),
- )
- .arg(
- Arg::new("type")
- .long("type")
- .help("exchange type")
- .required(false),
- )
- .arg(
- Arg::new("durable")
- .long("durable")
- .help("should it persist after a restart")
- .required(false)
- .value_parser(clap::value_parser!(bool)),
- )
- .arg(
- Arg::new("auto_delete")
- .long("auto_delete")
- .help("should it be deleted when the last queue is unbound")
- .required(false)
- .value_parser(clap::value_parser!(bool)),
- )
- .arg(
- Arg::new("arguments")
- .long("arguments")
- .help("additional exchange arguments")
- .required(false)
- .default_value("{}")
- .value_parser(clap::value_parser!(String)),
- ),
- Command::new("binding")
- .about("binds to an exchange")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("source")
- .long("source")
- .help("source exchange")
- .required(true),
- )
- .arg(
- Arg::new("destination_type")
- .long("destination_type")
- .help("destination type: exchange or queue")
- .required(true)
- .value_parser(clap::value_parser!(BindingDestinationType)),
- )
- .arg(
- Arg::new("destination")
- .long("destination")
- .help("destination exchange/queue name")
- .required(true),
- )
- .arg(
- Arg::new("routing_key")
- .long("routing_key")
- .help("routing key")
- .required(true),
- )
- .arg(
- Arg::new("arguments")
- .long("arguments")
- .help("additional arguments")
- .required(false)
- .default_value("{}")
- .value_parser(clap::value_parser!(String)),
- ),
- Command::new("parameter").
- about("sets a runtime parameter")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("parameter's name")
- .required(true)
- ).arg(
- Arg::new("component")
- .long("component")
- .help("component (eg. federation)")
- .required(true))
- .arg(
- Arg::new("value")
- .long("value")
- .help("parameter's value")
- .required(true)),
- Command::new("policy")
- .about("creates or updates a policy")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("policy name")
- .required(true),
- )
- .arg(
- Arg::new("pattern")
- .long("pattern")
- .help("queue/exchange name pattern")
- .required(true),
- )
- .arg(
- Arg::new("apply-to")
- .long("apply-to")
- .help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
- .required(true),
- )
- .arg(
- Arg::new("priority")
- .long("priority")
- .help("policy priority (only the policy with the highest priority is effective)")
- .required(false)
- .default_value("0"),
- )
- .arg(
- Arg::new("definition")
- .long("definition")
- .help("policy definition")
- .required(true),
- ),
- Command::new("operator_policy")
- .about("creates or updates an operator policy")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("operator policy name")
- .required(true),
- )
- .arg(
- Arg::new("pattern")
- .long("pattern")
- .help("queue/exchange name pattern")
- .required(true),
- )
- .arg(
- Arg::new("apply-to")
- .long("apply-to")
- .help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
- .required(true),
- )
- .arg(
- Arg::new("priority")
- .long("priority")
- .help("policy priority (only the policy with the highest priority is effective)")
- .required(false)
- .default_value("0"),
- )
- .arg(
- Arg::new("definition")
- .long("definition")
- .help("policy definition")
- .required(true),
- ),
- Command::new("vhost_limit")
- .about("set a vhost limit")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("limit name (eg. max-connections, max-queues)")
- .required(true),
- )
- .arg(
- Arg::new("value")
- .long("value")
- .help("limit value")
- .required(true),
- ),
- Command::new("user_limit").about("set a user limit")
- .arg(
- Arg::new("user")
- .long("user")
- .help("username")
- .required(true),
- )
- .arg(
- Arg::new("name")
- .long("name")
- .help("limit name (eg. max-connections, max-queues)")
- .required(true),
- )
- .arg(
- Arg::new("value")
- .long("value")
- .help("limit value")
- .required(true),
- )
- ]
+ .subcommands(command_groups)
}
-fn show_subcomands() -> [Command; 3] {
+fn list_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let nodes_cmd = Command::new("nodes").long_about("Lists cluster members");
+ let vhosts_cmd = Command::new("vhosts")
+ .long_about("Lists virtual hosts")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ VIRTUAL_HOST_GUIDE_URL
+ ));
+ let vhost_limits_cmd = Command::new("vhost_limits")
+ .long_about("Lists virtual host (resource) limits")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ VIRTUAL_HOST_GUIDE_URL
+ ));
+ let connections_cmd = Command::new("connections")
+ .long_about("Lists client connections")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CONNECTION_GUIDE_URL
+ ));
+ let channels_cmd = Command::new("channels")
+ .long_about("Lists AMQP 0-9-1 channels")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CHANNEL_GUIDE_URL
+ ));
+ let queues_cmd = Command::new("queues")
+ .long_about("Lists queues and streams")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ QUEUE_GUIDE_URL
+ ));
+ let exchanges_cmd = Command::new("exchanges").long_about("Lists exchanges");
+ let bindings_cmd = Command::new("bindings").long_about("Lists bindings");
+ let consumers_cmd = Command::new("consumers")
+ .long_about("Lists consumers")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CONSUMER_GUIDE_URL
+ ));
+ let parameters_cmd = Command::new("parameters")
+ .arg(
+ Arg::new("component")
+ .long("component")
+ .help("component (for example: federation-upstream, vhost-limits)")
+ .required(false),
+ )
+ .long_about("Lists runtime parameters")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ));
+ let policies_cmd = Command::new("policies")
+ .long_about("Lists policies")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ POLICY_GUIDE_URL
+ ));
+ let operator_policies_cmd = Command::new("operator_policies")
+ .long_about("Lists operator policies")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ OPERATOR_POLICY_GUIDE_URL
+ ));
+ let users_cmd = Command::new("users").long_about("Lists users in the internal database");
+ let permissions_cmd = Command::new("permissions")
+ .long_about("Lists user permissions")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ ACCESS_CONTROL_GUIDE_URL
+ ));
+ let user_connections_cmd = Command::new("user_connections")
+ .arg(
+ Arg::new("username")
+ .short('u')
+ .long("username")
+ .required(true)
+ .help("Name of the user whose connections should be listed"),
+ )
+ .long_about("Lists client connections that authenticated with a specific username")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CONNECTION_GUIDE_URL
+ ));
+ let user_limits_cmd = Command::new("user_limits")
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(false),
+ )
+ .long_about("Lists per-user (resource) limits")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ USER_LIMIT_GUIDE_URL
+ ));
+ let feature_flags_cmd = Command::new("feature_flags")
+ .long_about("Lists feature flags and their cluster state")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ FEATURE_FLAG_GUIDE_URL
+ ));
+ let deprecated_features_cmd = Command::new("deprecated_features")
+ .long_about("Lists all deprecated features")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEPRECATED_FEATURE_GUIDE_URL
+ ));
+ let deprecated_features_in_use_cmd = Command::new("deprecated_features_in_use")
+ .long_about("Lists the deprecated features that are in used in the cluster")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEPRECATED_FEATURE_GUIDE_URL
+ ));
[
- Command::new("overview")
- .about("displays a essential information about target node and its cluster"),
- Command::new("churn").about("displays object churn metrics"),
- Command::new("endpoint")
- .about("for troubleshooting: displays the computed HTTP API endpoint URI"),
+ nodes_cmd,
+ users_cmd,
+ vhosts_cmd,
+ permissions_cmd,
+ connections_cmd,
+ user_connections_cmd,
+ channels_cmd,
+ queues_cmd,
+ exchanges_cmd,
+ bindings_cmd,
+ consumers_cmd,
+ parameters_cmd,
+ policies_cmd,
+ operator_policies_cmd,
+ vhost_limits_cmd,
+ user_limits_cmd,
+ feature_flags_cmd,
+ deprecated_features_cmd,
+ deprecated_features_in_use_cmd,
]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
}
-fn delete_subcommands() -> [Command; 11] {
- // duplicate this very common global argument so that
- // it can be passed as the end of argument list
- let vhost_arg = Arg::new("vhost")
- .short('V')
- .long("vhost")
- .help("target virtual host")
- .required(false)
- .default_value(DEFAULT_VHOST);
-
- [
- Command::new("user").about("deletes a user").arg(
+fn declare_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let user_cmd = Command::new("user")
+ .about("Creates a user")
+ .arg(
Arg::new("name")
.long("name")
.help("username")
.required(true),
- ),
- Command::new("vhost").about("deletes a virtual host").arg(
- Arg::new("name")
- .long("name")
- .help("virtual host")
- .required(true),
- ),
- Command::new("permissions")
- .about("revokes user permissions to a given vhost")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("user")
- .long("user")
- .help("username")
- .required(true),
- ),
- Command::new("queue")
- .about("deletes a queue")
- .arg(vhost_arg.clone())
- .arg(Arg::new("name").long("name").help("queue").required(true)),
- Command::new("exchange")
- .about("deletes an exchange")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("exchange")
- .required(true),
- ),
- Command::new("binding")
- .about("deletes a binding")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("source")
- .long("source")
- .help("source exchange")
- .required(true),
- )
- .arg(
- Arg::new("destination_type")
- .long("destination_type")
- .help("destination type: exchange or queue")
- .required(true),
- )
- .arg(
- Arg::new("destination")
- .long("destination")
- .help("destination exchange/queue name")
- .required(true),
- )
- .arg(
- Arg::new("routing_key")
- .long("routing_key")
- .help("routing key")
- .required(true),
- )
- .arg(
- Arg::new("arguments")
- .long("arguments")
- .help("additional arguments")
- .required(false)
- .default_value("{}")
- .value_parser(clap::value_parser!(String)),
- ),
- Command::new("parameter")
- .about("clears a runtime parameter")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("parameter's name")
- .required(true),
- )
- .arg(
- Arg::new("component")
- .long("component")
- .help("component (eg. federation-upstream)")
- .required(true),
- ),
- Command::new("policy")
- .about("deletes a policy")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("policy name")
- .required(true),
- ),
- Command::new("operator_policy")
- .about("deletes an operator policy")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("operator policy name")
- .required(true),
- ),
- Command::new("vhost_limit")
- .about("delete a vhost limit")
- .arg(vhost_arg.clone())
- .arg(
- Arg::new("name")
- .long("name")
- .help("limit name (eg. max-connections, max-queues)")
- .required(true),
- ),
- Command::new("user_limit")
- .about("delete a user limit")
- .arg(
- Arg::new("user")
- .long("user")
- .help("username")
- .required(true),
- )
- .arg(
- Arg::new("name")
- .long("name")
- .help("limit name (eg. max-connections, max-queues)")
- .required(true),
- ),
- ]
-}
-
-fn purge_subcommands() -> [Command; 1] {
- [Command::new("queue")
- .long_about("purges (permanently removes unacknowledged messages from) a queue")
+ )
.arg(
- Arg::new("name")
- .long("name")
- .help("name of the queue to purge")
- .required(true),
- )]
-}
-
-fn rebalance_subcommands() -> [Command; 1] {
- [Command::new("queues").about("rebalances queue leaders")]
-}
-
-fn close_subcommands() -> [Command; 1] {
- [Command::new("connection")
- .about("closes a client connection")
+ Arg::new("password_hash")
+ .help(color_print::cformat!(
+ "salted password hash, see {}",
+ PASSWORD_GUIDE_URL
+ ))
+ .long("password-hash")
+ .required(false)
+ .default_value(""),
+ )
+ .arg(
+ Arg::new("password")
+ .long("password")
+ .help(color_print::cformat!(
+ "prefer providing a hash, see {}",
+ PASSWORD_GUIDE_URL
+ ))
+ .required(false)
+ .default_value(""),
+ )
+ .arg(
+ Arg::new("hashing_algorithm")
+ .long("hashing-algorithm")
+ .required(false)
+ .conflicts_with("password_hash")
+ .requires("password")
+ .value_parser(value_parser!(HashingAlgorithm))
+ .default_value("SHA256")
+ .help("The hashing algorithm to use: SHA256 or SHA512"),
+ )
+ .arg(
+ Arg::new("tags")
+ .long("tags")
+ .help("a list of comma-separated tags")
+ .default_value(""),
+ );
+ let vhost_cmd = Command::new("vhost")
+ .about("Creates a virtual host")
+ .after_help(color_print::cformat!("Doc guide:: {}", VIRTUAL_HOST_GUIDE_URL))
.arg(
Arg::new("name")
.long("name")
- .help("connection name (identifying string)")
+ .help("virtual host name")
.required(true),
- )]
-}
-
-fn definitions_subcommands() -> [Command; 2] {
- let export_cmd = Command::new("export")
- .about("export all definitions (queues, exchanges, bindings, users, etc)")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/docs/definitions/"
- ))
+ )
.arg(
- Arg::new("file")
- .long("file")
- .help("output path")
+ Arg::new("default_queue_type")
+ .long("default-queue-type")
+ .required(false)
+ .help(color_print::cformat!("default queue type, one of: classic, quorum, stream"))
+ )
+ .arg(
+ Arg::new("description")
+ .long("description")
+ .required(false)
+ .help("what's the purpose of this virtual host?"),
+ )
+ .arg(
+ Arg::new("tracing")
+ .long("tracing")
.required(false)
- .default_value("-"),
+ .action(ArgAction::SetTrue)
+ .help("should tracing be enabled for this virtual host?"),
);
-
- let import_cmd = Command::new("import")
- .about("import all definitions (queues, exchanges, bindings, users, etc) from a JSON file")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/docs/definitions/"
+ let permissions_cmd = Command::new("permissions")
+ .about("grants permissions to a user")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ ACCESS_CONTROL_GUIDE_URL
))
.arg(
- Arg::new("file")
- .long("file")
- .help("JSON file with definitions")
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(true),
+ )
+ .arg(
+ Arg::new("configure")
+ .long("configure")
+ .help("name pattern for configuration access")
+ .required(true),
+ )
+ .arg(
+ Arg::new("read")
+ .long("read")
+ .help("name pattern for read access")
+ .required(true),
+ )
+ .arg(
+ Arg::new("write")
+ .long("write")
+ .help("name pattern for write access")
.required(true),
);
-
- [export_cmd, import_cmd]
-}
-
-fn export_subcommands() -> [Command; 1] {
- [Command::new("definitions")
- .about("prefer 'definitions export'")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/docs/definitions/"
+ let queue_cmd = Command::new("queue")
+ .about("Declares a queue or a stream")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ QUEUE_GUIDE_URL
))
+ .arg(Arg::new("name").long("name").required(true).help("name"))
.arg(
- Arg::new("file")
- .long("file")
- .help("output path")
+ Arg::new("type")
+ .long("type")
+ .help("queue type")
+ .value_parser(value_parser!(QueueType))
.required(false)
- .default_value("-"),
- )]
-}
-
-fn import_subcommands() -> [Command; 1] {
- [Command::new("definitions")
- .about("prefer 'definitions import'")
- .after_long_help(color_print::cstr!(
- "Doc guide: https://rabbitmq.com/docs/definitions/"
+ .default_value("classic"),
+ )
+ .arg(
+ Arg::new("durable")
+ .long("durable")
+ .help("should it persist after a restart")
+ .required(false)
+ .value_parser(value_parser!(bool)),
+ )
+ .arg(
+ Arg::new("auto_delete")
+ .long("auto-delete")
+ .help("should it be deleted when the last consumer disconnects")
+ .required(false)
+ .value_parser(value_parser!(bool)),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional exchange arguments")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ );
+ let stream_cmd = Command::new("stream")
+ .about("Declares a stream")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ STREAM_GUIDE_URL
))
+ .arg(Arg::new("name").long("name").required(true).help("name"))
.arg(
- Arg::new("file")
- .long("file")
- .help("JSON file with definitions")
- .required(true),
- )]
-}
-
-pub fn publish_subcommands() -> [Command; 1] {
- [Command::new("message")
- .about("Publishes a message to an exchange")
+ Arg::new("expiration")
+ .long("expiration")
+ .help("stream expiration, e.g. 12h for 12 hours, 7D for 7 days, or 1M for 1 month")
+ .required(true)
+ .value_parser(value_parser!(String)),
+ )
.arg(
- Arg::new("routing-key")
- .short('k')
- .long("routing-key")
+ Arg::new("max_length_bytes")
+ .long("max-length-bytes")
+ .help("maximum stream length in bytes")
.required(false)
- .default_value("")
- .help("Name of virtual host"),
+ .value_parser(value_parser!(u64)),
)
.arg(
- Arg::new("exchange")
- .short('e')
- .long("exchange")
+ Arg::new("max_segment_length_bytes")
+ .long("stream-max-segment-size-bytes")
+ .help("maximum stream segment file length in bytes")
.required(false)
- .default_value("")
- .help("Exchange name (defaults to empty)"),
+ .value_parser(value_parser!(u64)),
)
.arg(
- Arg::new("payload")
- .short('m')
- .long("payload")
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional exchange arguments")
.required(false)
- .default_value("test")
- .help("Message payload/body"),
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ );
+ let exchange_cmd = Command::new("exchange")
+ .about("Declares an exchange")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("exchange name")
+ .required(true),
)
.arg(
- Arg::new("properties")
- .short('p')
- .long("properties")
+ Arg::new("type")
+ .long("type")
+ .help("exchange type")
+ .value_parser(value_parser!(ExchangeType))
+ .required(false),
+ )
+ .arg(
+ Arg::new("durable")
+ .long("durable")
+ .help("should it persist after a restart")
+ .required(false)
+ .value_parser(value_parser!(bool)),
+ )
+ .arg(
+ Arg::new("auto_delete")
+ .long("auto-delete")
+ .help("should it be deleted when the last queue is unbound")
+ .required(false)
+ .value_parser(value_parser!(bool)),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional exchange arguments")
.required(false)
.default_value("{}")
- .help("Message properties"),
- )]
-}
-
-pub fn get_subcommands() -> [Command; 1] {
- [Command::new("messages")
- .about("Consumes message(s) from a queue")
+ .value_parser(value_parser!(String)),
+ );
+ let binding_cmd = Command::new("binding")
+ .about("Creates a binding between a source exchange and a destination (a queue or an exchange)")
.arg(
- Arg::new("queue")
- .short('q')
- .long("queue")
+ Arg::new("source")
+ .long("source")
+ .help("source exchange")
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination_type")
+ .long("destination-type")
+ .help("destination type: exchange or queue")
.required(true)
- .help("Queue name"),
+ .value_parser(value_parser!(BindingDestinationType)),
)
.arg(
- Arg::new("count")
- .short('c')
- .long("count")
+ Arg::new("destination")
+ .long("destination")
+ .help("destination exchange/queue name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("routing_key")
+ .long("routing-key")
+ .help("routing key")
+ .required(true),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional arguments")
.required(false)
- .default_value("1")
- .help("Maximum number of messages to consume"),
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ );
+ let parameter_cmd = Command::new("parameter")
+ .about("Sets a runtime parameter")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("parameter's name")
+ .required(true),
)
.arg(
- Arg::new("ack-mode")
- .short('a')
- .long("ack-mode")
+ Arg::new("component")
+ .long("component")
+ .help("component (eg. federation)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("value")
+ .long("value")
+ .help("parameter's value")
+ .required(true),
+ );
+ let policy_cmd = Command::new("policy")
+ .about("Creates or updates a policy")
+ .after_help(color_print::cformat!("Doc guide:: {}", POLICY_GUIDE_URL))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("pattern")
+ .long("pattern")
+ .help("the pattern that is used to match entity (queue, stream, exchange) names")
+ .required(true),
+ )
+ .arg(
+ Arg::new("apply_to")
+ .long("apply-to")
+ .alias("applies-to")
+ .help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
+ .value_parser(value_parser!(PolicyTarget))
+ .required(true),
+ )
+ .arg(
+ Arg::new("priority")
+ .long("priority")
+ .help("policy priority (only the policy with the highest priority is effective)")
.required(false)
- .default_value("ack_requeue_false")
- .help("ack_requeue_false, reject_requeue_false, ack_requeue_true or reject_requeue_true"),
- )]
+ .default_value("0"),
+ )
+ .arg(
+ Arg::new("definition")
+ .long("definition")
+ .help("policy definition")
+ .required(true),
+ );
+ let operator_policy_cmd = Command::new("operator_policy")
+ .about("Creates or updates an operator policy")
+ .after_help(color_print::cformat!("Doc guide:: {}", OPERATOR_POLICY_GUIDE_URL))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("operator policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("pattern")
+ .long("pattern")
+ .help("queue/exchange name pattern")
+ .required(true),
+ )
+ .arg(
+ Arg::new("apply_to")
+ .long("apply-to")
+ .alias("applies-to")
+ .help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
+ .value_parser(value_parser!(PolicyTarget))
+ .required(true),
+ )
+ .arg(
+ Arg::new("priority")
+ .long("priority")
+ .help("policy priority (only the policy with the highest priority is effective)")
+ .required(false)
+ .default_value("0"),
+ )
+ .arg(
+ Arg::new("definition")
+ .long("definition")
+ .help("policy definition")
+ .required(true),
+ );
+ let vhost_limit_cmd = Command::new("vhost_limit")
+ .about("Set a vhost limit")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ VIRTUAL_HOST_LIMIT_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("value")
+ .long("value")
+ .help("limit value")
+ .required(true),
+ );
+ let user_limit_cmd = Command::new("user_limit")
+ .about("Set a user limit")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ USER_LIMIT_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(true),
+ )
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("value")
+ .long("value")
+ .help("limit value")
+ .required(true),
+ );
+ [
+ user_cmd,
+ vhost_cmd,
+ permissions_cmd,
+ queue_cmd,
+ stream_cmd,
+ exchange_cmd,
+ binding_cmd,
+ parameter_cmd,
+ policy_cmd,
+ operator_policy_cmd,
+ vhost_limit_cmd,
+ user_limit_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn show_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let overview_cmd = Command::new("overview")
+ .about("Displays essential information about target node and its cluster");
+ let churn_cmd = Command::new("churn").about("Displays object churn metrics");
+ let endpoint_cmd = Command::new("endpoint")
+ .about("Displays the computed HTTP API endpoint URI. Use for troubleshooting only.");
+ let memory_breakdown_in_bytes_cmd = Command::new("memory_breakdown_in_bytes")
+ .about("Provides a memory footprint breakdown (in bytes) for the target node")
+ .arg(
+ Arg::new("node")
+ .long("node")
+ .help("target node, must be a cluster member")
+ .required(true),
+ )
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ MEMORY_FOOTPRINT_GUIDE_URL
+ ));
+
+ let memory_breakdown_in_percent_cmd = Command::new("memory_breakdown_in_percent")
+ .about("Provides a memory footprint breakdown (in percent) for the target node")
+ .arg(
+ Arg::new("node")
+ .long("node")
+ .help("target node, must be a cluster member")
+ .required(true),
+ )
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ MEMORY_FOOTPRINT_GUIDE_URL
+ ));
+
+ [
+ overview_cmd,
+ churn_cmd,
+ endpoint_cmd,
+ memory_breakdown_in_bytes_cmd,
+ memory_breakdown_in_percent_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn delete_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let user_cmd = Command::new("user")
+ .about("Deletes a user")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("username")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let vhost_cmd = Command::new("vhost")
+ .about("Deletes a virtual host")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("virtual host")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let permissions_cmd = Command::new("permissions")
+ .about("Revokes user permissions to a given vhost")
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let queue_cmd = Command::new("queue")
+ .about("Deletes a queue")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("queue name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let stream_cmd = Command::new("stream")
+ .about("Deletes a stream")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("stream name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let exchange_cmd = Command::new("exchange")
+ .about("Deletes an exchange")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("exchange name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let binding_cmd = Command::new("binding")
+ .about("Deletes a binding")
+ .arg(
+ Arg::new("source")
+ .long("source")
+ .help("source exchange")
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination_type")
+ .long("destination-type")
+ .help("destination type: exchange or queue")
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination")
+ .long("destination")
+ .help("destination exchange/queue name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("routing_key")
+ .long("routing-key")
+ .help("routing key")
+ .required(true),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional arguments")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ )
+ .arg(idempotently_arg.clone());
+ let parameter_cmd = Command::new("parameter")
+ .about("Clears a runtime parameter")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("parameter's name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("component")
+ .long("component")
+ .help("component (eg. federation-upstream)")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let policy_cmd = Command::new("policy")
+ .about("Deletes a policy")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("policy name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let operator_policy_cmd = Command::new("operator_policy")
+ .about("Deletes an operator policy")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("operator policy name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let vhost_limit_cmd = Command::new("vhost_limit")
+ .about("delete a vhost limit")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ );
+ let user_limit_cmd = Command::new("user_limit")
+ .about("Clears a user limit")
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(true),
+ )
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ );
+ let shovel_cmd = Command::new("shovel")
+ .about("Delete a shovel")
+ .arg(idempotently_arg.clone())
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("shovel name")
+ .required(true),
+ );
+ [
+ user_cmd,
+ vhost_cmd,
+ permissions_cmd,
+ queue_cmd,
+ stream_cmd,
+ exchange_cmd,
+ binding_cmd,
+ parameter_cmd,
+ policy_cmd,
+ operator_policy_cmd,
+ vhost_limit_cmd,
+ user_limit_cmd,
+ shovel_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn purge_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let queue_cmd = Command::new("queue")
+ .long_about("Purges (permanently removes unacknowledged messages from) a queue")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("name of the queue to purge")
+ .required(true),
+ );
+ [queue_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn binding_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let declare_cmd = Command::new("declare")
+ .about("Creates a binding between a source exchange and a destination (a queue or an exchange)")
+ .arg(
+ Arg::new("source")
+ .long("source")
+ .help("source exchange")
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination_type")
+ .long("destination-type")
+ .help("destination type: exchange or queue")
+ .required(true)
+ .value_parser(value_parser!(BindingDestinationType)),
+ )
+ .arg(
+ Arg::new("destination")
+ .long("destination")
+ .help("destination exchange/queue name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("routing_key")
+ .long("routing-key")
+ .help("routing key")
+ .required(true),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional arguments")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ );
+ let delete_cmd = Command::new("delete")
+ .about("Deletes a binding")
+ .arg(
+ Arg::new("source")
+ .long("source")
+ .help("source exchange")
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination_type")
+ .long("destination-type")
+ .help("destination type: exchange or queue")
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination")
+ .long("destination")
+ .help("destination exchange/queue name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("routing_key")
+ .long("routing-key")
+ .help("routing key")
+ .required(true),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional arguments")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ )
+ .arg(idempotently_arg.clone());
+ let list_cmd = Command::new("list").long_about("Lists bindings");
+
+ [declare_cmd, delete_cmd, list_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn queues_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let declare_cmd = Command::new("declare")
+ .about("Declares a queue or a stream")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ QUEUE_GUIDE_URL
+ ))
+ .arg(Arg::new("name").long("name").required(true).help("name"))
+ .arg(
+ Arg::new("type")
+ .long("type")
+ .help("queue type")
+ .value_parser(value_parser!(QueueType))
+ .required(false)
+ .default_value("classic"),
+ )
+ .arg(
+ Arg::new("durable")
+ .long("durable")
+ .help("should it persist after a restart")
+ .required(false)
+ .value_parser(value_parser!(bool)),
+ )
+ .arg(
+ Arg::new("auto_delete")
+ .long("auto-delete")
+ .help("should it be deleted when the last consumer disconnects")
+ .required(false)
+ .value_parser(value_parser!(bool)),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional exchange arguments")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ );
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+ let delete_cmd = Command::new("delete")
+ .about("Deletes a queue")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("queue name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let list_cmd = Command::new("list")
+ .long_about("Lists queues and streams")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ QUEUE_GUIDE_URL
+ ));
+ let purge_cmd = Command::new("purge")
+ .long_about("Purges (permanently removes unacknowledged messages from) a queue")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("name of the queue to purge")
+ .required(true),
+ );
+ let rebalance_cmd = Command::new("rebalance").about("Rebalances queue leaders");
+ [declare_cmd, delete_cmd, list_cmd, purge_cmd, rebalance_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn streams_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let declare_cmd = Command::new("declare")
+ .about("Declares a stream")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ STREAM_GUIDE_URL
+ ))
+ .arg(Arg::new("name").long("name").required(true).help("name"))
+ .arg(
+ Arg::new("expiration")
+ .long("expiration")
+ .help("stream expiration, e.g. 12h for 12 hours, 7D for 7 days, or 1M for 1 month")
+ .required(true)
+ .value_parser(value_parser!(String)),
+ )
+ .arg(
+ Arg::new("max_length_bytes")
+ .long("max-length-bytes")
+ .help("maximum stream length in bytes")
+ .required(false)
+ .value_parser(value_parser!(u64)),
+ )
+ .arg(
+ Arg::new("max_segment_length_bytes")
+ .long("stream-max-segment-size-bytes")
+ .help("maximum stream segment file length in bytes")
+ .required(false)
+ .value_parser(value_parser!(u64)),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional exchange arguments")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ );
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+ let delete_cmd = Command::new("delete")
+ .about("Deletes a queue")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("queue name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let list_cmd = Command::new("list")
+ .long_about("Lists streams and queues and")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ STREAM_GUIDE_URL
+ ));
+ [declare_cmd, delete_cmd, list_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn parameters_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let list_all_cmd = Command::new("list_all")
+ .long_about("Lists all runtime parameters across all virtual hosts")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ));
+ let list_cmd = Command::new("list")
+ .arg(
+ Arg::new("component")
+ .long("component")
+ .help("component (for example: federation-upstream, vhost-limits)")
+ .required(false),
+ )
+ .long_about("Lists runtime parameters")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ));
+ let list_in_cmd = Command::new("list_in")
+ .arg(
+ Arg::new("component")
+ .long("component")
+ .help("component (for example: federation-upstream, vhost-limits)")
+ .required(true),
+ )
+ .long_about("Lists runtime parameters")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ));
+
+ let set_cmd = Command::new("set")
+ .alias("declare")
+ .about("Sets a runtime parameter")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("parameter's name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("component")
+ .long("component")
+ .help("component (eg. federation)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("value")
+ .long("value")
+ .help("parameter's value")
+ .required(true),
+ );
+
+ let clear_cmd = Command::new("clear")
+ .alias("delete")
+ .about("Clears (deletes) a runtime parameter")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("parameter's name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("component")
+ .long("component")
+ .help("component (eg. federation-upstream)")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+
+ [clear_cmd, list_all_cmd, list_cmd, list_in_cmd, set_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn global_parameters_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let list_cmd = Command::new("list")
+ .long_about("Lists global runtime parameters")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ));
+
+ let set_cmd = Command::new("set")
+ .alias("declare")
+ .about("Sets a global runtime parameter")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ RUNTIME_PARAMETER_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("parameter's name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("value")
+ .long("value")
+ .help("parameter's value")
+ .required(true),
+ );
+
+ let clear_cmd = Command::new("clear")
+ .alias("delete")
+ .about("Clears (deletes) a global runtime parameter")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("parameter's name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+
+ [clear_cmd, list_cmd, set_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn operator_policies_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let declare_cmd = Command::new("declare")
+ .visible_aliases(vec!["update", "set"])
+ .about("Creates or updates an operator policy")
+ .after_help(color_print::cformat!("Doc guide:: {}", POLICY_GUIDE_URL))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("operator policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("pattern")
+ .long("pattern")
+ .help("the pattern that is used to match entity (queue, stream, exchange) names")
+ .required(true),
+ )
+ .arg(
+ Arg::new("apply_to")
+ .long("apply-to")
+ .alias("applies-to")
+ .help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
+ .value_parser(value_parser!(PolicyTarget))
+ .required(true),
+ )
+ .arg(
+ Arg::new("priority")
+ .long("priority")
+ .help("operator policy priority (only the policy with the highest priority is effective)")
+ .required(false)
+ .default_value("0"),
+ )
+ .arg(
+ Arg::new("definition")
+ .long("definition")
+ .help("operator policy definition")
+ .required(true),
+ );
+
+ let list_cmd = Command::new("list")
+ .long_about("Lists operator policies")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ POLICY_GUIDE_URL
+ ));
+
+ let delete_cmd = Command::new("delete")
+ .about("Deletes an operator policy")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("policy name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+
+ let delete_definition_key_cmd = Command::new("delete_definition_keys")
+ .about("Deletes definition keys from an operator policy, unless it is the only key")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("operator policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition_keys")
+ .long("definition-keys")
+ .num_args(1..)
+ .value_delimiter(',')
+ .action(ArgAction::Append)
+ .help("comma-separated definition keys"),
+ );
+
+ let delete_definition_key_from_all_in_cmd = Command::new("delete_definition_keys_from_all_in")
+ .about("Deletes a definition key from all operator policies in a virtual host, unless it is the only key")
+ .arg(
+ Arg::new("definition_keys")
+ .long("definition-keys")
+ .num_args(1..)
+ .value_delimiter(',')
+ .action(ArgAction::Append)
+ .help("comma-separated definition keys")
+ );
+
+ let list_in_cmd = Command::new("list_in")
+ .about("Lists operator policies in a specific virtual host")
+ .arg(
+ Arg::new("apply_to")
+ .long("apply-to")
+ .alias("applies-to")
+ .value_parser(value_parser!(PolicyTarget)),
+ );
+
+ let list_matching_cmd = Command::new("list_matching_object")
+ .about("Lists operator policies that match an object (queue, stream, exchange) name")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("name to verify")
+ .required(true),
+ )
+ .arg(
+ Arg::new("type")
+ .long("type")
+ .value_parser(value_parser!(PolicyTarget))
+ .required(true)
+ .help("target type, one of 'queues', 'streams', 'exchanges'"),
+ );
+
+ let patch_cmd = Command::new("patch")
+ .about("Merges a set of keys into existing operator policy definitions")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("operator policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition")
+ .long("definition")
+ .help("operator policy definition changes to merge into the existing ones"),
+ );
+
+ let update_cmd = Command::new("update_definition")
+ .about("Updates an operator policy definition key")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("operator policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition_key")
+ .long("definition-key")
+ .help("operator policy definition key to update")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition_value")
+ .long("new-value")
+ .help("new definition value to set")
+ .required(true),
+ );
+
+ let update_all_in_cmd = Command::new("update_definitions_of_all_in")
+ .about("Updates a definition key in all operator policies in a virtual host")
+ .arg(
+ Arg::new("definition_key")
+ .long("definition-key")
+ .help("operator policy definition key to update")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition_value")
+ .long("new-value")
+ .help("new operator definition value to set")
+ .required(true),
+ );
+
+ [
+ declare_cmd,
+ delete_cmd,
+ delete_definition_key_cmd,
+ delete_definition_key_from_all_in_cmd,
+ list_cmd,
+ list_in_cmd,
+ list_matching_cmd,
+ patch_cmd,
+ update_cmd,
+ update_all_in_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn policies_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let declare_cmd = Command::new("declare")
+ .visible_aliases(vec!["update", "set"])
+ .about("Creates or updates a policy")
+ .after_help(color_print::cformat!("Doc guide:: {}", POLICY_GUIDE_URL))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("pattern")
+ .long("pattern")
+ .help("the pattern that is used to match entity (queue, stream, exchange) names")
+ .required(true),
+ )
+ .arg(
+ Arg::new("apply_to")
+ .long("apply-to")
+ .alias("applies-to")
+ .help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
+ .value_parser(value_parser!(PolicyTarget))
+ .required(true),
+ )
+ .arg(
+ Arg::new("priority")
+ .long("priority")
+ .help("policy priority (only the policy with the highest priority is effective)")
+ .required(false)
+ .default_value("0"),
+ )
+ .arg(
+ Arg::new("definition")
+ .long("definition")
+ .help("policy definition")
+ .required(true),
+ );
+
+ let declare_override_cmd = Command::new("declare_override")
+ .about("Declares a new policy from an existing one, with a higher priority, and merges a set of keys into the new overriding policy definition")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("the name of the policy to create an override for")
+ .required(true),
+ )
+ .arg(
+ Arg::new("override_name")
+ .long("override-name")
+ .help("the name of the new overriding policy. If omitted, an 'override' suffix will be added to the original name.")
+ .required(false),
+ )
+ .arg(
+ Arg::new("definition")
+ .long("definition")
+ .help("additional definitions to merge into the new overriding policy"),
+ );
+
+ let declare_blanket_cmd = Command::new("declare_blanket")
+ .about("Creates a low priority blanket policy, a policy that matches all objects not matched by any other policy")
+ .after_help(color_print::cformat!("Doc guide:: {}", POLICY_GUIDE_URL))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("blanket policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("apply_to")
+ .long("apply-to")
+ .alias("applies-to")
+ .help("entities to apply to (queues, classic_queues, quorum_queues, streams, exchanges, all)")
+ .value_parser(value_parser!(PolicyTarget))
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition")
+ .long("definition")
+ .help("policy definition")
+ .required(true),
+ );
+
+ let list_cmd = Command::new("list")
+ .long_about("Lists policies")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ POLICY_GUIDE_URL
+ ));
+
+ let delete_cmd = Command::new("delete")
+ .about("Deletes a policy")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("policy name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+
+ let delete_definition_keys_cmd = Command::new("delete_definition_keys")
+ .about("Deletes a definition key from a policy, unless it is the only key")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition_keys")
+ .long("definition-keys")
+ .num_args(1..)
+ .value_delimiter(',')
+ .action(ArgAction::Append)
+ .help("comma-separated definition keys"),
+ );
+
+ let delete_definition_keys_from_all_in_cmd = Command::new("delete_definition_keys_from_all_in")
+ .about("Deletes definition keys from all policies in a virtual host, unless it is the only policy key")
+ .arg(
+ Arg::new("definition_keys")
+ .long("definition-keys")
+ .help("comma-separated definition keys")
+ .num_args(1..)
+ .value_delimiter(',')
+ .action(ArgAction::Append)
+ .required(true)
+ );
+
+ let list_in_cmd = Command::new("list_in")
+ .about("Lists policies in a specific virtual host")
+ .arg(
+ Arg::new("apply_to")
+ .long("apply-to")
+ .alias("applies-to")
+ .value_parser(value_parser!(PolicyTarget)),
+ );
+
+ let list_matching_cmd = Command::new("list_matching_object")
+ .about("Lists policies that match an object (queue, stream, exchange) name")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("name to verify")
+ .required(true),
+ )
+ .arg(
+ Arg::new("type")
+ .long("type")
+ .value_parser(value_parser!(PolicyTarget))
+ .required(true)
+ .help("target type, one of 'queues', 'streams', 'exchanges'"),
+ );
+
+ let patch_cmd = Command::new("patch")
+ .about("Merges a set of keys into existing policy definitions")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition")
+ .long("definition")
+ .help("policy definition changes to merge into the existing ones"),
+ );
+
+ let update_cmd = Command::new("update_definition")
+ .about("Updates a policy definition key")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("policy name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition_key")
+ .long("definition-key")
+ .help("policy definition key to update")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition_value")
+ .long("new-value")
+ .help("new definition value to set")
+ .required(true),
+ );
+
+ let update_all_in_cmd = Command::new("update_definitions_of_all_in")
+ .about("Updates a definition key in all policies in a virtual host")
+ .arg(
+ Arg::new("definition_key")
+ .long("definition-key")
+ .help("policy definition key to update")
+ .required(true),
+ )
+ .arg(
+ Arg::new("definition_value")
+ .long("new-value")
+ .help("new definition value to set")
+ .required(true),
+ );
+
+ [
+ declare_cmd,
+ declare_override_cmd,
+ declare_blanket_cmd,
+ delete_cmd,
+ delete_definition_keys_cmd,
+ delete_definition_keys_from_all_in_cmd,
+ list_cmd,
+ list_in_cmd,
+ list_matching_cmd,
+ patch_cmd,
+ update_cmd,
+ update_all_in_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn health_check_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let node_is_quorum_critical_after_help = color_print::cformat!(
+ r#"
+Doc guides:
+
+ * {}
+ * {}"#,
+ QUORUM_QUEUE_FAILURE_HANDLING_GUIDE_URL,
+ UPGRADE_GUIDE_URL
+ );
+
+ let local_alarms = Command::new("local_alarms")
+ .about("Checks if there are any resource alarms in effect on the target node");
+ let cluster_wide_alarms = Command::new("cluster_wide_alarms")
+ .about("Checks if there are any resource alarms in effect across the entire cluster");
+ let node_is_quorum_critical = Command::new("node_is_quorum_critical")
+ .about("Fails if there are queues/streams with minimum online quorum (queues/streams that will lose their quorum if the target node shuts down)")
+ .after_help(node_is_quorum_critical_after_help);
+ let deprecated_features_in_use = Command::new("deprecated_features_in_use")
+ .about("Fails if there are any deprecated features in use in the cluster")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEPRECATED_FEATURE_GUIDE_URL
+ ));
+
+ let port_listener = Command::new("port_listener")
+ .about(
+ "Verifies that there's a reachable TCP listener on the given port on the target node",
+ )
+ .arg(
+ Arg::new("port")
+ .long("port")
+ .value_parser(value_parser!(u16)),
+ )
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ HEALTH_CHECK_GUIDE_URL
+ ));
+
+ let protocol_listener = Command::new("protocol_listener")
+ .about(
+ "Verifies that there's a reachable TCP listener on the given protocol alias on the target node",
+ )
+ .arg(
+ Arg::new("protocol")
+ .long("protocol")
+ .value_parser(value_parser!(SupportedProtocol))
+ .long_help("An alias for one of the protocols that RabbitMQ supports, with or without TLS: 'amqp', 'amqp/ssl', 'stream', 'stream/ssl', 'mqtt', 'mqtt/ssl', 'stomp', 'stomp/ssl', 'http/web-mqtt', 'https/web-mqtt', 'http/web-stomp', 'https/web-stomp', 'http/prometheus', 'https/prometheus', 'http', 'https'"),
+ )
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ HEALTH_CHECK_GUIDE_URL
+ ));
+
+ [
+ local_alarms,
+ cluster_wide_alarms,
+ node_is_quorum_critical,
+ deprecated_features_in_use,
+ port_listener,
+ protocol_listener,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn rebalance_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let queues_cmd = Command::new("queues").about("Rebalances queue leaders");
+ [queues_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn close_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let close_connection = Command::new("connection")
+ .about("Closes a client connection")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("connection name (identifying string)")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let close_user_connections = Command::new("user_connections")
+ .about("Closes all connections that authenticated with a specific username")
+ .arg(
+ Arg::new("username")
+ .short('u')
+ .long("username")
+ .help("Name of the user whose connections to close")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ [close_connection, close_user_connections]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn channels_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let list_cmd = Command::new("list")
+ .long_about("Lists all channels across all virtual hosts")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ "https://www.rabbitmq.com/docs/channels"
+ ));
+
+ [list_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn connections_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let close_connection = Command::new("close")
+ .about("Closes a client connection")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("connection name (identifying string)")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let close_user_connections = Command::new("close_of_user")
+ .about("Closes all connections that are authenticated with a specific username")
+ .arg(
+ Arg::new("username")
+ .short('u')
+ .long("username")
+ .help("Name of the user whose connections should be closed")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let list_cmd = Command::new("list")
+ .long_about("Lists client connections")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CONNECTION_GUIDE_URL
+ ));
+ let list_user_connections_cmd = Command::new("list_of_user")
+ .arg(
+ Arg::new("username")
+ .short('u')
+ .long("username")
+ .required(true)
+ .help("Name of the user whose connections should be listed"),
+ )
+ .long_about("Lists client connections that are authenticated with a specific username")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CONNECTION_GUIDE_URL
+ ));
+
+ [
+ close_connection,
+ close_user_connections,
+ list_cmd,
+ list_user_connections_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn definitions_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let export_cmd = Command::new("export")
+ .about("Export cluster-wide definitions")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEFINITION_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("file")
+ .group("output")
+ .long("file")
+ .help("output file path")
+ .required(false)
+ .default_value("-")
+ .conflicts_with("stdout"),
+ )
+ .arg(
+ Arg::new("stdout")
+ .group("output")
+ .long("stdout")
+ .help("print result to the standard output stream")
+ .required(false)
+ .num_args(0)
+ .action(ArgAction::SetTrue)
+ .conflicts_with("file"),
+ )
+ .arg(
+ Arg::new("transformations")
+ .long("transformations")
+ .short('t')
+ .long_help(
+ r#"
+A comma-separated list of names of the definition transformations to apply.
+
+Supported transformations:
+
+ * prepare_for_quorum_queue_migration
+ * strip_cmq_keys_from_policies
+ * drop_empty_policies
+ * obfuscate_usernames
+ * exclude_users
+ * exclude_permissions
+ * exclude_runtime_parameters
+ * exclude_policies
+ * no_op
+
+All unknown transformations will be ignored (will be replaced with a `no_op`).
+
+Examples:
+
+ * --transformations prepare_for_quorum_queue_migration,drop_empty_policies
+ * --transformations strip_cmq_keys_from_policies,drop_empty_policies
+ * --transformations exclude_users,exclude_permissions
+ * --transformations obfuscate_usernames
+ * --transformations exclude_runtime_parameters,exclude_policies
+ * --transformations no_op
+ "#,
+ )
+ .num_args(1..)
+ .value_delimiter(',')
+ .action(ArgAction::Append)
+ .required(false),
+ );
+
+ let export_from_vhost_cmd = Command::new("export_from_vhost")
+ .about("Export definitions of a specific virtual host")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEFINITION_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("file")
+ .group("output")
+ .long("file")
+ .help("output file path")
+ .required(false)
+ .default_value("-")
+ .conflicts_with("stdout"),
+ )
+ .arg(
+ Arg::new("stdout")
+ .group("output")
+ .long("stdout")
+ .help("print result to the standard output stream")
+ .required(false)
+ .num_args(0)
+ .action(ArgAction::SetTrue)
+ .conflicts_with("file"),
+ )
+ .arg(
+ Arg::new("transformations")
+ .long("transformations")
+ .short('t')
+ .long_help(
+ r#"
+A comma-separated list of names of the definition transformations to apply.
+
+Supported transformations:
+
+ * prepare_for_quorum_queue_migration
+ * strip_cmq_keys_from_policies
+ * drop_empty_policies
+ * no_op
+
+All unknown transformations will be ignored (will be replaced with a `no_op`).
+
+Examples:
+
+ * --transformations prepare_for_quorum_queue_migration,drop_empty_policies
+ * --transformations strip_cmq_keys_from_policies,drop_empty_policies
+ * --transformations no_op
+ "#,
+ )
+ .num_args(1..)
+ .value_delimiter(',')
+ .action(ArgAction::Append)
+ .required(false),
+ );
+
+ let import_cmd = Command::new("import")
+ .about("Import cluster-wide definitions (of multiple virtual hosts)")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEFINITION_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("file")
+ .group("input")
+ .long("file")
+ .help("cluster-wide definitions JSON file path; mutually exclusive with --stdin")
+ .required(true)
+ .conflicts_with("stdin"),
+ )
+ .arg(
+ Arg::new("stdin")
+ .group("input")
+ .long("stdin")
+ .help("read input JSON from the standard input stream, mutually exclusive with --file")
+ .required(false)
+ .num_args(0)
+ .action(ArgAction::SetTrue)
+ .conflicts_with("file"),
+ );
+
+ let import_into_vhost_cmd = Command::new("import_into_vhost")
+ .about("Import a virtual host-specific definitions file into a virtual host")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEFINITION_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("file")
+ .group("input")
+ .long("file")
+ .help("cluster-wide definitions JSON file path; mutually exclusive with --stdin")
+ .required(true)
+ .conflicts_with("stdin"),
+ )
+ .arg(
+ Arg::new("stdin")
+ .group("input")
+ .long("stdin")
+ .help("read input JSON from the standard input stream, mutually exclusive with --file")
+ .required(false)
+ .num_args(0)
+ .action(ArgAction::SetTrue)
+ .conflicts_with("file"),
+ );
+
+ [
+ export_cmd,
+ export_from_vhost_cmd,
+ import_cmd,
+ import_into_vhost_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn exchanges_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let bind_cmd = Command::new("bind")
+ .about("Creates a binding between a source exchange and a destination (a queue or an exchange)")
+ .arg(
+ Arg::new("source")
+ .long("source")
+ .help("source exchange")
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination_type")
+ .long("destination-type")
+ .help("destination type: exchange or queue")
+ .required(true)
+ .value_parser(value_parser!(BindingDestinationType)),
+ )
+ .arg(
+ Arg::new("destination")
+ .long("destination")
+ .help("destination exchange/queue name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("routing_key")
+ .long("routing-key")
+ .help("routing key")
+ .required(true),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional arguments")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ );
+ let declare_cmd = Command::new("declare")
+ .about("Declares an exchange")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("exchange name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("type")
+ .long("type")
+ .help("exchange type")
+ .value_parser(value_parser!(ExchangeType))
+ .required(false),
+ )
+ .arg(
+ Arg::new("durable")
+ .long("durable")
+ .help("should it persist after a restart")
+ .required(false)
+ .value_parser(value_parser!(bool)),
+ )
+ .arg(
+ Arg::new("auto_delete")
+ .long("auto-delete")
+ .help("should it be deleted when the last queue is unbound")
+ .required(false)
+ .value_parser(value_parser!(bool)),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional exchange arguments")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ );
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+ let delete_cmd = Command::new("delete")
+ .about("Deletes an exchange")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("exchange name")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+ let list_cmd = Command::new("list").long_about("Lists exchanges");
+ let unbind_cmd = Command::new("unbind")
+ .about("Deletes a binding")
+ .arg(
+ Arg::new("source")
+ .long("source")
+ .help("source exchange")
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination_type")
+ .long("destination-type")
+ .help("destination type: exchange or queue")
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination")
+ .long("destination")
+ .help("destination exchange/queue name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("routing_key")
+ .long("routing-key")
+ .help("routing key")
+ .required(true),
+ )
+ .arg(
+ Arg::new("arguments")
+ .long("arguments")
+ .help("additional arguments")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ )
+ .arg(idempotently_arg.clone());
+ [bind_cmd, declare_cmd, delete_cmd, list_cmd, unbind_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+fn export_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let definitions = Command::new("definitions")
+ .about("Export cluster-wide definitions")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEFINITION_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("file")
+ .group("output")
+ .long("file")
+ .help("output file path")
+ .required(false)
+ .default_value("-")
+ .conflicts_with("stdout"),
+ )
+ .arg(
+ Arg::new("stdout")
+ .group("output")
+ .long("stdout")
+ .help("print result to the standard output stream")
+ .required(false)
+ .num_args(0)
+ .action(ArgAction::SetTrue)
+ .conflicts_with("file"),
+ )
+ .arg(
+ Arg::new("transformations")
+ .long("transformations")
+ .short('t')
+ .long_help(
+ r#"
+A comma-separated list of names of the definition transformations to apply.
+
+Supported transformations:
+
+ * no_op
+ * prepare_for_quorum_queue_migration
+ * strip_cmq_keys_from_policies
+ * drop_empty_policies
+ * obfuscate_usernames
+ * exclude_users
+ * exclude_permissions
+ * exclude_runtime_parameters
+ * exclude_policies
+
+Examples:
+
+ * --transformations prepare_for_quorum_queue_migration,drop_empty_policies
+ * --transformations strip_cmq_keys_from_policies,drop_empty_policies
+ * --transformations exclude_users,exclude_permissions
+ * --transformations obfuscate_usernames
+ * --transformations exclude_runtime_parameters,exclude_policies
+ * --transformations no_op
+ "#,
+ )
+ .num_args(1..)
+ .value_delimiter(',')
+ .action(ArgAction::Append)
+ .required(false),
+ );
+ [definitions]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn import_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ [Command::new("definitions")
+ .about("Prefer 'definitions import'")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEFINITION_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("file")
+ .long("file")
+ .help("JSON file with definitions")
+ .required(true),
+ )]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn feature_flags_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let list_cmd = Command::new("list")
+ .long_about("Lists feature flags and their cluster state")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ FEATURE_FLAG_GUIDE_URL
+ ));
+
+ let enable_cmd = Command::new("enable")
+ .long_about("Enables a feature flag")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ FEATURE_FLAG_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("feature flag name (identifier)")
+ .required(true),
+ );
+
+ let enable_all_cmd = Command::new("enable_all")
+ .long_about("Enables all stable feature flags")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ FEATURE_FLAG_GUIDE_URL
+ ));
+
+ [list_cmd, enable_cmd, enable_all_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn deprecated_features_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let list_cmd = Command::new("list")
+ .long_about("Lists deprecated features")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEPRECATED_FEATURE_GUIDE_URL
+ ));
+
+ let list_in_use_cmd = Command::new("list_used")
+ .long_about("Lists the deprecated features that are found to be in use in the cluster")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ DEPRECATED_FEATURE_GUIDE_URL
+ ));
+
+ [list_cmd, list_in_use_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn plugins_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let list_all_cmd = Command::new("list_all")
+ .about("Lists plugins across all cluster nodes")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ PLUGIN_GUIDE_URL
+ ));
+
+ let list_on_node_cmd = Command::new("list_on_node")
+ .about("Lists plugins enabled on a specific node")
+ .arg(
+ Arg::new("node")
+ .long("node")
+ .help("target node, must be a cluster member")
+ .required(true),
+ )
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ PLUGIN_GUIDE_URL
+ ));
+
+ [list_all_cmd, list_on_node_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn nodes_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let list_cmd = Command::new("list")
+ .long_about("Lists cluster nodes")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CLUSTERING_GUIDE_URL
+ ));
+
+ let memory_breakdown_in_bytes_cmd = Command::new("memory_breakdown_in_bytes")
+ .about("Provides a memory footprint breakdown (in bytes) for the target node")
+ .arg(
+ Arg::new("node")
+ .long("node")
+ .help("target node, must be a cluster member")
+ .required(true),
+ )
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ MEMORY_FOOTPRINT_GUIDE_URL
+ ));
+
+ let memory_breakdown_in_percent_cmd = Command::new("memory_breakdown_in_percent")
+ .about("Provides a memory footprint breakdown (in percent) for the target node")
+ .arg(
+ Arg::new("node")
+ .long("node")
+ .help("target node, must be a cluster member")
+ .required(true),
+ )
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ MEMORY_FOOTPRINT_GUIDE_URL
+ ));
+
+ [
+ list_cmd,
+ memory_breakdown_in_percent_cmd,
+ memory_breakdown_in_bytes_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn vhosts_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let list_cmd = Command::new("list")
+ .long_about("Lists virtual hosts")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ VIRTUAL_HOST_GUIDE_URL
+ ));
+
+ let declare_cmd = Command::new("declare")
+ .about("Creates a virtual host")
+ .after_help(color_print::cformat!("Doc guide:: {}", VIRTUAL_HOST_GUIDE_URL))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("virtual host name")
+ .required(true),
+ )
+ .arg(
+ Arg::new("default_queue_type")
+ .long("default-queue-type")
+ .required(false)
+ .help(color_print::cformat!("default queue type, one of: classic, quorum, stream"))
+ )
+ .arg(
+ Arg::new("description")
+ .long("description")
+ .required(false)
+ .help("what's the purpose of this virtual host?"),
+ )
+ .arg(
+ Arg::new("tracing")
+ .long("tracing")
+ .required(false)
+ .action(ArgAction::SetTrue)
+ .help("should tracing be enabled for this virtual host?"),
+ );
+
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+ let delete_cmd = Command::new("delete")
+ .about("Deletes a virtual host")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("virtual host")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+
+ let bulk_delete_cmd = Command::new("delete_multiple")
+ .about(color_print::cstr!("DANGER ZONE. Deletes multiple virtual hosts at once using a name matching pattern"))
+ .after_help(color_print::cformat!("Doc guide:: {}", VIRTUAL_HOST_GUIDE_URL))
+ .arg(
+ Arg::new("name_pattern")
+ .long("name-pattern")
+ .help("a regular expression that will be used to match virtual host names")
+ .required(true),
+ )
+ .arg(
+ Arg::new("approve")
+ .long("approve")
+ .action(ArgAction::SetTrue)
+ .help("this operation is very destructive and requires an explicit approval")
+ .required(false),
+ )
+ .arg(
+ Arg::new("dry_run")
+ .long("dry-run")
+ .action(ArgAction::SetTrue)
+ .help("show what would be deleted without performing the actual deletion")
+ .required(false),
+ )
+ .arg(idempotently_arg.clone());
+ let enable_deletion_protection_cmd = Command::new("enable_deletion_protection")
+ .about("Enables deletion protection for a virtual host")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ VHOST_DELETION_PROTECTION_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("virtual host name")
+ .required(true),
+ );
+ let disable_deletion_protection_cmd = Command::new("disable_deletion_protection")
+ .about("Disables deletion protection for a virtual host")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ VHOST_DELETION_PROTECTION_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("virtual host name")
+ .required(true),
+ );
+
+ [
+ list_cmd,
+ declare_cmd,
+ delete_cmd,
+ bulk_delete_cmd,
+ enable_deletion_protection_cmd,
+ disable_deletion_protection_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn users_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let declare_cmd = Command::new("declare")
+ .about("Creates a user")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("username")
+ .required(true),
+ )
+ .arg(
+ Arg::new("password_hash")
+ .help(color_print::cformat!(
+ "salted password hash, see {}",
+ PASSWORD_GUIDE_URL
+ ))
+ .long("password-hash")
+ .required(false)
+ .default_value(""),
+ )
+ .arg(
+ Arg::new("password")
+ .long("password")
+ .help(color_print::cformat!(
+ "prefer providing a hash, see {}",
+ PASSWORD_GUIDE_URL
+ ))
+ .required(false)
+ .default_value(""),
+ )
+ .arg(
+ Arg::new("hashing_algorithm")
+ .long("hashing-algorithm")
+ .required(false)
+ .conflicts_with("password_hash")
+ .requires("password")
+ .value_parser(value_parser!(HashingAlgorithm))
+ .default_value("SHA256")
+ .help("The hashing algorithm to use: SHA256 or SHA512"),
+ )
+ .arg(
+ Arg::new("tags")
+ .long("tags")
+ .help("a list of comma-separated tags")
+ .default_value(""),
+ );
+ let list_cmd = Command::new("list").long_about("Lists users in the internal database");
+ let permissions_cmd = Command::new("permissions")
+ .long_about("Lists user permissions")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ ACCESS_CONTROL_GUIDE_URL
+ ));
+ let connections_cmd = Command::new("connections")
+ .arg(
+ Arg::new("username")
+ .short('u')
+ .long("username")
+ .required(true)
+ .help("Name of the user whose connections should be listed"),
+ )
+ .long_about("Lists client connections that authenticated with a specific username")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ CONNECTION_GUIDE_URL
+ ));
+ let limits_cmd = Command::new("limits")
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(false),
+ )
+ .long_about("Lists per-user (resource) limits")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ USER_LIMIT_GUIDE_URL
+ ));
+
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+ let delete_cmd = Command::new("delete")
+ .about("Deletes a user")
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("username")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+
+ [
+ connections_cmd,
+ declare_cmd,
+ delete_cmd,
+ limits_cmd,
+ list_cmd,
+ permissions_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn passwords_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let hash_password = Command::new("salt_and_hash")
+ .arg(
+ Arg::new("password")
+ .required(true)
+ .help("A cleartext password value to hash"),
+ )
+ .arg(
+ Arg::new("hashing_algorithm")
+ .long("hashing-algorithm")
+ .required(false)
+ .value_parser(value_parser!(HashingAlgorithm))
+ .default_value("SHA256")
+ .help("The hashing algorithm to use: SHA256 or SHA512"),
+ );
+
+ [hash_password]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn permissions_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let list_cmd = Command::new("list")
+ .long_about("Lists user permissions")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ ACCESS_CONTROL_GUIDE_URL
+ ));
+
+ let declare_cmd = Command::new("declare")
+ .about("grants permissions to a user")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ ACCESS_CONTROL_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(true),
+ )
+ .arg(
+ Arg::new("configure")
+ .long("configure")
+ .help("name pattern for configuration access")
+ .required(true),
+ )
+ .arg(
+ Arg::new("read")
+ .long("read")
+ .help("name pattern for read access")
+ .required(true),
+ )
+ .arg(
+ Arg::new("write")
+ .long("write")
+ .help("name pattern for write access")
+ .required(true),
+ );
+
+ let delete_cmd = Command::new("delete")
+ .about("Revokes user permissions to a given vhost")
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+
+ [list_cmd, declare_cmd, delete_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn user_limits_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let list_cmd = Command::new("list")
+ .long_about("Lists per-user (resource) limits")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ USER_LIMIT_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(false),
+ );
+
+ let declare_cmd = Command::new("declare")
+ .about("Set a user limit")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ USER_LIMIT_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(true),
+ )
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("value")
+ .long("value")
+ .help("limit value")
+ .required(true),
+ );
+
+ let delete_cmd = Command::new("delete")
+ .about("Clears a user limit")
+ .arg(
+ Arg::new("user")
+ .long("user")
+ .help("username")
+ .required(true),
+ )
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ );
+
+ [list_cmd, declare_cmd, delete_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn vhost_limits_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let list_cmd = Command::new("list")
+ .long_about("Lists virtual host (resource) limits")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ VIRTUAL_HOST_GUIDE_URL
+ ));
+
+ let declare_cmd = Command::new("declare")
+ .about("Set a vhost limit")
+ .after_help(color_print::cformat!(
+ "Doc guide:: {}",
+ VIRTUAL_HOST_LIMIT_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("value")
+ .long("value")
+ .help("limit value")
+ .required(true),
+ );
+
+ let delete_cmd = Command::new("delete").about("delete a vhost limit").arg(
+ Arg::new("name")
+ .long("name")
+ .help("limit name (eg. max-connections, max-queues)")
+ .required(true),
+ );
+
+ [list_cmd, declare_cmd, delete_cmd]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn publish_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ [Command::new("message")
+ .about(color_print::cstr!("Publishes (inefficiently) message(s) to a queue or a stream. Only suitable for development and test environments. Prefer messaging or streaming protocol clients!"))
+ .after_help(color_print::cformat!("Doc guide: {}", PUBLISHER_GUIDE_URL))
+ .arg(
+ Arg::new("routing_key")
+ .short('k')
+ .long("routing-key")
+ .required(false)
+ .default_value("")
+ .help("Name of virtual host"),
+ )
+ .arg(
+ Arg::new("exchange")
+ .short('e')
+ .long("exchange")
+ .required(false)
+ .default_value("")
+ .help("Exchange name (defaults to empty)"),
+ )
+ .arg(
+ Arg::new("payload")
+ .short('m')
+ .long("payload")
+ .required(false)
+ .default_value("test")
+ .help("Message payload/body"),
+ )
+ .arg(
+ Arg::new("properties")
+ .short('p')
+ .long("properties")
+ .required(false)
+ .default_value("{}")
+ .help("Message properties"),
+ )]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn get_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ [Command::new("messages")
+ .about(color_print::cstr!("Fetches (via polling, very inefficiently) message(s) from a queue. Only suitable for development and test environments"))
+ .after_help(color_print::cformat!("Doc guide: {}", POLLING_CONSUMER_GUIDE_URL))
+ .arg(
+ Arg::new("queue")
+ .short('q')
+ .long("queue")
+ .required(true)
+ .help("Target queue or stream name"),
+ )
+ .arg(
+ Arg::new("count")
+ .short('c')
+ .long("count")
+ .required(false)
+ .default_value("1")
+ .help("Maximum number of messages to consume"),
+ )
+ .arg(
+ Arg::new("ack_mode")
+ .short('a')
+ .long("ack-mode")
+ .required(false)
+ .default_value("ack_requeue_false")
+ .help("Accepted values are: ack_requeue_false, reject_requeue_false, ack_requeue_true, reject_requeue_true"),
+ )]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+pub fn shovel_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let list_all_cmd = Command::new("list_all")
+ .long_about("Lists shovels in all virtual hosts")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ SHOVEL_GUIDE_URL
+ ));
+
+ let list_cmd = Command::new("list")
+ .long_about("Lists shovels in a specific virtual host")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ SHOVEL_GUIDE_URL
+ ));
+
+ let declare_091_cmd = Command::new("declare_amqp091")
+ .long_about(
+ "Declares a dynamic shovel that uses AMQP 0-9-1 for both source and destination",
+ )
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ SHOVEL_GUIDE_URL
+ ))
+ .arg(Arg::new("name").short('n').long("name").required(true))
+ .arg(Arg::new("source_uri").long("source-uri").required(true))
+ .arg(
+ Arg::new("destination_uri")
+ .long("destination-uri")
+ .required(true),
+ )
+ .arg(
+ Arg::new("ack_mode")
+ .long("ack-mode")
+ .help("One of: on-confirm, on-publish, no-ack")
+ .default_value("on-confirm")
+ .value_parser(value_parser!(MessageTransferAcknowledgementMode)),
+ )
+ .arg(
+ Arg::new("source_queue")
+ .long("source-queue")
+ .conflicts_with("source_exchange"),
+ )
+ .arg(
+ Arg::new("source_exchange")
+ .long("source-exchange")
+ .conflicts_with("source_queue"),
+ )
+ .arg(
+ Arg::new("source_exchange_key")
+ .long("source-exchange-routing-key")
+ .conflicts_with("source_queue")
+ .requires("source_exchange"),
+ )
+ .group(
+ ArgGroup::new("source")
+ .args(["source_queue", "source_exchange"])
+ .required(true),
+ )
+ .arg(
+ Arg::new("destination_queue")
+ .long("destination-queue")
+ .conflicts_with("destination_exchange"),
+ )
+ .arg(
+ Arg::new("destination_exchange")
+ .long("destination-exchange")
+ .conflicts_with("destination_queue"),
+ )
+ .arg(
+ Arg::new("destination_exchange_key")
+ .long("destination-exchange-routing-key")
+ .conflicts_with("destination_queue"),
+ )
+ .arg(
+ Arg::new("predeclared_source")
+ .long("predeclared-source")
+ .help("The source topology will be pre-declared (should not be declared by the shovel)")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ )
+ .arg(
+ Arg::new("predeclared_destination")
+ .long("predeclared-destination")
+ .help("The destination topology will be pre-declared (should not be declared by the shovel)")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ )
+ .arg(
+ Arg::new("reconnect_delay")
+ .long("reconnect-delay")
+ .default_value("5")
+ .value_parser(value_parser!(u32)),
+ )
+ .group(
+ ArgGroup::new("destination")
+ .args(["destination_queue", "destination_exchange"])
+ .required(true),
+ )
+ .arg(
+ Arg::new("publish_properties")
+ .long("publish-properties")
+ .help("A JSON object with message properties for the Shovel to set")
+ .required(false)
+ .default_value("{}")
+ .value_parser(value_parser!(String)),
+ );
+
+ let declare_10_cmd = Command::new("declare_amqp10")
+ .long_about("Declares a dynamic shovel that uses AMQP 1.0 for both source and destination")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ SHOVEL_GUIDE_URL
+ ))
+ .arg(Arg::new("name").short('n').long("name").required(true))
+ .arg(Arg::new("source_uri").long("source-uri").required(true))
+ .arg(
+ Arg::new("destination_uri")
+ .long("destination-uri")
+ .required(true),
+ )
+ .arg(
+ Arg::new("ack_mode")
+ .long("ack-mode")
+ .help("One of: on-confirm, on-publish, no-ack")
+ .default_value("on-confirm")
+ .value_parser(value_parser!(MessageTransferAcknowledgementMode)),
+ )
+ .arg(Arg::new("source_address").long("source-address"))
+ .arg(Arg::new("destination_address").long("destination-address"))
+ .arg(
+ Arg::new("reconnect_delay")
+ .long("reconnect-delay")
+ .default_value("5")
+ .value_parser(value_parser!(u32)),
+ );
+
+ let delete_cmd = Command::new("delete")
+ .long_about("Deletes a dynamic shovel")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ SHOVEL_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("shovel name (identifier)")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+
+ let disable_tls_peer_verification_cmd = Command::new("disable_tls_peer_verification_for_all_source_uris")
+ // shorter, displayed in the shovels group's help
+ .about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all shovels."))
+ // longer, displayed in the command's help
+ .long_about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all shovels by updating their source and destination URIs' 'verify' parameter."))
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ SHOVEL_GUIDE_URL,
+ TLS_GUIDE_URL,
+ "https://www.rabbitmq.com/docs/shovel#tls-connections"
+ ));
+
+ let disable_tls_peer_verification_dest_cmd = Command::new("disable_tls_peer_verification_for_all_destination_uris")
+ .about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all shovel destination URIs."))
+ .long_about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all shovel destination URIs by updating their 'verify' parameter."))
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ SHOVEL_GUIDE_URL,
+ TLS_GUIDE_URL,
+ "https://www.rabbitmq.com/docs/shovel#tls-connections"
+ ));
+
+ let enable_tls_peer_verification_source_cmd = Command::new("enable_tls_peer_verification_for_all_source_uris")
+ .about("Enables TLS peer verification for all shovel source URIs with provided [RabbitMQ node-local] certificate paths.")
+ .long_about("Enables TLS peer verification for all shovel source URIs by updating their 'verify' parameter and adding [RabbitMQ node-local] certificate and private key file paths.")
+ .arg(
+ Arg::new("node_local_ca_certificate_bundle_path")
+ .long("node-local-ca-certificate-bundle-path")
+ .help("Path to the CA certificate bundle file on the target RabbitMQ node(s)")
+ .required(true)
+ .value_name("PATH")
+ )
+ .arg(
+ Arg::new("node_local_client_certificate_file_path")
+ .long("node-local-client-certificate-file-path")
+ .help("Path to the client certificate file on the target RabbitMQ node(s)")
+ .required(true)
+ .value_name("PATH")
+ )
+ .arg(
+ Arg::new("node_local_client_private_key_file_path")
+ .long("node-local-client-private-key-file-path")
+ .help("Path to the client private key file on the target RabbitMQ node(s)")
+ .required(true)
+ .value_name("PATH")
+ )
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ SHOVEL_GUIDE_URL,
+ TLS_GUIDE_URL,
+ "https://www.rabbitmq.com/docs/shovel#tls-connections"
+ ));
+
+ let enable_tls_peer_verification_dest_cmd = Command::new("enable_tls_peer_verification_for_all_destination_uris")
+ .about("Enables TLS peer verification for all shovel destination URIs with provided [RabbitMQ node-local] certificate paths.")
+ .long_about("Enables TLS peer verification for all shovel destination URIs by updating their 'verify' parameter and adding [RabbitMQ node-local] certificate and private key file paths.")
+ .arg(
+ Arg::new("node_local_ca_certificate_bundle_path")
+ .long("node-local-ca-certificate-bundle-path")
+ .help("Path to the CA certificate bundle file on the target RabbitMQ node(s)")
+ .required(true)
+ .value_name("PATH")
+ )
+ .arg(
+ Arg::new("node_local_client_certificate_file_path")
+ .long("node-local-client-certificate-file-path")
+ .help("Path to the client certificate file on the target RabbitMQ node(s)")
+ .required(true)
+ .value_name("PATH")
+ )
+ .arg(
+ Arg::new("node_local_client_private_key_file_path")
+ .long("node-local-client-private-key-file-path")
+ .help("Path to the client private key file on the target RabbitMQ node(s)")
+ .required(true)
+ .value_name("PATH")
+ )
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ SHOVEL_GUIDE_URL,
+ TLS_GUIDE_URL,
+ "https://www.rabbitmq.com/docs/shovel#tls-connections"
+ ));
+
+ [
+ list_all_cmd,
+ list_cmd,
+ declare_091_cmd,
+ declare_10_cmd,
+ delete_cmd,
+ disable_tls_peer_verification_cmd,
+ disable_tls_peer_verification_dest_cmd,
+ enable_tls_peer_verification_source_cmd,
+ enable_tls_peer_verification_dest_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
+}
+
+fn federation_subcommands(pre_flight_settings: PreFlightSettings) -> Vec {
+ let idempotently_arg = Arg::new("idempotently")
+ .long("idempotently")
+ .value_parser(value_parser!(bool))
+ .action(ArgAction::SetTrue)
+ .help("do not consider 404 Not Found API responses to be errors")
+ .required(false);
+
+ let list_all_upstreams = Command::new("list_all_upstreams")
+ .long_about("Lists federation upstreams in all virtual hosts")
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ FEDERATION_GUIDE_URL,
+ FEDERATED_EXCHANGES_GUIDE_URL,
+ FEDERATED_QUEUES_GUIDE_URL
+ ));
+
+ let declare_upstream = Command::new("declare_upstream")
+ .long_about("Declares a federation upstream to be used with both exchange and queue federation")
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}
+ * {}"#,
+ FEDERATION_GUIDE_URL,
+ FEDERATED_EXCHANGES_GUIDE_URL,
+ FEDERATED_QUEUES_GUIDE_URL,
+ FEDERATION_REFERENCE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("upstream name (identifier)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("uri")
+ .long("uri")
+ .help("the URI to use to connect to this upstream")
+ .required(true),
+ )
+ .arg(
+ Arg::new("reconnect_delay")
+ .long("reconnect-delay")
+ .default_value("5")
+ .value_parser(value_parser!(u32))
+ .help("Reconnection delay in seconds")
+ )
+ .arg(
+ Arg::new("trust_user_id")
+ .long("trust-user-id")
+ .default_value("true")
+ .value_parser(value_parser!(bool))
+ .help("If set to true, federation will pass through any validated user-id from the upstream, even though it cannot validate it")
+ )
+ .arg(
+ Arg::new("prefetch_count")
+ .long("prefetch-count")
+ .default_value("1000")
+ .value_parser(value_parser!(u32))
+ .help("The prefetch value to use with internal consumers")
+ .value_parser(value_parser!(u32))
+ )
+ .arg(
+ Arg::new("ack_mode")
+ .long("ack-mode")
+ .value_parser(value_parser!(MessageTransferAcknowledgementMode))
+ .help("Accepted values are: on-confirm, on-publish, no-ack")
+ .default_value("on-confirm"),
+ )
+ .arg(
+ Arg::new("queue_name")
+ .long("queue-name")
+ .help("queue federation: the queue name to use on the upstream. Defaults to the federated queue name")
+ )
+ .arg(
+ Arg::new("consumer_tag")
+ .long("consumer-tag")
+ .help("Custom consumer tag to use for the internal federation consumer")
+ .requires("queue_name")
+ )
+ .arg(
+ Arg::new("exchange_name")
+ .long("exchange-name")
+ .help("exchange federation: the exchange name to use on the upstream. Defaults to the federated exchange name")
+ )
+ .arg(
+ Arg::new("queue_type")
+ .long("queue-type")
+ .help("exchange federation: the type of the internal queue to use")
+ .default_value(DEFAULT_QUEUE_TYPE)
+ .help(color_print::cformat!("default queue type, one of: classic, quorum, stream"))
+ )
+ .arg(
+ Arg::new("max_hops")
+ .long("max_hops")
+ .default_value("1")
+ .value_parser(value_parser!(u8))
+ )
+ .arg(
+ Arg::new("bind_nowait")
+ .long("bind-using-nowait")
+ .default_value("false")
+ .value_parser(value_parser!(bool))
+ )
+ .arg(
+ Arg::new("resource_cleanup_mode")
+ .long("resource-cleanup-mode")
+ .default_value("default")
+ .value_parser(value_parser!(FederationResourceCleanupMode))
+ )
+ .arg(
+ Arg::new("channel_use_mode")
+ .long("channel-use-mode")
+ .default_value("multiple")
+ .value_parser(value_parser!(ChannelUseMode))
+ )
+ .arg(
+ Arg::new("ttl")
+ .long("ttl")
+ .long_help("exchange federation: the TTL to apply to the internal queue")
+ .value_parser(value_parser!(u32))
+ )
+ .arg(
+ Arg::new("message_ttl")
+ .long("message-ttl")
+ .long_help("exchange federation: the message TTL to use with the internal queue")
+ .value_parser(value_parser!(u32))
+ );
+
+ let declare_upstream_for_queue_federation = Command::new("declare_upstream_for_queues")
+ .long_about("Declares an upstream that will be used only for queue federation")
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ FEDERATION_GUIDE_URL,
+ FEDERATED_QUEUES_GUIDE_URL,
+ FEDERATION_REFERENCE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("upstream name (identifier)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("uri")
+ .long("uri")
+ .help("the URI to use to connect to this upstream")
+ .required(true),
+ )
+ .arg(
+ Arg::new("reconnect_delay")
+ .long("reconnect-delay")
+ .default_value("5")
+ .value_parser(value_parser!(u32))
+ .help("Reconnection delay in seconds")
+ )
+ .arg(
+ Arg::new("trust_user_id")
+ .long("trust-user-id")
+ .default_value("true")
+ .value_parser(value_parser!(bool))
+ .help("If set to true, federation will pass through any validated user-id from the upstream, even though it cannot validate it")
+ )
+ .arg(
+ Arg::new("prefetch_count")
+ .long("prefetch-count")
+ .default_value("1000")
+ .value_parser(value_parser!(u32))
+ .help("The prefetch value to use with internal consumers")
+ )
+ .arg(
+ Arg::new("ack_mode")
+ .long("ack-mode")
+ .value_parser(value_parser!(MessageTransferAcknowledgementMode))
+ .help("Accepted values are: on-confirm, on-publish, no-ack")
+ .default_value("on-confirm"),
+ )
+ .arg(
+ Arg::new("bind_nowait")
+ .long("bind-using-nowait")
+ .default_value("false")
+ .value_parser(value_parser!(bool))
+ )
+ .arg(
+ Arg::new("channel_use_mode")
+ .long("channel-use-mode")
+ .default_value("multiple")
+ .value_parser(value_parser!(ChannelUseMode))
+ )
+ .arg(
+ Arg::new("queue_name")
+ .long("queue-name")
+ .help("queue federation: the queue name to use on the upstream. Defaults to the federated queue name")
+ )
+ .arg(
+ Arg::new("consumer_tag")
+ .long("consumer-tag")
+ .help("Custom consumer tag to use for the internal federation consumer")
+ .requires("queue_name")
+ );
+
+ let declare_upstream_for_exchange_federation = Command::new("declare_upstream_for_exchanges")
+ .long_about("Declares an upstream that will be used only for exchange federation")
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ FEDERATION_GUIDE_URL,
+ FEDERATED_EXCHANGES_GUIDE_URL,
+ FEDERATION_REFERENCE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("upstream name (identifier)")
+ .required(true),
+ )
+ .arg(
+ Arg::new("uri")
+ .long("uri")
+ .help("the URI to use to connect to this upstream")
+ .required(true),
+ )
+ .arg(
+ Arg::new("reconnect_delay")
+ .long("reconnect-delay")
+ .default_value("5")
+ .value_parser(value_parser!(u32))
+ .help("Reconnection delay in seconds")
+ )
+ .arg(
+ Arg::new("trust_user_id")
+ .long("trust-user-id")
+ .default_value("true")
+ .value_parser(value_parser!(bool))
+ .help("If set to true, federation will pass through any validated user-id from the upstream, even though it cannot validate it")
+ )
+ .arg(
+ Arg::new("prefetch_count")
+ .long("prefetch-count")
+ .default_value("1000")
+ .value_parser(value_parser!(u32))
+ .help("The prefetch value to use with internal consumers")
+ )
+ .arg(
+ Arg::new("ack_mode")
+ .long("ack-mode")
+ .value_parser(value_parser!(MessageTransferAcknowledgementMode))
+ .help("Accepted values are: on-confirm, on-publish, no-ack")
+ .default_value("on-confirm"),
+ )
+ .arg(
+ Arg::new("exchange_name")
+ .long("exchange-name")
+ .help("exchange federation: the exchange name to use on the upstream. Defaults to the federated exchange name")
+ )
+ .arg(
+ Arg::new("queue_type")
+ .long("queue-type")
+ .help("exchange federation: the type of the internal queue to use")
+ .default_value(DEFAULT_QUEUE_TYPE)
+ .help(color_print::cformat!("default queue type, one of: classic, quorum, stream"))
+ )
+ .arg(
+ Arg::new("max_hops")
+ .long("max-hops")
+ .default_value("1")
+ .value_parser(value_parser!(u8))
+ )
+ .arg(
+ Arg::new("resource_cleanup_mode")
+ .long("resource-cleanup-mode")
+ .default_value("default")
+ .value_parser(value_parser!(FederationResourceCleanupMode))
+ )
+ .arg(
+ Arg::new("channel_use_mode")
+ .long("channel-use-mode")
+ .default_value("multiple")
+ .value_parser(value_parser!(ChannelUseMode))
+ )
+ .arg(
+ Arg::new("bind_nowait")
+ .long("bind-using-nowait")
+ .default_value("false")
+ .value_parser(value_parser!(bool))
+ )
+ .arg(
+ Arg::new("ttl")
+ .long("ttl")
+ .long_help("exchange federation: the TTL to apply to the internal queue")
+ .value_parser(value_parser!(u32))
+ )
+ .arg(
+ Arg::new("message_ttl")
+ .long("message-ttl")
+ .long_help("exchange federation: the message TTL to use with the internal queue")
+ .value_parser(value_parser!(u32))
+ );
+
+ let delete_upstream = Command::new("delete_upstream")
+ .long_about("Declares a federation upstream")
+ .after_help(color_print::cformat!(
+ "Doc guide: {}",
+ FEDERATION_GUIDE_URL
+ ))
+ .arg(
+ Arg::new("name")
+ .long("name")
+ .help("upstream name (identifier)")
+ .required(true),
+ )
+ .arg(idempotently_arg.clone());
+
+ let list_all_links = Command::new("list_all_links")
+ .long_about("List federation links in all virtual hosts")
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}
+ * {}"#,
+ FEDERATION_GUIDE_URL,
+ FEDERATED_EXCHANGES_GUIDE_URL,
+ FEDERATED_QUEUES_GUIDE_URL,
+ FEDERATION_REFERENCE_URL
+ ));
+
+ let disable_tls_peer_verification_cmd = Command::new("disable_tls_peer_verification_for_all_upstreams")
+ // shorter, displayed in the federation group's help
+ .about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all federation upstreams."))
+ // longer, displayed in the command's help
+ .long_about(color_print::cstr!("Use only in case of emergency. Disables TLS peer verification for all federation upstreams by updating their 'verify' parameter."))
+
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ FEDERATION_GUIDE_URL,
+ TLS_GUIDE_URL,
+ "https://www.rabbitmq.com/docs/federation#tls-connections"
+ ));
+
+ let enable_tls_peer_verification_cmd = Command::new("enable_tls_peer_verification_for_all_upstreams")
+ .about("Enables TLS peer verification for all federation upstreams with provided [RabbitMQ node-local] certificate paths.")
+ .long_about("Enables TLS peer verification for all federation upstreams by updating their 'verify' parameter and adding [RabbitMQ node-local] certificate and private key file paths.")
+ .arg(
+ Arg::new("node_local_ca_certificate_bundle_path")
+ .long("node-local-ca-certificate-bundle-path")
+ .help("Path to the CA certificate bundle file on the target RabbitMQ node(s)")
+ .required(true)
+ .value_name("PATH")
+ )
+ .arg(
+ Arg::new("node_local_client_certificate_file_path")
+ .long("node-local-client-certificate-file-path")
+ .help("Path to the client certificate file on the target RabbitMQ node(s)")
+ .required(true)
+ .value_name("PATH")
+ )
+ .arg(
+ Arg::new("node_local_client_private_key_file_path")
+ .long("node-local-client-private-key-file-path")
+ .help("Path to the client private key file on the target RabbitMQ node(s)")
+ .required(true)
+ .value_name("PATH")
+ )
+ .after_help(color_print::cformat!(
+ r#"Doc guides:
+
+ * {}
+ * {}
+ * {}"#,
+ FEDERATION_GUIDE_URL,
+ TLS_GUIDE_URL,
+ "https://www.rabbitmq.com/docs/federation#tls-connections"
+ ));
+
+ [
+ list_all_upstreams,
+ declare_upstream,
+ declare_upstream_for_exchange_federation,
+ declare_upstream_for_queue_federation,
+ delete_upstream,
+ list_all_links,
+ disable_tls_peer_verification_cmd,
+ enable_tls_peer_verification_cmd,
+ ]
+ .into_iter()
+ .map(|cmd| cmd.infer_long_args(pre_flight_settings.infer_long_options))
+ .collect()
}
diff --git a/src/commands.rs b/src/commands.rs
index 0398c39..877d5f5 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -1,4 +1,4 @@
-// Copyright (C) 2023-2024 RabbitMQ Core Team (teamrabbitmq@gmail.com)
+// Copyright (C) 2023-2025 RabbitMQ Core Team (teamrabbitmq@gmail.com)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -11,28 +11,59 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+#![allow(clippy::result_large_err)]
+
+use crate::constants::{DEFAULT_BLANKET_POLICY_PRIORITY, DEFAULT_VHOST};
+use crate::errors::CommandRunError;
+use crate::output::ProgressReporter;
+use crate::pre_flight;
use clap::ArgMatches;
+use rabbitmq_http_client::blocking_api::Client;
+use rabbitmq_http_client::blocking_api::Result as ClientResult;
use rabbitmq_http_client::commons;
-use rabbitmq_http_client::commons::UserLimitTarget;
-use rabbitmq_http_client::commons::VirtualHostLimitTarget;
-use std::fs;
-use std::process;
-
-use rabbitmq_http_client::blocking::Client;
-use rabbitmq_http_client::blocking::Result as ClientResult;
-use rabbitmq_http_client::requests::EnforcedLimitParams;
-
-use rabbitmq_http_client::commons::BindingDestinationType;
use rabbitmq_http_client::commons::QueueType;
-use rabbitmq_http_client::responses::Overview;
+use rabbitmq_http_client::commons::{
+ BindingDestinationType, ChannelUseMode, TlsPeerVerificationMode,
+};
+use rabbitmq_http_client::commons::{ExchangeType, SupportedProtocol};
+use rabbitmq_http_client::commons::{MessageTransferAcknowledgementMode, UserLimitTarget};
+use rabbitmq_http_client::commons::{PolicyTarget, VirtualHostLimitTarget};
+use rabbitmq_http_client::password_hashing::{HashingAlgorithm, HashingError};
+use rabbitmq_http_client::requests::shovels::OwnedShovelParams;
+use rabbitmq_http_client::requests::{
+ Amqp10ShovelDestinationParams, Amqp10ShovelParams, Amqp10ShovelSourceParams,
+ Amqp091ShovelDestinationParams, Amqp091ShovelParams, Amqp091ShovelSourceParams,
+ BindingDeletionParams, DEFAULT_FEDERATION_PREFETCH, EnforcedLimitParams,
+ ExchangeFederationParams, FEDERATION_UPSTREAM_COMPONENT, FederationResourceCleanupMode,
+ FederationUpstreamParams, PolicyParams, QueueFederationParams, RuntimeParameterDefinition,
+};
+
+use rabbitmq_http_client::transformers::{TransformationChain, VirtualHostTransformationChain};
use rabbitmq_http_client::{password_hashing, requests, responses};
+use regex::Regex;
+use serde::de::DeserializeOwned;
+use serde_json::Value;
+use std::fs;
+use std::io;
+use std::process;
+use tabled::Tabled;
-type APIClient<'a> = Client<&'a str, &'a str, &'a str>;
+type APIClient = Client;
-pub fn show_overview(client: APIClient) -> ClientResult {
+pub fn show_overview(client: APIClient) -> ClientResult {
client.overview()
}
+pub fn show_memory_breakdown(
+ client: APIClient,
+ command_args: &ArgMatches,
+) -> ClientResult